import test from 'node:test' import assert from 'node:assert/strict' import { parseDuplicateConsole, parseLedgerPage, parseConsoleGroup, parseLedgerRow } from '../src/pages/tasks/duplicate-console-model.ts' import { indexShopGroups, dupWithinGroup, storeColumnsOf, } from '../src/pages/tasks/duplicate-console-logic.ts' function occurrence(asin: string, shop: string, group: string, country: string, date: string) { return { asin, date, price: '', brand: 'B', shop_name: shop, group_name: group, country_codes: [country], country } } /** 已解析模型形态(camel)的 occurrence,供纯逻辑派生入参。 */ function camelOcc(asin: string, shop: string, group: string, country: string, date: string) { return { asin, date, price: '', brand: 'B', shopName: shop, groupName: group, countryCodes: [country], country } } test('test_dup_console_model_parses_overview_dup_and_groups', () => { const consoleData = parseDuplicateConsole({ success: true, data: { pending: false, scanned_at: '2026-09-04 03:10:00', summary: { shop_count: 4, asin_total: 6, record_total: 10, duplicate_asin_total: 3, duplicate_shop_count: 4, site_count: 3, asin_per_shop: 2.5, source: 'job' }, shops: [ { shop_name: 'ShopA', group_name: 'GroupA', country_codes: ['UK', 'DE'], asin_count: 3, record_count: 3 }, { shop_name: 'ShopB', group_name: 'GroupA', country_codes: ['UK'], asin_count: 3, record_count: 3 }, { shop_name: 'ShopC', group_name: 'GroupC', country_codes: ['FR'], asin_count: 2, record_count: 2 }, ], dup: [ { asin: 'E0000001', shop_count: 3, record_count: 3, occurrences: [ occurrence('E0000001', 'ShopA', 'GroupA', 'UK', '2026-08-29'), occurrence('E0000001', 'ShopB', 'GroupA', 'UK', '2026-08-30'), occurrence('E0000001', 'ShopC', 'GroupC', 'FR', '2026-08-31'), ] }, ], groups: [{ name: 'GroupA', shop_count: 2, asin_unique: 4, record_count: 6, dup_count: 2 }], total_dup: 1, }, }) assert.equal(consoleData.overview.pending, false) assert.equal(consoleData.overview.summary?.asinTotal, 6) assert.equal(consoleData.overview.shops.length, 3) assert.equal(consoleData.totalDup, 1) assert.equal(consoleData.dup[0].asin, 'E0000001') assert.equal(consoleData.dup[0].occurrences.length, 3) const group = consoleData.groups[0] assert.deepEqual(group, { name: 'GroupA', shopCount: 2, asinUnique: 4, recordCount: 6, dupCount: 2 }) }) test('test_dup_console_parse_group_ignores_missing_name', () => { assert.equal(parseConsoleGroup({ shop_count: 1 }), null) assert.ok(parseConsoleGroup({ name: 'G', shop_count: 2 })) }) test('test_dup_console_group_derivation_restricts_to_members', () => { const dupA = { asin: 'A', shopCount: 2, recordCount: 2, occurrences: [ camelOcc('A', 'ShopA', 'GroupA', 'UK', '2026-08-01'), camelOcc('A', 'ShopB', 'GroupA', 'UK', '2026-08-02'), ] } const dupE = { asin: 'E', shopCount: 3, recordCount: 3, occurrences: [ camelOcc('E', 'ShopA', 'GroupA', 'UK', '2026-08-01'), camelOcc('E', 'ShopB', 'GroupA', 'UK', '2026-08-02'), camelOcc('E', 'ShopC', 'GroupC', 'FR', '2026-08-03'), ] } const groups = indexShopGroups([ { shopName: 'ShopA', groupName: 'GroupA', countryCodes: [], asinCount: 0, recordCount: 0 }, { shopName: 'ShopB', groupName: 'GroupA', countryCodes: [], asinCount: 0, recordCount: 0 }, { shopName: 'ShopC', groupName: 'GroupC', countryCodes: [], asinCount: 0, recordCount: 0 }, ]) assert.deepEqual(groups.members.GroupA, ['ShopA', 'ShopB']) assert.deepEqual(groups.shopGroupNames.ShopC, ['GroupC']) const inGroupA = dupWithinGroup([dupA, dupE], groups.members.GroupA) assert.equal(inGroupA.length, 2) // E 组内只剩 ShopA/ShopB 两条记录。 const e = inGroupA.find((item) => item.asin === 'E') assert.equal(e?.shopCount, 2) assert.equal(e?.recordCount, 2) // 单店成员组内不会命中(ShopD 不在数据里)。 assert.deepEqual(dupWithinGroup([dupA, dupE], ['ShopD']), []) }) test('test_dup_console_store_columns_order_and_restriction', () => { const dupA = { asin: 'A', shopCount: 2, recordCount: 2, occurrences: [ camelOcc('A', 'ShopB', 'GroupA', 'UK', '2026-08-01'), camelOcc('A', 'ShopA', 'GroupA', 'UK', '2026-08-02'), ] } assert.deepEqual(storeColumnsOf([dupA]), ['ShopB', 'ShopA']) assert.deepEqual(storeColumnsOf([dupA], ['ShopA', 'ShopB', 'ShopC']), ['ShopB', 'ShopA']) // 空行 + 显式成员列:仍输出成员店铺。 assert.deepEqual(storeColumnsOf([], ['ShopA', 'ShopB']), ['ShopA', 'ShopB']) }) test('test_dup_console_ledger_parse_rows_and_paging', () => { const page = parseLedgerPage({ success: true, data: { pending: false, scanned_at: '2026-09-04 03:10:00', items: [ { asin: 'E0000001', brand: 'BrandE', store_count: 3, stores: ['ShopA', 'ShopB', 'ShopC'], groups: ['GroupA', 'GroupC'], countries: ['UK', 'FR'], record_count: 3, earliest: '2026-08-29 00:00:00', latest: '2026-08-31 00:00:00', occurrences: [occurrence('E0000001', 'ShopA', 'GroupA', 'UK', '2026-08-29')] }, ], total: 1, page: 1, page_size: 200, }, }) assert.equal(page.pending, false) assert.equal(page.total, 1) assert.equal(page.pageSize, 200) const row = page.items[0] assert.equal(row.asin, 'E0000001') assert.equal(row.storeCount, 3) assert.equal(row.occurrences.length, 1) const single = parseLedgerRow({ asin: 'X0000001', store_count: 0, stores: [], groups: [], countries: [], record_count: 0, earliest: '', latest: '', occurrences: [] }) assert.equal(single?.storeCount, 0) assert.equal(parseLedgerRow({}), null) })