Files
baiyue-firstleg/scripts/admin-test-env-modules/fleet-return-redispatch-acceptance-contract.test.mjs
T

254 lines
33 KiB
JavaScript
Raw Normal View History

import test from 'node:test'
import assert from 'node:assert/strict'
import { mkdtempSync, mkdirSync, readFileSync, writeFileSync, rmSync } from 'node:fs'
import { join, resolve, dirname } from 'node:path'
import { tmpdir } from 'node:os'
import { createHash } from 'node:crypto'
import { selectReturnSources, verifyFleetReturnRedispatch, waitForRedispatchSignatureAudit } from './fleet-return-redispatch-acceptance.mjs'
2026-10-20 10:35:52 +08:00
const BASE = '/api/platform/v1/last-leg', DISPATCH = `${BASE}/dispatches`, PDA = '/api/pda/v1/last-leg/dispatches', CUSTOMER = '900719925474099301', WAREHOUSE = '92'
const copy = value => structuredClone(value)
function fixture(fault = '') {
2026-10-20 10:35:52 +08:00
const trip = { tripNo: 'TR-ORIGINAL', dispatchNo: 'DP-ORIGINAL' }, requests = [], checkpoints = [], ledger = new Map(), controls = {}
const masterIds = { A: '9007199254740993', B: '9007199254740995' }
2026-10-20 10:35:52 +08:00
const relations = ['A', 'B'].map((name, n) => ({ lastMileOrderId: masterIds[name], lastMileOrderNo: `LM-${name}`, customerId: CUSTOMER, overseasWarehouseId: WAREHOUSE,
inboundOrderId: fault === 'no-inbound' ? null : `${n + 10}`, inboundCode: `IN-${name}`, deliveryNo: 'DL-ORIGINAL', outboundNo: 'OUT-ORIGINAL',
returnSources: [{ outboundPalletItemId: `${n + 20}`, inventoryPositionId: `${n + 30}`, maxReturnPackages: n ? 3 : 4, returnCartonId: null }] }))
let returned = false, originalSigned = false, newSigned = false, departed = false, picked = false, recordVersion = 0, stage = 'BEFORE_LOADING', review = false, arranged = false, created = false, nextCreated = false, nextCancelled = false
let vehiclePlate, vehicleOwner = 'TR-NEW', driverOwner = 'TR-NEW', closed = false, vehicleReleased = false, driverReleased = false, tripVersion = 0
const rootDone = name => name === 'A' ? newSigned || fault === 'root-premature-A' && originalSigned : originalSigned
const tail = name => ({ id: masterIds[name], orderNo: `LM-${name}`, deliveryOrderNo: 'DL-ORIGINAL', outboundOrderNo: 'OUT-ORIGINAL', orderType: 'FIRST_LEG',
sourceInboundId: name === 'A' ? '801' : '802', sourceOrderId: name === 'A' ? '701' : '702', customerId: CUSTOMER, overseasWarehouseId: WAREHOUSE, inboundCode: `IN-${name}`, packageCount: name === 'A' ? 4 : 3,
subOrderStatus: name === 'A' ? newSigned || fault === 'premature-A' && originalSigned ? 'DELIVERY:DELIVERED' : 'DELIVERY:WAIT_DELIVERY' : originalSigned && fault !== 'blocked-B' ? 'DELIVERY:DELIVERED' : 'DELIVERY:WAIT_DELIVERY' })
const signedQuantity = (kind, fallback) => controls[kind]?.override ? controls[kind].value : fallback
const order = name => name === 'DL-NEW' ? { deliveryNo: name, customerId: CUSTOMER, overseasWarehouseId: WAREHOUSE, dispatchNo: 'DP-NEW', outboundNo: 'OUT-NEW', status: newSigned ? 'DELIVERED' : departed ? 'DELIVERING' : 'WAIT_DELIVERY', packageCount: 2,
signedPackages: newSigned ? signedQuantity('newQuantity', 2) : null, signedAt: newSigned ? '2026-10-19T04:00:00Z' : null, version: 0 }
: { deliveryNo: 'DL-ORIGINAL', customerId: CUSTOMER, overseasWarehouseId: WAREHOUSE, dispatchNo: 'DP-ORIGINAL', outboundNo: 'OUT-ORIGINAL', status: originalSigned ? 'DELIVERED' : controls.waitDelivery ? 'WAIT_DELIVERY' : 'DELIVERING', packageCount: 7,
signedPackages: originalSigned ? signedQuantity('originalQuantity', 5) : null, signedAt: originalSigned ? newSigned && fault === 'rewritten-original' ? 'changed' : '2026-10-19T02:00:00Z' : null, version: controls.deliveryVersion ?? 0 }
const counts = empty => ({ itemCount: empty ? 0 : 1, palletCount: empty ? 0 : 1, packageCount: empty ? 0 : 2, pickedPackageCount: empty ? 0 : picked ? 2 : 0, shippedPackageCount: empty ? 0 : departed ? 2 : 0 })
const destination = { key: 'FBA:ONT8', type: 'FBA', warehouseCode: 'ONT8' }
const stops = [{ index: 0, name: 'Actual Origin', destinationKey: 'ORIGIN:92' }, { index: 1, name: 'ONT8', destinationKey: 'FBA:ONT8' }]
const nativeItem = () => ({ dispatchItemId: '501', customerId: CUSTOMER, sourceKey: 'RETURN:300', sourceVersion: 'a'.repeat(64), sourceType: 'RETURN_RECEIPT', planPalletId: null, returnReceiptId: '300', warehousePalletId: '100', palletNo: context.palletCode,
inboundCode: 'IN-A', destination, scheduledPackages: 2, pickedPackages: picked ? 2 : 0, positions: [{ positionId: '201', availablePackages: picked ? 0 : 2 }] })
const detail = next => ({ dispatchNo: next ? 'DP-NEXT' : 'DP-NEW', recordVersion: next ? 0 : recordVersion, status: next && nextCancelled ? 'CANCELLED' : 'ACTIVE', stage: next ? 'BEFORE_LOADING' : newSigned ? 'DELIVERED' : stage,
2026-10-21 14:30:41 +08:00
documents: { deliveries: departed && !next ? [ref()] : [], outbounds: departed && !next ? [outbound()] : [] },
2026-10-20 10:35:52 +08:00
reviewState: review ? 'APPROVED' : 'DRAFT', reviews: { dispatchReview: { status: review ? 'APPROVED' : 'NOT_SUBMITTED' }, loadingReview: { status: review ? 'APPROVED' : 'NOT_SUBMITTED' } }, counts: counts(next), pickingReconciled: picked,
vehicleInfo: { vehicle: { vehicleId: '601' }, driver: { driverId: '701' } }, dispatchInfo: { route: next ? '' : 'ONT8' }, orderDetails: next ? [] : [{ customerId: CUSTOMER, sourceKey: 'RETURN:300', inboundCode: 'IN-A', destinationFbaWarehouse: 'ONT8' }] })
const ref = () => ({ groupKey: 'GROUP-A', customerId: CUSTOMER, destination, outboundNo: 'OUT-NEW', deliveryNo: 'DL-NEW', status: departed ? 'DELIVERING' : 'WAIT_DELIVERY', packageCount: 2 })
const outbound = () => ({ customerId: CUSTOMER, outboundNo: 'OUT-NEW', status: departed ? 'OUTBOUNDED' : 'WAIT_OUTBOUND', packageCount: 2 })
const command = (action, result = {}, next = false) => ({ ...detail(next), action, result, replayed: false })
const tripDetail = next => ({ trip: { tripNo: next ? 'TR-NEXT' : 'TR-NEW', warehouseId: WAREHOUSE, vehicleId: '601', driverId: '701', vehiclePlate, tripPurpose: 'DELIVERY', recordVersion: tripVersion, status: closed && !next ? 'COMPLETED' : 'PLANNED',
completedAt: closed && !next ? '2026-10-19T05:00:00Z' : null, vehicleRelease: vehicleReleased && !next ? { endLocation: 'ONT8' } : null, driverRelease: driverReleased && !next ? { endLocation: 'ONT8' } : null, stops }, dispatches: [{ dispatchNo: next ? 'DP-NEXT' : 'DP-NEW' }] })
const stockLedger = () => returned ? [{ id: '901', customerId: CUSTOMER, overseasWarehouseId: WAREHOUSE, inboundCode: 'IN-A', palletNo: context.palletCode, businessType: 'RETURN_STOCK_IN', businessOrderNo: 'EX-NEW', outboundOrderNo: 'OUT-ORIGINAL', deltaPackages: 2 }] : []
const envelope = (data, code = '200', message = '') => ({ http: 200, body: { code, message, data: copy(data) }, requestId: 'OFFLINE-REQ', traceId: 'OFFLINE-TRACE' })
const context = { marker: 'FRESH', createdResources: { fleetTrips: [trip], fleetAcceptance: { marker: 'FRESH', customerId: CUSTOMER, warehouseId: WAREHOUSE, tripNo: trip.tripNo,
2026-10-21 14:30:41 +08:00
departureDeliveries: [{ customerId: CUSTOMER, deliveryNo: 'DL-ORIGINAL' }],
departure: { dispatchNo: trip.dispatchNo, action: 'LOADING_COMPLETE', status: 'ACTIVE', stage: 'DELIVERING', result: { confirmedLoad: { manifestHash: 'ORIGINAL' } } } } },
2026-10-20 10:35:52 +08:00
assert(_m, label, _r, passed) { if (!passed) throw Error(label) }, addStep(_m, label, _r, passed) { if (!passed) throw Error(label) }, addCoverageGap() {}, apiSuccess(_m, _l, r) { return r.body?.data },
async checkpoint() { checkpoints.push(copy(trip.returnRedispatch)) },
async requestMultipart(method, path, file) {
requests.push({ method, path, file: file.name })
if (path.includes('/fleet/drivers/701/license')) return envelope({ licenseFileId: '801', recordVersion: 1 })
return envelope({ fileId: '99' })
},
2026-10-20 10:35:52 +08:00
async request(method, raw, body, token) {
requests.push({ method, path: raw, body: copy(body), token })
const path = raw.split('?')[0], params = new URLSearchParams(raw.split('?')[1]), normalized = path.replace(PDA, DISPATCH), ledgerKey = `${method}:${normalized}:${body?.idempotencyKey}`
2026-10-21 14:30:41 +08:00
if (normalized.endsWith('/loading-complete') && !token && ledger.has(ledgerKey)) return envelope(null, 'A0403', '该请求标识不属于当前操作员')
2026-10-20 10:35:52 +08:00
if (body?.idempotencyKey && ledger.has(ledgerKey) && !path.endsWith('/resources/release')) { const prior = ledger.get(ledgerKey); assert.deepEqual(body, prior.body); return envelope({ ...copy(prior.data), replayed: true }) }
const remember = data => { if (body?.idempotencyKey) ledger.set(ledgerKey, { body: copy(body), data: copy(data) }); return envelope(data) }
if (path === '/api/platform/inbounds') { const name = params.get('inboundCode').slice(-1), delivered = name === 'A' ? newSigned || fault === 'domestic-premature-A' && originalSigned : originalSigned && fault !== 'domestic-blocked-B'; return envelope({ records: [{ inboundCode: `IN-${name}`, processStatus: delivered ? 'DELIVERED' : 'LOADED' }] }) }
if (path.startsWith('/api/platform/v1/orders/first-leg/')) {
const orderId = path.split('/').at(-1), name = orderId === '701' ? 'A' : 'B', done = rootDone(name)
if (['shared-root', 'borrowed-child', 'child-premature-A', 'root-premature-with-pending'].includes(fault)) {
const isRoot = orderId === '700', complete = isRoot ? newSigned || fault === 'root-premature-with-pending' : done || name === 'A' && fault === 'child-premature-A'
return envelope({ order: { id: orderId, orderNo: isRoot ? 'FIRST-ROOT' : `FIRST-${name}`, customerId: CUSTOMER, tenantId: CUSTOMER, orderRole: isRoot ? 'MASTER' : 'CHILD',
rootOrderId: fault === 'borrowed-child' && name === 'B' && !isRoot ? '999' : '700', parentOrderId: '700', status: complete ? 'COMPLETED' : 'PROCESSING' },
statusHistory: isRoot && complete ? [{ action: 'OVERSEAS_DELIVERED' }, { action: 'FULFILLMENT_COMPLETED' }] : [] })
}
return envelope({ order: { id: orderId, orderNo: `FIRST-${name}`, customerId: CUSTOMER, tenantId: CUSTOMER, orderRole: 'MASTER', status: done ? 'COMPLETED' : 'PROCESSING' }, statusHistory: done ? [{ action: 'OVERSEAS_DELIVERED' }, { action: 'FULFILLMENT_COMPLETED' }] : [] })
}
if (path.endsWith('/fulfillment-tree')) { const orderId = path.split('/').at(-2); return envelope({ parentOrderId: orderId, parentOrderNo: orderId === '700' ? 'FIRST-ROOT' : orderId === '701' ? 'FIRST-A' : 'FIRST-B',
children: orderId === '700' ? [{ orderId: '701', orderNo: 'FIRST-A' }, { orderId: '702', orderNo: 'FIRST-B' }] : [{ orderId, orderNo: orderId === '701' ? 'FIRST-A' : 'FIRST-B' }] }) }
if (path.startsWith(`${BASE}/orders/`)) { const name = Object.keys(masterIds).find(name => masterIds[name] === path.split('/').at(-1)); assert.ok(name); return envelope({ order: tail(name) }) }
if (path.endsWith('/return-inbound-options')) return envelope(fault === 'missing-B' ? relations.slice(0, 1) : relations)
if (path.endsWith('/mark-return')) { assert.equal(body.lastMileOrderId, masterIds.A); assert.equal(Object.hasOwn(body, 'inboundOrderId'), false); assert.deepEqual(body.items, [{ outboundPalletItemId: '20', inventoryPositionId: '30', packageCount: 2 }]); return remember({ exceptionNo: 'EX-NEW', version: 0, replayed: false }) }
if (path.endsWith('/start-processing')) return remember({ status: 'PROCESSING', version: 1 })
if (path.includes('/exception-orders/') && path.endsWith('/complete')) { returned = true; if (fault === 'unknown-return') throw Error('connection lost after commit'); return remember({ status: 'COMPLETED', restoredPackages: fault === 'wrong-return' ? 3 : 2, inventoryAdjustmentId: '88', version: 2, replayed: false }) }
if (path.endsWith('/exception-orders/EX-NEW')) return envelope({ order: { exceptionNo: 'EX-NEW', customerId: CUSTOMER, outboundNo: 'OUT-ORIGINAL' }, returnReceipts: [{ receiptNo: 'RSR-300', receivedPackages: 2 }] })
if (path.endsWith('/pallets') && method === 'POST') { context.palletCode = body.palletCode; return envelope('100') }
if (path.endsWith('/pallets/100')) return envelope({ warehouseId: WAREHOUSE, locationId: '200', palletCode: context.palletCode })
if (path.endsWith('/locations')) return envelope({ records: [{ id: '200', locationCode: 'L-1' }] })
if (path.endsWith('/inventory/balances')) return envelope({ records: returned && !departed ? [{ id: '401', customerId: CUSTOMER, overseasWarehouseId: WAREHOUSE }] : [], total: returned && !departed ? 1 : 0 })
if (path.includes('/inventory/balances/') && path.endsWith('/positions')) return envelope([{ id: '201', customerId: CUSTOMER, overseasWarehouseId: WAREHOUSE, inboundCode: 'IN-A', palletId: '100', palletNo: context.palletCode, availablePackages: picked ? 0 : 2, pickedPackages: picked ? 2 : 0 }])
if (path.endsWith('/inventory/ledger')) { const rows = stockLedger(); if (fault === 'duplicate-ledger' && returned) rows.push({ ...rows[0], id: '902' }); return envelope({ records: rows, total: rows.length }) }
if (path === `${DISPATCH}/candidates`) { assert.equal(params.get('warehouseId'), WAREHOUSE); return envelope({ complete: true, snapshotAt: '2026-10-19', candidates: [{ selection: { sourceType: 'RETURN_RECEIPT', returnReceiptId: '300', planPalletId: null, relabelNo: null, warehousePalletId: '100', sourceVersion: 'a'.repeat(64) },
sourceKey: 'RETURN:300', sourceVersion: 'a'.repeat(64), customerId: CUSTOMER, warehouseId: WAREHOUSE, orderNo: 'LM-A', inboundCode: 'IN-A', palletNo: context.palletCode, destination, availablePackages: fault === 'wrong-candidate' ? 4 : 2, appointmentBatches: [], blockReasons: [] }] }) }
if (path === DISPATCH && method === 'POST') { if (!body.cargo.length) { nextCreated = true; vehicleOwner = 'TR-NEXT'; driverOwner = 'TR-NEXT'; return remember(command('CREATE', {}, true)) }; assert.equal(body.cargo[0].sourceType, 'RETURN_RECEIPT'); assert.equal(body.cargo[0].planPalletId, null); created = true; return remember(command('CREATE')) }
if (path === DISPATCH) return envelope({ records: [{ ...detail(params.get('dispatchNo') === 'DP-NEXT'), warehouseId: WAREHOUSE }] })
2026-10-21 14:30:41 +08:00
if (path.endsWith('/master-data/vehicles')) { vehiclePlate = body.plateNumber.toUpperCase(); return envelope({ id: '601' }) }
2026-10-20 10:35:52 +08:00
if (path.endsWith('/fleet/drivers') && method === 'POST') return envelope({ id: '701', recordVersion: 0 })
if (path.endsWith('/fleet/drivers/701/review')) return envelope({ reviewStatus: 'APPROVED' })
if (path === `${BASE}/fleet/trips`) { const trips = nextCreated ? [tripDetail(false).trip, tripDetail(true).trip] : [tripDetail(false).trip]; return envelope({ records: trips, page: 1, size: 200, total: trips.length }) }
if (path.endsWith('/fleet/trips/TR-NEW/completion')) return envelope({ canComplete: true, outcomes: [{}], recordVersion: tripVersion })
if (path.endsWith('/fleet/trips/TR-NEW/complete')) { closed = true; tripVersion++; return remember(tripDetail(false)) }
if (path.endsWith('/resource-occupancy')) return envelope([{ resourceId: params.get('resourceIds'), occupiedTripNo: params.get('kind') === 'VEHICLE' ? vehicleOwner : driverOwner }])
if (path.endsWith('/resources/release')) {
const prior = ledger.get(ledgerKey)
if (!prior) { if (body.resourceKind === 'VEHICLE') { vehicleReleased = true; vehicleOwner = null } else { driverReleased = true; driverOwner = null }; tripVersion++; ledger.set(ledgerKey, { body: copy(body), data: null }) }
else assert.deepEqual(prior.body, body)
return envelope(tripDetail(false))
}
if (path.endsWith('/fleet/trips/TR-NEW')) return envelope(tripDetail(false))
if (path.endsWith('/fleet/trips/TR-NEXT')) return envelope(tripDetail(true))
if (path.includes('/delivery-orders/') && path.endsWith('/documents')) {
const number = path.split('/delivery-orders/')[1].split('/')[0]
if (method === 'GET') return envelope({ version: 0, categories: [] })
if (number === 'DL-NEW') newSigned = true; else originalSigned = true
return remember({ version: 1, categories: [{ category: 'RECEIPT', status: 'UPLOADED', fileCount: 1 }], replayed: false })
}
if (path.endsWith('/status') && path.includes('/delivery-orders/')) { assert.equal(body.expectedVersion, controls.deliveryVersion ?? 0); controls.waitDelivery = false; return envelope({ deliveryStatus: 'DELIVERING' }) }
if (path.includes('/delivery-orders/')) return envelope({ order: order(path.split('/').at(-1)), operations: [{ operationNode: 'RECEIPT_AUTO_COMPLETE' }] })
const next = normalized.startsWith(`${DISPATCH}/DP-NEXT`)
if (method === 'GET' && normalized.endsWith('/items')) return envelope({ dispatchNo: next ? 'DP-NEXT' : 'DP-NEW', recordVersion: next ? 0 : recordVersion, status: 'ACTIVE', stage: next ? 'BEFORE_LOADING' : stage, items: next ? [] : [nativeItem()] })
if (method === 'GET' && normalized.endsWith('/route')) return envelope({ dispatchNo: 'DP-NEW', recordVersion, stops })
if (method === 'GET' && path.startsWith(PDA) && normalized.endsWith('/DP-NEW')) return envelope({ ...detail(false), items: [nativeItem()] })
if (method === 'GET' && normalized.endsWith('/loading')) return envelope({ ...detail(false), manifestHash: 'b'.repeat(64), loadingComplete: { allowed: true }, pallets: [{ warehousePalletId: '100', palletNo: context.palletCode, scheduledPackages: 2, pickedPackages: 2,
...(fault === 'missing-measurements' ? {} : { palletSpaces: 2, weightKg: 37, volumeCbm: 0.7, deliveryIndex: 1, evidence: [['palletSpaces', 2], ['weightKg', 37], ['volumeCbm', 0.7]].map(([field, value]) => ({ field, value, actual: true, sourceType: 'MEASURED', sourceBusinessNo: 'EX-NEW', observedAt: '2026-10-19T02:00:00Z' })) }) }] })
if (method === 'GET' && normalized.endsWith('/delivery-arrangement')) return envelope({ ...detail(false), cargoHash: 'a'.repeat(64), loadingCompletionHash: 'b'.repeat(64), arrange: { allowed: true }, groups: [{ groupKey: 'GROUP-A', customerId: CUSTOMER, destination, groupCargoHash: 'c'.repeat(64), externalFreightHash: 'd'.repeat(64), feeCoverageCargoHash: 'c'.repeat(64), blockReasons: [], requiredServices: ['DELIVERY_FEE'], cargo: [{ ...nativeItem(), packages: 2 }] }] })
if (method === 'GET' && normalized === `${DISPATCH}/${next ? 'DP-NEXT' : 'DP-NEW'}`) return envelope(detail(next))
assert.ok(created, `${method} ${raw}`)
if (normalized.endsWith('/cancel')) { nextCancelled = true; vehicleOwner = null; driverOwner = null; return remember({ ...command('CANCEL', {}, true), status: 'CANCELLED' }) }
if (method === 'PUT' && normalized.endsWith('/DP-NEW')) { if (picked) return envelope(null, 'A0409', '已扫码不能编辑'); assert.equal(body.recordVersion, recordVersion); recordVersion++; return remember(command('EDIT')) }
if (normalized.endsWith('/pick') && body.recordVersion !== recordVersion) return envelope(null, 'A0409', '公共版本已变化')
assert.equal(body.recordVersion, recordVersion, raw)
if (/\/(submit-review|dispatch-review|loading-review)$/.test(normalized)) { if (normalized.endsWith('/loading-review')) { review = true; stage = 'LOADING' }; recordVersion++; return remember(command('REVIEW')) }
if (normalized.endsWith('/pick')) { assert.equal(token, 'private-pda-token'); picked = true; recordVersion++; return remember(command('PICK', { packages: 2 })) }
2026-10-21 14:30:41 +08:00
if (normalized.endsWith('/loading-complete')) { assert.equal(picked, true); stage = 'DELIVERING'; arranged = true; departed = true; recordVersion++; return remember(command('LOADING_COMPLETE', { confirmedLoad: { manifestHash: body.manifestHash, pallets: body.measurements } })) }
2026-10-20 10:35:52 +08:00
throw Error(`Unexpected ${method} ${raw}`)
} }
const dependencies = { wait: async () => {}, async onBeforeRedispatchSignature(proof) { assert.equal(newSigned, false); assert.equal(originalSigned, true); assert.equal(proof.preSignatureGate.status, 'WAITING'); if (fault === 'audit-gate') throw Error('Audit gate not approved'); return { sourceAEventCount: 0, sourceBEventCount: 1, auditEvidenceSha256: 'a'.repeat(64) } } }
const input = { customerId: CUSTOMER, warehouseId: WAREHOUSE, pickerToken: 'private-pda-token', dispatchNos: ['DP-ORIGINAL'], deliveryNos: ['DL-ORIGINAL'] }
return { context, trip, input, dependencies, requests, checkpoints, relations, masterIds, controls }
}
const run = f => verifyFleetReturnRedispatch(f.context, 'test', f.trip, f.input, f.dependencies)
2026-10-20 10:35:52 +08:00
test('native one-main multi-source return redispatch executes real APIs through new signature and resource closure', async () => {
const f = fixture(), result = await run(f)
2026-10-20 10:35:52 +08:00
assert.equal(result.status, 'HTTP_CHAIN_VERIFIED_SOURCE_EVENT_EVIDENCE_REQUIRED'); assert.equal(result.sourceEventsVerified, false); assert.equal(result.readOnlyEvidenceRequired.length, 3)
assert.equal(result.beforeRedispatch.A.order.subOrderStatus, 'DELIVERY:WAIT_DELIVERY'); assert.equal(result.beforeRedispatch.B.order.subOrderStatus, 'DELIVERY:DELIVERED')
2026-10-21 14:30:41 +08:00
assert.equal(result.afterRedispatch.A.order.subOrderStatus, 'DELIVERY:DELIVERED'); assert.equal(result.afterRedispatch.departed.action, 'LOADING_COMPLETE'); assert.equal(result.afterRedispatch.departed.counts.shippedPackageCount, 2)
2026-10-20 10:35:52 +08:00
assert.equal(result.inventory.map(s => s.ledger.length).every((n, i) => n === (i === 0 ? 0 : 1)), true)
assert.ok(!JSON.stringify(f.checkpoints).includes('private-pda-token')); assert.ok(f.checkpoints.some(p => p.requests.at(-1)?.status === 'SENDING' && p.requests.at(-1)?.file?.sha256))
2026-10-21 14:30:41 +08:00
assert.ok(f.requests.some(r => r.path.endsWith('/loading-complete')))
assert.equal(f.requests.some(r => /\/(transfer-delivery|depart)$/.test(r.path)), false)
2026-10-20 10:35:52 +08:00
assert.equal(f.requests.some(r => r.path.includes('/pre-arrange')), false)
})
2026-10-20 10:35:52 +08:00
for (const signedPackages of [undefined, null, '5', -1, 1.5]) test(`invalid original signature quantity ${signedPackages} stops before new Create`, async () => {
const f = fixture(); f.controls.originalQuantity = { override: true, value: signedPackages }; await assert.rejects(run(f), /stored quantity/)
assert.equal(f.trip.returnRedispatch.originalSignatures.length, 0); assert.equal(f.requests.some(r => r.path === DISPATCH && r.method === 'POST'), false)
})
for (const signedPackages of [undefined, null, '2', 0, 3]) test(`new signature must contain numeric exactly two boxes: ${signedPackages}`, async () => {
const f = fixture(); f.controls.newQuantity = { override: true, value: signedPackages }; await assert.rejects(run(f), /two boxes actually signed/)
assert.equal(f.requests.some(r => r.path.endsWith('/TR-NEW/complete')), false)
})
test('actual selected master works without ordinary inbound relationship and uses immutable return receipt selector', async () => {
const f = fixture('no-inbound'); assert.equal((await run(f)).returnReceiptId, '300'); assert.ok(f.relations.every(r => r.inboundOrderId == null))
assert.equal(f.requests.filter(r => r.path.includes('/mark-return')).length, 2)
const create = f.requests.find(r => r.path === DISPATCH && r.body.cargo.length); assert.equal(create.body.cargo[0].sourceType, 'RETURN_RECEIPT'); assert.equal(create.body.cargo[0].planPalletId, null)
})
for (const fault of ['missing-master', 'wrong-delivery', 'wrong-outbound', 'foreign-customer', 'foreign-warehouse', 'borrowed-master']) test(`reject return provenance ${fault} before all writes`, async () => {
const f = fixture(), request = f.context.request
if (fault === 'missing-master') delete f.relations[0].lastMileOrderId
if (fault === 'wrong-delivery') f.relations[0].deliveryNo = 'DL-OTHER'
if (fault === 'wrong-outbound') f.relations[0].outboundNo = 'OUT-OTHER'
if (fault === 'foreign-customer') f.relations[0].customerId = '999'
if (fault === 'foreign-warehouse') f.relations[0].overseasWarehouseId = '999'
if (fault === 'borrowed-master') f.context.request = async (...args) => { const response = await request(...args); if (args[1].includes(`/orders/${f.masterIds.A}`)) response.body.data.order.id = f.masterIds.B; return response }
await assert.rejects(run(f), /real master ID|match this candidate/); assert.ok(f.requests.every(r => r.method === 'GET'))
})
for (const mutate of [source => { source.inventoryPositionId = null }, source => { source.returnCartonId = '701' }]) test('ordinary two-box return cannot borrow a source or native carton', async () => {
const f = fixture(); mutate(f.relations[0].returnSources[0]); await assert.rejects(run(f), /Selected master must contain/); assert.ok(f.requests.every(r => r.method === 'GET'))
})
for (const fault of ['missing-B', 'wrong-return', 'premature-A', 'domestic-premature-A', 'blocked-B', 'domestic-blocked-B', 'root-premature-A', 'wrong-candidate', 'rewritten-original', 'duplicate-ledger', 'missing-measurements', 'borrowed-child', 'child-premature-A', 'root-premature-with-pending']) test(`native chain rejects ${fault}`, async () => {
const f = fixture(fault); await assert.rejects(run(f)); assert.equal(f.trip.returnRedispatch.status, 'FAILED')
if (fault === 'missing-B') assert.ok(f.requests.every(r => r.method === 'GET'))
if (fault === 'missing-measurements') assert.equal(f.requests.some(r => r.path.endsWith('/loading-complete') || r.path.endsWith('/transfer-delivery')), false)
})
test('a shared formal root stays pending for A while child B completes and finishes only after new A signature', async () => {
const f = fixture('shared-root'), proof = await run(f)
assert.equal(proof.rootsBeforeRedispatch[0].root.order.status, 'PROCESSING')
assert.equal(proof.rootsBeforeRedispatch[0].children.find(c => c.id === '701').status, 'PROCESSING')
assert.equal(proof.rootsBeforeRedispatch[0].children.find(c => c.id === '702').status, 'COMPLETED')
assert.ok(proof.rootsAfterRedispatch.every(s => s.root.order.status === 'COMPLETED' && s.children.every(c => c.status === 'COMPLETED')))
})
for (const missing of [false, true]) test(`unapproved or missing readonly audit prevents new signature and closure; missing=${missing}`, async () => {
const f = fixture('audit-gate'); if (missing) delete f.dependencies.onBeforeRedispatchSignature
await assert.rejects(run(f), missing ? /audit gate is required/ : /Audit gate not approved/); assert.equal(f.trip.returnRedispatch.preSignatureGate.status, 'WAITING')
assert.equal(f.requests.some(r => r.method === 'PUT' && r.path.includes('/DL-NEW/documents')), false); assert.equal(f.requests.some(r => r.path.endsWith('/TR-NEW/complete')), false)
})
test('core producer, pick and successful signature cannot be externally substituted', async () => {
for (const name of ['prepareFleetTrip', 'confirmFleetLoading', 'pickAll', 'saveReceiptAndComplete', 'finishFleetTrip']) { const f = fixture(); f.dependencies[name] = async () => ({ success: true }); await assert.rejects(run(f), /cannot be replaced/); assert.equal(f.requests.length, 0) }
})
test('native owned departure guard rejects old status and wrong original whole before HTTP', async () => {
const f = fixture(); f.context.createdResources.fleetAcceptance.departure.status = 'IN_TRANSIT'; await assert.rejects(run(f), /owned departure/); assert.equal(f.requests.length, 0)
const other = fixture(); other.input.dispatchNos = ['BORROWED']; await assert.rejects(run(other), /owned departure/); assert.equal(other.requests.length, 0)
})
test('reviewed source selection retains started deliveries and refuses to repeat WAIT_DELIVERY start', async () => {
const f = fixture(); f.input.sourceSelectionRecovery = true; await run(f); assert.equal(f.requests.some(r => r.path.includes('/status')), false)
const waiting = fixture(); waiting.input.sourceSelectionRecovery = true; waiting.controls.waitDelivery = true; await assert.rejects(run(waiting), /never start it again/); assert.ok(waiting.requests.every(r => r.method === 'GET'))
})
test('fresh WAIT_DELIVERY start preserves exact server Long version including zero', async () => {
for (const version of [0, '9223372036854775807']) { const f = fixture(); f.controls.waitDelivery = true; f.controls.deliveryVersion = version; await run(f); const started = f.requests.find(r => r.path.includes('/status')); assert.equal(started.body.expectedVersion, version) }
})
test('fixture source A/B selection refuses borrowed or partial shipments', () => {
const row = name => ({ tail: { orderType: 'FIRST_LEG', sourceInboundId: name, inboundCode: name, packageCount: 4 }, relation: { inboundCode: name, returnSources: [{ maxReturnPackages: 3 }] } })
assert.throws(() => selectReturnSources([row('A'), row('B')]), /cover each selected source completely/)
})
test('write-ahead failure sends no HTTP and unknown return preserves original key without restart', async () => {
const disk = fixture(); disk.context.checkpoint = async () => { throw Error('disk full') }; await assert.rejects(run(disk), /disk full/); assert.equal(disk.requests.length, 0)
const f = fixture('unknown-return'); await assert.rejects(run(f), /connection lost/); const last = f.checkpoints.at(-1).requests.at(-1); assert.equal(last.status, 'UNKNOWN_RESULT'); assert.ok(last.body.idempotencyKey)
const count = f.requests.length; await assert.rejects(run(f), /no automatic restart/); assert.equal(f.requests.length, count)
})
for (const [name, response] of Object.entries({ http0: { http: 0, body: null }, server500: { http: 500, body: { code: 'B0001' }, requestId: 'r', traceId: 't' }, proxy502: { http: 502, body: null }, malformed200: { http: 200, body: { unexpected: true }, requestId: 'r', traceId: 't' }, htmlFailure: { http: 403, body: '<html>blocked</html>', requestId: 'r', traceId: 't' }, uncorrelatedSuccess: { http: 200, body: { code: '200', data: {} } } })) test(`ambiguous ${name} stops without automatic replay`, async () => {
const f = fixture(), request = f.context.request; f.context.request = async (...args) => { if (args[1].includes('/mark-return')) { f.requests.push({ method: args[0], path: args[1], body: copy(args[2]) }); return response }; return request(...args) }
await assert.rejects(run(f), /outcome unknown/); const last = f.checkpoints.at(-1).requests.at(-1); assert.equal(last.status, 'UNKNOWN_RESULT'); assert.ok(last.body.idempotencyKey)
assert.equal(f.requests.filter(r => r.path.includes('/mark-return')).length, 1); const count = f.requests.length; await assert.rejects(run(f), /no automatic restart/); assert.equal(f.requests.length, count)
})
test('unknown multipart writes retain file hash and stop before return stock receipt', async () => {
const f = fixture(); f.context.requestMultipart = async (method, path, file) => { f.requests.push({ method, path, file: file.name }); return { http: 0, body: null } }
await assert.rejects(run(f), /outcome unknown/); const entry = f.checkpoints.at(-1).requests.at(-1); assert.equal(entry.status, 'UNKNOWN_RESULT'); assert.match(entry.file.sha256, /^[a-f0-9]{64}$/)
assert.equal(f.requests.some(r => r.path.includes('/exception-orders/') && r.path.includes('/complete')), false)
})
test('correlated business conflict is retained as a definite rejection without retry', async () => {
const f = fixture(), request = f.context.request; f.context.request = async (...args) => { if (args[1].includes('/mark-return')) { f.requests.push({ method: args[0], path: args[1], body: copy(args[2]) }); return { http: 409, body: { code: 'A0409' }, requestId: 'r', traceId: 't' } }; return request(...args) }
await assert.rejects(run(f), /HTTP failed/); assert.equal(f.checkpoints.at(-1).requests.at(-1).status, 'RESPONDED'); assert.equal(f.requests.filter(r => r.path.includes('/mark-return')).length, 1)
})
for (const fault of ['none', 'nonce', 'counts', 'hash', 'trip', 'timeout']) test(`file gate requires exact checkpoint and hashed source audit: ${fault}`, async () => {
const root = mkdtempSync(join(tmpdir(), 'fleet-signature-gate-')), output = join(root, 'reports', 'run')
mkdirSync(output, { recursive: true })
const context = { marker: 'CURRENT', context: { fleetRunId: 'FRESH' }, createdResources: { fleetAcceptance: { tripNo: 'TR-ORIGINAL', customerId: 'C' } } }
const proof = { newTripNo: 'TR-NEW', sources: { A: { tail: { sourceInboundId: 'A' } }, B: { tail: { sourceInboundId: 'B' } } } }
try {
const wait = async () => {
const checkpoint = JSON.parse(readFileSync(join(output, 'before-redispatch-signature.json'), 'utf8'))
assert.equal(checkpoint.proof.newTripNo, 'TR-NEW'); assert.equal(checkpoint.sourceAInboundId, 'A')
const evidence = Buffer.from('{"A":0,"B":1}')
writeFileSync(join(root, 'reports', 'audit.json'), evidence)
writeFileSync(join(output, 'approve-before-redispatch-signature.json'), JSON.stringify({ gateNonce: fault === 'nonce' ? 'OTHER' : checkpoint.gateNonce,
marker: checkpoint.marker, newTripNo: fault === 'trip' ? 'OTHER' : checkpoint.newTripNo,
sourceAEventCount: fault === 'counts' ? 1 : 0, sourceBEventCount: 1, auditEvidencePath: 'reports/audit.json',
auditEvidenceSha256: fault === 'hash' ? 'a'.repeat(64) : createHash('sha256').update(evidence).digest('hex') }))
}
const promise = waitForRedispatchSignatureAudit(context, proof, { root, output, wait, timeoutMs: fault === 'timeout' ? 0 : 5000 })
if (fault === 'none') { const result = await promise; assert.equal(result.sourceAEventCount, 0); assert.equal(result.sourceBEventCount, 1) }
else await assert.rejects(promise, /gate|approval|SHA256/)
} finally {
assert.equal(dirname(resolve(root)), resolve(tmpdir())); assert.ok(resolve(root).startsWith(join(resolve(tmpdir()), 'fleet-signature-gate-')))
rmSync(root, { recursive: true, force: true })
}
})