Files
huangzd1997 5058baffe0 task-72(ASIN 数据中心): 实现查询 ASIN 导入与轮询
POST /api/admin/query-asins/import(file+可选groupId) 启动导入添加, GET
/import/{importId} 轮询进度(QueryAsinImportProgressVo 含 deletedCount),
11 个纯逻辑契约测试覆盖启动/轮询/终态/文案。
2026-09-05 16:06:40 +08:00

122 lines
5.1 KiB
TypeScript
Raw Permalink 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 {
isAllowedQueryAsinImportFile,
parseQueryAsinImportStart,
parseQueryAsinImportProgress,
queryAsinImportFinished,
queryAsinImportSucceeded,
queryAsinImportOutcomeText,
} from '../src/pages/asin/query-asin-import-model.ts'
test('test_task_072_query_asin_import_start_normal_primary_path', () => {
// 正常主路径:导入启动结果 {importId} 解析出任务 id。
assert.equal(
parseQueryAsinImportStart({ success: true, data: { importId: 'job_qas_a1' } }),
'job_qas_a1',
)
})
test('test_task_072_query_asin_import_start_normal_variant_input', () => {
// 正常变体:已解包对象直传同样解析。
assert.equal(parseQueryAsinImportStart({ importId: 'job_qas_b2' }), 'job_qas_b2')
})
test('test_task_072_query_asin_import_start_normal_repeated_operation_is_idempotent', () => {
// 正常重复:解析纯函数稳定。
const payload = { success: true, data: { importId: 'job_qas_abc' } }
assert.equal(parseQueryAsinImportStart(payload), parseQueryAsinImportStart(payload))
})
test('test_task_072_query_asin_import_start_boundary_empty_input', () => {
// 边界空值:缺 importId → 可读错误。
assert.throws(() => parseQueryAsinImportStart({ success: true, data: {} }), /任务/)
assert.throws(() => parseQueryAsinImportStart({}), /任务/)
})
test('test_task_072_query_asin_import_start_boundary_single_item', () => {
// 边界单元素:.xlsx 文件名合法(xls 亦允许)。
assert.equal(isAllowedQueryAsinImportFile('query-asin.xlsx'), true)
assert.equal(isAllowedQueryAsinImportFile('query-asin.xls'), true)
})
test('test_task_072_query_asin_import_start_boundary_limit_or_missing_field', () => {
// 边界上限/缺字段:非 Excel 被拒、空文件名被拒。
assert.equal(isAllowedQueryAsinImportFile('data.csv'), false)
assert.equal(isAllowedQueryAsinImportFile(''), false)
assert.equal(isAllowedQueryAsinImportFile(undefined as never), false)
})
test('test_task_072_query_asin_import_start_invalid_input_rejected', () => {
// 异常输入:success=false 抛后端 message。
assert.throws(
() => parseQueryAsinImportStart({ success: false, message: '仅支持 .xlsx 或 .xls 文件' }),
/仅支持/,
)
})
test('test_task_072_query_asin_import_poll_normal_running_and_terminal', () => {
// 正常主路径:running 进度各计数可读;success 终态判定成功不再轮询。
const running = parseQueryAsinImportProgress({
success: true,
data: {
status: 'running',
totalRows: 120,
processedRows: 60,
asinCount: 58,
insertedCount: 40,
deletedCount: 0,
skippedCount: 18,
},
})
assert.equal(running.status, 'running')
assert.equal(running.processedRows, 60)
assert.equal(running.insertedCount, 40)
assert.equal(running.deletedCount, 0)
assert.equal(queryAsinImportFinished(running), false)
const done = parseQueryAsinImportProgress({ success: true, data: { status: 'success', insertedCount: 9 } })
assert.equal(done.insertedCount, 9)
assert.equal(queryAsinImportSucceeded(done), true)
assert.equal(queryAsinImportFinished(done), true)
})
test('test_task_072_query_asin_import_poll_boundary_and_failed', () => {
// 边界:空负载按 pending 不崩溃;failed 终态完成但未成功且错误可读。
const empty = parseQueryAsinImportProgress({})
assert.equal(empty.status, 'pending')
assert.equal(queryAsinImportFinished(empty), false)
assert.equal(queryAsinImportSucceeded(empty), false)
const failed = parseQueryAsinImportProgress({
success: true,
data: { status: 'failed', errorMessage: 'Excel 缺少店铺名列' },
})
assert.equal(queryAsinImportFinished(failed), true)
assert.equal(queryAsinImportSucceeded(failed), false)
assert.match(failed.errorMessage || '', /店铺名/)
})
test('test_task_072_query_asin_import_outcome_text_normal', () => {
// 结果文案:running/success/failed/pending 各给可读行。
assert.match(queryAsinImportOutcomeText({ status: 'running', processedRows: 3, totalRows: 9 }), /3\/9/)
const ok = queryAsinImportOutcomeText({ status: 'success', insertedCount: 4, skippedCount: 1 })
assert.match(ok, /4/)
assert.match(queryAsinImportOutcomeText({ status: 'failed', errorMessage: '解析失败' }), /解析失败/)
assert.match(queryAsinImportOutcomeText({ status: 'pending' }), /等待/)
})
test('test_task_072_query_asin_import_dependency_failure_returns_actionable_message', () => {
// 依赖失败/轮询走 adaptermultipart POST /import + GET /import/{importId},页面不内联。
const api = readSource('src/pages/asin/query-asin-import-api.ts')
assert.match(api, /query-asins\/import/)
assert.match(api, /http\.post/)
assert.match(api, /http\.get/)
assert.match(api, /FormData/)
assert.match(api, /startQueryAsinImport/)
assert.match(api, /fetchQueryAsinImportProgress/)
const mod = readSource('src/pages/asin/query-asin-import-model.ts')
assert.equal(/axios|http\./.test(mod), false, '查询 ASIN 导入模型保持纯逻辑')
})