Files
baiyue-firstleg/scripts/fleet-test-env-acceptance.test.mjs
2026-10-21 14:30:41 +08:00

898 lines
75 KiB
JavaScript

import test from 'node:test'
import assert from 'node:assert/strict'
import { mkdtempSync, mkdirSync, readFileSync, writeFileSync, existsSync, rmSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join, dirname, resolve } from 'node:path'
import { AcceptanceRun } from './admin-test-env-acceptance.mjs'
import { run as runInboundMeasurement } from './admin-test-env-modules/inbound-measurement.mjs'
import { verifyPalletLabels } from './admin-test-env-modules/phase3-loading-finalize.mjs'
function departedRecoveryFixture() {
const snapshotTrip={tripNo:'TR-OWN',status:'IN_TRANSIT',recordVersion:'27',vehicleId:'V1',driverId:'D1',warehouseId:'W1',departedAt:'2026-10-03T11:00:00Z'}
const cargo=[{dispatchNo:'OWN',dispatchItemId:'I1',customerId:'C1',warehousePalletId:'P1',packages:2},{dispatchNo:'OWN',dispatchItemId:'I2',customerId:'C1',warehousePalletId:'P2',packages:1}]
const measured=[{warehousePalletId:'P1',palletSpaces:2},{warehousePalletId:'P2',palletSpaces:1}]
const departure={action:'DEPART',recordVersion:'27',snapshot:{trip:snapshotTrip,confirmedLoad:{manifestHash:'a'.repeat(64),cargo,pallets:measured}}}
const refs=[{customerId:'C1',deliveryNo:'DL-OWN',outboundNo:'OUT-C1',packageCount:2},{customerId:'C1',deliveryNo:'DL-EXT',outboundNo:'OUT-C1',packageCount:1}]
const detail={dispatchInfo:{departedAt:snapshotTrip.departedAt},dispatchNo:'OWN',status:'ACTIVE',stage:'DELIVERING',recordVersion:'31',counts:{packageCount:3,pickedPackageCount:3,shippedPackageCount:3},documents:{deliveries:refs}}
const items={dispatchNo:'OWN',recordVersion:'31',items:cargo.map(row=>({dispatchItemId:row.dispatchItemId,customerId:row.customerId,warehousePalletId:row.warehousePalletId,scheduledPackages:row.packages,pickedPackages:row.packages}))}
const orders=refs.map(ref=>({...ref,dispatchNo:'OWN',status:'DELIVERING',version:'1',documentSummary:{categories:[{fileCount:0}]}}))
const proof={protocol:'NATIVE_DISPATCH_V1',marker:'RUN',tripNo:'TR-OWN',dispatchNo:'OWN',warehouseId:'W1',customerId:'C1',status:'CHECKING',stage:'TRIP_OPERATING_COSTS',checkpoints:[],
dispatchNos:['OWN'],picker:{membershipId:'OLD-PDA'},departureRequest:{idempotencyKey:'old-key'},departureSnapshot:{detail:structuredClone(detail),items:structuredClone(items)},departureDeliveries:structuredClone(orders),
departure:{action:'LOADING_COMPLETE',dispatchNo:'OWN',status:'ACTIVE',stage:'DELIVERING',recordVersion:'31',result:{loadingCompletionHash:'HASH',loadingCompletedAt:snapshotTrip.departedAt,confirmedLoad:{manifestHash:'a'.repeat(64)}}}}
const trip={marker:'RUN',tripNo:'TR-OWN',dispatchNo:'OWN',warehouseId:'W1',confirmedLoad:{manifestHash:'a'.repeat(64),pallets:measured},costAcceptance:{marker:'RUN',status:'FAILED',costs:[],departure,
requests:[{method:'GET',status:'RESPONDED',http:200,code:'200',requestId:'r',traceId:'t'}]},externalReceipt:{status:'VERIFIED',bookingNo:'BK',warehousePalletId:'P2'},externalDispatch:{dispatchNo:'OWN',deliveryNo:'DL-EXT'}}
const report={success:false,steps:[{name:'独立预期覆盖全部实物且没有重叠托盘',passed:false},{name:'模块执行',passed:false}],createdResources:{fleetAcceptance:proof,fleetTrips:[trip],fleetCostActors:{status:'DISABLED'},fleetFeeCoverageCases:[{mode:'PARTIAL',dispatchNo:'OWN',proof:{deliveryNos:['DL-OWN','DL-EXT']}}]}}
const base='/api/platform/v1/last-leg/fleet/trips/TR-OWN'
const responses=new Map([[base,{trip:structuredClone(snapshotTrip),dispatches:[{dispatchNo:'OWN'}]}],
[base+'/history?page=1&size=100',{total:1,records:[structuredClone(departure)]}],
[base+'/costs?page=1&size=100',{page:{records:[],total:0},summary:{manifestHash:'a'.repeat(64),departureVersion:'27',totals:[]}}],[base+'/handovers',{records:[],total:0}],
['/api/platform/v1/last-leg/dispatches/OWN',structuredClone(detail)],['/api/platform/v1/last-leg/dispatches/OWN/items',structuredClone(items)]])
for(const order of orders)responses.set(`/api/platform/v1/last-leg/delivery-orders/${order.deliveryNo}?customerId=C1`,{order:structuredClone(order)})
const calls=[],ctx={marker:'RUN',token:'writer',context:{},createdResources:report.createdResources,async checkpoint(){calls.push('checkpoint')},
async request(method,path){assert.equal(method,'GET','recovery verification must never write');calls.push(path);assert.ok(responses.has(path),path);return {http:200,body:{code:'200',data:responses.get(path)},requestId:'r',traceId:'t'}}}
return {report,ctx,calls,responses,base,trip}
}
function createdVehicleRecoveryFixture() {
const f = afterCostsRecoveryFixture(), snapshot = { id: '2106360046045675522', plateNumber: 'HO-24255909-3BC', recordVersion: '0',
status: 'IN_SERVICE', ownership: 'OWNED', region: 'OVERSEAS', maxPalletSpaces: 30, maxLoadKg: 18000, maxVolumeCbm: 90 }
f.report.steps[0].name = '接替车辆返回真实身份及车牌'
const request = { method: 'POST', path: '/api/platform/v1/last-leg/master-data/vehicles', body: { plateNumber: 'HO-24255909-3bc' },
status: 'RESPONDED', http: 200, code: '200', requestId: 'created-r', traceId: 'created-t', result: snapshot }
f.trip.handover.requests.push(request)
f.responses.set('/api/platform/v1/last-leg/master-data/vehicles/' + snapshot.id, structuredClone(snapshot))
f.ctx.context.readReviewedVehicleOccupation = async vehicleId => ({ vehicleId, assignmentCount: 0 })
return { ...f, snapshot, request }
}
function returnSourceRecoveryFixture() {
const f=afterCostsRecoveryFixture(),{report,trip,responses,base,ctx}=f,run=report.createdResources.fleetAcceptance
run.stage='RETURN_REDISPATCH';run.receipts=[];run.departure.result.shippedPackages=5
report.steps=[{name:'模块执行',passed:false,evidence:'Fresh redispatch fixture requires source A with at least 3 boxes and a separate domestic source B'}]
const current={...responses.get(base).trip,recordVersion:'30',vehicleId:'V2',driverId:'D2',vehiclePlate:'NEW',driverName:'NEW',driverPhone:'NEW'}
responses.get(base).trip=current
const original=trip.costAcceptance.departure
original.snapshot.confirmedLoad.cargo=[{dispatchNo:'OWN',customerId:'C1',dispatchItemId:'IA',warehousePalletId:'PA',packages:3},
{dispatchNo:'OWN',customerId:'C1',dispatchItemId:'IB',warehousePalletId:'PB',packages:1},{dispatchNo:'OWN',customerId:'C1',dispatchItemId:'IX',warehousePalletId:'PX',packages:1}]
const history={records:[structuredClone(original),{action:'HANDOVER',recordVersion:'28'},{action:'RELEASE',recordVersion:'29'},{action:'RELEASE',recordVersion:'30'}],total:4}
responses.set(base+'/history?page=1&size=100',history)
const handover={records:[{handoverNo:'HO-CURRENT',recordVersion:'2'}],total:1},vehicle={id:'V2',recordVersion:'0',plateNumber:'NEW',status:'IN_SERVICE'},driver={id:'D2',recordVersion:'2',active:true,reviewStatus:'APPROVED',licenseFileId:'FILE',details:{}}
trip.handover={status:'VERIFIED',finalTrip:structuredClone(current),handoverNo:'HO-CURRENT',requests:[
{method:'GET',path:base+'/history?page=1&size=100',result:structuredClone(history)},{method:'GET',path:base+'/handovers?page=1&size=10',result:structuredClone(handover)},
{method:'POST',path:'/api/platform/v1/last-leg/master-data/vehicles',result:structuredClone(vehicle)},{method:'POST',path:'/api/platform/v1/last-leg/fleet/drivers/D2/review',result:structuredClone(driver)}]}
responses.set(base+'/handovers',handover);responses.set('/api/platform/v1/last-leg/master-data/vehicles/V2',vehicle);responses.set('/api/platform/v1/last-leg/fleet/drivers/D2',driver)
ctx.context.readRecoveryTripResources=async()=>[{resourceKind:'VEHICLE',resourceId:'V2',tripNo:'TR-OWN'},{resourceKind:'DRIVER',resourceId:'D2',tripNo:'TR-OWN'}]
const rows=[['A',3],['B',1],['EXT',1]]
const originals=rows.map(([label,packages])=>({deliveryNo:'DL-'+label,outboundNo:'OUT-C1',dispatchNo:'OWN',customerId:'C1',overseasWarehouseId:'W1',status:'DELIVERING',version:'3',packageCount:packages,documentSummary:{categories:[{fileCount:0}]}}))
trip.returnRedispatch={marker:'RUN',status:'FAILED',failure:report.steps[0].evidence,originalDeliveries:originals,
requests:originals.map(order=>({method:'GET',path:`/api/platform/v1/last-leg/delivery-orders/${order.deliveryNo}?customerId=C1`,status:'RESPONDED',http:200,code:'200',requestId:'r',traceId:'t',result:{order:structuredClone(order)}}))}
report.createdResources.fleetFeeCoverageCases[0].proof.deliveryNos=['DL-A','DL-B','DL-EXT']
const detail={dispatchInfo:{departedAt:original.snapshot.trip.departedAt},dispatchNo:'OWN',status:'ACTIVE',stage:'DELIVERING',recordVersion:'31',counts:{packageCount:5,pickedPackageCount:5,shippedPackageCount:5},documents:{deliveries:originals.map(o=>({deliveryNo:o.deliveryNo,outboundNo:o.outboundNo,customerId:o.customerId,packageCount:o.packageCount}))}}
const items={dispatchNo:'OWN',recordVersion:'31',items:original.snapshot.confirmedLoad.cargo.map(c=>({dispatchItemId:c.dispatchItemId,customerId:c.customerId,warehousePalletId:c.warehousePalletId,scheduledPackages:c.packages,pickedPackages:c.packages}))}
run.departureSnapshot={detail:structuredClone(detail),items:structuredClone(items)}
responses.set('/api/platform/v1/last-leg/dispatches/OWN',detail);responses.set('/api/platform/v1/last-leg/dispatches/OWN/items',items)
for(const[label,packages]of rows){const order=originals.find(o=>o.deliveryNo==='DL-'+label)
responses.set(`/api/platform/v1/last-leg/delivery-orders/${order.deliveryNo}?customerId=C1`,{order:{...structuredClone(order),vehiclePlate:'NEW',driverName:'NEW',driverPhone:'NEW'}})
const relation={lastMileOrderId:label,inboundCode:'IN-'+label,deliveryNo:order.deliveryNo,outboundNo:order.outboundNo,returnSources:[{maxReturnPackages:packages}]}
responses.set(`/api/platform/v1/last-leg/delivery-orders/${order.deliveryNo}/return-inbound-options?customerId=C1`,[relation])
responses.set(`/api/platform/v1/last-leg/orders/${label}?customerId=C1`,{order:{id:label,customerId:'C1',overseasWarehouseId:'W1',orderType:label==='EXT'?'WAREHOUSE_TRANSFER':'FIRST_LEG',sourceInboundId:'SOURCE-'+label,inboundCode:relation.inboundCode,packageCount:packages}})
}
return f
}
test('source-selection recovery accepts three definitive native reads and rejects any actual return or unknown result', () => {
const f = returnSourceRecoveryFixture()
assert.equal(reviewedFullCheckpoint(f.report, 'RETURN_SOURCE_SELECTION_BEFORE_RETURN').returnSourceSelection, true)
for (const change of [x=>x.trip.returnRedispatch.requests.push({ method:'POST',path:'/mark-return' }),
x=>x.trip.returnRedispatch.requests[0].status='UNKNOWN_RESULT',x=>x.trip.returnRedispatch.requests[0].result.replayed=true]) {
const mutated = returnSourceRecoveryFixture(); change(mutated)
assert.throws(()=>reviewedFullCheckpoint(mutated.report,'RETURN_SOURCE_SELECTION_BEFORE_RETURN'), /source recovery/)
}
})
for (const fault of ['none','candidate-empty','delivery-waiting','resource-wrong','cost-changed','handover-changed'])
test(`source recovery validates current business facts and repaired candidates before PDA write: ${fault}`,async()=>{
const f=returnSourceRecoveryFixture()
if(fault==='candidate-empty')f.responses.get('/api/platform/v1/last-leg/delivery-orders/DL-A/return-inbound-options?customerId=C1')[0].returnSources=[]
if(fault==='delivery-waiting')f.responses.get('/api/platform/v1/last-leg/delivery-orders/DL-A?customerId=C1').order.status='WAIT_DELIVERY'
if(fault==='resource-wrong')f.ctx.context.readRecoveryTripResources=async()=>[]
if(fault==='cost-changed')f.responses.get(f.base+'/costs?page=1&size=100').summary.totals[0].directCost='99.00'
if(fault==='handover-changed')f.responses.get(f.base+'/handovers').records[0].recordVersion='3'
if(fault==='none'){await verifyReturnSourceRecovery(f.ctx);assert.equal(f.ctx.createdResources.fleetDepartureRecoveryVerification.returnSourceSelection.a.tail.packageCount,3)}
else await assert.rejects(continueReviewedReturnSources(f.ctx,{makePicker:()=>assert.fail('no PDA write before verification')}))
})
test('source recovery preserves original costs/handover and invokes only actual return continuation and closure',async()=>{
const f=returnSourceRecoveryFixture(), operations=[], costs=f.trip.costAcceptance,hand=f.trip.handover
f.ctx.context.saveFleetReturnRedispatchFixture=()=>operations.push('save-return-proof')
await continueReviewedReturnSources(f.ctx,{resolveAccess:async()=>({}),makePicker:async()=>({token:'new-pda',membershipId:'NEW'}),closePicker:async()=>operations.push('close-pda'),
redispatch:async(_ctx,_module,trip,input)=>{assert.equal(input.sourceSelectionRecovery,true);assert.equal(trip.costAcceptance,costs);assert.equal(trip.handover,hand);operations.push('return-candidates');return {originalSignatures:[{deliveryNo:'DL-A',signedAt:'signed'}]}},
closeOriginal:async()=>{operations.push('close-original');return {status:'CARGO_COMPLETED_AND_RESOURCES_RELEASED'}},cleanup:async()=>operations.push('cleanup')})
assert.deepEqual(operations,['return-candidates','save-return-proof','close-original','cleanup','close-pda'])
})
test('created-vehicle checkpoint requires exact successful uppercase response and no subsequent writes', () => {
const { report } = createdVehicleRecoveryFixture()
const accepted = reviewedFullCheckpoint(report, 'DEPARTED_VEHICLE_CREATED')
assert.equal(accepted.createdVehicle.snapshot.plateNumber, 'HO-24255909-3BC')
assert.equal(accepted.createdVehicle.creationRequest.body.plateNumber, 'HO-24255909-3bc')
for (const mutation of [f => f.request.http = 0, f => f.request.status = 'UNKNOWN_RESULT', f => f.request.code = 'A0409',
f => f.request.traceId = null, f => f.trip.handover.requests.push({ method: 'POST', path: '/drivers' })]) {
const f = createdVehicleRecoveryFixture(); mutation(f)
assert.throws(() => reviewedFullCheckpoint(f.report, 'DEPARTED_VEHICLE_CREATED'), /Created vehicle recovery/)
}
assert.throws(() => reviewedFullCheckpoint(report, 'DEPARTED_AFTER_COSTS_BEFORE_HANDOVER'), /checkpoint/)
})
for (const fault of ['none', 'changed', 'occupied', 'missing-reader']) test(`created vehicle exact snapshot and no occupation mandatory: ${fault}`, async () => {
const f = createdVehicleRecoveryFixture(), accepted = reviewedFullCheckpoint(f.report, 'DEPARTED_VEHICLE_CREATED')
if (fault === 'changed') f.responses.get('/api/platform/v1/last-leg/master-data/vehicles/' + f.snapshot.id).recordVersion = '1'
if (fault === 'occupied') f.ctx.context.readReviewedVehicleOccupation = async vehicleId => ({ vehicleId, assignmentCount: 1 })
if (fault === 'missing-reader') delete f.ctx.context.readReviewedVehicleOccupation
const options = { costsAlreadyVerified: true, createdVehicle: accepted.createdVehicle }
if (fault === 'none') { const r = await verifyDepartedRecovery(f.ctx, options); assert.equal(r.reviewedVehicle.snapshot.id, f.snapshot.id) }
else await assert.rejects(continueReviewedDeparture(f.ctx, { ...options, makePicker: () => assert.fail('no session before verification') }), /recovery refused/)
})
test('occupancy reader is scoped to exact numeric vehicle and readonly evidence transport', async () => {
const env = { JENKINS_URL: 'https://test.invalid/', JENKINS_USER: 'reader', JENKINS_API_TOKEN: 'secret', FBA_CORE_JENKINS_AGENT: 'java-test-agent' }
const result = await readReviewedVehicleOccupation(env, '123', async (url, init) => {
assert.equal(url.pathname, '/scriptText'); assert.equal(init.method, 'POST'); assert.ok(init.body.get('script').includes('executeGroovy'))
return new Response('FBA_CORE_JSON={"ok":true,"rows":[{"vehicleId":"123","assignmentCount":0}]}')
})
assert.deepEqual(result, { vehicleId: '123', assignmentCount: 0 })
await assert.rejects(readReviewedVehicleOccupation(env, '123; DELETE', () => assert.fail('invalid identity')), /numeric/)
})
function afterCostsRecoveryFixture() {
const fixture = departedRecoveryFixture(), { report, trip, responses, base } = fixture
report.createdResources.fleetAcceptance.stage = 'IN_TRANSIT_HANDOVER'
report.steps[0].name = '每张排车已发车且货量与客户起运仓一致'
trip.costAcceptance.status = 'VERIFIED'
trip.costAcceptance.costs = [{ costNo: 'CNY-COST' }, { costNo: 'USD-COST' }]
trip.costAcceptance.final = { page: { records: structuredClone(trip.costAcceptance.costs), total: 2 },
summary: { manifestHash: 'a'.repeat(64), departureVersion: '27', totals: [{ currency: 'CNY', directCost: '100.03' }, { currency: 'USD', directCost: '13.07' }], allocationBlockReason: 'MULTI_STOP' } }
trip.handover = { status: 'FAILED', requests: [{ method: 'GET', status: 'RESPONDED', http: 200, code: '200' }] }
responses.set(base + '/costs?page=1&size=100', structuredClone(trip.costAcceptance.final))
return fixture
}
test('after-cost recovery requires its exact known read-only handover failure and rejects any handover write', () => {
const { report } = afterCostsRecoveryFixture()
assert.equal(reviewedFullCheckpoint(report, 'DEPARTED_AFTER_COSTS_BEFORE_HANDOVER').costsAlreadyVerified, true)
report.createdResources.fleetTrips[0].handover.requests.push({ method: 'POST', status: 'RESPONDED', http: 200, code: '200' })
assert.throws(() => reviewedFullCheckpoint(report, 'DEPARTED_AFTER_COSTS_BEFORE_HANDOVER'), /checkpoint/)
})
test('after-cost recovery checks original cost contents and refuses a changed amount before any write', async () => {
const { ctx, responses, base } = afterCostsRecoveryFixture()
responses.get(base + '/costs?page=1&size=100').summary.totals[0].directCost = '100.04'
await assert.rejects(continueReviewedDeparture(ctx, { costsAlreadyVerified: true,
prepareActors: () => assert.fail('no new actors'), makePicker: () => assert.fail('no new PDA before verification') }), /costs already written or cargo basis changed/)
})
test('after-cost recovery retains verified costs and only enters handover, signatures and resource closure', async () => {
const { ctx, trip } = afterCostsRecoveryFixture(), costs = trip.costAcceptance, oldHandover = trip.handover, operations = []
await continueReviewedDeparture(ctx, { costsAlreadyVerified: true, prepareActors: () => assert.fail('verified costs need no new cost actors'),
resolveAccess: async () => ({}), makePicker: async () => ({ token: 'new-pda', membershipId: 'NEW-PDA' }), closePicker: async () => operations.push('close-pda'),
complete: (context, trip, picker, details, options) => completeDepartedFleetScenario(context, trip, picker, details, { ...options,
verifyCosts: () => assert.fail('costs must never be recreated'), handover: async () => operations.push('handover'),
completeDelivery: async (_ctx, order) => { operations.push('sign:' + order.deliveryNo); return { order: { status: 'DELIVERED', signedAt: 'signed' } } },
closeTrip: async () => { operations.push('release'); return { status: 'CARGO_COMPLETED_AND_RESOURCES_RELEASED' } }, cleanup: async () => operations.push('cleanup'),
}) })
assert.equal(trip.costAcceptance, costs); assert.equal(trip.previousReadOnlyHandoverAttempts[0], oldHandover)
assert.deepEqual(operations, ['handover', 'sign:DL-OWN', 'sign:DL-EXT', 'release', 'cleanup', 'close-pda'])
})
test('departed recovery requires reviewed SHA and exactly the original read-only cost failure', () => {
assert.throws(() => parseFleetArguments(['--resume-report', 'reports/r5/report.json', '--resume-stage', 'DEPARTED_BEFORE_COSTS']), /SHA256/)
const args = ['--resume-report', 'reports/r5/report.json', '--resume-stage', 'DEPARTED_BEFORE_COSTS', '--resume-sha256', 'a'.repeat(64)]
assert.equal(parseFleetArguments(args).resumeSha256, 'a'.repeat(64))
assert.throws(() => parseFleetArguments([...args, '--scenario', 'return-redispatch']), /fresh run/)
assert.equal(reviewedFullCheckpoint(departedRecoveryFixture().report, 'DEPARTED_BEFORE_COSTS').departed, true)
for (const change of [r => r.createdResources.fleetTrips[0].costAcceptance.requests.push({ method: 'POST' }),
r => r.createdResources.fleetTrips[0].handover = {}, r => r.createdResources.fleetAcceptance.receipts = [],
r => r.createdResources.fleetCostActors.status = 'READY', r => r.steps[0].name = 'unknown write']) {
const { report } = departedRecoveryFixture(); change(report)
assert.throws(() => reviewedFullCheckpoint(report, 'DEPARTED_BEFORE_COSTS'), /checkpoint/)
}
})
for (const mutation of ['none', 'version', 'history', 'cost', 'handover', 'cargo', 'signature', 'document'])
test(`recovery rechecks actual state before any session/business write: ${mutation}`, async () => {
const { ctx, responses, base } = departedRecoveryFixture()
if (mutation === 'version') responses.get(base).trip.recordVersion = '28'
if (mutation === 'history') responses.get(base + '/history?page=1&size=100').records[0].snapshot.confirmedLoad.manifestHash = 'changed'
if (mutation === 'cost') responses.get(base + '/costs?page=1&size=100').page.records.push({ costNo: 'COST' })
if (mutation === 'handover') responses.get(base + '/handovers').records.push({ handoverNo: 'H' })
if (mutation === 'cargo') responses.get('/api/platform/v1/last-leg/dispatches/OWN/items').items[0].scheduledPackages = 3
const delivery = responses.get('/api/platform/v1/last-leg/delivery-orders/DL-OWN?customerId=C1').order
if (mutation === 'signature') delivery.signedAt = 'now'
if (mutation === 'document') delivery.documentSummary.categories[0].fileCount = 1
if (mutation === 'none') { await verifyDepartedRecovery(ctx); assert.equal(ctx.createdResources.fleetDepartureRecoveryVerification.status, 'VERIFIED') }
else await assert.rejects(continueReviewedDeparture(ctx, { prepareActors: () => assert.fail('no new actors before validation') }), /recovery refused/)
})
test('reviewed recovery archives old evidence and creates fresh sessions before shared post-departure flow only', async () => {
const { ctx, trip, calls } = departedRecoveryFixture(); const oldCost = trip.costAcceptance
await continueReviewedDeparture(ctx, {
prepareActors: async () => { assert.equal(ctx.createdResources.fleetDepartureRecoveryVerification.status, 'VERIFIED'); calls.push('new-actors'); return { marker: 'RUN', listOnlyToken: 'reader', deniedToken: 'denied', async close() { calls.push('close-actors') } } },
resolveAccess: async () => ({}), makePicker: async child => { assert.match(child.marker, /RECOVERY-PDA/); calls.push('new-picker'); return { membershipId: 'NEW', token: 'new-pda' } },
closePicker: async () => calls.push('close-picker'),
complete: async (context, actualTrip, picker, dispatches) => {
assert.equal(actualTrip, trip); assert.equal(trip.costAcceptance, undefined); assert.equal(picker.membershipId, 'NEW'); assert.equal(dispatches.length, 1)
assert.equal(trip.previousReadOnlyCostAttempts[0], oldCost); assert.equal(context.createdResources.fleetAcceptance.previousPicker.membershipId, 'OLD-PDA')
calls.push('continue-from-costs')
},
})
assert.deepEqual(calls.filter(x => !x.startsWith('/') && x !== 'checkpoint'), ['new-actors', 'new-picker', 'continue-from-costs', 'close-picker', 'close-actors'])
})
test('shared continuation starts at costs, retains unavailable allocation and never enters picking or DEPART', async () => {
const { ctx, trip, calls } = departedRecoveryFixture()
const verified = await verifyDepartedRecovery(ctx), operations = []
await completeDepartedFleetScenario(ctx, trip, { token: 'pda' }, verified.dispatchDetails, {
costActors: { marker: 'RUN' },
verifyCosts: async () => { operations.push('costs'); trip.costAcceptance = { status: 'VERIFIED', expected: { allocationStatus: 'UNAVAILABLE' } } },
handover: async () => { assert.equal(trip.costAcceptance.expected.allocationStatus, 'UNAVAILABLE'); operations.push('handover') },
completeDelivery: async (context, order) => { operations.push('signature:' + order.deliveryNo); return { order: { status: 'DELIVERED', signedAt: 'signed' } } },
closeTrip: async () => { operations.push('close-trip'); return { status: 'CARGO_COMPLETED_AND_RESOURCES_RELEASED' } },
cleanup: async () => operations.push('cleanup'),
})
assert.deepEqual(operations, ['costs', 'handover', 'signature:DL-OWN', 'signature:DL-EXT', 'close-trip', 'cleanup'])
assert.equal(ctx.createdResources.fleetAcceptance.stage, 'FINISHED')
assert.ok(!calls.some(x => /\/depart$|\/pick|\/loading/.test(x)))
})
test('changed source report SHA blocks recovery before creating context or logging in', async () => {
const temporary = mkdtempSync(join(tmpdir(), 'fleet-recovery-sha-'))
const root = join(temporary, 'chongchong-global', 'baiyue-firstleg'); mkdirSync(join(root, 'reports', 'original'), { recursive: true })
writeFileSync(join(root, 'reports', 'original', 'report.json'), JSON.stringify(departedRecoveryFixture().report))
try {
const result = await runFleetAcceptance({ execute: true, environment: 'TEST', runId: 'reviewed', resumeStage: 'DEPARTED_BEFORE_COSTS',
resumeReport: 'reports/original/report.json', resumeSha256: 'b'.repeat(64) }, { root,
env: { CHONGCHONG_ENV: 'test', CHONGCHONG_ADMIN_API_BASE_URL: 'https://test.invalid', CHONGCHONG_ADMIN_USERNAME: 'admin',
CHONGCHONG_ADMIN_PASSWORD: 'secret', ADMIN_PDA_USERNAME: 'pda', ADMIN_PDA_PASSWORD: 'secret' },
createContext: () => assert.fail('SHA mismatch must precede context/network'),
})
assert.equal(result.success, false); assert.match(result.fatal, /SHA256 mismatch/)
} finally {
assert.equal(dirname(resolve(temporary)), resolve(tmpdir()))
assert.ok(resolve(temporary).startsWith(join(resolve(tmpdir()), 'fleet-recovery-sha-')))
rmSync(temporary, { recursive: true, force: true })
}
})
import { selectNativeFleetFullSource, nativeFleetDeliveries, departNativeFleet, currentFleetMeasurements, parseFleetArguments, fleetExecutionPlan, runFleetAcceptance, reviewedFullCheckpoint, verifyFleetTerminalState, runFleetHandoverStage, fleetCostShares, prepareFleetCostActors, findFleetActorPhone, guardFleetWrites, redactFleetWrite, verifyDepartedRecovery, continueReviewedDeparture, completeDepartedFleetScenario, readReviewedVehicleOccupation, verifyReturnSourceRecovery, continueReviewedReturnSources } from './fleet-test-env-acceptance.mjs'
for (const response of [
{ http: 0 }, { http: 503, body: { code: 'A0500' }, requestId: 'r', traceId: 't' },
{ http: 200, body: null }, { http: 200, body: { code: '200' } },
{ http: 302, body: { code: '200' }, requestId: 'r', traceId: 't' },
]) test(`unknown prerequisite write stops every subsequent request: ${JSON.stringify(response)}`, async () => {
const saved = [], calls = []
const ctx = { async request(method, path, body) {
assert.equal(saved.at(-1).status, 'SENDING')
assert.deepEqual(saved.at(-1).body, body)
calls.push(path); return response
} }
guardFleetWrites(ctx, entry => saved.push(structuredClone(entry)))
const body = { expectedVersion: 6, idempotencyKey: 'original-key', internalNotes: ['test remark'] }
await assert.rejects(ctx.request('PUT', '/orders/1/remarks?tenantId=2', body), /Unknown write outcome/)
body.expectedVersion = 99
assert.equal(saved[0].body.expectedVersion, 6)
assert.equal(saved[1].status, 'UNKNOWN_RESULT')
assert.equal(ctx.createdResources.fleetWriteBoundary.status, 'STOPPED')
for (const method of ['POST', 'GET', 'DELETE']) await assert.rejects(ctx.request(method, '/must-not-run'), /Unknown write outcome/)
assert.deepEqual(calls, ['/orders/1/remarks?tenantId=2'])
})
test('formal correlated rejection is recorded and remains available for expected negative assertions', async () => {
const saved = [], response = { http: 200, body: { code: 'A0409' }, requestId: 'r', traceId: 't' }
const ctx = { async request() { return response } }
guardFleetWrites(ctx, entry => saved.push(structuredClone(entry)))
assert.equal(await ctx.request('POST', '/negative', { recordVersion: 7 }), response)
assert.equal(saved[1].status, 'RESPONDED')
await ctx.request('GET', '/readback'); assert.equal(saved.length, 2)
})
test('response evidence preserves business messages and returned resource IDs with explicit session provenance and no tokens', async () => {
const saved = [], ctx = { token: 'admin-session', pdaToken: 'pda-session', options: { username: 'test-admin' },
async request() { return { http: 200, body: { code: '200', message: 'created', data: {
id: '123', businessNo: 'FIXTURE-123', accessToken: 'new-secret', refreshToken: 'refresh-secret' } }, requestId: 'r', traceId: 't' } } }
guardFleetWrites(ctx, entry => saved.push(redactFleetWrite(structuredClone(entry))))
await ctx.request('POST', '/create', { expectedVersion: 3 })
assert.deepEqual(saved[0].actor, { kind: 'PLATFORM', source: 'CONTEXT_ADMIN_SESSION' })
assert.equal(saved[1].responseBody.message, 'created'); assert.equal(saved[1].responseBody.data.id, '123')
await ctx.request('POST', '/pda', {}, 'pda-session'); assert.equal(saved[2].actor.kind, 'PDA')
await ctx.request('POST', '/other', {}, 'unresolved-session'); assert.equal(saved[4].actor.kind, 'UNKNOWN')
await ctx.request('POST', '/login', {}, ''); assert.equal(saved[6].actor.source, 'NO_SESSION')
for (const secret of ['admin-session', 'pda-session', 'new-secret', 'refresh-secret', 'unresolved-session'])
assert.ok(!JSON.stringify(saved).includes(secret))
})
test('POST carton-label PDF is a definite binary response with durable hash and permits same-fixture readback', async () => {
const saved = [], calls = [], response = { http: 200, body: null, bytes: Buffer.from('%PDF-1.7\nfixture'),
contentType: 'application/pdf', requestId: 'r', traceId: 't' }
const ctx = { async requestBinary(method, path) {
assert.equal(saved.at(-1).status, 'SENDING'); calls.push(path); return response
}, async request() { calls.push('readback') } }
guardFleetWrites(ctx, entry => saved.push(structuredClone(entry)))
const path = '/api/platform/inbounds/1/carton-label-pdf?tenantId=2&cartonIndex=1'
assert.equal(await ctx.requestBinary('POST', path), response)
assert.equal(saved[0].path, path); assert.equal(saved[1].status, 'RESPONDED')
assert.equal(saved[1].binary.bytes, response.bytes.length); assert.match(saved[1].binary.sha256, /^[a-f0-9]{64}$/)
assert.equal(saved[1].binary.validPdf, true)
await ctx.request('GET', '/api/platform/inbounds/1'); assert.deepEqual(calls, [path, 'readback'])
})
test('real inbound prerequisite and AcceptanceRun binary transport accept three PDF writes then stop raw multipart retries', async t => {
const calls = [], saved = [], summary = { id: '1', tenantId: '2', processStatus: 'WAIT_MEASURE', inboundCode: 'FIXTURE-INBOUND', totalPackageCount: 2 }
const native = t.mock.method(globalThis, 'fetch', async (input, init) => {
const req = new Request(input, init), path = new URL(req.url).pathname
calls.push({ method: req.method, path })
const headers = { 'x-request-id': 'r', 'x-trace-id': 't' }
if (req.method === 'GET') return new Response(JSON.stringify({ code: '200', data: { summary } }), { headers: { ...headers, 'content-type': 'application/json' } })
assert.equal(saved.at(-1).status, 'SENDING')
if (path.endsWith('/carton-label-pdf')) return new Response('%PDF-1.7\nfixture', { headers: { ...headers, 'content-type': 'application/pdf' } })
assert.ok(path.endsWith('/measurement/carton-scans'))
throw Error('transport failure Bearer sensitive-session https://example.invalid/private')
})
const context = new AcceptanceRun({ gateway: 'https://test.invalid' })
context.createdResources.orders = { marker: context.marker, inboundId: '1', tenantId: '2' }
const restore = guardFleetWrites(context, entry => saved.push(redactFleetWrite(structuredClone(entry))), { guardFetch: true })
try {
await runInboundMeasurement(context)
assert.equal(calls.filter(row => row.path.endsWith('/carton-label-pdf')).length, 3)
assert.equal(calls.filter(row => row.path.endsWith('/measurement/carton-scans')).length, 1, 'raw helper retries must not reach transport')
const pdfs = saved.filter(row => row.binary?.validPdf)
assert.equal(pdfs.length, 3); assert.ok(pdfs.every(row => row.status === 'RESPONDED'))
assert.equal(context.steps.find(row => row.name === '生成箱唛 PDF 并冻结稳定箱码').passed, true)
const last = saved.at(-1); assert.equal(last.status, 'UNKNOWN_RESULT')
assert.ok(last.body.multipart.some(row => row.field === 'data' && row.base64))
assert.ok(last.raw.includes('transport failure')); assert.ok(!last.raw.includes('sensitive-session')); assert.ok(!last.raw.includes('example.invalid'))
await assert.rejects(context.request('POST', '/subsequent'), /transport failure/)
} finally { restore(); assert.equal(globalThis.fetch, native); t.mock.restoreAll() }
})
test('raw JSON prerequisite writes retain headers, original version and bounded diagnostics without duplicate journaling', async t => {
let calls = 0
t.mock.method(globalThis, 'fetch', async () => { calls++; return new Response('x'.repeat(600), { status: 502 }) })
const saved = [], context = new AcceptanceRun({ gateway: 'https://test.invalid' })
const restore = guardFleetWrites(context, entry => saved.push(redactFleetWrite(structuredClone(entry))), { guardFetch: true })
try {
await assert.rejects(fetch('https://test.invalid/approve', { method: 'PUT', headers: { 'content-type': 'application/json',
Authorization: 'Bearer secret', 'Idempotency-Key': 'same-key' }, body: JSON.stringify({ expectedVersion: 6 }) }), /Unknown write outcome/)
assert.equal(saved.length, 2); assert.equal(saved[0].body.expectedVersion, 6)
assert.equal(saved[0].headers['idempotency-key'], 'same-key'); assert.equal(saved[0].headers.authorization, '[REDACTED]')
assert.equal(saved[1].raw.length, 500)
await assert.rejects(fetch('https://test.invalid/retry', { method: 'POST' }), /Unknown write outcome/)
assert.equal(calls, 1)
} finally { restore(); t.mock.restoreAll() }
})
for (const invalidPdf of [false, true, 'zip']) test(`real loading finalize prints both PDFs through guarded direct fetch; invalid=${invalidPdf}`, async t => {
const saved = [], calls = [], fixture = { destinationType: 'FBA', warehouseId: '1', containerNo: 'CONTAINER', cabinetNo: 'CABINET' }
const manifest = { containerNo: fixture.containerNo, totalCount: 1, groups: [{ destinationWarehouseCode: 'FBA', labels: [{
containerNo: fixture.containerNo, destinationWarehouseInternalCode: 'F01', palletIndex: 1, palletCode: 'CABINET-F01-001' }] }] }
t.mock.method(globalThis, 'fetch', async (input, init) => {
const req = new Request(input, init); calls.push(req.url)
assert.equal(saved.at(-1).status, 'SENDING')
const headers = { 'x-request-id': 'r', 'x-trace-id': 't' }
return req.url.includes('/prepare?')
? new Response(JSON.stringify({ code: '200', data: manifest }), { headers: { ...headers, 'content-type': 'application/json' } })
: new Response(invalidPdf === true ? '<html>proxy failure</html>' : '%PDF-1.6\nfixture',
{ headers: { ...headers, 'content-type': invalidPdf === 'zip' ? 'application/zip' : 'application/pdf' } })
})
const ctx = new AcceptanceRun({ gateway: 'https://test.invalid' })
const restore = guardFleetWrites(ctx, entry => saved.push(redactFleetWrite(structuredClone(entry))), { guardFetch: true })
try {
await verifyPalletLabels(ctx, fixture, '2')
const prints = saved.filter(row => row.path.includes('/print?'))
assert.equal(calls.filter(url => url.includes('/print?')).length, invalidPdf ? 1 : 2)
assert.equal(prints.at(-1).status, invalidPdf ? 'UNKNOWN_RESULT' : 'RESPONDED')
assert.equal(prints.at(-1).binary.validPdf, !invalidPdf)
assert.equal(prints.at(-1).raw, null, 'binary response bytes must not enter text evidence')
assert.equal(prints[0].body.destinationWarehouseCode, null)
if (!invalidPdf) { assert.equal(prints[2].body.destinationWarehouseCode, 'FBA'); assert.ok(ctx.steps.every(row => row.passed)) }
else await assert.rejects(ctx.request('POST', '/no-retry'), /Unknown write outcome/)
} finally { restore(); t.mock.restoreAll() }
})
for (const override of [{ http: 0 }, { http: 503 }, { bytes: Buffer.from('<html>proxy</html>') },
{ bytes: new Uint8Array() }, { contentType: 'text/html' }, { traceId: null },
{ contentType: 'application/json', bytes: Buffer.from('{"code":"200"}'), body: { code: '200' } }])
test(`binary write still stops on unknown or invalid response: ${JSON.stringify(override)}`, async () => {
const saved = [], ctx = { async requestBinary() { return { http: 200, body: null, bytes: Buffer.from('%PDF-1.7'),
contentType: 'application/pdf', requestId: 'r', traceId: 't', ...override } }, async request() { assert.fail('must stop') } }
guardFleetWrites(ctx, entry => saved.push(structuredClone(entry)))
await assert.rejects(ctx.requestBinary('POST', '/carton-label-pdf'), /Unknown write outcome/)
assert.equal(saved[1].status, 'UNKNOWN_RESULT')
await assert.rejects(ctx.request('GET', '/readback'), /Unknown write outcome/)
})
test('checkpoint failure stops before dispatch; transport throw stops even after caller catches', async () => {
let calls = 0
const ctx = { async request() { calls++; throw Error('transport failure') } }
guardFleetWrites(ctx, () => { throw Error('disk full') })
await assert.rejects(ctx.request('POST', '/write'), /disk full/)
await assert.rejects(ctx.request('GET', '/later'), /disk full/); assert.equal(calls, 0)
const saved = [], other = { async request() { calls++; throw Error('transport failure') } }
guardFleetWrites(other, entry => saved.push(structuredClone(entry)))
await assert.rejects(other.request('PUT', '/write'), /transport failure/)
await assert.rejects(other.request('POST', '/cleanup'), /transport failure/)
assert.equal(calls, 1); assert.equal(saved[1].status, 'UNKNOWN_RESULT')
})
test('multipart delegates once and retains recoverable fixture bytes, business keys and versions without credentials', async () => {
const saved = [], ctx = {
async requestMultipart(method, path, form) { return this.requestWithBody(method, path, form) },
async requestWithBody() { return { http: 200, body: { code: '200' }, requestId: 'r', traceId: 't' } },
}
guardFleetWrites(ctx, entry => saved.push(redactFleetWrite(structuredClone(entry))))
const form = new FormData(); form.append('file', new Blob(['fixture']), 'proof.txt'); form.append('password', 'hidden')
await ctx.requestMultipart('POST', '/upload', form)
assert.equal(saved.length, 2); assert.equal(Buffer.from(saved[0].body.multipart[0].base64, 'base64').toString(), 'fixture')
assert.ok(!JSON.stringify(saved).includes('hidden'))
const clean = redactFleetWrite({ draftToken: 'business-draft', idempotencyKey: 'command-key', expectedVersion: 6,
headers: { Authorization: 'Bearer hidden', 'X-Access-Token': 'hidden' }, body: { password: 'hidden', accessToken: 'hidden' } })
assert.equal(clean.draftToken, 'business-draft'); assert.equal(clean.idempotencyKey, 'command-key')
assert.equal(clean.expectedVersion, 6); assert.ok(!JSON.stringify(clean).includes('hidden'))
})
test('fleet entry defaults to a no-side-effect plan and requires explicit TEST and unique run ID', () => {
const p = parseFleetArguments([])
assert.equal(p.execute, false); assert.equal(fleetExecutionPlan(p).execution, 'PLAN')
for (const args of [['--execute'], ['--execute', '--run-id', 'new'], ['--execute', '--environment', 'prod', '--run-id', 'new'],
['--execute', '--environment', 'TEST', '--run-id', '../old'], ['--plan', '--execute'], ['--force'], ['--run-id', 'a', '--run-id', 'b']]) {
assert.throws(() => parseFleetArguments(args))
}
assert.equal(parseFleetArguments(['--execute', '--environment', 'TEST', '--run-id', 'new']).execute, true)
const handover = parseFleetArguments(['--plan', '--environment', 'TEST', '--run-id', 'fleet-handover-20261003', '--output', 'reports/fleet-handover-live-20261003'])
assert.equal(fleetExecutionPlan(handover).outputDir, 'reports/fleet-handover-live-20261003')
for (const output of ['reports/../outside', 'C:/temp/output', 'reports', 'reports/nested/path']) assert.throws(() => parseFleetArguments(['--output', output]))
})
test('return redispatch is explicit, fresh-only and declares source-event proof separately', () => {
const config = parseFleetArguments(['--plan', '--environment', 'TEST', '--run-id', 'return-new', '--scenario', 'return-redispatch'])
const plan = fleetExecutionPlan(config)
assert.equal(plan.scenario, 'return-redispatch'); assert.ok(plan.returnRedispatch.required.includes('Two current-run domestic sources'))
assert.throws(() => parseFleetArguments(['--scenario', 'return-redispatch', '--resume-report', 'reports/old/report.json', '--resume-stage', 'FULL_COVERAGE']), /fresh run/)
assert.throws(() => parseFleetArguments(['--scenario', 'unknown']))
})
test('handover stage exports only a verified same-run departure fixture before the optional UI checkpoint', async () => {
const order = [], departure = { action:'LOADING_COMPLETE', dispatchNo:'PC-NEW', status:'ACTIVE', stage:'DELIVERING', result:{loadingCompletionHash:'HASH',loadingCompletedAt:'2026-10-19T00:00:00Z',confirmedLoad:{manifestHash:'HASH'}} }
const context = { marker: 'CURRENT', context: { saveFleetHandoverFixture(value) { order.push('save'); assert.equal(value.marker, 'CURRENT') } },
createdResources: { fleetAcceptance: { tripNo:'TR-NEW', dispatchNo:'PC-NEW', departure } } }
const trip = { tripNo: 'TR-NEW', dispatchNo:'PC-NEW', warehouseId: '92', costAcceptance: { status: 'VERIFIED', marker: 'CURRENT', expected: { finalCny: '100.03', usd: '13.07' } } }, details = [{ dispatchNo: 'PC-NEW' }]
const verified = { status: 'VERIFIED', marker: 'CURRENT', originalDeparture: { snapshot: { confirmedLoad: { pallets: [] } } },
browser: { tripNo: 'TR-NEW' }, handoverNo: 'HO-NEW' }
const result = await runFleetHandoverStage(context, trip, 'secret-picker-token', details, {
verify: async (ctx, module, proof, token, dispatches) => { order.push('verify'); assert.equal(ctx, context); assert.equal(proof, trip); assert.equal(token, 'secret-picker-token'); assert.equal(dispatches, details); return verified },
onReady: async fixture => { order.push('ui'); assert.equal(fixture.status, 'DEPART_AND_HANDOVER_VERIFIED'); assert.equal(fixture.departure, departure) },
})
assert.deepEqual(order, ['verify', 'save', 'ui']); assert.ok(!JSON.stringify(result).includes('secret-picker-token'))
assert.deepEqual(result.costs, trip.costAcceptance)
for (const invalid of [{ ...verified, status: 'FAILED' }, { ...verified, marker: 'OLD' }, { ...verified, originalDeparture: null }]) {
await assert.rejects(runFleetHandoverStage(context, trip, 'token', details, { verify: async () => invalid,
onReady: async () => assert.fail('invalid evidence must not reach the browser checkpoint') }), /current verified/)
}
})
test('one dispatch cost shares use actual physical measurements and the original external booking', () => {
const trip = { dispatchNo: 'OWN', externalReceipt: { status: 'VERIFIED', bookingNo: 'BK-ONE', warehousePalletId: '3' },
externalDispatch: { dispatchNo: 'OWN' }, confirmedLoad: { manifestHash: 'ACTUAL', pallets: [
{ warehousePalletId: '1', palletSpaces: 2 }, { warehousePalletId: '2', palletSpaces: 3 }, { warehousePalletId: '3', palletSpaces: 4 }] } }
const details = [{ detail: { dispatchNo: 'OWN', recordVersion: '0' }, items: { dispatchNo: 'OWN', recordVersion: '0',
items: [{ warehousePalletId: '1', customerId: 'A' }, { warehousePalletId: '1', customerId: 'B' },
{ warehousePalletId: '2', customerId: 'A' }, { warehousePalletId: '3', customerId: 'B' }] } }]
assert.deepEqual(fleetCostShares(trip, details), [
{ businessNo: 'OWN', sourceType: 'OWN', dispatchNo: 'OWN', warehousePalletIds: ['1', '2'], palletSpaces: 5 },
{ businessNo: 'BK-ONE', sourceType: 'EXTERNAL', dispatchNo: 'OWN', warehousePalletIds: ['3'], palletSpaces: 4 },
])
assert.throws(() => fleetCostShares({ ...trip, confirmedLoad: null }, details), /measurements/)
assert.throws(() => fleetCostShares(trip, [{ ...details[0], items: { ...details[0].items, recordVersion: '1' } }]), /version/)
assert.throws(() => fleetCostShares({ ...trip, externalReceipt: { ...trip.externalReceipt, warehousePalletId: '4' } }, details), /distinct/)
assert.throws(() => fleetCostShares({ ...trip, confirmedLoad: { ...trip.confirmedLoad,
pallets: [...trip.confirmedLoad.pallets, trip.confirmedLoad.pallets[0]] } }, details), /measurements/)
})
test('missing isolated cost actor preparation rejects before readers or fixture creation', async () => {
let touched = false
await assert.rejects(runFleetAcceptance({ execute: true, environment: 'TEST' }, {
env: { CHONGCHONG_ENV: 'test' }, prepareCostActors: null, readers: { preflight() { touched = true } },
}), /actor preparation/)
assert.equal(touched, false)
})
test('cost actor journal removes credential keys while retaining IDs, roles and boolean removal evidence', async () => {
const { fleetCostActorJournalData } = await import('./fleet-test-env-acceptance.mjs')
const source = { membershipId: '12', roleIds: ['7'], password: 'actual-secret',
nested: { accessToken: 'actual-session', refreshToken: 'actual-refresh', status: 'ACTIVE' } }
const result = fleetCostActorJournalData(source)
assert.deepEqual(result, { membershipId: '12', roleIds: ['7'], credentialFieldsRemoved: true,
nested: { status: 'ACTIVE', credentialFieldsRemoved: true } })
assert.doesNotMatch(JSON.stringify(result), /"(?:password|accessToken|refreshToken)"\s*:/)
assert.equal(source.nested.accessToken, 'actual-session')
})
for (const failLogin of [false, true]) test(`cost actors create exact isolated roles and disable only owned accounts; login failure=${failLogin}`, async () => {
const calls = [], saved = [], roles = [], users = []
const context = { marker: 'RUN', token: 'admin-secret', createdResources: {}, async findAvailablePhone() { return '19900000000' },
async checkpoint() { saved.push(structuredClone(context.createdResources)) },
async request(method, path, body, token) {
if (method === 'GET' && (path.startsWith('/api/platform/v1/iam/users?') || path.includes('/administrator-phone-availability?')))
return { http: 200, body: { code: '200', data: path.includes('/administrator-phone-availability?') ? true : { records: [], total: 0 } }, requestId: 'r', traceId: 't' }
const pending = saved.at(-1).fleetCostActors.requests.at(-1)
assert.equal(pending.status, 'SENDING'); assert.equal(pending.path, path)
calls.push({ method, path, body, token })
let data
if (path.endsWith('/authorization/permissions')) data = token === 'session-1' ? ['platform:fleet:trip:list'] : []
else if (path.endsWith('/permissions') && method === 'GET') data = [{ id: '7', permissionCode: 'platform:fleet:trip:list', status: 'ACTIVE' }]
else if (path.endsWith('/roles') && method === 'POST') { data = { ...body, id: String(roles.length + 1), roleType: 'CUSTOM' }; roles.push(data) }
else if (path.endsWith('/roles/permissions')) data = body
else if (path.endsWith('/users') && method === 'POST') { data = { ...body, membershipId: String(users.length + 10) }; users.push(data) }
else if (path === '/api/auth/login') { if (failLogin) throw Error('login unavailable'); data = { accessToken: `session-${users.length}` } }
else if (path.endsWith('/status')) data = body
else assert.fail(path)
return { http: 200, body: { code: '200', data }, requestId: 'request', traceId: 'trace' }
} }
if (failLogin) await assert.rejects(prepareFleetCostActors(context), /login unavailable/)
else {
const actors = await prepareFleetCostActors(context)
assert.equal(actors.listOnlyToken, 'session-1'); assert.equal(actors.deniedToken, 'session-2')
assert.equal(calls.filter(row => row.path.endsWith('/roles/permissions'))[1].body.permissionIds.length, 0)
await actors.close()
const count = calls.length; await actors.close(); assert.equal(calls.length, count)
}
assert.equal(context.createdResources.fleetCostActors.status, 'DISABLED')
assert.equal(calls.filter(row => /\/users\/\d+\/status$/.test(row.path)).length, users.length)
assert.equal(calls.filter(row => /\/roles\/\d+\/status$/.test(row.path)).length, roles.length)
const evidence = JSON.stringify(saved)
assert.ok(!evidence.includes('session-1')); assert.ok(!evidence.includes('admin-secret'))
for (const user of users) assert.ok(!evidence.includes(user.password))
assert.ok(!calls.some(row => row.method === 'DELETE'))
})
test('fresh cost actor phones avoid existing platform users and use an independent namespace under the same original marker', async () => {
const phones = [], ctx = { marker: 'ORIGINAL-RUN', async request(method, path) {
assert.equal(method, 'GET')
let data
if (path.includes('/users?')) {
const phone = new URL('https://test.invalid' + path).searchParams.get('keyword'); phones.push(phone)
data = { records: phones.length === 1 ? [{ phone }] : [], total: phones.length === 1 ? 1 : 0 }
} else data = true
return { http: 200, body: { code: '200', data }, requestId: 'r', traceId: 't' }
} }
const first = await findFleetActorPhone(ctx, 'a'.repeat(16))
assert.notEqual(first, phones[0]); assert.match(first, /^18\d{9}$/)
const second = await findFleetActorPhone(ctx, 'b'.repeat(16))
assert.notEqual(first, second)
})
test('phone lookup failure stops without any user creation or guessing', async () => {
let calls = 0
await assert.rejects(findFleetActorPhone({ marker: 'RUN', async request(method) {
assert.equal(method, 'GET'); calls++; return { http: 0 }
} }, 'a'.repeat(16)), /Cannot verify/)
assert.equal(calls, 1)
})
test('main chain performs handover after actual native DEPART and before receipts, and cleanup follows current-resource closure', async () => {
const fullSource=readFileSync(new URL('./fleet-test-env-acceptance.mjs',import.meta.url),'utf8')
const source=fullSource.slice(fullSource.indexOf('export async function runFleetScenario'))
assert.ok(source.indexOf('const departure = await departNativeFleet')<source.indexOf('await completeDepartedFleetScenario'))
const handover=source.indexOf("stage('IN_TRANSIT_HANDOVER')"),receipt=source.indexOf("stage('RECEIPTS_AND_RESOURCE_RELEASE')"),closure=source.indexOf('proof.closure=await closeTrip'),cleanup=source.indexOf('await cleanup(context,MODULE,trip)')
assert.ok(handover>0&&handover<receipt&&receipt<closure&&closure<cleanup)
assert.match(source,/finally \{ await disablePicker\(context, picker, 71\) \}/)
assert.doesNotMatch(source,/dispatches\/(?:unbind|bind)|READY_TO_DEPART|CANCEL_FULL_AND_REARRANGE/)
assert.ok(source.indexOf('proof.fullExecution.closure = await finishFleetTrip')<source.indexOf("stage('RESERVATIONS')"))
assert.ok(source.indexOf('await disablePicker(context, fullPicker, 161)')<source.indexOf("stage('RESERVATIONS')"))
assert.ok(source.indexOf("stage('RESERVATIONS')")<source.indexOf('createPicker(child, 71'))
})
async function executeFixture(failProducer = false) {
const temporary = mkdtempSync(join(tmpdir(), 'fleet-entry-contract-'))
const root = join(temporary, 'chongchong-global', 'baiyue-firstleg'); mkdirSync(root, { recursive: true })
const calls = [], secrets = 'never-print-this-secret'
const env = { CHONGCHONG_ENV: 'test', CHONGCHONG_ADMIN_API_BASE_URL: 'https://test.invalid', CHONGCHONG_ADMIN_USERNAME: 'fixture-admin',
CHONGCHONG_ADMIN_PASSWORD: secrets, ADMIN_PDA_USERNAME: 'fixture-pda', ADMIN_PDA_PASSWORD: secrets }
const config = parseFleetArguments(['--execute', '--environment', 'TEST', '--run-id', 'fixture'])
const createContext = options => ({ options, context: {}, createdResources: {}, steps: [], modules: [], coverageGaps: [], token: secrets,
async request(method, path, body) {
const pending = JSON.parse(readFileSync(join(root, 'reports/fleet-fixture/evidence/write-000001.json'), 'utf8'))
assert.equal(pending.request.status, 'SENDING'); assert.deepEqual(pending.request.body, body)
assert.equal(pending.request.path, path); calls.push('unknown-write'); return { http: 0 }
},
async executeModule(name, handler) {
try { await handler(); this.modules.push({ name, success: true }) }
catch { this.modules.push({ name, success: false }); this.steps.push({ name: 'producer failed', passed: false }) }
},
async run() {
await this.executeModule('producer', async () => {
calls.push('producer')
if (failProducer === 'unknown') {
try { await this.request('PUT', '/orders/1/remarks?tenantId=2', { expectedVersion: 6, idempotencyKey: 'original-key', internalNotes: ['fixture'] }) } catch { /* Simulate a prerequisite swallowing an error. */ }
await this.request('POST', '/loading/must-not-run', {})
}
if (failProducer) throw Error('fixture failure')
})
await this.executeModule('phase4-overseas-dispatch', async () => { calls.push('dispatch'); this.createdResources.current = { accessToken: secrets } })
return { success: true, steps: this.steps, modules: this.modules, createdResources: this.createdResources, summary: { failed: 0 } }
},
})
try {
const options = { root, env, createContext, readers: { async preflight() { calls.push('preflight') }, async readReceiving() { throw Error('not used') } },
scenario: async context => {
calls.push('fleet'); context.createdResources.fleetAcceptance = { status: 'HTTP_CHAIN_VERIFIED_WITH_ACK_GAP' }
const command = { requests: [{ method: 'POST', status: 'SENDING', body: { idempotencyKey: 'original-key' }, accessToken: secrets }] }
await context.context.checkpointFleetHandover(command)
const firstPath = join(root, 'reports/fleet-fixture/evidence/handover-000001.json')
const before = readFileSync(firstPath, 'utf8'), saved = JSON.parse(before)
assert.equal(saved.proof.requests[0].body.idempotencyKey, 'original-key')
assert.equal(saved.proof.requests[0].status, 'SENDING'); assert.ok(!before.includes(secrets))
command.requests[0].status = 'RESPONDED'
await context.context.checkpointFleetHandover(command)
assert.equal(readFileSync(firstPath, 'utf8'), before)
assert.equal(JSON.parse(readFileSync(join(root, 'reports/fleet-fixture/evidence/handover-000002.json'), 'utf8')).proof.requests[0].status, 'RESPONDED')
} }
const result = await runFleetAcceptance(config, options)
const report = JSON.parse(readFileSync(result.report, 'utf8'))
if (failProducer === 'unknown') {
const unknown = JSON.parse(readFileSync(join(root, 'reports/fleet-fixture/evidence/write-000002.json'), 'utf8'))
assert.equal(unknown.request.status, 'UNKNOWN_RESULT'); assert.equal(unknown.request.body.expectedVersion, 6)
assert.equal(unknown.request.body.idempotencyKey, 'original-key')
}
assert.ok(!readFileSync(result.report, 'utf8').includes(secrets))
assert.equal(existsSync(join(root, 'reports/.fleet-acceptance.lock')), false)
const oldReport = readFileSync(result.report, 'utf8')
const repeated = await runFleetAcceptance(config, options)
assert.equal(repeated.success, false)
assert.equal(readFileSync(result.report, 'utf8'), oldReport, 'existing run is never overwritten or resumed')
return { result, report, calls }
} finally {
assert.equal(dirname(resolve(temporary)), resolve(tmpdir()))
assert.ok(resolve(temporary).startsWith(join(resolve(tmpdir()), 'fleet-entry-contract-')))
rmSync(temporary, { recursive: true, force: true })
}
}
test('fresh entry runs dedicated fleet scenario after prerequisites without altering the default F40 chain', async () => {
const { result, report, calls } = await executeFixture()
assert.equal(result.success, true)
assert.deepEqual(calls, ['preflight', 'producer', 'dispatch', 'fleet'])
assert.equal(report.fleetGoalComplete, false)
assert.equal(report.createdResources.current.accessToken, '[REDACTED]')
})
test('failed prerequisite stops before further fixture creation and retains the failed report', async () => {
const { result, report, calls } = await executeFixture(true)
assert.equal(result.success, false)
assert.deepEqual(calls, ['preflight', 'producer'])
assert.equal(report.fatal, 'Producer stopped: producer')
})
test('real fleet entry journals prerequisite writes before sending and stops loading even if producer catches unknown outcome', async () => {
const { result, report, calls } = await executeFixture('unknown')
assert.equal(result.success, false)
assert.deepEqual(calls, ['preflight', 'producer', 'unknown-write'])
assert.equal(report.fatal, 'Producer stopped: producer')
})
test('environment mismatch rejects before invoking any readers or contexts', async () => {
let touched = false
await assert.rejects(() => runFleetAcceptance({ execute: true, environment: 'TEST', runId: 'new' }, {
env: { CHONGCHONG_ENV: 'production' }, readers: { preflight() { touched = true } },
}), /TEST/)
assert.equal(touched, false)
})
test('reviewed resume requires explicit paired checkpoint options', () => {
assert.throws(() => parseFleetArguments(['--resume-report', 'reports/old/report.json']))
assert.throws(() => parseFleetArguments(['--resume-report', 'reports/old/report.json', '--resume-stage', 'PICK_EACH_DISPATCH']))
const c = parseFleetArguments(['--execute', '--environment', 'TEST', '--run-id', 'reviewed', '--resume-report', 'reports/old/report.json', '--resume-stage', 'FULL_COVERAGE'])
assert.equal(c.resumeStage, 'FULL_COVERAGE')
})
test('checkpoint guard forbids repeating commands whose write outcome may be unknown', () => {
const report = { success: false, createdResources: {
fleetAcceptance: { protocol:'NATIVE_DISPATCH_V1', dispatchNo:'D2', dispatchNos:['D2'], fullTripNo:'F', marker: 'M', stage: 'FULL_COVERAGE', status: 'CHECKING', tripNo: 'T', originalDispatch: { detail:{dispatchNo:'D2'} } },
fleetTrips: [{ tripNo: 'T', dispatchNo: 'D2' },{tripNo:'F',dispatchNo:'D'}],
fleetFeeCoverageCases: [{ mode: 'FULL', marker: 'M', dispatchNo: 'D', proof: { status: 'CHECKING', requests: [{ method: 'GET',status:'RESPONDED',http:200,code:'200',requestId:'r',traceId:'t' }] } },{mode:'PARTIAL',marker:'M',dispatchNo:'D2'}],
} }
assert.equal(reviewedFullCheckpoint(report).marker, 'M')
const mutated = structuredClone(report)
mutated.createdResources.fleetFeeCoverageCases[0].proof.requests.push({ method: 'POST' })
assert.throws(() => reviewedFullCheckpoint(mutated), /unknown outcomes/)
mutated.createdResources.fleetFeeCoverageCases[0].proof.requests.pop()
mutated.createdResources.fleetFeeCoverageCases[0].proof.outboundNo = 'OUT-1'
assert.throws(() => reviewedFullCheckpoint(mutated), /unknown outcomes/)
assert.throws(() => reviewedFullCheckpoint(mutated, 'FULL_COVERAGE_PERSISTED'), /exact successful command/)
Object.assign(mutated.createdResources.fleetFeeCoverageCases[0].proof, { deliveryNos: ['DEL-1'], confirmationRequest: { recordVersion:0,groups:[{groupKey:'G'}],idempotencyKey: 'original-key' } })
mutated.createdResources.fleetTrips[1].loadingCommand={body:{recordVersion:0,idempotencyKey:'original-key',measurements:[{warehousePalletId:'P'}]},result:{action:'LOADING_COMPLETE',dispatchNo:'D',status:'ACTIVE',stage:'DELIVERING',result:{loadingCompletionHash:'HASH',loadingCompletedAt:'2026-10-02T20:00:00Z',confirmedLoad:{manifestHash:'HASH'}}},afterReplay:{main:{documents:{outbounds:[{outboundNo:'OUT-1'}],deliveries:[{deliveryNo:'DEL-1'}]}}}}
assert.equal(reviewedFullCheckpoint(mutated, 'FULL_COVERAGE_PERSISTED').persisted, true)
delete mutated.createdResources.fleetTrips[1].loadingCommand.result.result.loadingCompletionHash
assert.throws(() => reviewedFullCheckpoint(mutated, 'FULL_COVERAGE_PERSISTED'), /exact successful command/)
})
test('receipt-ready reconciliation cannot repeat a receipt that has already started', () => {
const report = { success: false, steps: [{ name: '创建海外PDA仓库操作员71', code: 'A0409', message: '手机号已存在' }], createdResources: {
fleetAcceptance: { protocol:'NATIVE_DISPATCH_V1',dispatchNo:'D2',dispatchNos:['D2'],fullExecution:{closure:{status:'CARGO_COMPLETED_AND_RESOURCES_RELEASED'}},marker: 'M', stage: 'RESERVATIONS', status: 'CHECKING', tripNo: 'T' },
fleetTrips: [{ tripNo: 'T', dispatchNo: 'D2', reservation: { status: 'VERIFIED' } }],
fleetFeeCoverageCases: [{ mode: 'FULL', proof: { status: 'VERIFIED' } }, { mode: 'PARTIAL', dispatchNo: 'D2' }],
} }
assert.equal(reviewedFullCheckpoint(report, 'EXTERNAL_RECEIPT_READY').receiptReady, true)
report.createdResources.fleetTrips[0].externalReceipt = { status: 'CHECKING' }
assert.throws(() => reviewedFullCheckpoint(report, 'EXTERNAL_RECEIPT_READY'), /no receipt writes/)
})
test('palletized checkpoint requires a saved successful command, owned operator, and no later confirmation', () => {
const report = { success: false, createdResources: {
fleetAcceptance: { protocol:'NATIVE_DISPATCH_V1',dispatchNo:'D2',dispatchNos:['D2'],fullExecution:{closure:{status:'CARGO_COMPLETED_AND_RESOURCES_RELEASED'}},marker: 'M', stage: 'EXTERNAL_RECEIPT', status: 'CHECKING', tripNo: 'T', picker: { membershipId: '71' }, pickerMarker: 'M-PDA' },
fleetTrips: [{ tripNo: 'T', dispatchNo: 'D2', reservation: { status: 'VERIFIED' }, externalReceipt: { status: 'CHECKING',
warehousePalletId: 'P1', palletizeRequest: { idempotencyKey: 'original' }, requests: [
{ label: 'PDA正式完成外接收货及库存入账', method: 'POST', http: 200, code: '200' },
] } }],
fleetFeeCoverageCases: [{ mode: 'FULL', proof: { status: 'VERIFIED' } }, { mode: 'PARTIAL', dispatchNo: 'D2' }],
} }
assert.equal(reviewedFullCheckpoint(report, 'EXTERNAL_PALLETIZED').palletized, true)
assert.throws(() => reviewedFullCheckpoint(report, 'EXTERNAL_RECEIPT_CONFIRMED'), /exact successful PDA command/)
const confirmed = structuredClone(report)
confirmed.createdResources.fleetTrips[0].externalReceipt.receiptRequest = { idempotencyKey: 'confirm-original' }
assert.throws(() => reviewedFullCheckpoint(confirmed, 'EXTERNAL_RECEIPT_CONFIRMED'), /exact successful PDA command/)
confirmed.createdResources.fleetTrips[0].externalReceipt.requests.push({ label: '运营按实际1托位和新单价30确认实收', method: 'POST', http: 200, code: '200' })
assert.equal(reviewedFullCheckpoint(confirmed, 'EXTERNAL_RECEIPT_CONFIRMED').receiptConfirmed, true)
assert.throws(() => reviewedFullCheckpoint(confirmed, 'EXTERNAL_PALLETIZED'), /exact successful PDA command/)
for (const mutate of [
r => { r.fleetTrips[0].externalReceipt.requests[0].code = 'A0409' },
r => { delete r.fleetTrips[0].externalReceipt.palletizeRequest.idempotencyKey },
r => { delete r.fleetAcceptance.picker },
r => { r.fleetTrips[0].externalReceipt.receiptRequest = { idempotencyKey: 'later' } },
r => { r.fleetTrips[0].externalDispatch = { status: 'CHECKING' } },
]) {
const invalid = structuredClone(report); mutate(invalid.createdResources)
assert.throws(() => reviewedFullCheckpoint(invalid, 'EXTERNAL_PALLETIZED'), /exact successful PDA command/)
}
})
function terminalFixture(fault = '') {
const finalTrip={status:'COMPLETED',completedAt:'2026-10-02T22:01:00Z',recordVersion:'30',vehicleRelease:{endLocation:'ONT8'},driverRelease:{endLocation:'ONT8'}}
const closure={status:'CARGO_COMPLETED_AND_RESOURCES_RELEASED',finalTrip,subsequentTripClaimProtection:'VERIFIED_NATIVE_EMPTY_DISPATCH',nextDispatch:{dispatchNo:'PROBE',finalStatus:'CANCELLED'}}
const trip={tripNo:'T',dispatchNo:'OWN',closure,externalReceipt:{status:'VERIFIED'},reassignment:{status:'REJECTED_WITHOUT_MUTATION'},externalDispatch:{status:'VERIFIED',dispatchNo:'OWN',deliveryNo:'DEL-EXT'}}
const refs=[{customerId:'92',deliveryNo:'DEL-OWN',outboundNo:'OUT',packageCount:5},{customerId:'92',deliveryNo:'DEL-EXT',outboundNo:'OUT',packageCount:1}]
const proof={protocol:'NATIVE_DISPATCH_V1',marker:'M',tripNo:'T',dispatchNo:'OWN',dispatchNos:['OWN'],customerId:'92',stage:'RECEIPTS_AND_RESOURCE_RELEASE',closure,
receipts:refs.map(row=>({customerId:'92',deliveryNo:row.deliveryNo,signedAt:'2026-10-02T22:00:00'})),
departureSnapshot:{detail:{counts:{shippedPackageCount:6},dispatchInfo:{departedAt:'2026-10-02T20:00:00Z'}}},departure:{action:'LOADING_COMPLETE',dispatchNo:'OWN',status:'ACTIVE',stage:'DELIVERING',result:{loadingCompletionHash:'HASH',loadingCompletedAt:'2026-10-02T20:00:00Z',confirmedLoad:{manifestHash:'HASH'}}},finance:{customerId:'92',status:'CONSUMER_PERSISTED',source:{sourceFeeId:'F1',sourceVersion:1}}}
const resources={fleetAcceptance:proof,fleetTrips:[trip],fleetFeeCoverageCases:[{mode:'PARTIAL',dispatchNo:'OWN',proof:{deliveryNos:['DEL-OWN','DEL-EXT']}}]}
const calls=[],context={createdResources:resources,assert(){},async request(method,path){
calls.push({method,path});assert.equal(method,'GET');let data
if(path.endsWith('/trips/T'))data={trip:finalTrip,dispatches:[{dispatchNo:'OWN'}]}
else if(path.endsWith('/dispatches/PROBE'))data={dispatchNo:'PROBE',status:'CANCELLED',counts:{packageCount:0,pickedPackageCount:0}}
else if(path.endsWith('/dispatches/OWN'))data={dispatchNo:'OWN',status:'ACTIVE',stage:'DELIVERED',counts:{packageCount:6,pickedPackageCount:6,shippedPackageCount:fault==='quantity'?0:6},documents:{deliveries:refs}}
else if(path.includes('/delivery-orders/'))data={order:{customerId:'92',deliveryNo:path.split('/').at(-1).split('?')[0],status:fault==='unsigned'?'DELIVERING':'DELIVERED',signedAt:'2026-10-02T22:00:00'}}
else if(path.includes('/fee-pool?'))data=[{customerId:'92',sourceId:'F1',sourceVersion:fault==='wrong-fee-version'?2:1,amount:30,currency:'CNY',sourceType:'OVERSEAS_DELIVERY_FEE'}]
else throw Error('unexpected path')
return {http:200,body:{code:'200',data:structuredClone(data)},requestId:'r',traceId:'t'}
}}
return {context,calls,report:{success:false,createdResources:resources,steps:[{name:'本次车次货物结案及车辆司机释放完成',passed:false}]}}
}
test('terminal reconciliation reads actual completion, every dispatch/delivery and exact fee without replaying writes', async () => {
const f = terminalFixture()
assert.equal(reviewedFullCheckpoint(f.report, 'TERMINAL_READONLY').terminal, true)
const result = await verifyFleetTerminalState(f.context)
assert.equal(result.terminalReadback.status, 'VERIFIED')
assert.equal(f.calls.length, 6)
assert.ok(f.calls.every(row => row.method === 'GET'))
const invalid = terminalFixture().report
invalid.steps.push({ name: '真实业务失败', passed: false })
assert.throws(() => reviewedFullCheckpoint(invalid, 'TERMINAL_READONLY'), /known aggregate assertion/)
})
for (const fault of ['quantity', 'unsigned', 'wrong-fee-version']) test(`terminal readback rejects ${fault}`, async () => {
const f = terminalFixture(fault)
await assert.rejects(() => verifyFleetTerminalState(f.context))
assert.equal(f.context.createdResources.fleetAcceptance.terminalReadback.status, 'CHECKING')
})
function nativeFullSourceFixture() {
const context = { marker: 'RUN', createdResources: {
phase4OverseasPda: { marker: 'RUN', finalStatus: 'COMPLETED', customerId: '11', overseasWarehouseId: '12', palletIds: ['101','102','103'] },
phase4OverseasDispatch: { marker: 'RUN', status: 'VERIFIED', dispatchNo: 'PARTIAL', customerId: '11', recordVersion: '9007199254740993',
routeLifecycle: { stages: [{ label: '新增同目的地第二托', snapshot: { items: [{ sourceKey: 'PLAN:23', warehousePalletId: '103' }] } }] } }
} }
const original = { warehouseId: '12', data: { dispatchNo: 'PARTIAL', status: 'ACTIVE', stage: 'BEFORE_LOADING', recordVersion: '9007199254740993',
counts: { pickedPackageCount: 0, shippedPackageCount: 0 } }, items: { items: [
{ sourceKey: 'PLAN:21', warehousePalletId: '101', destination: { key: 'FBA:A' } },
{ sourceKey: 'PLAN:22', warehousePalletId: '102', destination: { key: 'FBA:B' } }] } }
const candidate = { customerId: '11', warehouseId: '12', availablePackages: 2, blockReasons: [], appointmentBatches: [], sourceKey: 'PLAN:23',
sourceVersion: 'a'.repeat(64), destination: { type: 'FBA', key: 'FBA:A' }, selection: {
sourceType: 'INBOUND_PLAN', planPalletId: '23', warehousePalletId: '103', sourceVersion: 'a'.repeat(64) } }
return { context, original, candidate }
}
test('FULL consumes only the current-run released free duplicate while PARTIAL retains its distinct two destinations', () => {
const f = nativeFullSourceFixture()
assert.equal(selectNativeFleetFullSource(f.context, f.original, [f.candidate]), f.candidate)
for (const mutate of [
x => x.original.data.recordVersion = '9007199254740992',
x => x.original.data.counts.pickedPackageCount = 1,
x => x.original.items.items[1].destination.key = 'FBA:A',
x => x.candidate.customerId = '99', x => x.candidate.warehouseId = '99',
x => x.candidate.blockReasons.push('已占用'), x => x.candidate.availablePackages = 0,
x => x.context.createdResources.phase4OverseasPda.palletIds = ['101','102'],
x => x.context.createdResources.phase4OverseasDispatch.routeLifecycle.stages = [],
x => x.original.items.items[0].warehousePalletId = '103',
x => x.original.items.items[0].sourceKey = 'PLAN:23'
]) { const invalid = nativeFullSourceFixture(); mutate(invalid); assert.throws(() => selectNativeFleetFullSource(invalid.context, invalid.original, [invalid.candidate])) }
assert.throws(() => selectNativeFleetFullSource(f.context, f.original, [f.candidate, structuredClone(f.candidate)]), /exactly one/)
})
test('native delivery references deduplicate the shared external destination but reject conflicting customer or cargo ownership', () => {
const own = { customerId: '11', deliveryNo: 'D-A', outboundNo: 'OUT-A', groupKey: 'G-A', packageCount: 3 }
const external = { customerId: '22', deliveryNo: 'D-B', outboundNo: 'OUT-B', groupKey: 'G-B', packageCount: 1 }
const detail = { dispatchNo: 'MAIN', documents: { deliveries: [own, external, structuredClone(external)] } }
assert.deepEqual(nativeFleetDeliveries(detail), [own, external])
for (const changes of [{ customerId: '33' }, { outboundNo: 'FOREIGN' }, { packageCount: 2 }, { groupKey: 'OTHER' }]) {
assert.throws(() => nativeFleetDeliveries({ ...detail, documents: { deliveries: [own, external, { ...external, ...changes }] } }))
}
assert.throws(() => nativeFleetDeliveries({ dispatchNo: 'MAIN', documents: { deliveries: [{ ...own, customerId: null }] } }))
assert.throws(() => nativeFleetDeliveries({ dispatchNo: 'MAIN', documents: { deliveries: [] } }))
})
function nativeDepartureFixture(fault='') {
const refs=[{customerId:'11',deliveryNo:'DEL-A',outboundNo:'OUT-A',packageCount:2},{customerId:'22',deliveryNo:'DEL-B',outboundNo:'OUT-B',packageCount:1}]
const result={action:'LOADING_COMPLETE',dispatchNo:'MAIN',status:'ACTIVE',stage:'DELIVERING',recordVersion:'9007199254740994',result:{loadingCompletionHash:'HASH',loadingCompletedAt:'2026-10-19T01:00:00Z',confirmedLoad:{manifestHash:'HASH'}}}
const detail={dispatchNo:'MAIN',status:'ACTIVE',stage:fault==='not-loaded'?'LOADING':'DELIVERING',recordVersion:fault==='stale-version'?'1':result.recordVersion,counts:{packageCount:3,pickedPackageCount:3,shippedPackageCount:fault==='quantity'?2:3},documents:{deliveries:refs}}
const run={tripNo:'TRIP',dispatchNo:'MAIN'},trip={tripNo:'TRIP',dispatchNo:'MAIN',warehouseId:'12',loadingCommand:{body:{idempotencyKey:'original'},result:structuredClone(result),replay:{...result,replayed:true}}},calls=[]
if(fault==='missing-command')delete trip.loadingCommand
const context={async checkpoint(){},async request(method,path,body,token){
calls.push({method,path,body,token});assert.equal(method,'GET');let data
if(path.includes('/commands/')){assert.equal(token,'pda');data=structuredClone(result);if(fault==='changed-command')data.result.loadingCompletionHash='OTHER'}
else if(path.endsWith('/items'))data={dispatchNo:'MAIN',recordVersion:detail.recordVersion,items:[]}
else if(path.includes('/delivery-orders/')){const ref=refs.find(r=>path.includes(r.deliveryNo));data={order:{...ref,dispatchNo:'MAIN',status:'DELIVERING',version:'3'}};if(fault==='foreign-delivery')data.order.customerId='OTHER'}
else data=detail
return {http:200,body:{code:'200',data:structuredClone(data)},requestId:'r',traceId:'t'}
}}
return {context,trip,run,calls}
}
test('one completed loading command is recovered read-only with real customer delivery versions',async()=>{
const f=nativeDepartureFixture(),result=await departNativeFleet(f.context,f.trip,'pda',f.run)
assert.equal(result.departed,f.run.departure);assert.deepEqual(f.run.dispatchNos,['MAIN'])
assert.deepEqual(f.run.departureDeliveries.map(r=>[r.customerId,r.version]),[['11','3'],['22','3']]);assert.ok(f.calls.every(c=>c.method==='GET'))
assert.deepEqual(f.run.departureRequest,f.trip.loadingCommand.body)
const before=f.calls.length;await assert.rejects(departNativeFleet(f.context,f.trip,'pda',f.run),/original command/);assert.equal(f.calls.length,before)
})
for(const fault of ['not-loaded','stale-version','quantity','missing-command','changed-command','foreign-delivery'])test(`automatic departure readback rejects ${fault}`,async()=>{
const f=nativeDepartureFixture(fault);await assert.rejects(departNativeFleet(f.context,f.trip,'pda',f.run));assert.ok(f.calls.every(c=>c.method==='GET'))
})
test('physical loading requires current-run actual per-field evidence and never fills pallet spaces from planned capacity', async () => {
const calls = [], trip = { dispatchNo: 'MAIN', stops: [{ destinationKey: 'ORIGIN:12' }, { destinationKey: 'FBA:A' }] }
const preview = { dispatchNo: 'MAIN', status: 'ACTIVE', pallets: [{ warehousePalletId: '101', palletNo: 'PALLET', palletSpaces: null, weightKg: null, volumeCbm: null }] }
const context = { marker: 'RUN', createdResources: { phase4OverseasDispatch: {} }, async request(method, path) {
assert.equal(method, 'GET'); calls.push(path)
return { http: 200, body: { code: '200', data: structuredClone(preview) }, requestId: 'r', traceId: 't' }
} }
await assert.rejects(currentFleetMeasurements(context, trip), /Missing current-run actual palletSpaces/)
const actual = { warehousePalletId: '101', palletSpaces: 2, weightKg: 121, volumeCbm: 1.37, deliveryIndex: 1,
evidence: ['palletSpaces','weightKg','volumeCbm'].map(field => ({ field, value: { palletSpaces: 2, weightKg: 121, volumeCbm: 1.37 }[field],
actual: true, sourceType: 'PDA_MEASUREMENT', sourceBusinessNo: 'CURRENT-INCOMING', observedAt: '2026-10-19T01:00:00Z' })) }
context.createdResources.phase4OverseasDispatch.loadingMeasurements = { environment: 'TEST', marker: 'RUN', dispatchNo: 'MAIN', pallets: [actual] }
assert.deepEqual((await currentFleetMeasurements(context, trip)).pallets, [actual])
for (const mutation of [receipt => receipt.marker = 'OLD', receipt => receipt.dispatchNo = 'OTHER',
receipt => receipt.pallets[0].evidence[0].actual = false, receipt => receipt.pallets[0].evidence[0].value = 1,
receipt => receipt.pallets.push({ ...receipt.pallets[0], warehousePalletId: '999' })]) {
const receipt = structuredClone(context.createdResources.phase4OverseasDispatch.loadingMeasurements)
mutation(receipt)
await assert.rejects(currentFleetMeasurements({ ...context, createdResources: { phase4OverseasDispatch: { loadingMeasurements: receipt } } }, trip))
}
assert.equal(calls.length, 7)
})
test('native checkpoint rejects retired flat reports, multi-main binding and globally unknown writes', () => {
for (const mutation of [report => delete report.createdResources.fleetAcceptance.protocol,
report => report.createdResources.fleetAcceptance.dispatchNos.push('OTHER'),
report => report.createdResources.fleetTrips[0].dispatchNo = 'OTHER',
report => report.createdResources.fleetWriteBoundary = { status: 'STOPPED', outcome: 'UNKNOWN_RESULT' }]) {
const report = terminalFixture().report; mutation(report)
assert.throws(() => reviewedFullCheckpoint(report, 'TERMINAL_READONLY'), /native single-dispatch reports/)
}
})