Files
crawler-plugin/admin-frontend-vue/tests/task-71.test.ts
T
huangzd1997 5af4e1c23e fix(admin-vue 查询ASIN): 列表筛选/分页参数对齐后端 snake_case,导出 XLSX 带上当前筛选
- query-asin-filter: toQueryAsinQuery 输出 page_size/group_id/shop_name(35bed42b 后端已改显式 snake,
  camel 参数被静默忽略导致分组/店铺名筛选无作用、pageSize 固定回落 15)
- 新增 query-asin-export: 按当前筛选构建导出 URL,页面「导出 XLSX」不再恒为全量
- query-asin-import-api: 导入兜底分组 groupId→group_id(后端 importExcel 已改名,此前同样被丢弃)
- query-asin-delete-import-api: 保持 groupId 并加注释(后端仍隐式 camel,统一需两端同步部署)
- task-71 测试断言更新为 snake 契约(含 camel 不下发反断言)+ 新增导出 URL 用例

已上线 admin-vue-20260910-145200,线上 chunk 验证含 snake 参数;task-71 10/10 通过,vue-tsc 通过
2026-09-10 14:53:57 +08:00

193 lines
8.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import test from 'node:test'
import assert from 'node:assert/strict'
import { readSource } from './helpers.ts'
import {
parseQueryAsinPage,
toQueryAsinItem,
asinForCountry,
queryAsinItemHasAnyAsin,
QUERY_ASIN_COUNTRIES,
} from '../src/pages/asin/query-asin-model.ts'
import {
createQueryAsinFilterState,
queryAsinFilterActive,
normalizeQueryAsinCountry,
toQueryAsinParams,
toQueryAsinQuery,
} from '../src/pages/asin/query-asin-filter.ts'
import { buildQueryAsinExportQuery, toQueryAsinExportUrl } from '../src/pages/asin/query-asin-export.ts'
test('test_task_071_query_asin_list_normal_primary_path', () => {
// 正常主路径:Java QueryAsinPageVo(camel) 宽行(每店铺 5 国家 ASIN)解析为前端分页结果。
const page = parseQueryAsinPage({
success: true,
data: {
items: [
{
id: 11,
groupId: 3,
groupName: '华东组',
shopName: 'BlueWave',
asinDe: 'B0DE001',
asinUk: 'B0UK001',
asinFr: 'B0FR001',
asinIt: 'B0IT001',
asinEs: 'B0ES001',
createdAt: '2026-01-01T10:00:00',
updatedAt: '2026-01-02T11:00:00',
},
],
total: 1,
page: 1,
pageSize: 15,
},
})
assert.equal(page.items.length, 1)
const item = page.items[0]
assert.equal(item.id, 11)
assert.equal(item.groupId, 3)
assert.equal(item.groupName, '华东组')
assert.equal(item.shopName, 'BlueWave')
assert.equal(item.asinDe, 'B0DE001')
assert.equal(item.asinUk, 'B0UK001')
assert.equal(item.asinFr, 'B0FR001')
assert.equal(item.asinIt, 'B0IT001')
assert.equal(item.asinEs, 'B0ES001')
assert.equal(item.createdAt, '2026-01-01T10:00:00')
assert.equal(page.total, 1)
assert.equal(page.pageSize, 15)
})
test('test_task_071_query_asin_list_normal_variant_input', () => {
// 正常变体:已解包 VO 直传归一;蛇形 asin_de 兜底;缺国家 ASIN/分组留空。
const page = parseQueryAsinPage({
items: [{ id: 12, shopName: 'RedSun', asin_de: 'B0DE002', asinUk: 'B0UK002' }],
total: 4,
page: 2,
pageSize: 20,
})
const item = page.items[0]
assert.equal(item.shopName, 'RedSun')
assert.equal(item.asinDe, 'B0DE002')
assert.equal(item.asinUk, 'B0UK002')
assert.equal(item.asinFr, '')
assert.equal(item.groupId, null)
assert.equal(item.groupName, '')
assert.equal(page.page, 2)
assert.equal(page.pageSize, 20)
})
test('test_task_071_query_asin_list_normal_repeated_is_idempotent', () => {
// 正常重复:解析与筛选参数生成不改输入、结果稳定。
const payload = {
data: { items: [{ id: 13, shopName: 'A', asinDe: 'X' }], total: 1, page: 1, pageSize: 15 },
}
assert.deepEqual(parseQueryAsinPage(payload), parseQueryAsinPage(payload))
const state = { groupId: null, shopName: ' 店 ', asin: '', country: '' }
const first = toQueryAsinParams(state, 1, 15)
const second = toQueryAsinParams(state, 1, 15)
assert.deepEqual(first, second)
assert.deepEqual(state, { groupId: null, shopName: ' 店 ', asin: '', country: '' }, '不修改调用方状态')
})
test('test_task_071_query_asin_list_boundary_empty_input', () => {
// 边界空值:空负载回默认分页结果(空列表/total 0/page 1/页大小 15)。
const page = parseQueryAsinPage({})
assert.deepEqual(page.items, [])
assert.equal(page.total, 0)
assert.equal(page.page, 1)
assert.equal(page.pageSize, 10)
})
test('test_task_071_query_asin_list_boundary_single_item', () => {
// 边界单元素:单条记录;缺 id 的行被过滤。
const page = parseQueryAsinPage({
data: { items: [{ id: 7, shopName: 'Z' }, { shopName: 'no-id' }], total: 2, page: 1, pageSize: 15 },
})
assert.equal(page.items.length, 1)
assert.equal(page.items[0].id, 7)
})
test('test_task_071_query_asin_list_boundary_limit_or_missing_field', () => {
// 边界上限/缺字段:页码下限 1、页大小上限 100 对齐后端;空白/非法/小于1的分组不下发。
const query = toQueryAsinQuery(toQueryAsinParams(
{ groupId: 0, shopName: ' ', asin: '', country: 'XX' },
0,
999,
))
assert.equal(query.page, 1)
assert.equal(query.page_size, 100)
assert.equal('group_id' in query, false, '分组 0 不应下发')
assert.equal('shop_name' in query, false, '空白店铺名不应下发')
assert.equal('asin' in query, false)
assert.equal('country' in query, false, '不支持的国家码不应下发')
// 正常非空值以 Java snake_case 参数名下发(QueryAsinController @RequestParam(page_size/group_id/shop_name) 契约)。
const filled = toQueryAsinQuery(toQueryAsinParams(
{ groupId: 5, shopName: ' 蓝店 ', asin: 'B0X', country: 'de' },
3,
25,
))
assert.equal(filled.page, 3)
assert.equal(filled.page_size, 25)
assert.equal(filled.group_id, 5)
assert.equal(filled.shop_name, '蓝店')
assert.equal(filled.asin, 'B0X')
assert.equal(filled.country, 'DE')
assert.equal('pageSize' in (filled as Record<string, unknown>), false, '不应下发 pageSize(camel):后端收不到会导致筛选/分页失效')
assert.equal('groupId' in (filled as Record<string, unknown>), false, '不应下发 groupId(camel)')
assert.equal('shopName' in (filled as Record<string, unknown>), false, '不应下发 shopName(camel)')
})
test('test_task_071_query_asin_country_semantics', () => {
// 国家语义:每店铺行按国家取 ASIN 列、整行是否有任一国家 ASIN;国家码大小写归一与白名单。
const item = toQueryAsinItem({ id: 21, shopName: 'S', asinDe: 'DE1', asinEs: 'ES1' })
assert.ok(item)
assert.equal(QUERY_ASIN_COUNTRIES.length, 5)
assert.equal(asinForCountry(item!, 'DE'), 'DE1')
assert.equal(asinForCountry(item!, 'UK'), '')
assert.equal(asinForCountry(item!, 'ES'), 'ES1')
assert.equal(asinForCountry(item!, 'XX'), '')
assert.equal(queryAsinItemHasAnyAsin(item!), true)
const empty = toQueryAsinItem({ id: 22, shopName: 'E' })
assert.equal(queryAsinItemHasAnyAsin(empty!), false)
assert.equal(normalizeQueryAsinCountry('de'), 'DE')
assert.equal(normalizeQueryAsinCountry('UK'), 'UK')
assert.equal(normalizeQueryAsinCountry(''), '')
assert.equal(normalizeQueryAsinCountry('CN'), '')
assert.equal(queryAsinFilterActive({ groupId: null, shopName: '', asin: '', country: '' }), false)
assert.equal(queryAsinFilterActive({ groupId: null, shopName: ' 店 ', asin: '', country: '' }), true)
assert.equal(queryAsinFilterActive({ groupId: 2, shopName: '', asin: '', country: '' }), true)
})
test('test_task_071_query_asin_list_invalid_input_rejected', () => {
// 异常输入:success=false 抛后端 message;非对象/非数字 id 行不入列表;非法国家被归一丢弃。
assert.throws(() => parseQueryAsinPage({ success: false, message: '无权访问查询 ASIN 数据' }), /无权访问/)
assert.equal(toQueryAsinItem('garbage'), null)
assert.equal(toQueryAsinItem({ shopName: 'no id' }), null)
assert.deepEqual(createQueryAsinFilterState(), { groupId: null, shopName: '', asin: '', country: '' })
})
test('test_task_071_query_asin_list_dependency_failure_returns_actionable_message', () => {
// 依赖失败/加载走 adapterGET /api/admin/query-asins + http.get + 解析,页面不内联。
const api = readSource('src/pages/asin/query-asin-api.ts')
assert.match(api, /\/api\/admin\/query-asins/)
assert.match(api, /http\.get/)
assert.match(api, /parseQueryAsinPage/)
assert.match(api, /toQueryAsinQuery/)
const model = readSource('src/pages/asin/query-asin-model.ts')
assert.equal(/axios|http\./.test(model), false, '查询 ASIN 列表解析保持纯逻辑')
})
test('test_task_071_query_asin_export_url_follows_filter', () => {
// 导出 URL 跟随当前筛选:非空条件以 snake 参数下发(后端 export @RequestParam 契约),空白/非法条件与无筛选时退回全量。
const url = toQueryAsinExportUrl({ groupId: 5, shopName: ' 蓝店 ', asin: ' B0X ', country: 'de' })
assert.equal(url.startsWith('/api/admin/query-asins/export?'), true)
const params = new URLSearchParams(url.split('?')[1])
assert.equal(params.get('group_id'), '5')
assert.equal(params.get('shop_name'), '蓝店')
assert.equal(params.get('asin'), 'B0X')
assert.equal(params.get('country'), 'DE')
assert.equal(buildQueryAsinExportQuery({ groupId: 0, shopName: ' ', asin: '', country: 'XX' }), '', '空白/非法条件不下发')
assert.equal(toQueryAsinExportUrl({ groupId: null, shopName: '', asin: '', country: '' }), '/api/admin/query-asins/export', '无筛选即全量导出')
})