2026-09-30 15:16:39 +08:00
import assert from 'node:assert/strict'
import test from 'node:test'
import { readFileSync } from 'node:fs'
import { fulfillmentHistorySnapshot , validTwoFragmentTree , validSignatureGate ,
2026-10-08 17:07:57 +08:00
completionCounts , exactCompletionOutboxProof , rootCompletionTimeMatches , resolveF40MerchantCredentials ,
2026-10-20 10:35:52 +08:00
f40CreateDispatchBody , resolveF40LoadingMeasurements , sendFragment , f40LocalDateTime , validFragmentCabinetIdentity , validUnloadedRemainder ,
2026-09-30 17:13:11 +08:00
validRemainderCancellationGate , exactCancelledRemainderOutboxProof , cancelUnloadedRemainder ,
2026-10-08 17:07:57 +08:00
verifyRootProjections , verifyRemainderTaxIdentity , validFreshReceiptSave , preserveReceivingEvidence ,
resolveF40ReceivingWarehouse , run } from './phase5-root-fulfillment.mjs'
2026-09-30 15:16:39 +08:00
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' )
2026-10-08 17:07:57 +08:00
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 )
})
2026-09-30 15:16:39 +08:00
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 },
})
2026-10-03 06:26:33 +08:00
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/ )
})
2026-09-30 17:13:11 +08:00
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 \|\|= \[\]/ )
})
2026-10-20 10:35:52 +08:00
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 )
2026-09-30 15:16:39 +08:00
})
2026-10-20 10:35:52 +08:00
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 ]))
})
2026-09-30 15:16:39 +08:00
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 }})
2026-10-03 06:26:33 +08:00
await runReceiving ( context , 'offline-pda' , null , null ,{ containerNo : 'EXPECTED' , containerBookingId : '100' }, false ,{ attempts : 1 })
2026-09-30 15:16:39 +08:00
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' })
})
2026-10-08 17:07:57 +08:00
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]/ )
})
2026-10-23 04:48:02 +08:00
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 )
}
})
2026-10-08 17:07:57 +08:00
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 )
}
})
2026-09-30 17:13:11 +08:00
test ( 'complete orchestrator calls actual source chain and blocks missing Outbox evidence; no fake callback or SQL' ,() => {
2026-09-30 15:16:39 +08:00
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' ,
2026-10-03 08:13:04 +08:00
'convertReceivingContainers' , 'prepareTerminalDrayage' , 'runReceiving' , 'pickAll' , 'withDeliveryEventRecovery' ])
2026-09-30 15:16:39 +08:00
assert . match ( source , new RegExp ( `await ${ name } \\(` ))
2026-10-03 08:13:04 +08:00
assert . match ( source , /\(\) => saveReceiptAndComplete\(context, deliveries\[0\], customerId, index\)/ )
2026-09-30 17:13:11 +08:00
assert . match ( source , /rootOrderId,\s*sourceOrderIds:\[rootOrderId,childOrderId\]/ )
2026-09-30 15:16:39 +08:00
assert . match ( source , /HTTP_VERIFIED_OUTBOX_PENDING/ )
2026-09-30 17:13:11 +08:00
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 })
2026-10-02 23:14:24 +08:00
let frozenCancel
context . request = async ( method , path , body , token , headers )=>{
calls . push ({ method , path , body , headers })
2026-09-30 17:13:11 +08:00
if ( method === 'POST' ){
assert . equal ( path , '/api/platform/v1/orders/first-leg/101/cancel?tenantId=90' )
assert . ok ( body . reason . includes ( 'offline-only' ))
2026-10-02 23:14:24 +08:00
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 )}
2026-09-30 17:13:11 +08:00
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 )
2026-10-02 23:14:24 +08:00
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 )
2026-09-30 17:13:11 +08:00
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/ )
2026-09-30 15:16:39 +08:00
})
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 )
})
2026-10-20 10:35:52 +08:00
function nativeFragmentFixture () {
const context = fixture (), events = [], calls = []
2026-10-23 04:48:02 +08:00
context . createdResources . phase4OverseasPda = { generatedPlanIds : [ '81' , '82' ] }
2026-10-20 10:35:52 +08:00
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' )
2026-10-23 04:48:02 +08:00
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' )
2026-10-20 10:35:52 +08:00
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' )) {
2026-10-23 05:21:30 +08:00
data = { outbound : { outboundNo : 'OB-F40' , dispatchNo : 'DS-F40' , status : 'OUTBOUNDED' , packageCount : 1 },
deliveries : [{ ... delivery , dispatchNo : 'DS-F40' }], pallets : [{ inboundCode : 'IN-F40' }] }
2026-10-20 10:35:52 +08:00
} 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' )
2026-10-22 11:56:47 +08:00
departed = true ; proof . loadingCommand = { replay : { replayed : true } }; return '9007199254740998'
2026-10-20 10:35:52 +08:00
},
2026-10-22 11:56:47 +08:00
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 } } } },
2026-10-20 10:35:52 +08:00
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' ,
2026-10-22 11:56:47 +08:00
'loading-complete' , 'arrange' , 'read-current-depart-version' , 'receipt' , 'normal-completed' , 'finish' ])
2026-10-20 10:35:52 +08:00
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' )
2026-10-22 11:56:47 +08:00
assert . equal ( f . rootProof . dispatches [ 0 ]. loadingReplay . replayed , true )
2026-10-20 10:35:52 +08:00
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 )
})