Files
order_site/apps/frontend/tests/consignment-match-config-and-scope.test.ts
T

127 lines
5.6 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 assert from 'node:assert/strict'
import { readFileSync } from 'node:fs'
import { fileURLToPath } from 'node:url'
import test from 'node:test'
function readSource(relativePath: string): string {
return readFileSync(fileURLToPath(new URL(relativePath, import.meta.url)), 'utf8')
}
test('寄卖配置:每个规格字段同时提供自动/手动两套匹配方式', () => {
const source = readSource('../src/pages/admin/panels/ConsignmentConfigPanel.tsx')
assert.ok(source.includes('自动匹配方式'), '必须保留自动匹配方式下拉')
assert.ok(source.includes('手动匹配方式'), '必须新增手动匹配方式下拉')
assert.ok(source.includes("manualMatchMode: 'exact'"), '新增字段与模板生成的字段默认手动精确相等')
assert.ok(
source.includes("value={spec.manualMatchMode ?? 'exact'}"),
'历史配置没有手动口径时按精确相等展示',
)
const typeSource = readSource('../src/types/consignment.ts')
assert.ok(typeSource.includes('manualMatchMode?'), '前端类型必须包含 manualMatchMode')
})
test('寄卖配置:两套匹配方式与通配值/进物品名/支持筛选必须在同一行', () => {
const source = readSource('../src/pages/admin/panels/ConsignmentConfigPanel.tsx')
const css = readSource('../src/styles/admin.css')
assert.ok(
source.includes('order-service-field-extra is-match-modes'),
'字段行必须使用 5 列栅格类',
)
assert.ok(css.includes('.order-service-field-extra.is-match-modes'))
assert.ok(
css.includes('minmax(90px, 0.72fr)'),
'手动匹配方式必须比自动匹配方式窄,避免把通配值挤没',
)
assert.ok(
css.includes('.order-service-field-extra.is-match-modes,'),
'窄屏媒体查询必须包含该 5 列布局,保证折行',
)
})
test('后端:规格字段归一化补 manualMatchMode,历史配置缺省精确相等', () => {
const source = readSource(
'../../backend/src/services/worker-platform/consignment-spec-service.ts',
)
assert.ok(source.includes('manualMatchMode?: ConsignmentSpecMatchMode'))
assert.ok(source.includes('source.manualMatchMode ?? source.manual_match_mode'))
assert.ok(
source.includes(
"return specs.map((spec) => ({ ...spec, matchMode: spec.manualMatchMode ?? 'exact' }))",
),
'手动口径必须把 matchMode 替换成 manualMatchMode(缺省 exact)',
)
})
test('后端:手动候选、手动复检、待匹配看板都使用手动口径', () => {
const engine = readSource(
'../../backend/src/services/worker-platform/consignment-match-engine-service.ts',
)
const pending = readSource(
'../../backend/src/services/worker-platform/consignment-pending-match-service.ts',
)
assert.equal(
(engine.match(/matchScope: 'manual'/g) || []).length,
2,
'手动候选与手动复检必须传 manual 口径',
)
assert.ok(engine.includes("matchScope: 'auto'"), '自动匹配候选必须显式使用 auto 口径')
assert.ok(pending.includes("matchScope: 'manual'"), '一键查询待匹配订单必须按手动口径')
})
test('内部库存匹配弹窗:默认展示全部候选,按打分降序,并显示寄卖单号', () => {
const source = readSource('../src/components/admin/ConsignmentWorkOrderMatchModal.tsx')
assert.ok(source.includes('默认按匹配打分从高到低展示全部候选'), '默认展示口径必须在界面上说明')
assert.ok(
!source.includes("if (!searchable) return (item.matchType || 'exact') === 'exact'"),
'不能再默认只过滤精确匹配',
)
assert.ok(
source.includes('computeConsignmentMatchScore(right) - computeConsignmentMatchScore(left)'),
'列表必须按匹配打分降序排列',
)
assert.ok(
source.includes('buildCandidateHaystack(item).includes(normalizedKeyword)'),
'关键词必须覆盖全部匹配状态',
)
assert.ok(source.includes("title: '寄卖单号'"), '必须新增寄卖单号列')
assert.ok(
source.includes('item.consignmentNo || item.inventoryNo'),
'寄卖单号优先展示提报单号,无提报号回退库存号',
)
assert.ok(source.includes("item.consignmentNo || ''"), '搜索必须能命中寄卖单号')
assert.ok(!source.includes('显示不匹配项'), '「显示不匹配项」勾选框必须删除')
assert.ok(!source.includes('includeConflicts'), '不再保留 includeConflicts 状态')
assert.ok(source.includes('initialInventoryIds'), '必须支持外部预选库存')
assert.ok(source.includes('if (presetInventoryKey)'), '预选库存必须优先于默认自动勾选')
})
test('手动匹配弹窗:状态不允许时仍能搜索,但禁止提交', () => {
const source = readSource('../src/components/admin/ConsignmentWorkOrderMatchModal.tsx')
assert.ok(source.includes('const matchable = data?.matchable !== false'))
assert.ok(source.includes('当前仍可搜索和核对库存'), '只读状态必须给出明确提示')
assert.ok(source.includes('disabled={!canSubmit}'))
assert.ok(
/matchable &&\s+selectedIds\.length > 0/.test(source),
'canSubmit 必须受 matchable 约束',
)
const searchIndex = source.indexOf('搜索寄卖单号 / 库存号 / 物品 / 规格 / 打手')
const optionsBranchIndex = source.indexOf('visibleOptions.length > 0 ? (')
assert.ok(searchIndex > 0)
assert.ok(
searchIndex < optionsBranchIndex,
'搜索框必须常驻在候选分支之外,空列表 / 只读状态也能使用',
)
const typeSource = readSource('../src/types/consignment.ts')
assert.ok(typeSource.includes('matchable: boolean'), '候选响应类型必须带 matchable')
assert.ok(typeSource.includes('blockedReason: string'))
})