feat(firstleg): implement container customs workflow
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import { readFileSync } from 'node:fs'
|
||||
import test from 'node:test'
|
||||
|
||||
import {
|
||||
CONTAINER_CUSTOMS_FIXTURE_KINDS,
|
||||
CONTAINER_CUSTOMS_MATRIX,
|
||||
EXACT_50_MB,
|
||||
READ_ONLY_DB_EVIDENCE,
|
||||
REQUIRED_ROUTE_OPERATIONS,
|
||||
validateContainerCustomsFixtures,
|
||||
validateRouteContract,
|
||||
xlsxMagic,
|
||||
zeroDrift,
|
||||
} from './container-customs-acceptance-matrix.mjs'
|
||||
|
||||
const source = readFileSync(new URL('./phase3-container-customs.mjs', import.meta.url), 'utf8')
|
||||
|
||||
test('matrix covers three isolated LOADED container kinds', () => {
|
||||
assert.deepEqual(CONTAINER_CUSTOMS_FIXTURE_KINDS,
|
||||
['ALL_BUY_DECLARATION', 'ALL_TAX_REFUND', 'MIXED'])
|
||||
const fixtures = { marker: 'M', containers: CONTAINER_CUSTOMS_FIXTURE_KINDS.map((kind, index) => ({
|
||||
marker: 'M', kind, status: 'LOADED', loadingPlanId: String(100 + index),
|
||||
containerBookingId: String(200 + index),
|
||||
inbounds: [{ inboundId: String(300 + index), customerId: String(400 + index),
|
||||
taxMode: kind === 'ALL_BUY_DECLARATION' ? 'BUY_DECLARATION' : 'TAX_REFUND' }],
|
||||
})) }
|
||||
assert.equal(validateContainerCustomsFixtures(fixtures, 'M'), true)
|
||||
assert.equal(validateContainerCustomsFixtures({ ...fixtures, marker: 'OLD' }, 'M'), false)
|
||||
})
|
||||
|
||||
test('coverage matrix freezes every requested product boundary', () => {
|
||||
const text = CONTAINER_CUSTOMS_MATRIX.map((item) => `${item.scenario}:${item.expected}`).join('\n')
|
||||
for (const token of ['买单XLSX', '多对多', '覆盖', '改关联', '删除', '冻结', '逐份完成文件',
|
||||
'最后一份', '混合', '缺资料', '开始后锁定', '客户隔离', '买单隐藏', '50MiB', '任意格式',
|
||||
'幂等', '并发', 'A0409', '零漂移', '自动离仓', 'Outbox', '历史', 'Admin与Client权限']) {
|
||||
assert.match(text, new RegExp(token))
|
||||
}
|
||||
})
|
||||
|
||||
test('route adapter fails closed until final backend and frontend contracts align', () => {
|
||||
assert.equal(validateRouteContract({}), false)
|
||||
const routes = { version: 'final', ...Object.fromEntries(REQUIRED_ROUTE_OPERATIONS.map((name) => [name, () => null])) }
|
||||
assert.equal(validateRouteContract(routes), true)
|
||||
assert.match(source, /后端Controller\/OpenAPI与Admin\/Client最终路径尚未对齐,禁止猜测URL发请求/)
|
||||
assert.doesNotMatch(source, /https?:\/\//)
|
||||
})
|
||||
|
||||
test('HTTP scenario contains file, state, idempotency, concurrency and zero-drift probes', () => {
|
||||
for (const token of ['EXACT_50_MB + 1', '.customblob', 'application/octet-stream',
|
||||
'缺退税资料禁止开始且零漂移', '开始后资料范围锁定A0409零漂移',
|
||||
"['上传'", "['覆盖'", "['改关联'", "['删除'", '开始后${label}锁定A0409零漂移',
|
||||
'最后一份完成文件前禁止整柜完成', '最后一份完成文件后纯退税柜完成',
|
||||
'自动离仓失败不回滚报关完成', '自动或待条件重试后整柜单次离仓',
|
||||
'同幂等键异载荷A0409', 'Promise.all', 'requestId=', 'traceId=', 'xlsxMagic']) {
|
||||
assert.match(source, new RegExp(token.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')))
|
||||
}
|
||||
assert.equal(EXACT_50_MB, 52_428_800)
|
||||
assert.equal(zeroDrift({ a: 1 }, { a: 1 }), true)
|
||||
assert.equal(xlsxMagic(Uint8Array.from([0x50, 0x4b, 0x03, 0x04])), true)
|
||||
})
|
||||
|
||||
test('database evidence list covers atomicity, history, dispatch and outbox without embedding SQL writes', () => {
|
||||
for (const table of ['fl_customs_container', 'fl_customs_customer_document_inbound',
|
||||
'fl_customs_completion_document', 'fl_customs_command', 'fl_customs_container_history',
|
||||
'fl_outbound_dispatch', 'fl_event_outbox']) assert.ok(READ_ONLY_DB_EVIDENCE.includes(table), table)
|
||||
assert.doesNotMatch(source, /\b(?:INSERT|UPDATE|DELETE)\s+(?:INTO|FROM|[a-z_])/i)
|
||||
})
|
||||
Reference in New Issue
Block a user