This commit is contained in:
2026-09-30 15:16:39 +08:00
parent 0919a61661
commit 22584e7fde
15 changed files with 994 additions and 45 deletions
@@ -1,7 +1,8 @@
import assert from 'node:assert/strict'
import { readFileSync } from 'node:fs'
import test from 'node:test'
import { completeDropshipPalletization } from './inbound-measurement.mjs'
import { completeDropshipPalletization, verifyMeasurementSubmitReplay,
verifyMeasurementApprovalReplay } from './inbound-measurement.mjs'
const source = readFileSync(new URL('inbound-measurement.mjs', import.meta.url), 'utf8')
const read = (relativeUrl) => readFileSync(new URL(relativeUrl, import.meta.url), 'utf8')
@@ -191,3 +192,131 @@ test('pregenerated pallet regression detects duplicate replay and orphan empty p
const emptyChecks = await exerciseSystemPalletScenario('empty-pallet')
assert.ok(emptyChecks.some(check => check.name.includes('预生成托盘覆盖测量冻结的全部箱数') && !check.passed))
})
function measurementReplayContext(approved = false, mutate = () => {}, submitCode = 'A0409') {
const input = { inboundId: '9007199254740993101', tenantId: '9007199254740993102',
measurementId: '9007199254740993103' }
const state = {
measurement: { inboundId: input.inboundId, measurementId: input.measurementId, inboundCode: 'REPLAY-IN',
measurementStatus: approved ? 'APPROVED' : 'SUBMITTED', measureReviewStatus: approved ? 'APPROVED' : 'PENDING',
processStatus: approved ? 'WAIT_SPLIT' : 'MEASURE_PENDING', version: 3, inboundVersion: 4,
actualPackageCount: 4, actualGrossWeightKg: 100, actualVolumeCbm: 1,
items: [{ id: '9007199254740993104', packageCount: 4, boxCode: 'REPLAY-IN#4-1' }],
weightResults: [{ id: '9007199254740993105', fileId: '9007199254740993115', grossWeightKg: 100 }],
palletLocationGuide: { blockId: '9007199254740993106', slots: [{ reservationId: '9007199254740993107' }] } },
inbound: { summary: { id: input.inboundId, tenantId: input.tenantId, version: 4 },
statusHistory: [{ id: '9007199254740993108', action: approved ? 'MEASURE_APPROVE' : 'MEASURE_SUBMIT' }] },
reviews: [{ id: '9007199254740993109', measurementId: input.measurementId, reviewResult: 'APPROVED' }],
fee: { feeId: '9007199254740993110', feeVersion: 1, inboundId: input.inboundId,
measurementId: input.measurementId, feeStatus: 'CALCULATED', totalAmount: 77 },
details: [{ id: '9007199254740993111', sourceFeeId: '9007199254740993110', sourceFeeVersion: 1,
inboundId: input.inboundId, amount: 77 }],
}
const calls = []
const context = { marker: 'MEASUREMENT-REPLAY', token: 'memory-only-test-token',
request: async (method, requestPath, body, token, headers) => {
calls.push({ method, requestPath, body: structuredClone(body), token, headers })
if (method === 'POST') {
mutate(state)
if (requestPath.includes('/measurement/submit?')) return {
http: 200, body: { code: submitCode }, requestId: 'submit-repeat', traceId: 'trace-submit',
}
assert.match(requestPath, /\/measurements\/approve\?/)
return { http: 200, body: { code: '200', data: {
initialPricingSucceeded: true, initialFee: structuredClone(state.fee),
} }, requestId: 'approve-repeat', traceId: 'trace-approve' }
}
let data
if (requestPath.includes('/measurement?')) data = state.measurement
else if (requestPath.startsWith(`/api/platform/inbounds/${input.inboundId}?`)) data = state.inbound
else if (requestPath.includes('/measurement-review-history?')) data = {
records: state.reviews, total: state.reviews.length, page: 1, size: 200,
}
else if (requestPath.includes('/fees/latest?')) data = state.fee
else if (requestPath.includes('/fee-details?')) {
assert.equal(new URL(requestPath, 'https://example.invalid').searchParams.get('includeHistory'), 'true')
data = state.details
} else throw new Error(`unexpected ${method} ${requestPath}`)
return { http: 200, body: { code: '200', data: structuredClone(data) }, requestId: 'read', traceId: 'trace' }
},
apiSuccess: (_module, name, response) => {
assert.equal(response.http, 200, name); assert.equal(response.body.code, '200', name); return response.body.data
},
assert: (_module, name, _response, passed) => assert.ok(passed, name),
}
return { input, state, context, calls }
}
test('submitted measurement repeats the original payload, requires conflict and preserves all readable facts', async () => {
const fixture = measurementReplayContext()
const payload = { measurementId: fixture.input.measurementId, version: 2 }
const evidence = await verifyMeasurementSubmitReplay(fixture.context, fixture.input, payload)
assert.equal(evidence.rejected, true); assert.equal(evidence.unchanged, true)
assert.equal(evidence.beforeDigest, evidence.afterDigest)
assert.equal(evidence.historyCount, 1)
assert.deepEqual(fixture.calls.find(call => call.method === 'POST').body, payload)
assert.equal(fixture.calls.find(call => call.method === 'POST').headers, undefined)
assert.equal(Object.hasOwn(evidence, 'weightResults'), false)
})
for (const [name, mutate] of [
['new batch', state => { state.measurement.measurementId = '9007199254740993199' }],
['batch version', state => { state.measurement.version += 1 }],
['extra item', state => state.measurement.items.push({ id: '9007199254740993198', packageCount: 1 })],
['changed weight', state => { state.measurement.weightResults[0].grossWeightKg += 1 }],
['extra inbound history', state => state.inbound.statusHistory.push({ id: '9007199254740993197' })],
]) {
test(`duplicate measurement submission detects ${name}`, async () => {
const fixture = measurementReplayContext(false, mutate)
await assert.rejects(verifyMeasurementSubmitReplay(fixture.context, fixture.input,
{ measurementId: fixture.input.measurementId, version: 2 }), /零漂移|原入库原批次/)
})
}
test('duplicate measurement submission must not accept a successful mutation response', async () => {
const fixture = measurementReplayContext(false, () => {}, '200')
await assert.rejects(verifyMeasurementSubmitReplay(fixture.context, fixture.input,
{ measurementId: fixture.input.measurementId, version: 2 }), /再次提交明确A0409/)
})
test('approval replay uses the original key and old expectedVersion and compares all fee versions and histories', async () => {
const fixture = measurementReplayContext(true)
const payload = { measurementId: fixture.input.measurementId, expectedVersion: 3, reason: null }
const evidence = await verifyMeasurementApprovalReplay(fixture.context, fixture.input, payload, 'original-review-key')
const post = fixture.calls.find(call => call.method === 'POST')
assert.deepEqual(post.body, payload)
assert.equal(post.headers['Idempotency-Key'], 'original-review-key')
assert.equal(evidence.unchanged, true)
assert.equal(evidence.beforeDigest, evidence.afterDigest)
assert.equal(evidence.feeId, fixture.state.fee.feeId)
assert.equal(evidence.feeVersion, 1)
assert.equal(evidence.reviewIds.length, 1)
assert.equal(evidence.feeDetailIds.length, 1)
assert.equal(JSON.stringify(evidence).includes(fixture.context.token), false)
})
for (const [name, mutate] of [
['different fee ID', state => { state.fee.feeId = '9007199254740993191' }],
['extra fee version', state => { state.fee.feeVersion += 1 }],
['extra fee detail', state => state.details.push({ ...state.details[0], id: '9007199254740993192' })],
['changed historical fee detail', state => { state.details[0].amount += 1 }],
['duplicate review record', state => state.reviews.push({ ...state.reviews[0], id: '9007199254740993193' })],
['extra inbound history', state => state.inbound.statusHistory.push({ id: '9007199254740993194' })],
]) {
test(`approval replay detects ${name} despite successful response`, async () => {
const fixture = measurementReplayContext(true, mutate)
await assert.rejects(verifyMeasurementApprovalReplay(fixture.context, fixture.input,
{ measurementId: fixture.input.measurementId, expectedVersion: 3, reason: null }, 'original-review-key'),
/零新增|唯一审核及已生成初算费用事实有效/)
})
}
test('review history uses the real reviewResult field and rejects a fabricated result alias', async () => {
const fixture = measurementReplayContext(true)
fixture.state.reviews[0] = { id: '9007199254740993109', measurementId: fixture.input.measurementId, result: 'APPROVED' }
await assert.rejects(verifyMeasurementApprovalReplay(fixture.context, fixture.input,
{ measurementId: fixture.input.measurementId, expectedVersion: 3, reason: null }, 'original-review-key'),
/唯一审核及已生成初算费用事实有效/)
assert.equal(fixture.calls.some(call => call.method === 'POST'), false)
assert.match(source, /item\.reviewResult === 'APPROVED'/)
})