c1d6ce6d20
抽屉副标题/5 国家草稿(ASIN+最低价)与变更差异(清空 ASIN→删除, 仅清价→
update)纯逻辑, 保存走 PUT/DELETE /api/admin/skip-price-asins/{id}/countries/{country},
10 个契约测试。
115 lines
5.3 KiB
TypeScript
115 lines
5.3 KiB
TypeScript
import test from 'node:test'
|
||
import assert from 'node:assert/strict'
|
||
import { readSource } from './helpers.ts'
|
||
import {
|
||
skipPriceDetailSubtitle,
|
||
skipPriceCountryDraftsOf,
|
||
diffSkipPriceCountryChanges,
|
||
normalizeSkipPriceCellPrice,
|
||
type SkipPriceCountryDraft,
|
||
} from '../src/pages/asin/skip-price-detail-model.ts'
|
||
import { toSkipPriceItem } from '../src/pages/asin/skip-price-model.ts'
|
||
|
||
test('test_task_077_skip_price_detail_subtitle_normal_primary_path', () => {
|
||
// 正常主路径:抽屉副标题 = 分组 / 店铺名。
|
||
const item = toSkipPriceItem({ id: 1, groupName: '华东组', shopName: 'BlueWave' })!
|
||
assert.equal(skipPriceDetailSubtitle(item), '华东组 / BlueWave')
|
||
})
|
||
|
||
test('test_task_077_skip_price_detail_subtitle_normal_variant_input', () => {
|
||
// 正常变体:无分组时仅店铺名。
|
||
const item = toSkipPriceItem({ id: 2, shopName: 'RedSun' })!
|
||
assert.equal(skipPriceDetailSubtitle(item), 'RedSun')
|
||
})
|
||
|
||
test('test_task_077_skip_price_detail_drafts_normal_primary', () => {
|
||
// 正常主路径:由行生成 5 国家草稿(ASIN + 最低价输入初值)。
|
||
const item = toSkipPriceItem({ id: 3, shopName: 'S', asinDe: 'B0DE1', minimumPriceDe: 12.5, asinEs: 'B0ES1' })!
|
||
const drafts = skipPriceCountryDraftsOf(item)
|
||
assert.equal(drafts.length, 5)
|
||
const de = drafts[0]
|
||
assert.equal(de.code, 'DE')
|
||
assert.equal(de.label, '德国')
|
||
assert.equal(de.asin, 'B0DE1')
|
||
assert.equal(de.price, '12.5')
|
||
assert.equal(drafts[4].code, 'ES')
|
||
assert.equal(drafts[4].asin, 'B0ES1')
|
||
assert.equal(drafts[1].price, '')
|
||
})
|
||
|
||
test('test_task_077_skip_price_detail_diff_normal_no_change', () => {
|
||
// 正常重复:ASIN 与最低价均未变(大小写/空白/数字等价归一) → 无变更。
|
||
const item = toSkipPriceItem({ id: 4, shopName: 'S', asinDe: 'B0DE1', minimumPriceDe: 12 })!
|
||
const changes = diffSkipPriceCountryChanges(item, [
|
||
{ code: 'DE', asin: ' b0de1 ', price: '12' },
|
||
{ code: 'UK', asin: '', price: '' },
|
||
])
|
||
assert.deepEqual(changes, [])
|
||
})
|
||
|
||
test('test_task_077_skip_price_detail_diff_boundary_empty_asin_means_delete', () => {
|
||
// 边界空值:当前有 ASIN 但草稿清空 → 删除该国家。
|
||
const item = toSkipPriceItem({ id: 5, shopName: 'S', asinUk: 'B0UK1', minimumPriceUk: 8, asinDe: 'B0DE1' })!
|
||
const changes = diffSkipPriceCountryChanges(item, [
|
||
{ code: 'UK', asin: '', price: '' },
|
||
{ code: 'DE', asin: 'B0DE1', price: '' },
|
||
])
|
||
assert.equal(changes.length, 1)
|
||
assert.equal(changes[0].kind, 'delete')
|
||
assert.equal(changes[0].code, 'UK')
|
||
})
|
||
|
||
test('test_task_077_skip_price_detail_diff_normal_update_asin_and_price', () => {
|
||
// 正常主路径:ASIN 改变非空 → update,ASIN 大写、最低价随行。
|
||
const item = toSkipPriceItem({ id: 6, shopName: 'S', asinFr: 'B0OLD', minimumPriceFr: 5 })!
|
||
const changes = diffSkipPriceCountryChanges(item, [{ code: 'FR', asin: ' b0new1 ', price: ' 18.5 ' }])
|
||
assert.equal(changes.length, 1)
|
||
assert.equal(changes[0].kind, 'update')
|
||
assert.equal(changes[0].asin, 'B0NEW1')
|
||
assert.equal(changes[0].minimumPrice, 18.5)
|
||
})
|
||
|
||
test('test_task_077_skip_price_detail_diff_boundary_price_cleared_keeps_asin', () => {
|
||
// 边界:ASIN 不变仅清空最低价 → update(保留 ASIN, 最低价置空)。
|
||
const item = toSkipPriceItem({ id: 7, shopName: 'S', asinEs: 'B0ES1', minimumPriceEs: 9.9 })!
|
||
const changes = diffSkipPriceCountryChanges(item, [{ code: 'ES', asin: 'B0ES1', price: '' }])
|
||
assert.equal(changes.length, 1)
|
||
assert.equal(changes[0].kind, 'update')
|
||
assert.equal(changes[0].asin, 'B0ES1')
|
||
assert.equal(changes[0].minimumPrice, null)
|
||
})
|
||
|
||
test('test_task_077_skip_price_detail_diff_invalid_input_rejected', () => {
|
||
// 异常输入:未知国家码草稿忽略;缺失草稿不产生变更;非法价格归空。
|
||
const item = toSkipPriceItem({ id: 8, shopName: 'S', asinDe: 'B0DE1', minimumPriceDe: 1 })!
|
||
const changes = diffSkipPriceCountryChanges(item, [
|
||
{ code: 'XX', asin: 'abc', price: '' },
|
||
{ code: 'DE', asin: 'B0DE1', price: '1' },
|
||
])
|
||
assert.deepEqual(changes, [])
|
||
assert.equal(normalizeSkipPriceCellPrice(''), null)
|
||
assert.equal(normalizeSkipPriceCellPrice(' 12.5 '), 12.5)
|
||
assert.equal(normalizeSkipPriceCellPrice('abc'), null)
|
||
assert.equal(normalizeSkipPriceCellPrice('-1'), null)
|
||
})
|
||
|
||
test('test_task_077_skip_price_detail_dependency_failure_returns_actionable_message', () => {
|
||
// 依赖失败/保存走 adapter:PUT/DELETE /skip-price-asins/{id}/countries/{country}。
|
||
const api = readSource('src/pages/asin/skip-price-detail-api.ts')
|
||
assert.match(api, /skip-price-asins/)
|
||
assert.match(api, /countries/)
|
||
assert.match(api, /http\.put/)
|
||
assert.match(api, /http\.delete/)
|
||
assert.match(api, /updateSkipPriceCountry|saveSkipPriceCountry/)
|
||
assert.match(api, /deleteSkipPriceCountry/)
|
||
const mod = readSource('src/pages/asin/skip-price-detail-model.ts')
|
||
assert.equal(/axios|http\./.test(mod), false, '最低价 ASIN 抽屉模型保持纯逻辑')
|
||
})
|
||
|
||
test('test_task_077_skip_price_detail_diff_boundary_no_drafts', () => {
|
||
// 边界:空草稿列表不产生变更;skip-price 行缺失字段安全。
|
||
const item = toSkipPriceItem({ id: 9, shopName: 'S' })!
|
||
assert.deepEqual(diffSkipPriceCountryChanges(item, [] as SkipPriceCountryDraft[]), [])
|
||
assert.deepEqual(diffSkipPriceCountryChanges(item, null as unknown as SkipPriceCountryDraft[]), [])
|
||
})
|