Files
order_site/apps/frontend/tests/consignment-match-coverage.test.ts
T

159 lines
5.2 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'
import {
computeConsignmentMatchScore,
describeConsignmentMatch,
resolveMatchCoverage,
} from '../src/utils/consignment-match-coverage.ts'
test('匹配打分:与仓库列表同口径(精确 100 / 高配 85 / 未知 50 / 冲突 0)', () => {
assert.equal(computeConsignmentMatchScore({ matchType: 'exact', matchDetails: [] }), 100)
assert.equal(computeConsignmentMatchScore({ matchType: 'partial', matchDetails: [] }), 85)
assert.equal(computeConsignmentMatchScore({ matchType: 'unknown', matchDetails: [] }), 50)
assert.equal(computeConsignmentMatchScore({ matchType: 'conflict', matchDetails: [] }), 0)
assert.equal(
computeConsignmentMatchScore({
matchType: 'partial',
matchDetails: [
{
key: 'grade',
label: '等级',
orderValue: 'A',
inventoryValue: 'S',
status: 'match',
},
{
key: 'series',
label: '系列',
orderValue: 'A',
inventoryValue: 'S',
status: 'partial',
},
],
}),
93,
)
})
test('匹配说明:精确/非精确/冲突都必须有可读文案', () => {
assert.equal(describeConsignmentMatch({ matchType: 'exact' }), '规格完全一致')
assert.equal(
describeConsignmentMatch({ matchType: 'partial', matchReasons: [] }),
'规格高于要求(非精确)',
)
assert.equal(
describeConsignmentMatch({
matchType: 'conflict',
matchReasons: ['系统不一致', '区服不一致'],
}),
'不匹配:系统不一致;区服不一致',
)
})
test('拼单覆盖:1+1 不够 3 份,2+1 才允许提交', () => {
const short = resolveMatchCoverage({
sharingEnabled: true,
totalQuantity: 3,
activeQuantity: 0,
selected: [
{ inventoryId: 1, remainingQuantity: 1 },
{ inventoryId: 2, remainingQuantity: 1 },
],
})
assert.equal(short.requiredQuantity, 3)
assert.equal(short.selectedQuantity, 2)
assert.equal(short.sufficient, false)
assert.equal(short.gap, 1)
const enough = resolveMatchCoverage({
sharingEnabled: true,
totalQuantity: 3,
activeQuantity: 0,
selected: [
{ inventoryId: 1, remainingQuantity: 2 },
{ inventoryId: 2, remainingQuantity: 1 },
],
})
assert.equal(enough.selectedQuantity, 3)
assert.equal(enough.sufficient, true)
assert.equal(enough.gap, 0)
})
test('拼单覆盖:已匹配 1 份时只需再选 2 份', () => {
const result = resolveMatchCoverage({
sharingEnabled: true,
totalQuantity: 3,
activeQuantity: 1,
selected: [{ inventoryId: 1, remainingQuantity: 2 }],
})
assert.equal(result.requiredQuantity, 2)
assert.equal(result.sufficient, true)
})
test('整单覆盖:必须一条库存满足全部数量', () => {
const short = resolveMatchCoverage({
sharingEnabled: false,
totalQuantity: 3,
activeQuantity: 0,
selected: [{ inventoryId: 1, remainingQuantity: 2 }],
})
assert.equal(short.requiredQuantity, 3)
assert.equal(short.selectedQuantity, 2)
assert.equal(short.sufficient, false)
assert.equal(short.gap, 1)
const enough = resolveMatchCoverage({
sharingEnabled: false,
totalQuantity: 3,
activeQuantity: 0,
selected: [{ inventoryId: 1, remainingQuantity: 3 }],
})
assert.equal(enough.sufficient, true)
})
test('工单页:internal_matching 显示「手动匹配寄卖」入口', () => {
const source = readFileSync(
fileURLToPath(new URL('../src/pages/admin/panels/WorkOrdersPanel.tsx', import.meta.url)),
'utf8',
)
assert.ok(source.includes('手动匹配寄卖'), '内部匹配中的按钮文案必须是手动匹配寄卖')
assert.ok(
source.includes("String(row.status || '') === 'internal_matching'"),
'按钮文案必须只在 internal_matching 下切换',
)
})
test('工单页:手动匹配寄卖成功后必须按真实查询键刷新列表与库存', () => {
const source = readFileSync(
fileURLToPath(new URL('../src/pages/admin/panels/WorkOrdersPanel.tsx', import.meta.url)),
'utf8',
)
const modalIndex = source.indexOf('<ConsignmentWorkOrderMatchModal')
assert.ok(modalIndex > 0, '工单页必须挂载手动匹配寄卖弹窗')
const matchedIndex = source.indexOf('onMatched={() => {', modalIndex)
const matchedBlock = source.slice(matchedIndex, source.indexOf('}}', matchedIndex) + 2)
assert.ok(matchedBlock.includes('refreshAll()'), '匹配成功必须调用 refreshAll() 重取列表')
assert.ok(
!matchedBlock.includes("'admin-work-orders'") &&
!matchedBlock.includes("'admin-work-orders-stats'"),
'不得 invalidate 不存在的 admin-work-orders / admin-work-orders-stats 查询键',
)
const refreshIndex = source.indexOf('async function refreshAll()')
const refreshBlock = source.slice(
refreshIndex,
source.indexOf('async function runAction', refreshIndex),
)
for (const key of [
'admin-worker-platform-orders',
'admin-worker-platform-summary',
'admin-consignment-inventories',
'admin-consignment-stats',
]) {
assert.ok(refreshBlock.includes(key), `refreshAll() 必须刷新 ${key}`)
}
})