61b2d7152d
新增 duplicate-model.ts(总览/店铺分布/明细项/出现记录 snake 解析) 与 duplicate-api.ts(fetchDuplicateOverview GET /duplicate-check-overview)。 TDD: task-115.test.ts 8 用例先 RED 后 GREEN。
90 lines
3.7 KiB
TypeScript
90 lines
3.7 KiB
TypeScript
import test from 'node:test'
|
||
import assert from 'node:assert/strict'
|
||
import { readSource } from './helpers.ts'
|
||
import { parseDuplicateOverview, parseDuplicateShop } from '../src/pages/tasks/duplicate-model.ts'
|
||
|
||
test('test_task_115_duplicate_overview_normal_primary_path', () => {
|
||
// 正常主路径:撞款总览(snake summary + shops)解析为类型化指标。
|
||
const overview = parseDuplicateOverview({
|
||
success: true,
|
||
data: {
|
||
pending: false,
|
||
scanned_at: '2026-01-02 10:00:00',
|
||
summary: {
|
||
shop_count: 8,
|
||
asin_total: 1200,
|
||
record_total: 3400,
|
||
duplicate_asin_total: 45,
|
||
duplicate_shop_count: 6,
|
||
site_count: 5,
|
||
asin_per_shop: 150,
|
||
source: 'Excel',
|
||
},
|
||
shops: [
|
||
{ shop_name: 'BlueWave', group_name: '华东组', country_codes: ['DE', 'UK'], asin_count: 30, record_count: 90 },
|
||
],
|
||
},
|
||
})
|
||
assert.equal(overview.pending, false)
|
||
assert.equal(overview.summary!.asinTotal, 1200)
|
||
assert.equal(overview.summary!.duplicateAsinTotal, 45)
|
||
assert.equal(overview.summary!.duplicateShopCount, 6)
|
||
assert.equal(overview.summary!.siteCount, 5)
|
||
assert.equal(overview.shops.length, 1)
|
||
assert.equal(overview.shops[0].shopName, 'BlueWave')
|
||
assert.deepEqual(overview.shops[0].countryCodes, ['DE', 'UK'])
|
||
})
|
||
|
||
test('test_task_115_duplicate_overview_normal_variant_input', () => {
|
||
// 正常变体:无扫描结果 pending 空态。
|
||
const overview = parseDuplicateOverview({ data: { pending: true, scanned_at: '', summary: {}, shops: [] } })
|
||
assert.equal(overview.pending, true)
|
||
assert.equal(overview.summary, null)
|
||
assert.deepEqual(overview.shops, [])
|
||
})
|
||
|
||
test('test_task_115_duplicate_overview_repeated_is_idempotent', () => {
|
||
// 正常重复:解析稳定、不改输入。
|
||
const payload = { data: { pending: false, summary: { asin_total: 3 }, shops: [] } }
|
||
assert.deepEqual(parseDuplicateOverview(payload), parseDuplicateOverview(payload))
|
||
})
|
||
|
||
test('test_task_115_duplicate_overview_boundary_empty_input', () => {
|
||
// 边界空值:空负载回默认空态。
|
||
const overview = parseDuplicateOverview({})
|
||
assert.equal(overview.pending, true)
|
||
assert.equal(overview.summary, null)
|
||
assert.deepEqual(overview.shops, [])
|
||
})
|
||
|
||
test('test_task_115_duplicate_overview_boundary_single_item', () => {
|
||
// 边界单元素:单店铺分布解析完整。
|
||
const shop = parseDuplicateShop({ shop_name: 'S', group_name: 'G', country_codes: ['DE'], asin_count: 1, record_count: 2 })
|
||
assert.ok(shop)
|
||
assert.equal(shop.asinCount, 1)
|
||
})
|
||
|
||
test('test_task_115_duplicate_overview_boundary_limit_or_missing_field', () => {
|
||
// 边界上限/缺字段:缺省数值回 0;缺国家回空数组。
|
||
const shop = parseDuplicateShop({ shop_name: 'S' })
|
||
assert.ok(shop)
|
||
assert.equal(shop.asinCount, 0)
|
||
assert.deepEqual(shop.countryCodes, [])
|
||
assert.equal(shop.groupName, '')
|
||
})
|
||
|
||
test('test_task_115_duplicate_overview_invalid_input_rejected', () => {
|
||
// 异常输入:success=false 抛后端 message;非对象店铺行忽略。
|
||
assert.throws(() => parseDuplicateOverview({ success: false, message: '无权访问重复检查' }), /无权访问/)
|
||
assert.equal(parseDuplicateShop('garbage'), null)
|
||
})
|
||
|
||
test('test_task_115_duplicate_overview_dependency_failure_returns_actionable_message', () => {
|
||
// 依赖失败/加载走 adapter:GET /api/admin/shop-data-crawl/duplicate-check-overview。
|
||
const api = readSource('src/pages/tasks/duplicate-api.ts')
|
||
assert.match(api, /\/duplicate-check-overview/)
|
||
assert.match(api, /fetchDuplicateOverview/)
|
||
const model = readSource('src/pages/tasks/duplicate-model.ts')
|
||
assert.equal(/axios|http\./.test(model), false, '总览解析保持纯逻辑')
|
||
})
|