Files
baiyue-firstleg/scripts/admin-test-env-modules/phase5-root-fulfillment-contract.test.mjs

717 lines
50 KiB
JavaScript

import assert from 'node:assert/strict'
import test from 'node:test'
import { readFileSync } from 'node:fs'
import { fulfillmentHistorySnapshot, validTwoFragmentTree, validSignatureGate,
completionCounts, exactCompletionOutboxProof, rootCompletionTimeMatches, resolveF40MerchantCredentials,
f40CreateDispatchBody, resolveF40LoadingMeasurements, sendFragment, f40LocalDateTime, validFragmentCabinetIdentity, validUnloadedRemainder,
validRemainderCancellationGate, exactCancelledRemainderOutboxProof, cancelUnloadedRemainder,
verifyRootProjections, verifyRemainderTaxIdentity, validFreshReceiptSave, preserveReceivingEvidence,
resolveF40ReceivingWarehouse, run } from './phase5-root-fulfillment.mjs'
import { loadOneFragment } from './phase3-order-splitting.mjs'
import { verifyPalletLabels } from './phase3-loading-finalize.mjs'
import { awaitProducerConsumption, convertReceivingContainers, runReceiving,
resolveSharedWarehouse } from './phase4-overseas-pda.mjs'
const source = readFileSync(new URL('./phase5-root-fulfillment.mjs', import.meta.url),'utf8')
test('根完成UTC时刻与上海本地签收最大值一致,拒绝时区偏移、较早来源和缺失证据', () => {
const times = ['2026-10-05T21:55:00.000', '2026-10-05T21:56:07.922']
assert.equal(rootCompletionTimeMatches('2026-10-05T13:56:07.922', times), true)
assert.equal(rootCompletionTimeMatches('2026-10-05T13:56:07.922Z', [...times].reverse()), true)
assert.equal(rootCompletionTimeMatches('2026-10-05T13:56:07.922Z', ['2026-10-05T21:56:07.922+08:00']), true)
for (const value of ['2026-10-05T21:56:07.922', '2026-10-05T13:55:00', null]) {
assert.equal(rootCompletionTimeMatches(value, times), false)
}
assert.equal(rootCompletionTimeMatches('2026-10-05T13:56:07.922', []), false)
assert.equal(rootCompletionTimeMatches('2026-10-05T13:56:07.922', ['invalid']), false)
})
const history = (...actions) => actions.map((action,index) => ({id:String(index+1),action}))
const tree = () => ({parentOrderId:'100',originalPackageCount:2,cumulativeLoadedPackageCount:2,
currentRemainderPackageCount:0,children:[
{orderId:'100',sourceOrderId:'100',splitRole:'LOADED',packageCount:1},
{orderId:'101',sourceOrderId:'100',splitRole:'REMAINDER',packageCount:1},
{orderId:'101',sourceOrderId:'101',splitRole:'LOADED',packageCount:1},
]})
const signed = () => ({root:{status:'COMPLETED',histories:history('OVERSEAS_DELIVERED','FULFILLMENT_COMPLETED')},
child:{status:'COMPLETED',histories:history('OVERSEAS_DELIVERED')},
rootInbound:{processStatus:'DELIVERED',orderStatusSnapshot:'COMPLETED'}})
const outbox = () => ({readOnly:true,customerId:'90',rootOrderId:'100',orders:['100','101'].map(orderId => ({
orderId,customerId:'90',rootOrderId:'100',eventType:'FIRST_LEG_ORDER_FULFILLMENT_COMPLETED',
count:1,publishedCount:1,pendingCount:0,failedCount:0,
}))})
const fixture = () => ({steps:[],token:'offline',marker:'offline-only',createdResources:{},
addStep(_module,_label,_response,passed){this.steps.push({passed})},
assert(_module,_label,_response,passed){this.steps.push({passed})},
addCoverageGap(){},apiSuccess(_module,_label,response){return response?.body?.data},
})
function remainderTaxFixture(taxMode) {
const context=fixture(),calls=[]
const evidence={taxMode,customerId:'90',rootOrderId:'100',childOrderId:'101',rootOrderNo:'ORDER-ROOT',
rawChild:{id:'101',tenantId:'90',taxMode},rootInbound:{orderId:'100',taxMode},
childInbound:{orderId:'101',tenantId:'90',taxMode}}
const rows=[{orderId:'100',tenantId:'90',taxMode,relation:{role:'PARENT',rootOrderId:'100'}},
{orderId:'101',tenantId:'90',taxMode,relation:{role:'CHILD',rootOrderId:'100',parentOrderId:'100'}}]
context.request=async(method,path,body,token)=>{
calls.push({method,path,body,token})
const filter=new URL(path,'https://offline.invalid').searchParams.get('taxMode')
const records=filter&&filter!==taxMode?[]:structuredClone(rows)
return {http:200,body:{code:'200',data:{records,total:records.length}}}
}
return {context,evidence,calls,rows}
}
test('raw remainder and actual inbound summary taxMode agree for BUY and TAX and customer filters retain exact parent/child',async()=>{
for(const mode of ['BUY_DECLARATION','TAX_REFUND']){
const f=remainderTaxFixture(mode)
await verifyRemainderTaxIdentity(f.context,f.evidence,'own-customer')
assert.equal(f.calls.length,3)
assert.ok(f.calls.every(c=>c.method==='GET'&&c.token==='own-customer'&&!c.path.includes('/customs')))
assert.deepEqual(f.evidence.matchingTax.records.map(r=>r.orderId),['100','101'])
assert.equal(f.evidence.otherTax.total,0)
assert.match(f.calls[1].path,new RegExp('taxMode='+mode))
}
})
test('missing or wrong raw remainder taxMode is rejected before customer/customs calls despite valid inbound projection',async()=>{
for(const mode of ['BUY_DECLARATION','TAX_REFUND'])for(const actual of [null,undefined,mode==='BUY_DECLARATION'?'TAX_REFUND':'BUY_DECLARATION']){
const f=remainderTaxFixture(mode);f.evidence.rawChild.taxMode=actual
await assert.rejects(verifyRemainderTaxIdentity(f.context,f.evidence,'own-customer'),/原始余量子订单继承/)
assert.equal(f.calls.length,0)
}
})
test('wrong inbound tax, missing filtered child, foreign customer or wrong parent identity cannot pass',async()=>{
for(const mutate of [f=>f.evidence.childInbound.taxMode=null,f=>f.rows.pop(),f=>f.rows[1].tenantId='91',
f=>f.rows[1].relation.parentOrderId='999',f=>f.rows[1].taxMode='BUY_DECLARATION']) {
const f=remainderTaxFixture('TAX_REFUND');mutate(f)
await assert.rejects(verifyRemainderTaxIdentity(f.context,f.evidence,'own-customer'))
}
const f=remainderTaxFixture('TAX_REFUND'),original=f.context.request
f.context.request=async(...args)=>{const r=await original(...args);if(args[1].includes('taxMode=BUY_DECLARATION'))r.body.data={records:[f.rows[1]],total:1};return r}
await assert.rejects(verifyRemainderTaxIdentity(f.context,f.evidence,'own-customer'),/otherTax/)
})
test('F40 unified projections require exact root and customer session with final COMPLETED or unfinished DELIVERED',async()=>{
for (const completed of [true,false]) {
const context=fixture(),calls=[],snapshot={root:{status:completed?'COMPLETED':'TRANSPORTING',histories:history('OVERSEAS_DELIVERED')}}
context.request=async(method,path,body,token)=>{
calls.push({method,path,token})
return {http:200,body:{code:'200',data:{summary:{orderId:'100',mainStatus:completed?'COMPLETED':'DELIVERED'}}}}
}
const result=await verifyRootProjections(context,'90','100','customer-session',snapshot)
assert.deepEqual(result.platform,result.customer)
assert.equal(calls[0].path,'/api/platform/v1/orders/100?tenantId=90')
assert.equal(calls[1].path,'/api/merchant/v1/orders/100')
assert.equal(calls[1].token,'customer-session')
assert.equal(context.steps.every(step=>step.passed),true)
}
})
test('F40 customer mismatched root or prematurely completed projection fails closed',async()=>{
for (const summary of [{orderId:'101',mainStatus:'COMPLETED'},{orderId:'100',mainStatus:'DELIVERED'}]) {
const context=fixture()
context.request=async(_method,path)=>({http:200,body:{code:'200',data:{summary:
path.startsWith('/api/platform')?{orderId:'100',mainStatus:'COMPLETED'}:summary}}})
await assert.rejects(verifyRootProjections(context,'90','100','customer-session',{root:{status:'COMPLETED',histories:[]}}),
/平台客户统一状态/)
}
})
test('F40 README documents all three independent modes and fail-closed receiving/zero child event evidence',()=>{
const readme=readFileSync(new URL('./README.md',import.meta.url),'utf8')
for(const value of ['CHILD_FIRST','ROOT_SIGNED_REMAINDER_CANCELLED','orderStopAtApproved=true',
'PROCESSING','WAIT_SPLIT','A0409','phase4ReceivingEvidenceReader','零事件子行'])assert.ok(readme.includes(value),value)
assert.match(source,/proof\.beforeReceiptReplayOutbox = await waitState/)
assert.match(source,/outboxCounts\(proof.beforeReceiptReplayOutbox\) === outboxCounts\(proof.outboxProof\)/)
assert.match(source,/validFreshReceiptSave\(beforeFresh,afterFresh\)/)
assert.match(source,/JSON.stringify\(afterFresh\) === JSON.stringify\(receiptProof.afterSameKey\)/)
})
test('fresh receipt save allows exactly one document version/revision/audit while business completion stays unchanged',()=>{
const documents={version:'2',uploadedCategoryCount:1,categories:[{category:'RECEIPT',files:[{fileId:'1'}]}],replayed:false}
const before={documents,delivery:{order:{version:'2',status:'DELIVERED',signedAt:'2026-09-30T16:00:00'},
operations:[{id:'1',operationNode:'RECEIPT_AUTO_COMPLETE'}],skus:[{packageCount:1}],fees:[]},revisions:[{version:'2'}]}
const after=structuredClone(before);after.documents.version='3';after.delivery.order.version='3'
after.delivery.operations.push({id:'2',operationNode:'SAVE_DOCUMENTS',result:'保存派送资料:1/3类',
operatedAt:'2026-09-30T16:01:00',operatorName:'platform'})
after.revisions.push({version:'3',savedAt:'2026-09-30T16:01:00',operatorId:'platform',snapshot:after.documents})
assert.equal(validFreshReceiptSave(before,after),true)
for(const mutate of [
value=>value.documents.version='4',value=>value.delivery.order.version='2',
value=>value.delivery.order.signedAt='2026-09-30T16:01:00',value=>value.delivery.order.status='RETURNED',
value=>value.documents.categories[0].files[0].fileId='2',value=>value.delivery.skus[0].packageCount=2,
value=>value.delivery.fees.push({amount:1}),value=>value.delivery.operations[0].id='999',
value=>value.delivery.operations[1].operationNode='RECEIPT_AUTO_COMPLETE',
value=>value.delivery.operations.push({...value.delivery.operations[1],id:'3'}),
value=>value.revisions.pop(),value=>value.revisions[0].version='1',
]){const bad=structuredClone(after);mutate(bad);assert.equal(validFreshReceiptSave(before,bad),false)}
})
test('F40 retains partial and failed receiving raw snapshots without exporting identity credentials',()=>{
const proof={},scope={createdResources:{phase4OverseasPda:{receivingCompletionEvidence:{}}},token:'secret'}
preserveReceivingEvidence(proof,scope)
scope.createdResources.phase4OverseasPda.receivingCompletionEvidence['20']={status:'UNPROVEN',stages:{
BEFORE_COMPLETE:{captured:true,evidence:{balances:[]}},AFTER_COMPLETE:{captured:false,evidence:{balances:[{id:'1'}]}}}}
assert.deepEqual(proof.receivingCompletionEvidence,scope.createdResources.phase4OverseasPda.receivingCompletionEvidence)
assert.equal(JSON.stringify(proof).includes('secret'),false)
assert.match(source,/finally \{ preserveReceivingEvidence\(proof,scope\);/)
assert.match(source,/proof.receiptReplayProofs \|\|= \[\]/)
})
function nativeF40Candidate() {
const sourceVersion = 'a'.repeat(64)
return { sourceKey: 'PLAN:9007199254740993', sourceVersion, customerId: '90', warehouseId: '80',
inboundCode: 'IN-F40', availablePackages: '1', destination: { key: 'FBA:ONT8', type: 'FBA', warehouseCode: 'ONT8' },
selection: { sourceType: 'INBOUND_PLAN', planPalletId: '9007199254740993', warehousePalletId: '9007199254740994',
returnReceiptId: null, relabelNo: null, sourceVersion }, blockReasons: [], appointmentBatches: [{
batchKey: 'FBA_APPOINTMENT:9007199254740995', customerId: '90', recordVersion: '9007199254740996',
sources: [{ sourceKey: 'ORDER:9007199254740997', destinationKey: 'FBA:ONT8', packageCount: 1 }] }] }
}
test('F40 native Create uses frozen candidate source and exact FBA batch version including zero, without legacy vehicle/Transfer fields', () => {
const candidate = nativeF40Candidate()
const body = f40CreateDispatchBody('offline', '80', '90', [candidate])
assert.equal(body.warehouseId, '80')
assert.equal(body.cargo[0].planPalletId, '9007199254740993')
assert.equal(body.cargo[0].warehousePalletId, '9007199254740994')
assert.equal(body.cargo[0].sourceVersion, candidate.sourceVersion)
assert.deepEqual(body.batchAllocations, [{ planPalletId: '9007199254740993', batchKey: 'FBA_APPOINTMENT:9007199254740995',
customerId: '90', sourceKey: 'ORDER:9007199254740997', destinationKey: 'FBA:ONT8', packageCount: 1, recordVersion: '9007199254740996' }])
candidate.appointmentBatches[0].recordVersion = '0'
assert.equal(f40CreateDispatchBody('offline', '80', '90', [candidate]).batchAllocations[0].recordVersion, '0')
assert.equal(body.vehicle, null); assert.equal(body.driver, null)
assert.equal('vehiclePlate' in body, false); assert.equal('plannedDeliveryAt' in body, false)
assert.equal(f40LocalDateTime(new Date('2026-09-30T01:02:03.456Z')), '2026-09-30T01:02:03')
const dto = readFileSync(new URL('../../lastleg-service/src/main/java/com/baiyue/lastleg/dispatch/port/DispatchCommands.java', import.meta.url), 'utf8')
assert.match(dto, /record Create/)
for (const field of ['warehouseId', 'cargo', 'batchAllocations', 'sourceVersion']) assert.ok(dto.includes(field), field)
})
test('F40 native Create rejects foreign/ambiguous sources and incomplete batch allocation instead of guessing source or version', () => {
for (const mutate of [value => { value.customerId = '91' }, value => { value.warehouseId = '81' },
value => { value.sourceVersion = 'stale' }, value => { value.selection.sourceVersion = 'stale' },
value => { value.selection.planPalletId = 9007199254740992 }, value => { value.availablePackages = '9007199254740993' },
value => { value.blockReasons = ['NOT_RECEIVED'] }, value => { value.appointmentBatches[0].recordVersion = null },
value => { value.appointmentBatches[0].recordVersion = 9007199254740992 },
value => { value.appointmentBatches[0].customerId = '91' },
value => { value.appointmentBatches[0].sources[0].packageCount = 2 },
value => { value.appointmentBatches[0].sources[0].destinationKey = 'FBA:OTHER' },
value => { value.appointmentBatches.push(structuredClone(value.appointmentBatches[0])) }]) {
const candidate = nativeF40Candidate(); mutate(candidate)
assert.throws(() => f40CreateDispatchBody('offline', '80', '90', [candidate]))
}
const candidate = nativeF40Candidate()
assert.throws(() => f40CreateDispatchBody('offline', '80', '90', [candidate, candidate]))
})
test('F40 credentials prefer a complete current fixture pair and never combine fixture with configured customer',() => {
const options={username:'offline-admin',password:'offline-admin-secret',merchantUsername:'configured-customer',merchantPassword:'configured-secret'}
const current={username:'fixture-customer',password:'fixture-secret',tenantId:'90'}
assert.deepEqual(resolveF40MerchantCredentials({options,context:{merchantQualificationFixture:current}}),
{username:'fixture-customer',password:'fixture-secret',customerId:'90'})
assert.deepEqual(resolveF40MerchantCredentials({options,context:{}}),
{username:'configured-customer',password:'configured-secret',customerId:null})
for(const incomplete of [{username:'fixture-only',tenantId:'90'},{password:'fixture-only',tenantId:'90'},
{username:'fixture-customer',password:'fixture-secret'}])
assert.equal(resolveF40MerchantCredentials({options,context:{merchantQualificationFixture:incomplete}}),null)
assert.equal(resolveF40MerchantCredentials({options:{merchantUsername:'incomplete'},context:{}}),null)
assert.ok(source.indexOf("'F40 正式订单创建客户与本轮登录客户一致'") < source.indexOf("await stage(scope, 'inbound-measurement')"))
})
test('F40 reads formal nested domestic cabinet workbench and compares booking/plan/task identity instead of inventing GET containers/id',() => {
const controller=readFileSync(new URL('../../firstleg/src/main/java/com/baiyue/firstleg/controller/PlatformContainerController.java',import.meta.url),'utf8')
const dto=readFileSync(new URL('../../firstleg/src/main/java/com/baiyue/firstleg/terminaldrayage/dto/ContainerWorkbenchDtos.java',import.meta.url),'utf8')
assert.match(controller,/@GetMapping\("\/domestic\/\{id\}"\)/)
assert.doesNotMatch(controller,/@GetMapping\("\/\{id\}"\)/)
assert.match(dto,/record WorkbenchItem\(ContainerBookingEntity container, String workbenchStage/)
assert.match(source,/`\/api\/platform\/v1\/containers\/domestic\/\$\{result.containerId\}`/)
assert.doesNotMatch(source,/`\/api\/platform\/v1\/containers\/\$\{result.containerId\}`/)
const fragment={containerId:'20',planId:'30',taskId:'40',oceanBookingId:'50'},task={id:'40',containerBookingId:'20',loadingPlanId:'30'}
const workbench={container:{id:'20',cabinetNo:'D0001',containerNo:'OFFLINE',oceanBookingId:'50'}}
assert.equal(validFragmentCabinetIdentity(workbench,fragment,task),true)
assert.equal(validFragmentCabinetIdentity(workbench,{...fragment,containerId:'21'},task),false)
assert.equal(validFragmentCabinetIdentity(workbench,fragment,{...task,containerBookingId:'21'}),false)
assert.equal(validFragmentCabinetIdentity(workbench,fragment,{...task,loadingPlanId:'31'}),false)
assert.equal(validFragmentCabinetIdentity(workbench,fragment,{...task,id:'41'}),false)
assert.equal(validFragmentCabinetIdentity(workbench,{...fragment,oceanBookingId:'51'},task),false)
assert.equal(validFragmentCabinetIdentity(workbench.container,fragment,task),false)
})
test('formal tree requires one root physical source plus its exact real remainder child and conserves two boxes',() => {
assert.equal(validTwoFragmentTree(tree(),'100','101'),true)
for (const change of [
value => value.parentOrderId='999', value => value.currentRemainderPackageCount=1,
value => value.children[2].sourceOrderId='999', value => value.children[1].sourceOrderId='999',
value => value.children[2].packageCount=2,
value => value.children.push({splitRole:'UNKNOWN',orderId:'999'}),
]) {const value=tree();change(value);assert.equal(validTwoFragmentTree(value,'100','101'),false)}
})
test('F40 separates immutable physical task cartons from current actual FBA receiving prefix scans',() => {
assert.match(source,/cartonMarks: stableLoadedCartonMarks\(task.items\)/)
assert.match(source,/receivingCartonMarks:\[`\$\{current.summary.inboundCode\}#PDA-CONTRACT-1`\]/)
assert.doesNotMatch(source,/cartonMarks:\s*\[`\$\{current.summary.inboundCode\}#PDA-CONTRACT/)
})
test('raw Order and original status history remain in evidence including operator, reason and timestamp',() => {
const detail={order:{id:'100',orderRole:'MASTER',version:3,status:'TRANSPORTING'},statusHistory:[
{id:'11',orderId:'100',action:'OVERSEAS_DELIVERED',operatorName:'OVERSEAS_DELIVERY_EVENT',
reason:'signed actual delivery',occurredAt:'2026-09-30T01:00:00',fromStatus:'TRANSPORTING',toStatus:'TRANSPORTING'},
]}
const snapshot=fulfillmentHistorySnapshot(detail)
assert.deepEqual(snapshot.rawOrder,detail.order)
assert.deepEqual(snapshot.histories,detail.statusHistory)
assert.deepEqual(completionCounts(snapshot),{own:1,final:0})
})
test('root shipment signed first preserves unfinished root while effective child remains pending',() => {
const snapshot=signed();snapshot.root.status='TRANSPORTING'
snapshot.root.histories=history('OVERSEAS_DELIVERED');snapshot.child={status:'TRANSPORTING',histories:[]}
snapshot.rootInbound.orderStatusSnapshot='TRANSPORTING'
assert.equal(validSignatureGate(snapshot,'MASTER'),true)
snapshot.root.status='COMPLETED'
assert.equal(validSignatureGate(snapshot,'MASTER'),false)
})
test('child signed before root remains an independent completed child and cannot finish unsigned root',() => {
const snapshot=signed();snapshot.root={status:'TRANSPORTING',histories:[]}
snapshot.rootInbound={processStatus:'IN_TRANSIT',orderStatusSnapshot:'TRANSPORTING'}
assert.equal(validSignatureGate(snapshot,'CHILD'),true)
assert.equal(validSignatureGate(snapshot,'MASTER'),false)
snapshot.root.histories=history('OVERSEAS_DELIVERED')
assert.equal(validSignatureGate(snapshot,'CHILD'),false)
})
test('all signed requires root own fact, exactly one aggregate final history and real Firstleg delivered completion',() => {
assert.equal(validSignatureGate(signed(),'MASTER',true),true)
for(const change of [
value => value.root.histories=history('FULFILLMENT_COMPLETED'),
value => value.root.histories.push({action:'FULFILLMENT_COMPLETED'}),
value => value.child.histories.push({action:'OVERSEAS_DELIVERED'}),
value => value.child.status='CANCELLED',
value => value.rootInbound.orderStatusSnapshot='TRANSPORTING',
value => value.rootInbound.processStatus='RECEIVED',
]) {const value=signed();change(value);assert.equal(validSignatureGate(value,'MASTER',true),false)}
})
test('Outbox requires exact customer/root/source aggregate membership, unique completion and PUBLISHED',() => {
assert.equal(exactCompletionOutboxProof(outbox(),'90','100',['101','100']),true)
for(const change of [
value => value.readOnly=false, value => value.customerId='91', value => value.rootOrderId='999',
value => value.orders[0].rootOrderId='999', value => value.orders[0].customerId='91',
value => value.orders[1].orderId='100', value => value.orders[0].count=2,
value => value.orders[1].publishedCount=0, value => value.orders[1].pendingCount=1,
value => value.orders[0].failedCount=1, value => value.orders[0].eventType='OVERSEAS_DELIVERED',
value => value.orders.pop(),
]) {const value=outbox();change(value);assert.equal(exactCompletionOutboxProof(value,'90','100',['100','101']),false)}
})
test('exported fragment producer preserves fail closed supplier precondition and never writes without actual source master',async() => {
const context=fixture(),calls=[]
context.request=async(method,path) => {calls.push([method,path]);return {http:200,body:{code:200,data:{records:[]}}}}
assert.equal(await loadOneFragment(context,{sourceOrderNo:'offline-only'}),null)
assert.equal(calls.length,1)
assert.equal(calls[0][0],'GET')
assert.match(calls[0][1],/category=BOOKING&status=ACTIVE/)
})
test('exported pallet label helper preserves FBA eligibility without introducing writes for other destinations',async() => {
const context=fixture();context.request=async() => {throw new Error('unexpected HTTP')}
assert.equal(await verifyPalletLabels(context,{destinationType:'OVERSEAS'},'1'),null)
assert.equal(await verifyPalletLabels(context,{destinationType:'FBA',fulfillmentType:'ONE_CLICK_DROPSHIP'},'1'),null)
})
test('exported consumer polling and conversion fail closed when real producer consumption is absent',async() => {
const context=fixture(),calls=[]
context.request=async(method,path) => {calls.push([method,path]);return {http:200,body:{code:200,data:{records:[]}}}}
assert.equal(await awaitProducerConsumption(context,[]),true)
assert.equal(await convertReceivingContainers(context,[{containerNo:'OFFLINE'}]),false)
assert.equal(calls.every(([method]) => method==='GET'),true)
assert.equal(context.steps.at(-1).passed,false)
})
test('exported receiving rejects unmatched current producer instead of borrowing any history container',async() => {
const context=fixture();context.request=async(_method,path) => ({http:200,body:{code:200,data:
path.endsWith('/candidates')?[{containerNo:'ANOTHER',sourceContainerBookingId:'999',totalPackages:1}]:null}})
await runReceiving(context,'offline-pda',null,null,{containerNo:'EXPECTED',containerBookingId:'100'},false,{attempts:1})
assert.equal(context.steps.some(step => step.passed === false),true)
})
test('exported receiving warehouse resolver continues to choose only configured active official warehouse',async() => {
const context=fixture();context.request=async() => ({http:200,body:{code:200,data:{records:[
{id:'193000000000200003',warehouseType:'OVERSEAS',status:'ACTIVE',warehouseName:'offline'},
]}}})
assert.deepEqual(await resolveSharedWarehouse(context),{id:'193000000000200003',name:'offline'})
})
function warehouseSelectionFixture() {
const producers = [1, 2].map(n => ({ tenantId: '90', containerNo: `CONTAINER-${n}`,
containerBookingId: `BOOKING-${n}`, sourceInboundId: `INBOUND-${n}`, inboundCode: `CODE-${n}` }))
const orders = producers.map((p, n) => ({ id: `LASTLEG-${n}`, customerId: p.tenantId,
sourceContainerBookingId: p.containerBookingId, sourceInboundId: p.sourceInboundId,
inboundCode: p.inboundCode, containerNo: p.containerNo, overseasWarehouseId: 'actual-warehouse',
overseasWarehouseCode: 'ACTUAL' }))
const warehouses = [{ id: 'other-warehouse', warehouseName: 'other', warehouseCode: 'OTHER',
warehouseType: 'OVERSEAS', status: 'ACTIVE' },
{ id: 'actual-warehouse', warehouseName: 'actual', warehouseCode: 'ACTUAL',
warehouseType: 'OVERSEAS', status: 'ACTIVE' }]
const calls = [], context = fixture()
context.request = async (method, path) => {
calls.push({ method, path })
const rows = path.includes('/warehouse-admin?') ? warehouses
: orders.filter(row => row.containerNo === new URL(path, 'https://offline.invalid').searchParams.get('containerNo'))
return { http: 200, body: { code: 200, data: { records: rows, total: rows.length } } }
}
return { producers, orders, warehouses, calls, context }
}
test('F40 selects actual source warehouse irrespective of authority list order and only performs GET', async () => {
for (const reverse of [false, true]) {
const f = warehouseSelectionFixture()
if (reverse) { f.warehouses.reverse(); f.orders.reverse(); f.producers.reverse() }
const warehouse = await resolveF40ReceivingWarehouse(f.context, f.producers)
assert.equal(warehouse.id, 'actual-warehouse')
assert.equal(warehouse.code, 'ACTUAL')
assert.equal(warehouse.sourceOrders.length, 2)
assert.ok(f.calls.every(row => row.method === 'GET'))
}
assert.ok(source.indexOf('await awaitProducerConsumption(scope,producers)')
< source.indexOf('await resolveF40ReceivingWarehouse(scope,producers)'))
assert.ok(source.indexOf('await resolveF40ReceivingWarehouse(scope,producers)')
< source.indexOf('await createPicker(scope, 7'))
assert.doesNotMatch(source, /resolveSharedWarehouse|19300000000020000[34]/)
})
for (const fault of ['', 'changed-total', 'repeated-page', 'missing-page']) test(`F40 fully pages sources before proving unique receiving warehouse: ${fault || 'target on page two'}`, async () => {
const f = warehouseSelectionFixture(), original = f.context.request
f.context.request = async (method, path) => {
if (path.includes('/warehouse-admin?')) return original(method, path)
f.calls.push({ method, path })
const url = new URL(path, 'https://offline.invalid'), page = Number(url.searchParams.get('page'))
const target = f.orders.filter(row => row.containerNo === url.searchParams.get('containerNo'))
const unrelated = Array.from({ length: 200 }, (_, n) => ({ id: `OTHER-${n}`, customerId: 'OTHER' }))
const records = page === 1 || fault === 'repeated-page' ? unrelated : fault === 'missing-page' ? [] : target
return { http: 200, body: { code: 200, data: { records, total: fault === 'changed-total' && page === 2 ? 202 : 201 } } }
}
if (fault) await assert.rejects(resolveF40ReceivingWarehouse(f.context, f.producers), /分页/)
else {
assert.equal((await resolveF40ReceivingWarehouse(f.context, f.producers)).id, 'actual-warehouse')
assert.equal(f.calls.filter(row => row.path.includes('page=2')).length, 2)
}
})
test('F40 warehouse resolution rejects wrong customer, booking, inbound identity, duplicates and missing assignment', async () => {
for (const mutate of [
f => { f.orders[0].customerId = 'foreign' },
f => { f.orders[0].sourceContainerBookingId = 'foreign' },
f => { f.orders[0].sourceInboundId = 'foreign' },
f => { f.orders[0].inboundCode = 'foreign' },
f => { f.orders[0].containerNo = 'foreign' },
f => { f.orders[0].overseasWarehouseId = '' },
f => { f.orders.push({ ...f.orders[0] }) },
]) {
const f = warehouseSelectionFixture(); mutate(f)
await assert.rejects(resolveF40ReceivingWarehouse(f.context, f.producers), /来源柜客户入仓身份/)
assert.ok(f.calls.every(row => !row.path.includes('/warehouse-admin?')))
}
})
test('F40 warehouse resolution blocks different receiving warehouses and invalid or ambiguous official master data', async () => {
const mixed = warehouseSelectionFixture(); mixed.orders[1].overseasWarehouseId = 'other-warehouse'
await assert.rejects(resolveF40ReceivingWarehouse(mixed.context, mixed.producers), /同一实际接柜仓/)
for (const mutate of [
f => { f.warehouses.pop() },
f => { f.warehouses[1].status = 'DISABLED' },
f => { f.warehouses[1].warehouseType = 'DOMESTIC' },
f => { f.warehouses[1].warehouseCode = 'WRONG' },
f => { f.warehouses.push({ ...f.warehouses[1] }) },
]) {
const f = warehouseSelectionFixture(); mutate(f)
await assert.rejects(resolveF40ReceivingWarehouse(f.context, f.producers), /唯一启用海外收货仓/)
assert.ok(f.calls.every(row => row.method === 'GET'))
}
})
test('F40 warehouse resolution rejects empty or duplicate producers before HTTP', async () => {
for (const select of [() => [], f => [f.producers[0], f.producers[0]],
f => [{ ...f.producers[0], sourceInboundId: '' }]]) {
const f = warehouseSelectionFixture()
await assert.rejects(resolveF40ReceivingWarehouse(f.context, select(f)), /唯.*本轮实装来源/)
assert.equal(f.calls.length, 0)
}
})
test('complete orchestrator calls actual source chain and blocks missing Outbox evidence; no fake callback or SQL',() => {
assert.equal(typeof run,'function')
for(const name of ['orders','inbound-measurement','phase3-customs','phase3-outbound-dispatch',
'phase3-carrier-schedule-queries'])assert.ok(source.includes(`'${name}'`))
for(const name of ['loadOneFragment','verifyPalletLabels','awaitProducerConsumption',
'convertReceivingContainers','prepareTerminalDrayage','runReceiving','pickAll','withDeliveryEventRecovery'])
assert.match(source,new RegExp(`await ${name}\\(`))
assert.match(source,/\(\) => saveReceiptAndComplete\(context, deliveries\[0\], customerId, index\)/)
assert.match(source,/rootOrderId,\s*sourceOrderIds:\[rootOrderId,childOrderId\]/)
assert.match(source,/HTTP_VERIFIED_OUTBOX_PENDING/)
assert.match(source,/finally \{ preserveReceivingEvidence\(proof,scope\); proof.currentStage = 'PDA_IDENTITY_CLEANUP'; await disablePicker/)
assert.doesNotMatch(source,/['"`]\/internal\/|UPDATE\s+ord_|INSERT\s+INTO|\/complete-delivery/)
})
const oneLoadedTree = () => ({...tree(),cumulativeLoadedPackageCount:1,currentRemainderPackageCount:1,
children:tree().children.slice(0,2).map(row=>({...row,loadingPlanId:'30',containerBookingId:'20'}))})
const approvedChild = () => fulfillmentHistorySnapshot({order:{id:'101',tenantId:'90',orderRole:'CHILD',
rootOrderId:'100',parentOrderId:'100',status:'APPROVED',version:1,orderNo:'R-1',inboundCode:'I-1',completedAt:null},statusHistory:[]})
const unshippedInbound = () => ({id:'301',tenantId:'90',orderId:'101',orderNo:'R-1',inboundCode:'I-1',
processStatus:'WAIT_SPLIT',orderStatusSnapshot:'APPROVED',totalPackageCount:1,containerNo:null,cabinetNo:null})
const cancelled = () => ({root:{status:'COMPLETED',histories:history('OVERSEAS_DELIVERED','FULFILLMENT_COMPLETED')},
child:{status:'CANCELLED',rawOrder:{completedAt:null},histories:history('CANCELLED')},
rootInbound:{processStatus:'DELIVERED',orderStatusSnapshot:'COMPLETED'}})
const cancellationOutbox = () => {const value=outbox();value.orders[1].count=0;value.orders[1].publishedCount=0;return value}
test('unloaded remainder requires real raw APPROVED child, exact customer/root/source and one remaining physical box',() => {
const child=approvedChild(),inbound=unshippedInbound(),tree=oneLoadedTree()
assert.equal(validUnloadedRemainder(tree,child,inbound,'90','100','101'),true)
for(const mutate of [
value => value.child.status='PROCESSING',value => value.child.status='ACCEPTED',
value => value.child.rootOrderId='999',value => value.child.parentOrderId='999',
value => value.child.rawOrder.tenantId='91',value => value.child.orderId='102',
value => value.child.histories=history('OVERSEAS_DELIVERED'),
value => value.inbound.orderId='102',value => value.inbound.tenantId='91',
value => value.inbound.inboundCode='ANOTHER',value => value.inbound.orderNo='ANOTHER',
value => value.inbound.processStatus='LOADED',value => value.inbound.totalPackageCount=2,
value => value.inbound.orderStatusSnapshot='PROCESSING',value => value.inbound.containerNo='ACTUAL-CABINET',
value => value.inbound.cabinetNo='ACTUAL-CABINET',value => value.tree.children.push(tree.children[0]),
value => value.tree.children[0].sourceOrderId='101',value => value.tree.children[1].loadingPlanId='31',
value => value.tree.children[1].containerBookingId='21',value => value.tree.currentRemainderPackageCount=0,
]) {
const value={tree:oneLoadedTree(),child:approvedChild(),inbound:unshippedInbound()};mutate(value)
assert.equal(validUnloadedRemainder(value.tree,value.child,value.inbound,'90','100','101'),false)
}
})
test('cancelled remainder cannot substitute for child signature in either full-signature mode',() => {
assert.equal(validRemainderCancellationGate(cancelled()),true)
assert.equal(validSignatureGate(cancelled(),'MASTER',true),false)
assert.equal(validSignatureGate(cancelled(),'CHILD',true),false)
for(const mutate of [
value=>value.root.histories=history('FULFILLMENT_COMPLETED'),
value=>value.root.histories.push({action:'FULFILLMENT_COMPLETED'}),
value=>value.child.status='COMPLETED',value=>value.child.histories.push({action:'OVERSEAS_DELIVERED'}),
value=>value.child.histories.push({action:'CANCELLED'}),value=>value.child.rawOrder.completedAt='2026-09-30T01:00:00',
value=>value.rootInbound.orderStatusSnapshot='APPROVED',value=>value.rootInbound.processStatus='RECEIVED',
]){const value=cancelled();mutate(value);assert.equal(validRemainderCancellationGate(value),false)}
})
test('cancellation Outbox requires exactly one published root completion and explicit zero child completion counts',() => {
assert.equal(exactCancelledRemainderOutboxProof(cancellationOutbox(),'90','100','101'),true)
assert.equal(exactCompletionOutboxProof(cancellationOutbox(),'90','100',['100','101']),false)
for(const mutate of [
value=>value.orders.pop(),value=>value.orders[1].count=1,value=>value.orders[1].publishedCount=1,
value=>value.orders[1].pendingCount=1,value=>value.orders[1].failedCount=1,
value=>value.orders[1].count=null,value=>delete value.orders[1].failedCount,
value=>value.orders[1].rootOrderId='999',value=>value.orders[1].customerId='91',
value=>value.orders[1].orderId='100',value=>value.orders[1].eventType='ORDER_CANCELLED',
value=>value.orders[0].count=2,value=>value.orders[0].publishedCount=0,value=>value.readOnly=false,
]){const value=cancellationOutbox();mutate(value);assert.equal(exactCancelledRemainderOutboxProof(value,'90','100','101'),false)}
})
function cancellationFixture(change=null) {
const context=fixture(),calls=[],proof={snapshots:[]}
const rootDetail={order:{id:'100',tenantId:'90',orderRole:'MASTER',rootOrderId:'100',status:'APPROVED',
version:1,orderNo:'R',inboundCode:'I',completedAt:null},statusHistory:history('OVERSEAS_DELIVERED')}
const childDetail={order:approvedChild().rawOrder,statusHistory:[]}
const rootInbound={summary:{processStatus:'DELIVERED',orderStatusSnapshot:'APPROVED'}}
const childInbound={summary:unshippedInbound()},tree=oneLoadedTree()
let count=0
context.context={f40BaseMarker:'offline-only',f40ReadOnlyOutboxEvidence:async args=>{
assert.deepEqual(args,{customerId:'90',rootOrderId:'100',sourceOrderIds:['100','101'],
eventType:'FIRST_LEG_ORDER_FULFILLMENT_COMPLETED'})
return cancellationOutbox()
}}
if(change)change({rootDetail,childDetail,childInbound,tree})
let frozenCancel
context.request=async(method,path,body,token,headers)=>{
calls.push({method,path,body,headers})
if(method==='POST'){
assert.equal(path,'/api/platform/v1/orders/first-leg/101/cancel?tenantId=90')
assert.ok(body.reason.includes('offline-only'))
assert.ok(headers?.['Idempotency-Key'])
if(count++)return {http:200,body:{code:headers['Idempotency-Key']===frozenCancel.key && JSON.stringify(body)===frozenCancel.payload ? '200' : 'A0409'}}
assert.equal(body.expectedVersion,childDetail.order.version)
frozenCancel={key:headers['Idempotency-Key'],payload:JSON.stringify(body)}
childDetail.order.status='CANCELLED';childDetail.order.version++
childDetail.statusHistory=history('CANCELLED')
rootDetail.order.status='COMPLETED';rootDetail.order.version++
rootDetail.order.completedAt='2026-09-30T01:00:00'
rootDetail.statusHistory=history('OVERSEAS_DELIVERED','FULFILLMENT_COMPLETED')
rootInbound.summary.orderStatusSnapshot='COMPLETED'
return {http:200,body:{code:'200',data:null}}
}
let data
if(path.includes('/fulfillment-tree?'))data=tree
else if(path.includes('/orders/first-leg/100?'))data=rootDetail
else if(path.includes('/orders/first-leg/101?'))data=childDetail
else if(path.includes('/inbounds/300?'))data=rootInbound
else if(path.includes('/inbounds/301?'))data=childInbound
else throw new Error(`unexpected offline request ${path}`)
return {http:200,body:{code:'200',data:structuredClone(data)}}
}
return {context,proof,calls}
}
test('formal cancellation helper rereads physical eligibility, uses public cancel and accepts replay rejection only with unchanged facts and Outbox',async()=>{
const {context,proof,calls}=cancellationFixture()
const result=await cancelUnloadedRemainder(context,proof,'90','100','101','300','301')
assert.equal(validRemainderCancellationGate(result),true)
const writes=calls.filter(row=>row.method==='POST')
assert.equal(writes.length,4)
assert.deepEqual(writes[1].body,writes[0].body)
assert.deepEqual(writes[1].headers,writes[0].headers)
assert.equal(writes[2].headers['Idempotency-Key'],writes[0].headers['Idempotency-Key'])
assert.notEqual(writes[3].headers['Idempotency-Key'],writes[0].headers['Idempotency-Key'])
assert.ok(writes[3].body.expectedVersion>writes[0].body.expectedVersion)
assert.equal(proof.snapshots.length,3)
assert.deepEqual(proof.cancelReplayOutbox.before,proof.cancelReplayOutbox.after)
assert.equal(context.steps.every(step=>step.passed),true)
})
test('formal cancellation helper stops before any mutation for PROCESSING, unsigned root or already loaded child',async()=>{
for(const change of [value=>value.childDetail.order.status='PROCESSING',
value=>value.rootDetail.order.status='PROCESSING',value=>value.rootDetail.statusHistory=[],
value=>value.childInbound.summary.processStatus='LOADED',value=>value.tree.children.push(tree().children[2])]) {
const {context,proof,calls}=cancellationFixture(change)
await assert.rejects(cancelUnloadedRemainder(context,proof,'90','100','101','300','301'),/仅取消真实签收根下未装 APPROVED/)
assert.equal(calls.some(row=>row.method!=='GET'),false)
}
})
test('cancel scenario uses new approved-only order and exactly root shipment; both existing full-signature modes remain independent',()=>{
assert.match(source,/const cancelRemainder = signatureOrder === 'ROOT_SIGNED_REMAINDER_CANCELLED'/)
assert.match(source,/orderStopAtApproved:cancelRemainder/)
assert.match(source,/approvedRoot.status === 'APPROVED'/)
assert.match(source,/if \(cancelRemainder\) \{[\s\S]*?proof.unloadedRemainder[\s\S]*?\} else \{[\s\S]*?second = await fragment/)
assert.match(source,/const producers = cancelRemainder \? \[first\] : \[first,second\]/)
assert.match(source,/signatureOrder === 'CHILD_FIRST' \? \[second,first\] : producers/)
assert.match(source,/if \(cancelRemainder\) await cancelUnloadedRemainder/)
const orderSource=readFileSync(new URL('./orders.mjs',import.meta.url),'utf8')
assert.match(orderSource,/context.context\?\.orderStopAtApproved === true/)
})
test('early F40 failures become FAILED with only a safe stage and preserve the original exception',async() => {
const context=fixture();context.context={};context.options={}
await assert.rejects(run(context),/F40 本轮客户完整凭据有效/)
assert.equal(context.createdResources.phase5RootFulfillment.finalStatus,'FAILED')
assert.equal(context.createdResources.phase5RootFulfillment.failedStage,'CUSTOMER_IDENTITY')
assert.equal('error' in context.createdResources.phase5RootFulfillment,false)
const secretError=new Error('offline-secret-body-not-recorded')
const withLogin=fixture();withLogin.context={};withLogin.options={merchantUsername:'offline',merchantPassword:'offline'}
withLogin.request=async()=>{throw secretError}
await assert.rejects(run(withLogin),error=>error === secretError)
assert.equal(withLogin.createdResources.phase5RootFulfillment.finalStatus,'FAILED')
assert.equal(JSON.stringify(withLogin.createdResources.phase5RootFulfillment).includes(secretError.message),false)
})
function nativeFragmentFixture() {
const context = fixture(), events = [], calls = []
context.createdResources.phase4OverseasPda = { generatedPlanIds: ['81', '82'] }
const producer = { tenantId: '90', inboundCode: 'IN-F40', sourceInboundId: '901', sourceOrderId: '100',
rootOrderId: '100', containerBookingId: '902' }
const rootProof = { fragments: [producer], currentStage: 'DISPATCH_AND_SIGNATURE_1' }
context.context = { f40BaseMarker: 'offline-F40', f40RootFulfillmentProof: rootProof }
const measurement = { environment: 'TEST', marker: 'offline-F40-SEND-0', dispatchNo: 'DS-F40', pallets: [] }
context.context.f40LoadingMeasurements = { 'DS-F40': measurement }
context.checkpoint = async () => { events.push('checkpoint') }
const delivery = { customerId: '90', deliveryNo: 'DL-F40', outboundNo: 'OB-F40', packageCount: '1',
destination: { key: 'FBA:ONT8', warehouseCode: 'ONT8' } }
let created, departed, signed = false
context.request = async (method, path, body, token) => {
calls.push({ method, path, body: structuredClone(body), token })
let data
if (method === 'GET' && path.includes('/candidates?')) {
assert.equal(new URL(path, 'https://offline.invalid').searchParams.get('warehouseId'), '80')
assert.equal(new URL(path, 'https://offline.invalid').searchParams.get('sourceStage'), 'RECEIVED')
assert.equal(new URL(path, 'https://offline.invalid').searchParams.get('planIds'), '81,82')
assert.equal(new URL(path, 'https://offline.invalid').searchParams.get('size'), '50')
data = { complete: true, candidates: [nativeF40Candidate()] }
} else if (method === 'POST' && path.endsWith('/dispatches')) {
events.push('create')
if (created) data = { ...created, replayed: true }
else data = created = { dispatchNo: 'DS-F40', status: 'ACTIVE', stage: 'BEFORE_LOADING', recordVersion: '0',
result: { dispatchNo: 'DS-F40', changedItemIds: ['9007199254740998'] } }
} else if (method === 'POST' && path.endsWith('/depart')) {
events.push('depart')
assert.equal(token, 'pda-memory')
assert.equal(body.recordVersion, '9007199254740999')
if (departed) {
assert.deepEqual(body, calls.find(call => call.path.endsWith('/depart')).body)
data = { ...departed, replayed: true }
} else data = departed = { dispatchNo: 'DS-F40', status: 'ACTIVE', stage: 'DELIVERING', recordVersion: '9007199254741000',
result: { shippedPackages: '1', deliveries: [delivery], outbounds: [{ customerId: '90', outboundNo: 'OB-F40' }] } }
} else if (method === 'GET' && path.includes('/delivery-orders/')) {
data = { order: { id: '9007199254741001', customerId: '90', outboundNo: 'OB-F40', deliveryNo: 'DL-F40',
version: '9007199254741002', status: departed ? 'DELIVERING' : 'WAIT_DELIVERY' }, skus: [{ inboundCode: 'IN-F40', packageCount: 1 }] }
} else if (method === 'GET' && path.endsWith('/outbound-orders/OB-F40')) {
data = { outbound: { outboundNo: 'OB-F40', dispatchNo: 'DS-F40', status: 'OUTBOUNDED', packageCount: 1 },
deliveries: [{ ...delivery, dispatchNo: 'DS-F40' }], pallets: [{ inboundCode: 'IN-F40' }] }
} else if (method === 'GET' && path.endsWith('/dispatches/DS-F40')) {
assert.equal(signed, true); events.push('normal-completed')
data = { dispatchNo: 'DS-F40', status: 'ACTIVE', stage: 'DELIVERED', recordVersion: '9007199254741004' }
} else throw new Error(`Unexpected offline wire ${method} ${path}`)
return { http: 200, body: { code: '200', data } }
}
const deps = {
async fulfillmentEvidenceContext(current) { events.push('evidence'); return current },
async prepareFleetTrip(current, module, dispatchNo, warehouseId) {
events.push('prepare-review'); assert.equal(dispatchNo, 'DS-F40'); assert.equal(warehouseId, '80')
return { dispatchNo, warehouseId, tripNo: 'TR-F40', recordVersion: '1' }
},
async pickAll(current, token, dispatchNo) { events.push('pick'); assert.equal(token, 'pda-memory'); assert.equal(dispatchNo, 'DS-F40'); return true },
async confirmFleetLoading(current, module, proof, token, actual) {
events.push('loading-complete'); assert.equal(token, 'pda-memory'); assert.equal(actual, measurement)
if (!actual) throw new Error('实际测量缺少有效palletSpaces')
departed = true; proof.loadingCommand = { replay: { replayed: true } }; return '9007199254740998'
},
async readAutomaticDispatchDelivery() { events.push('arrange'); return { deliveries: [delivery],
outbounds: [{ customerId: '90', outboundNo: 'OB-F40', packageCount: '1' }], data: { documents: { deliveries: [delivery] } } } },
async verifyAutomaticDeparture() { events.push('read-current-depart-version'); return { data: { recordVersion: '9007199254740999', stage: 'DELIVERING', counts: { shippedPackageCount: 1 } } } },
async saveReceiptAndComplete(current, actual, customerId, index) {
events.push('receipt'); assert.equal(actual, delivery); assert.equal(customerId, '90'); assert.equal(index, 0)
signed = true
return { order: { customerId: '90', deliveryNo: 'DL-F40', status: 'DELIVERED', version: '9007199254741003', signedAt: '2026-10-19T01:00:00' } }
},
async finishFleetTrip() { events.push('finish'); return { status: 'VERIFIED' } },
}
return { context, producer, rootProof, deps, events, calls, measurement }
}
test('F40 fragment uses native creation, reviews, real PDA scan, explicit loading/token/evidence, arrangement, refreshed departure version and normal signature', async () => {
const f = nativeFragmentFixture()
const result = await sendFragment(f.context, f.producer, { warehouseId: '80', token: 'pda-memory' }, 'merchant-memory', 0, f.deps)
assert.equal(result.outboundNo, 'OB-F40'); assert.equal(result.receiptVersion, '9007199254741003')
assert.equal(result.signedAt, '2026-10-19T01:00:00')
assert.deepEqual(f.events.filter(event => event !== 'checkpoint'), ['evidence', 'create', 'create', 'prepare-review', 'pick',
'loading-complete', 'arrange', 'read-current-depart-version', 'receipt', 'normal-completed', 'finish'])
assert.equal(f.calls.some(call => /pre-arrange|\/status|\/api\/merchant\//.test(call.path)), false)
assert.equal(f.calls.some(call => call.method === 'PUT'), false)
assert.equal(f.rootProof.dispatches[0].stage, 'DELIVERED')
assert.equal(f.rootProof.dispatches[0].loadingReplay.replayed, true)
assert.equal(f.context.steps.every(step => step.passed), true)
})
test('F40 missing real measurement evidence stops before arranging, departing or signing while keeping the failed source proof', async () => {
const f = nativeFragmentFixture()
delete f.context.context.f40LoadingMeasurements
f.deps.confirmFleetLoading = async (_context, _module, _proof, token, actual) => {
assert.equal(token, 'pda-memory'); assert.equal(actual, undefined)
throw new Error('实际测量缺少有效palletSpaces')
}
await assert.rejects(sendFragment(f.context, f.producer, { warehouseId: '80', token: 'pda-memory' }, 'merchant-memory', 0, f.deps), /实际测量缺少/)
assert.equal(f.events.includes('arrange'), false)
assert.equal(f.calls.some(call => call.path.endsWith('/depart')), false)
assert.equal(f.events.includes('receipt'), false)
assert.equal(f.rootProof.dispatches[0].dispatchNo, 'DS-F40')
assert.equal(f.rootProof.dispatches[0].stage, 'FAILED')
assert.equal(f.rootProof.dispatches[0].failedAction, 'LOADING_COMPLETE')
assert.equal(f.events.at(-1), 'checkpoint')
})
test('F40 measurement reader receives the exact current TEST source scope and preserves its returned evidence without inferred values', async () => {
const f = nativeFragmentFixture(), response = { arbitrary: 'opaque-original-proof' }
f.context.marker = 'offline-F40-SEND-0'
f.context.context.f40LoadingMeasurements = async scope => {
assert.deepEqual(scope, { environment: 'TEST', marker: 'offline-F40-SEND-0', dispatchNo: 'DS-F40',
warehouseId: '80', sourceInboundId: '901', inboundCode: 'IN-F40' })
return response
}
assert.equal(await resolveF40LoadingMeasurements(f.context, 'DS-F40', f.producer, '80'), response)
})
test('F40 fresh receipt revision evidence preserves string versions above safe integer precision', () => {
const oldVersion = '9007199254740993', newVersion = '9007199254740994'
const documents = { version: oldVersion, uploadedCategoryCount: 1, categories: [{ category: 'RECEIPT', files: [{ fileId: '9007199254740995' }] }], replayed: false }
const before = { documents, delivery: { order: { version: oldVersion, status: 'DELIVERED', signedAt: '2026-10-19T01:00:00' },
operations: [{ id: '9007199254740996', operationNode: 'RECEIPT_AUTO_COMPLETE' }] }, revisions: [{ version: oldVersion }] }
const after = structuredClone(before)
after.documents.version = newVersion; after.delivery.order.version = newVersion
after.delivery.operations.push({ id: '9007199254740997', operationNode: 'SAVE_DOCUMENTS', result: '保存派送资料:1/3类',
operatedAt: '2026-10-19T01:01:00', operatorName: 'TEST' })
after.revisions.push({ version: newVersion, savedAt: '2026-10-19T01:01:00', operatorId: 'platform', snapshot: after.documents })
assert.equal(validFreshReceiptSave(before, after), true)
after.documents.version = '9007199254740995'
assert.equal(validFreshReceiptSave(before, after), false)
})