test(fba): preserve verified fleet and fulfillment acceptance workflows

This commit is contained in:
2026-10-03 06:26:33 +08:00
parent c55bd7f4b6
commit d858357c5d
52 changed files with 4217 additions and 103 deletions
@@ -5,7 +5,7 @@ import { fulfillmentHistorySnapshot, validTwoFragmentTree, validSignatureGate,
completionCounts, exactCompletionOutboxProof, resolveF40MerchantCredentials,
f40TransferBody, f40LocalDateTime, validFragmentCabinetIdentity, validUnloadedRemainder,
validRemainderCancellationGate, exactCancelledRemainderOutboxProof, cancelUnloadedRemainder,
verifyRootProjections, validFreshReceiptSave, preserveReceivingEvidence, run } from './phase5-root-fulfillment.mjs'
verifyRootProjections, verifyRemainderTaxIdentity, validFreshReceiptSave, preserveReceivingEvidence, run } from './phase5-root-fulfillment.mjs'
import { loadOneFragment } from './phase3-order-splitting.mjs'
import { verifyPalletLabels } from './phase3-loading-finalize.mjs'
import { awaitProducerConsumption, convertReceivingContainers, runReceiving,
@@ -32,6 +32,50 @@ const fixture = () => ({steps:[],token:'offline',marker:'offline-only',createdRe
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')}}
@@ -232,7 +276,7 @@ test('exported consumer polling and conversion fail closed when real producer co
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)
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() => {