优化
This commit is contained in:
@@ -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'/)
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user