test(fleet): verify two-customer departure rollback in TEST

This commit is contained in:
2026-10-04 10:32:26 +08:00
parent c46a68e32b
commit 8a735020e3
6 changed files with 206 additions and 2 deletions
@@ -78,6 +78,29 @@ test('current DTO-shaped two-customer trip rejects unready gate, departs once an
assert.ok(m.calls.filter(c=>c.raw.includes('/api/pda/')).every(c=>c.token===m.fixture.pdaToken))
assert.ok(!JSON.stringify(proof).includes(m.fixture.pdaToken))
})
test('explicit rollback verifier runs after both picks and before successful departure',async()=>{
const m=model();let calls=0
const proof=await verifyFleetCrossCustomer(m.context,m.fixture,{execute:true,readInventory:m.readInventory,
verifyDepartureRollback:async({ready,departPath,departBody})=>{
calls++;assert.equal(ready.trip.trip.status,'PLANNED')
assert.ok(ready.members.every(row=>row.dispatch.status==='READY_TO_DEPART'))
assert.equal(m.calls.filter(call=>call.raw.endsWith('/depart')).length,1)
assert.ok(departPath.includes(m.fixture.members[0].dispatchNo));assert.equal(departBody.idempotencyKey,'RUN-CROSS-DEPART')
return {verified:true,scope:'OWNED_TWO_CUSTOMER_DEPARTURE_ROLLBACK'}
}})
assert.equal(calls,1);assert.equal(proof.midTransactionRollback,'VERIFIED')
})
for(const failure of ['unverified','throws','mutates'])test(`rollback verification ${failure} prevents successful departure`,async()=>{
const m=model()
await assert.rejects(verifyFleetCrossCustomer(m.context,m.fixture,{execute:true,readInventory:m.readInventory,
verifyDepartureRollback:async()=>{
if(failure==='throws')throw Error('Observed rollback failed')
if(failure==='mutates')m.state[0].stock.balances[0].availablePackages='-1'
return {verified:failure!=='unverified',scope:'OWNED_TWO_CUSTOMER_DEPARTURE_ROLLBACK'}
}}))
assert.equal(m.calls.filter(call=>call.raw.endsWith('/depart')).length,1)
assert.ok(m.state.every(row=>row.delivery.order.status==='WAIT_DELIVERY'))
})
for(const fault of ['wrong stock scope','missing PDA position','duplicate ship','gate mutates second customer','gate reorders route','outage disguised as rejection','unrelated gate rejection','partial departure','depart deducts again','signs both']) {
test(`fails closed without widening the claimed evidence: ${fault}`,async()=>{
const m=model(fault);await assert.rejects(verifyFleetCrossCustomer(m.context,m.fixture,{execute:true,readInventory:m.readInventory}),