2026-10-07 16:46:56 +08:00
import { mkdirSync , openSync , writeFileSync , fsyncSync , closeSync , readFileSync } from 'node:fs'
import { resolve , dirname , join } from 'node:path'
import { fileURLToPath , pathToFileURL } from 'node:url'
import { createHash , randomUUID } from 'node:crypto'
import { AcceptanceRun } from './admin-test-env-acceptance.mjs'
import { parseCrossCustomerArguments , runCrossCustomer } from './fleet-cross-customer-test-env.mjs'
import { guardFleetWrites , redactFleetWrite , findFleetActorPhone } from './fleet-test-env-acceptance.mjs'
import { controlledPalletMeasurements } from './admin-test-env-modules/fleet-trip-fulfillment.mjs'
import { pickAll , saveReceiptAndComplete } from './admin-test-env-modules/phase5-overseas-fulfillment.mjs'
const ROOT = resolve ( dirname ( fileURLToPath ( import . meta . url )), '..' )
const API = '/api/platform/v1/last-leg' , POOL = ` ${ API } /fleet/capacity-offers` , IAM = '/api/platform/v1/iam'
const MODULE = 'fleet-capacity-pool' , PREFIX = 'platform:fleet:capacity-offer:'
const id = value => String ( value ?? '' ), requireThat = ( ok , reason ) => { if ( ! ok ) throw Error ( reason ) }
const success = response => response ? . http === 200 && id ( response . body ? . code ) === '200'
const denied = response => [ 403 , 409 ]. includes ( response ? . http ) || response ? . http === 200 && [ 'A0403' , 'A0409' ]. includes ( id ( response . body ? . code ))
const encode = encodeURIComponent
2026-10-07 16:54:05 +08:00
// DeliveryRequests.Transfer uses LocalDateTime. Fleet offer/booking Instant fields below retain Z.
export function poolLocalDateTime ( date ) {
requireThat ( date instanceof Date && Number . isFinite ( date . getTime ()), 'VALID_LOCAL_DATE_REQUIRED' )
return new Date ( date . getTime () - date . getTimezoneOffset () * 60000 ). toISOString (). slice ( 0 , 19 )
}
const localTime = ms => poolLocalDateTime ( new Date ( Date . now () + ms ))
2026-10-07 16:46:56 +08:00
const PNG = Buffer . from ( 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+aH1kAAAAASUVORK5CYII=' , 'base64' )
export const POOL_COVERAGE = Object . freeze ([
'same-segment-plan-18-of-30-reserve-8-leaves-4' , 'two-sales-owner-isolation' , 'two-dispatchers-last-four-race' ,
'unknown-load-stays-null' , 'close-and-successful-confirmation-replay' , 'one-commercial-order-and-one-incoming' ,
'both-business-types-real-pda-receipt' , 'shared-real-departure-and-signature' , 'business-completion-and-fee-uniqueness' ,
'merchant-cannot-read-pool' , 'old-manual-order-endpoints-rejected' ,
'assigned-vehicle-disable-and-shrink-rejected' , 'paused-create-edit-confirm-rejected' ,
'expired-cutoff-create-edit-confirm-rejected' , 'weight-and-volume-independent-capacity-rejections' ,
2026-10-07 18:11:55 +08:00
'confirmed-cancel-before-incoming-releases-capacity-and-replays' , 'incoming-cancel-rejected-with-source-unchanged' ,
'receipt-overcapacity-rejected-with-source-unchanged' , 'open-offer-bind-replan-requires-explicit-resume' ,
'bound-cargo-reservation-removed-without-double-count' , 'other-sales-write-and-sensitive-trip-denied' ,
'other-customer-commercial-read-denied-with-source-unchanged' ,
2026-10-07 19:38:56 +08:00
'pool-unspecified-container-and-palletizing-remain-unknown' ,
2026-10-10 08:07:27 +08:00
'save-and-confirm-original-command-replay' , 'pool-fulfillment-owner-read-isolation' ,
'pool-fulfillment-explicit-write-permissions' , 'pool-arrange-dispatch-without-manual-number' ,
2026-10-07 16:46:56 +08:00
])
export const POOL_REMAINING = Object . freeze ([
'out-of-band-assigned-vehicle-drift-not-constructible-through-supported-api' ,
'full-UI-1920x1080-and-network-request-budget' ,
])
export function parsePoolArguments ( args ) { return parseCrossCustomerArguments ( args ) }
2026-10-08 17:07:57 +08:00
export function poolReportName ( config ) {
requireThat ([ 'TEST' , 'GRAY' ]. includes ( config . environment ) && /^[A-Za-z0-9][A-Za-z0-9_-]{0,47}$/ . test ( config . runId ), 'EXPLICIT_POOL_REPORT_SCOPE_REQUIRED' )
return ` ${ config . environment === 'GRAY' ? 'fleet-pool-gray' : 'fleet-cross' } - ${ config . runId } `
}
2026-10-07 16:46:56 +08:00
export function poolPlan ( config ) {
2026-10-08 17:07:57 +08:00
return { environment : config . environment || 'TEST' , execute : Boolean ( config . execute ), runId : config . runId ,
output : `reports/ ${ poolReportName ({ ... config , environment : config . environment || 'TEST' } )}` , coveredByThisEntry : POOL_COVERAGE , remainingSeparateAcceptance : POOL_REMAINING ,
2026-10-07 18:11:55 +08:00
sample : 'Two fresh qualified customers and owned one-box pallets, one 30-space trip. Each pool business type has one safely cancelled pre-incoming sample and one fulfilled order with one real incoming/pallet. Planning uses 18 spaces; actual physical load is separately confirmed.' ,
2026-10-08 17:07:57 +08:00
prerequisites : [ config . environment === 'GRAY' ? 'explicit GRAY CLI with CHONGCHONG_ENV=gray|prod and CHONGCHONG_ADMIN_*'
: 'explicit TEST private env with CHONGCHONG_ENV=test and CHONGCHONG_ADMIN_*' , 'current Warehouse/FBA IDs' ,
config . environment === 'GRAY' ? 'GRAY read-only evidence via JENKINS_* and verified production container/database identity'
: 'TEST read-only inventory evidence credentials' , 'current capacity-offer permission seeds and backend deployed' ],
2026-10-07 16:46:56 +08:00
boundary : 'Default plan performs no network or filesystem writes. Execute retains every sample and original command, blocks all further writes on an unknown outcome, never deploys or writes business SQL. No automatic rerun or resume.' }
}
/** Strict quantities are based on the selected unloading segment, never a global displayed total. */
export function assertPoolRemaining ( offer , destinationKey , expected ) {
const destination = offer ? . destinations ? . filter ( row => row . destinationKey === destinationKey )
requireThat ( offer ? . capacityKnown === true && destination ? . length === 1
&& Number ( destination [ 0 ]. remaining ? . palletSpaces ) === expected , `EXPECTED_EXACT_SEGMENT_REMAINING_ ${ expected } ` )
}
export function assertSingleCommercial ( before , after ) {
requireThat ( before ? . bookingNo && before . businessOrderNo && before . businessOrderNo === after ? . businessOrderNo
&& before . bookingNo === after . bookingNo && before . businessType === after . businessType
&& before . customerId === after . customerId , 'COMMERCIAL_REPLAY_IDENTITY_CHANGED' )
}
2026-10-07 19:38:56 +08:00
export function assertPoolUnknownCommercialFacts ( businessType , summary ) {
requireThat ([ 'LOCAL_LOOSEBOARD' , 'US_TRUCK' ]. includes ( businessType ) && summary && typeof summary === 'object' ,
'POOL_COMMERCIAL_SUMMARY_REQUIRED' )
requireThat ( summary . containerType == null , 'POOL_CONTAINER_TYPE_MUST_REMAIN_UNKNOWN' )
if ( businessType === 'LOCAL_LOOSEBOARD' ) requireThat ( summary . palletRequired == null , 'POOL_PALLETIZING_SERVICE_MUST_REMAIN_UNKNOWN' )
return true
}
2026-10-07 18:11:55 +08:00
export function poolInquiryWrite ( inquiry , idempotencyKey ) {
return { tripRecordVersion : inquiry . tripRecordVersion , offerRecordVersion : inquiry . offerRecordVersion ,
recordVersion : inquiry . recordVersion , customerId : inquiry . customerId , businessType : inquiry . businessType ,
deliveryMode : inquiry . deliveryMode , estimatedArrivalAt : inquiry . estimatedArrivalAt ,
expectedPalletCount : inquiry . expectedPalletCount , cargo : inquiry . cargo , quoteRef : null , idempotencyKey }
}
export function poolReadIsDenied ( response ) {
return ([ 403 , 404 ]. includes ( response ? . http ) || response ? . http === 200 && [ 'A0403' , 'A0404' ]. includes ( id ( response . body ? . code )))
&& response . body ? . data == null
}
/** This fixture has exactly one shared unloading segment; verify all dimensions, not only a badge. */
export function assertPoolBoundCapacity ( offer , destinationKey , planned , reserved , capacity ) {
requireThat ( offer ? . status === 'OPEN' && offer . capacityKnown && offer . canConfirm && offer . segments ? . length === 1 ,
'OPEN_KNOWN_SINGLE_SEGMENT_REQUIRED' )
const segment = offer . segments [ 0 ]
requireThat ( segment . fromIndex === 0 && segment . toIndex === 1 , 'EXACT_SHARED_SEGMENT_REQUIRED' )
for ( const dimension of [ 'palletSpaces' , 'weightKg' , 'volumeCbm' ]) {
const expected = Number ( capacity [ dimension ]) - Number ( planned [ dimension ]) - Number ( reserved [ dimension ])
requireThat ( Number . isFinite ( expected ) && Number ( segment . planned ? .[ dimension ]) === Number ( planned [ dimension ])
&& Number ( segment . reserved ? .[ dimension ]) === Number ( reserved [ dimension ])
&& Number ( segment . remaining ? .[ dimension ]) === expected , `BOUND_LOAD_DOUBLE_COUNT_OR_MISSING_ ${ dimension } ` )
const destinations = offer . destinations ? . filter ( row => row . destinationKey === destinationKey )
requireThat ( destinations ? . length === 1 && Number ( destinations [ 0 ]. remaining ? .[ dimension ]) === expected ,
`BOUND_DESTINATION_REMAINING_MISMATCH_ ${ dimension } ` )
}
}
export async function verifyPoolConfirmedCancellation ({ inquiry , destinationKey , request , check , latest , command ,
businessPath , dispatcherToken , label }) {
const path = ` ${ POOL } /inquiries/ ${ inquiry . bookingNo } `
const confirmed = await request ( 'POST' , ` ${ path } /confirm` , command ( await latest ( inquiry . bookingNo ), ` ${ label } -CONFIRM` ), dispatcherToken )
check ( 'Cancellation sample really reserves one space' , confirmed . inquiry . status === 'CONFIRMED' && confirmed . inquiry . businessOrderNo )
assertPoolRemaining ( confirmed . offer , destinationKey , 11 )
const before = await request ( 'GET' , businessPath ( confirmed . inquiry ))
check ( 'Confirmed cancellation sample has no incoming' , before . summary . status === 'WAITING_INBOUND'
&& before . summary . fleetArrangement ? . bookingNo === inquiry . bookingNo && ! before . summary . fleetArrangement . incomingNo )
const cancelBody = { ... command ( await latest ( inquiry . bookingNo ), ` ${ label } -CANCEL` ), reason : 'TEST未登记来货取消并释放承诺' }
const cancelled = await request ( 'POST' , ` ${ path } /cancel` , cancelBody , dispatcherToken )
assertSingleCommercial ( confirmed . inquiry , cancelled . inquiry )
check ( 'Confirmed booking is cancelled before incoming' , cancelled . inquiry . status === 'CANCELLED' && ! cancelled . inquiry . incomingNo )
assertPoolRemaining ( cancelled . offer , destinationKey , 12 )
const commercial = await request ( 'GET' , businessPath ( cancelled . inquiry ))
check ( 'Commercial cancellation is atomic with reservation release' , commercial . summary . status === 'CANCELLED'
&& commercial . summary . fleetArrangement ? . bookingNo === inquiry . bookingNo && ! commercial . summary . fleetArrangement . incomingNo )
const replay = await request ( 'POST' , ` ${ path } /cancel` , cancelBody , dispatcherToken )
check ( 'Original cancellation command replays without changing source' , replay . replayed === true
&& inquiryFact ( replay . inquiry ) === inquiryFact ( cancelled . inquiry ))
check ( 'Cancel replay keeps persisted commercial and booking facts unchanged' ,
JSON . stringify ( commercial ) === JSON . stringify ( await request ( 'GET' , businessPath ( cancelled . inquiry )))
&& inquiryFact ( await latest ( inquiry . bookingNo )) === inquiryFact ( cancelled . inquiry ))
const current = await request ( 'GET' , ` ${ POOL } / ${ inquiry . offerNo } ` , undefined , dispatcherToken )
assertPoolRemaining ( current . offer , destinationKey , 12 )
return { bookingNo : inquiry . bookingNo , businessOrderNo : cancelled . inquiry . businessOrderNo , businessType : inquiry . businessType ,
status : 'CANCELLED' , replayed : true , remainingPalletSpaces : 12 }
}
export async function verifyPoolBindingRevalidation ({ offerDetail , replan , resume , blockedConfirm , check }) {
const invalidated = ( await offerDetail ()). offer
check ( 'Binding invalidates capacity while publication remains OPEN' , invalidated . status === 'OPEN'
&& ! invalidated . capacityKnown && ! invalidated . canConfirm && /计划|核对/ . test ( id ( invalidated . blockReason )))
await blockedConfirm ( 'BIND-INVALIDATED' )
await replan ()
const recalculated = ( await offerDetail ()). offer
check ( 'Recalculating cargo alone cannot silently resume acceptance' , recalculated . status === 'OPEN'
&& ! recalculated . capacityKnown && ! recalculated . canConfirm && /恢复接单|承诺已变化/ . test ( id ( recalculated . blockReason )))
await blockedConfirm ( 'REPLANNED-NOT-RESUMED' )
const restored = await resume ( recalculated )
check ( 'Only explicit resume makes current capacity confirmable' , restored . status === 'OPEN' && restored . capacityKnown && restored . canConfirm )
return restored
}
2026-10-07 17:21:58 +08:00
export function poolPickScope ( context , member ) {
requireThat ( context ? . marker && /^[1-9]\d*$/ . test ( id ( member ? . customerId )) && member ? . dispatchNo ,
'EXACT_CUSTOMER_DISPATCH_PICK_SCOPE_REQUIRED' )
const scoped = Object . create ( context )
// pickAll keys only include its context marker and item/position indexes; the PDA key scope is customer-wide.
scoped . marker = `POOL-PICK- ${ createHash ( 'sha256' ). update ( JSON . stringify ([ context . marker , id ( member . customerId ), member . dispatchNo ])). digest ( 'hex' ). slice ( 0 , 32 ) } `
return scoped
}
export async function pickPoolDispatch ( context , token , member , pick = pickAll ) {
const scoped = poolPickScope ( context , member )
const passed = await pick ( scoped , token , member . dispatchNo )
requireThat ( passed === true , `PDA_PICK_FAILED: ${ member . dispatchNo } ` )
return scoped . marker
}
2026-10-07 16:46:56 +08:00
export function shrinkingVehicleRequest ( vehicle ) {
requireThat ( vehicle ? . id && Number . isInteger ( Number ( vehicle . recordVersion )) && Number ( vehicle . maxPalletSpaces ) > 1
&& Number ( vehicle . maxLoadKg ) > 0 && Number ( vehicle . maxVolumeCbm ) > 0 , 'COMPLETE_OWNED_VEHICLE_SNAPSHOT_REQUIRED' )
return { recordVersion : vehicle . recordVersion , plateNumber : vehicle . plateNumber , model : vehicle . model ,
fuelConsumptionPer100Km : vehicle . fuelConsumptionPer100Km , mileageKm : vehicle . mileageKm ,
driverOrDispatchCompany : vehicle . driverOrDispatchCompany ?? null , phone : vehicle . phone ?? null ,
region : vehicle . region , ownership : vehicle . ownership , remark : vehicle . remark ?? null ,
maxPalletSpaces : Number ( vehicle . maxPalletSpaces ) - 1 , maxLoadKg : vehicle . maxLoadKg , maxVolumeCbm : vehicle . maxVolumeCbm }
}
export function expiryWaitMs ( cutoffAt , now = Date . now ()) {
const cutoff = Date . parse ( cutoffAt )
requireThat ( Number . isFinite ( cutoff ) && Number . isFinite ( now ), 'INVALID_SERVER_CUTOFF' )
const delay = Math . max ( 0 , cutoff + 300 - now )
requireThat ( delay <= 15000 , 'CUTOFF_SCENARIO_WAIT_EXCEEDS_BOUNDED_WINDOW' )
return delay
}
const capacitySnapshot = offer => JSON . stringify ({ capacityKnown : offer . capacityKnown , destinations : offer . destinations , segments : offer . segments })
const inquiryFact = row => JSON . stringify ({ bookingNo : row . bookingNo , customerId : row . customerId , businessType : row . businessType ,
recordVersion : row . recordVersion , status : row . status , cargo : row . cargo , expectedPalletCount : row . expectedPalletCount ,
businessOrderNo : row . businessOrderNo ?? null , incomingNo : row . incomingNo ?? null , dispatchNo : row . dispatchNo ?? null })
/** Current supported vehicle API prevents this state from being created on an assigned trip.
* Verify the real rejection; never write SQL or claim an impossible live-drift fixture succeeded. */
async function verifyAssignedVehicleGuards ( context , fixture , tripProof , { request , reject , check , offerDetail , proof }) {
requireThat ( context . createdResources . fleetTrips . includes ( tripProof ) && tripProof . tripNo === fixture . tripNo && tripProof . vehicleId ,
'ONLY_CURRENT_FIXTURE_VEHICLE_MAY_BE_TESTED' )
const path = ` ${ API } /master-data/vehicles/ ${ encode ( tripProof . vehicleId ) } `
const before = await request ( 'GET' , path ), offerBefore = ( await offerDetail ()). offer
check ( 'Vehicle guard targets the original fixture assigned vehicle' , id ( before . id ) === id ( tripProof . vehicleId ) && before . status === 'IN_SERVICE' )
const disabled = await reject ( 'Assigned vehicle cannot be disabled through master-data API' , 'PATCH' , ` ${ path } /status` ,
{ recordVersion : before . recordVersion , status : 'INACTIVE' })
check ( 'Disable rejected specifically because the vehicle is assigned' , /正在车次|改派|取消安排/ . test ( id ( disabled . body ? . message )))
const shrunk = await reject ( 'Assigned vehicle cannot shrink capacity through master-data API' , 'PUT' , path , shrinkingVehicleRequest ( before ))
check ( 'Shrink rejected specifically because the vehicle is assigned' , /正在车次|改派|取消安排/ . test ( id ( shrunk . body ? . message )))
const after = await request ( 'GET' , path ), offerAfter = ( await offerDetail ()). offer
check ( 'Rejected vehicle commands leave master version status and all capacities unchanged' , JSON . stringify ( after ) === JSON . stringify ( before ))
check ( 'Rejected vehicle commands do not alter pool capacity or acceptance eligibility' , capacitySnapshot ( offerBefore ) === capacitySnapshot ( offerAfter )
&& offerAfter . canConfirm === offerBefore . canConfirm )
proof . vehicleGuards = { status : 'VERIFIED_REJECTION' , vehicleId : id ( before . id ), originalVersion : before . recordVersion ,
liveDrift : 'NOT_CONSTRUCTIBLE_THROUGH_SUPPORTED_API' , explanation : 'VehicleApplicationService requires unassigned before status/capacity changes; no database bypass used.' }
proof . covered . push ( 'assigned-vehicle-disable-and-shrink-rejected' )
}
async function verifyPauseAndCutoff ({ offerNo , bookingNo , salesToken , dispatcherToken , request , reject , check , key , proof ,
wait = ms => new Promise ( resolve => setTimeout ( resolve , ms )) }) {
const detail = async () => ( await request ( 'GET' , ` ${ POOL } / ${ offerNo } ` , undefined , dispatcherToken )). offer
const latest = () => request ( 'GET' , ` ${ POOL } /inquiries/ ${ bookingNo } ` , undefined , dispatcherToken )
const control = async ( action , suffix ) => {
const offer = await detail ()
return ( await request ( 'POST' , ` ${ POOL } / ${ offerNo } / ${ action } ` , { tripRecordVersion : offer . tripRecordVersion ,
offerRecordVersion : offer . recordVersion , idempotencyKey : key ( suffix ), reason : 'TEST暂停与截止门禁' }, dispatcherToken )). offer
}
const editCutoff = async ( cutoffAt , suffix ) => {
const offer = await detail ()
return ( await request ( 'PUT' , ` ${ POOL } / ${ offerNo } ` , { tripNo : offer . tripNo , tripRecordVersion : offer . tripRecordVersion ,
offerRecordVersion : offer . recordVersion , cutoffAt , contactName : offer . contactName , contactPhone : offer . contactPhone ,
remark : offer . remark , idempotencyKey : key ( suffix ) }, dispatcherToken )). offer
}
const refusedCommands = async ( label , reason ) => {
const current = await latest (), before = inquiryFact ( current ), beforeOffer = await detail ()
const body = { tripRecordVersion : current . tripRecordVersion , offerRecordVersion : current . offerRecordVersion ,
recordVersion : current . recordVersion , customerId : current . customerId , businessType : current . businessType ,
deliveryMode : current . deliveryMode , estimatedArrivalAt : current . estimatedArrivalAt , expectedPalletCount : current . expectedPalletCount ,
cargo : current . cargo , quoteRef : null , idempotencyKey : key ( ` ${ label } -EDIT` ) }
for ( const [ method , path , command , token ] of [
[ 'POST' , ` ${ POOL } / ${ offerNo } /inquiries` , { ... body , recordVersion : null , idempotencyKey : key ( ` ${ label } -CREATE` ) }, salesToken ],
[ 'PUT' , ` ${ POOL } /inquiries/ ${ bookingNo } ` , body , salesToken ],
[ 'POST' , ` ${ POOL } /inquiries/ ${ bookingNo } /confirm` , { tripRecordVersion : current . tripRecordVersion ,
offerRecordVersion : current . offerRecordVersion , recordVersion : current . recordVersion , idempotencyKey : key ( ` ${ label } -CONFIRM` ) }, dispatcherToken ],
]) {
const response = await reject ( ` ${ label } ${ method } cannot mutate business` , method , path , command , token )
check ( ` ${ label } rejection comes from expected offer gate` , reason . test ( id ( response . body ? . message )))
}
check ( ` ${ label } preserves original draft and does not create a commercial order` , before === inquiryFact ( await latest ()))
check ( ` ${ label } preserves actual reservations` , capacitySnapshot ( beforeOffer ) === capacitySnapshot ( await detail ()))
}
const original = await detail ()
const paused = await control ( 'pause' , 'PAUSE-MATRIX' )
check ( 'Pause blocks both create and confirm affordances' , paused . status === 'PAUSED' && ! paused . canCreateInquiry && ! paused . canConfirm )
await refusedCommands ( 'PAUSED' , /暂停|开放|接单|发布/ )
const resumed = await control ( 'resume' , 'RESUME-MATRIX' )
check ( 'Explicit resume restores the same capacity' , resumed . status === 'OPEN' && resumed . canCreateInquiry && resumed . canConfirm
&& capacitySnapshot ( original ) === capacitySnapshot ( resumed ))
proof . covered . push ( 'paused-create-edit-confirm-rejected' )
const expiring = await editCutoff ( new Date ( Date . now () + 8000 ). toISOString (), 'SHORT-CUTOFF' )
const delay = expiryWaitMs ( expiring . cutoffAt )
await wait ( delay )
const expired = await detail ()
check ( 'Server marks cutoff expiry without mutating the stored OPEN status' , expired . status === 'OPEN' && ! expired . canCreateInquiry
&& ! expired . canConfirm && /截止|发车时间/ . test ( id ( expired . blockReason )))
await refusedCommands ( 'EXPIRED' , /截止|发车时间/ )
const restoreAt = Math . min ( Date . now () + 1800000 , Date . parse ( expired . plannedDepartureAt ) - 60000 )
requireThat ( restoreAt > Date . now () + 10000 , 'TRIP_NO_LONGER_HAS_TIME_TO_RESTORE_CUTOFF' )
await editCutoff ( new Date ( restoreAt ). toISOString (), 'RESTORE-CUTOFF' )
const restored = await control ( 'resume' , 'RESTORE-AFTER-CUTOFF' )
check ( 'Deadline scenario ends with explicit valid acceptance restored' , restored . canCreateInquiry && restored . canConfirm )
proof . cutoffMatrix = { status : 'VERIFIED' , cutoffAt : expiring . cutoffAt , waitedMs : delay , restoredCutoffAt : restored . cutoffAt }
proof . covered . push ( 'expired-cutoff-create-edit-confirm-rejected' )
}
async function poolScenario ( context , fixture , { execute = false , readInventory , credentials }) {
if ( ! execute ) return poolPlan ({ execute : false , runId : 'plan' })
requireThat ( fixture ? . marker === context . marker && fixture . members ? . length === 2 && fixture . pdaToken
&& typeof readInventory === 'function' , 'CURRENT_PHYSICAL_FIXTURE_REQUIRED' )
const proof = context . createdResources . fleetCapacityPool = { status : 'CHECKING' , marker : context . marker ,
tripNo : fixture . tripNo , covered : [], remaining : [... POOL_REMAINING ], actors : [], orders : [], snapshots : {} }
const tripProof = context . createdResources . fleetTrips . find ( row => row . tripNo === fixture . tripNo )
const tripPath = ` ${ API } /fleet/trips/ ${ encode ( fixture . tripNo ) } `
const key = label => createHash ( 'sha256' ). update ( ` ${ context . marker } :POOL: ${ label } ` ). digest ( 'hex' )
2026-10-07 17:21:58 +08:00
const check = ( label , ok , response = null ) => { context . assert ( MODULE , label , response , Boolean ( ok ), '' , { scenario : MODULE , deploymentBlocker : true }); requireThat ( ok , label ) }
2026-10-07 16:46:56 +08:00
const raw = ( method , path , body , token = context . token ) => context . request ( method , path , body , token )
const request = async ( method , path , body , token = context . token ) => {
const response = await raw ( method , path , body , token )
2026-10-07 17:21:58 +08:00
check ( ` ${ method } ${ path } ` , success ( response ) && response . requestId && response . traceId , response )
2026-10-07 16:46:56 +08:00
return response . body . data
}
const reject = async ( label , method , path , body , token ) => {
const response = await raw ( method , path , body , token )
2026-10-07 17:21:58 +08:00
check ( label , denied ( response ) && response . requestId && response . traceId , response )
2026-10-07 16:46:56 +08:00
return response
}
const permissions = await request ( 'GET' , ` ${ IAM } /permissions` )
const actorSessions = [], merchantSessions = []
const businessPath = order => ` ${ API } / ${ order . businessType === 'US_TRUCK' ? 'us-truck-orders' : 'local-looseboard-orders' } / ${ encode ( order . businessOrderNo ) } ?customerId= ${ encode ( order . customerId ) } `
const createActor = async ( kind , index , codes ) => {
const suffix = createHash ( 'sha256' ). update ( ` ${ context . marker } : ${ kind } : ${ index } ` ). digest ( 'hex' ). slice ( 0 , 16 )
const found = codes . map ( code => permissions . filter ( p => p . permissionCode === code && p . status === 'ACTIVE' ))
check ( 'Exact pool permissions exist' , found . every ( rows => rows . length === 1 && rows [ 0 ]. id ))
const role = await request ( 'POST' , ` ${ IAM } /roles` , { roleCode : `POOL_ ${ kind } _ ${ suffix } ` . toUpperCase (), roleName : ` ${ context . marker } - ${ kind } - ${ index } ` , description : context . marker })
const actor = { kind , index , roleId : id ( role . id ) }; proof . actors . push ( actor )
await request ( 'PUT' , ` ${ IAM } /roles/permissions` , { roleId : actor . roleId , permissionIds : found . map ( rows => id ( rows [ 0 ]. id )) })
const username = `pool_ ${ kind } _ ${ suffix } ` , password = `Pool ${ randomUUID (). replaceAll ( '-' , '' ) } A9`
const user = await request ( 'POST' , ` ${ IAM } /users` , { username , displayName : ` ${ context . marker } -同名测试人员` ,
phone : await findFleetActorPhone ( context , suffix ), email : ` ${ username } @example.com` , tenantId : '0' , roleIds : [ actor . roleId ], password , status : 'ACTIVE' })
actor . membershipId = id ( user . membershipId )
const login = await request ( 'POST' , '/api/auth/login' , { loginId : username , password }, '' )
actorSessions . push ({ accessToken : login . accessToken , refreshToken : login . refreshToken })
const effective = await request ( 'GET' , ` ${ IAM } /authorization/permissions` , undefined , login . accessToken )
check ( 'Temporary actor has exact permissions' , Array . isArray ( effective ) && JSON . stringify ([... effective ]. sort ()) === JSON . stringify ([... codes ]. sort ()))
return login . accessToken
}
try {
const sales = [], dispatchers = []
for ( let n = 0 ; n < 2 ; n ++ ) sales . push ( await createActor ( 'sales' , n , [ 'list' , 'inquiry-create' , 'inquiry-edit' , 'inquiry-cancel' ]. map ( s => PREFIX + s )))
for ( let n = 0 ; n < 2 ; n ++ ) dispatchers . push ( await createActor ( 'dispatch' , n , [ PREFIX + 'manage' ]))
2026-10-10 08:07:27 +08:00
const fulfillmentToken = await createActor ( 'fulfillment' , 0 , [ PREFIX + 'manage' ,
'platform:fleet:trip:booking-edit' , 'platform:fleet:trip:booking-confirm' , 'platform:fleet:trip:edit' ,
'platform:overseas-warehouse:inbound-order:smart-dispatch' ])
2026-10-07 16:46:56 +08:00
for ( const member of fixture . members ) {
const credential = credentials ? . get ( member . customerId )
check ( 'Original fixture customer credentials available only in memory' , Boolean ( credential ))
const login = await request ( 'POST' , '/api/auth/login' , { loginId : credential . username , password : credential . password }, '' )
merchantSessions . push ({ customerId : member . customerId , accessToken : login . accessToken , refreshToken : login . refreshToken })
await reject ( 'Merchant token cannot read internal capacity pool' , 'GET' , ` ${ POOL } ?page=1&size=20` , undefined , login . accessToken )
}
proof . covered . push ( 'merchant-cannot-read-pool' )
let detail = await request ( 'GET' , tripPath ), plan = await request ( 'GET' , ` ${ tripPath } /capacity-plan` )
check ( 'One actual thirty-space trip and two original real pallets' , Number ( detail . trip . maxPalletSpaces ) === 30 && plan . cargo . length === 2 )
const planned = controlledPalletMeasurements ( plan . cargo , detail . trip . stops ). map ( p => ({ ... p , palletSpaces : 9 }))
await request ( 'POST' , ` ${ tripPath } /capacity-plan` , { recordVersion : plan . recordVersion , manifestHash : plan . manifestHash , pallets : planned , reason : 'TEST容量计划18托位;实际装载另行实测确认' })
detail = await request ( 'GET' , tripPath )
const offerDraft = await request ( 'POST' , POOL , { tripNo : fixture . tripNo , tripRecordVersion : detail . trip . recordVersion ,
cutoffAt : new Date ( Date . now () + 1800000 ). toISOString (), contactName : 'TEST调度' , contactPhone : '19900000000' , remark : context . marker , idempotencyKey : key ( 'OFFER' ) }, dispatchers [ 0 ])
let offer = ( await request ( 'POST' , ` ${ POOL } / ${ offerDraft . offer . offerNo } /publish` , { tripRecordVersion : offerDraft . offer . tripRecordVersion ,
offerRecordVersion : offerDraft . offer . recordVersion , idempotencyKey : key ( 'PUBLISH' ) }, dispatchers [ 0 ])). offer
proof . offerNo = offer . offerNo
const destinationKey = plan . cargo [ 0 ]. destinationKey
check ( 'Owned physical sources share exact destination' , plan . cargo . every ( row => row . destinationKey === destinationKey ))
assertPoolRemaining ( offer , destinationKey , 12 )
const offerDetail = async token => request ( 'GET' , ` ${ POOL } / ${ proof . offerNo } ` , undefined , token )
const createInquiry = async ( index , type , spaces , label , incomplete = false , cargoOverrides = {}) => {
offer = ( await offerDetail ( sales [ index ])). offer
const body = { tripRecordVersion : offer . tripRecordVersion , offerRecordVersion : offer . recordVersion ,
customerId : fixture . members [ index ]. customerId , businessType : type , deliveryMode : 'SELF_DELIVERY' , expectedPalletCount : incomplete ? null : 1 ,
estimatedArrivalAt : new Date ( Date . now () + 900000 ). toISOString (), idempotencyKey : key ( label ),
cargo : { destinationKey , cargoName : ` ${ context . marker } - ${ label } ` , packageCount : incomplete ? null : 1 , palletSpaces : spaces ,
weightKg : 100 , volumeCbm : 1 , priceBasis : 'PALLET' , unitPrice : 30 , currency : 'CNY' , contactName : 'TEST货主' , contactPhone : '19900000000' , remark : context . marker , ... cargoOverrides } }
return ( await request ( 'POST' , ` ${ POOL } / ${ proof . offerNo } /inquiries` , body , sales [ index ])). inquiry
}
const incomplete = await createInquiry ( 0 , 'US_TRUCK' , null , 'UNKNOWN' , true )
check ( 'Unknown box/pallet counts are null instead of fabricated one' , incomplete . cargo . packageCount == null && incomplete . expectedPalletCount == null )
proof . covered . push ( 'unknown-load-stays-null' )
const first = await createInquiry ( 0 , 'LOCAL_LOOSEBOARD' , 8 , 'LOOSE' )
await reject ( 'Other sales cannot read this inquiry' , 'GET' , ` ${ POOL } /inquiries/ ${ first . bookingNo } ` , undefined , sales [ 1 ])
2026-10-10 08:07:27 +08:00
const ownFulfillment = await request ( 'GET' , ` ${ POOL } /inquiries/ ${ first . bookingNo } /fulfillment` , undefined , sales [ 0 ])
check ( 'Owner reads only the selected booking without trip-list permission' , ownFulfillment . booking . bookingNo === first . bookingNo
&& ownFulfillment . booking . customerId === first . customerId && ! ownFulfillment . dispatches && ! ownFulfillment . bookings )
await reject ( 'Other sales cannot read dedicated fulfillment' , 'GET' , ` ${ POOL } /inquiries/ ${ first . bookingNo } /fulfillment` , undefined , sales [ 1 ])
proof . covered . push ( 'pool-fulfillment-owner-read-isolation' )
2026-10-07 16:46:56 +08:00
const second = await createInquiry ( 1 , 'US_TRUCK' , 4 , 'TRUCK-A' )
check ( 'Same display name does not collapse distinct sales identities' , first . salesOwnerUserId && second . salesOwnerUserId && first . salesOwnerUserId !== second . salesOwnerUserId )
proof . covered . push ( 'two-sales-owner-isolation' )
const latest = no => request ( 'GET' , ` ${ POOL } /inquiries/ ${ no } ` , undefined , dispatchers [ 0 ])
const command = ( inquiry , label ) => ({ tripRecordVersion : inquiry . tripRecordVersion , offerRecordVersion : inquiry . offerRecordVersion ,
recordVersion : inquiry . recordVersion , idempotencyKey : key ( label ) })
2026-10-07 18:11:55 +08:00
const ownershipBefore = await latest ( first . bookingNo ), sensitiveTripBefore = await request ( 'GET' , tripPath )
const ownerOfferBefore = ( await offerDetail ( dispatchers [ 0 ])). offer
await reject ( 'Other sales cannot edit an owned inquiry' , 'PUT' , ` ${ POOL } /inquiries/ ${ first . bookingNo } ` ,
poolInquiryWrite ( ownershipBefore , key ( 'OTHER-SALES-EDIT' )), sales [ 1 ])
await reject ( 'Other sales cannot cancel an owned inquiry' , 'POST' , ` ${ POOL } /inquiries/ ${ first . bookingNo } /cancel` ,
{ ... command ( ownershipBefore , 'OTHER-SALES-CANCEL' ), reason : 'TEST越权负例' }, sales [ 1 ])
const sensitiveRead = await raw ( 'GET' , tripPath , undefined , sales [ 1 ])
check ( 'Minimal sales cannot read sensitive operational trip' , poolReadIsDenied ( sensitiveRead )
&& sensitiveRead . requestId && sensitiveRead . traceId , sensitiveRead )
check ( 'Rejected sales writes and reads preserve original owner draft and trip facts' ,
inquiryFact ( ownershipBefore ) === inquiryFact ( await latest ( first . bookingNo ))
&& JSON . stringify ( sensitiveTripBefore ) === JSON . stringify ( await request ( 'GET' , tripPath ))
&& capacitySnapshot ( ownerOfferBefore ) === capacitySnapshot (( await offerDetail ( dispatchers [ 0 ])). offer ))
proof . covered . push ( 'other-sales-write-and-sensitive-trip-denied' )
2026-10-07 16:46:56 +08:00
await verifyAssignedVehicleGuards ( context , fixture , tripProof , { request , reject , check , offerDetail : () => offerDetail ( dispatchers [ 0 ]), proof })
await verifyPauseAndCutoff ({ offerNo : proof . offerNo , bookingNo : first . bookingNo , salesToken : sales [ 0 ],
dispatcherToken : dispatchers [ 0 ], request , reject , check , key , proof })
proof . dimensionRejections = []
for ( const dimension of [ 'weightKg' , 'volumeCbm' ]) {
const available = ( await offerDetail ( dispatchers [ 0 ])). offer . destinations . find ( row => row . destinationKey === destinationKey ) ? . remaining
check ( 'Independent dimension test has ordinary spare pallet weight and volume capacity' , Number ( available ? . palletSpaces ) >= 1
&& Number ( available ? . weightKg ) > 100 && Number ( available ? . volumeCbm ) > 1 )
const inquiry = await createInquiry ( 1 , 'US_TRUCK' , 1 , `OVER- ${ dimension } ` , false , { [ dimension ] : Number ( available [ dimension ]) + 1 })
const before = await latest ( inquiry . bookingNo ), capacityBefore = ( await offerDetail ( dispatchers [ 0 ])). offer
const response = await reject ( ` ${ dimension } overrun cannot be hidden by spare pallet spaces` , 'POST' , ` ${ POOL } /inquiries/ ${ inquiry . bookingNo } /confirm` ,
command ( before , `REJECT- ${ dimension } ` ), dispatchers [ 1 ])
check ( 'Independent load rejection is an actual capacity-limit conflict' , /超过.*限制|容量|载重|容积/ . test ( id ( response . body ? . message )))
const after = await latest ( inquiry . bookingNo )
check ( 'Dimension refusal leaves draft source and all prior reservations unchanged' , inquiryFact ( before ) === inquiryFact ( after )
&& after . status === 'DRAFT' && ! after . businessOrderNo && capacitySnapshot ( capacityBefore ) === capacitySnapshot (( await offerDetail ( dispatchers [ 0 ])). offer ))
proof . dimensionRejections . push ({ dimension , bookingNo : inquiry . bookingNo , submitted : inquiry . cargo [ dimension ], available : available [ dimension ], status : 'REJECTED_UNCHANGED' })
}
proof . covered . push ( 'weight-and-volume-independent-capacity-rejections' )
2026-10-07 18:11:55 +08:00
proof . cancelledOrders = []
for ( const [ index , type ] of [ 'LOCAL_LOOSEBOARD' , 'US_TRUCK' ]. entries ()) {
const inquiry = await createInquiry ( index , type , 1 , `CANCEL- ${ type } ` )
proof . cancelledOrders . push ( await verifyPoolConfirmedCancellation ({ inquiry , destinationKey , request , check , latest ,
command , businessPath , dispatcherToken : dispatchers [ index ], label : `CANCEL- ${ type } ` }))
}
proof . covered . push ( 'confirmed-cancel-before-incoming-releases-capacity-and-replays' )
2026-10-10 08:07:27 +08:00
let current = await latest ( first . bookingNo ), firstCommand = poolInquiryWrite ( current , key ( 'SAVE-CONFIRM-LOOSE' ))
await reject ( 'Sales cannot save and confirm capacity' , 'POST' , ` ${ POOL } /inquiries/ ${ first . bookingNo } /save-and-confirm` , firstCommand , sales [ 0 ])
const firstConfirmed = await request ( 'POST' , ` ${ POOL } /inquiries/ ${ first . bookingNo } /save-and-confirm` , firstCommand , dispatchers [ 0 ])
const saveReplay = await request ( 'POST' , ` ${ POOL } /inquiries/ ${ first . bookingNo } /save-and-confirm` , firstCommand , dispatchers [ 0 ])
assertSingleCommercial ( firstConfirmed . inquiry , saveReplay . inquiry )
check ( 'Save and confirm original request replays one commercial commitment' , saveReplay . replayed === true )
proof . covered . push ( 'save-and-confirm-original-command-replay' )
2026-10-07 16:46:56 +08:00
assertPoolRemaining ( firstConfirmed . offer , destinationKey , 4 )
proof . covered . push ( 'same-segment-plan-18-of-30-reserve-8-leaves-4' )
const oversized = await createInquiry ( 1 , 'US_TRUCK' , 5 , 'OVER-CAPACITY' )
2026-10-07 18:11:55 +08:00
proof . revalidationDraftNo = oversized . bookingNo
2026-10-07 16:46:56 +08:00
await reject ( 'Five spaces cannot fit last four' , 'POST' , ` ${ POOL } /inquiries/ ${ oversized . bookingNo } /confirm` , command ( await latest ( oversized . bookingNo ), 'REJECT-FIVE' ), dispatchers [ 1 ])
const rival = await createInquiry ( 0 , 'US_TRUCK' , 4 , 'TRUCK-B' )
const contenders = [ await latest ( second . bookingNo ), await latest ( rival . bookingNo )]
const race = await Promise . all ( contenders . map (( row , index ) => raw ( 'POST' , ` ${ POOL } /inquiries/ ${ row . bookingNo } /confirm` , command ( row , `RACE- ${ index } ` ), dispatchers [ index ])))
check ( 'Two dispatcher race confirms exactly one remaining reservation' , race . filter ( success ). length === 1 && race . filter ( denied ). length === 1 )
const winner = race . find ( success ). body . data . inquiry
offer = ( await offerDetail ( dispatchers [ 0 ])). offer ; assertPoolRemaining ( offer , destinationKey , 0 )
proof . orders = [ firstConfirmed . inquiry , winner ]
proof . covered . push ( 'two-dispatchers-last-four-race' )
const poolMembers = []
for ( const [ index , order ] of proof . orders . entries ()) {
const source = await request ( 'GET' , businessPath ( order ))
2026-10-07 19:38:56 +08:00
check ( 'New pool commercial order retains unknown container and palletizing facts' , assertPoolUnknownCommercialFacts ( order . businessType , source . summary ))
2026-10-07 16:46:56 +08:00
check ( 'Pool commercial order has source arrangement and waits for real inbound' , source . summary . status === 'WAITING_INBOUND'
&& source . summary . fleetArrangement ? . bookingNo === order . bookingNo && ! source . summary . fleetArrangement . incomingNo )
const oldStatusPath = businessPath ( order ). replace ( '?' , '/status?' )
await reject ( 'Old commercial status mutation cannot bypass pool workflow' , 'PATCH' , oldStatusPath ,
{ version : source . summary . version , status : 'CANCELLED' , idempotencyKey : key ( `OLD-CANCEL- ${ index } ` ), remark : context . marker })
2026-10-07 18:11:55 +08:00
const otherCustomer = merchantSessions . find ( row => row . customerId !== order . customerId )
check ( 'Cross-customer negative has a genuinely different qualified customer' , Boolean ( otherCustomer ))
const merchantPath = `/api/merchant/v1/last-leg/ ${ order . businessType === 'US_TRUCK' ? 'us-truck-orders' : 'local-looseboard-orders' } / ${ encode ( order . businessOrderNo ) } `
const foreignRead = await raw ( 'GET' , merchantPath , undefined , otherCustomer . accessToken )
check ( 'Another customer cannot read this commercial order' , poolReadIsDenied ( foreignRead )
&& foreignRead . requestId && foreignRead . traceId , foreignRead )
check ( 'Denied old status and foreign read preserve commercial source' , JSON . stringify ( source ) === JSON . stringify ( await request ( 'GET' , businessPath ( order ))))
poolMembers . push ( await receiveAndBindPoolOrder ( context , fixture , order , index , { request , reject , check , key , readInventory ,
2026-10-10 08:07:27 +08:00
latest , command , businessPath , offerDetail : () => offerDetail ( dispatchers [ 0 ]), dispatcherToken : dispatchers [ 0 ], fulfillmentToken , destinationKey , proof }))
2026-10-07 16:46:56 +08:00
}
2026-10-07 18:11:55 +08:00
proof . covered . push ( 'old-manual-order-endpoints-rejected' , 'one-commercial-order-and-one-incoming' , 'both-business-types-real-pda-receipt' ,
'other-customer-commercial-read-denied-with-source-unchanged' , 'incoming-cancel-rejected-with-source-unchanged' ,
'receipt-overcapacity-rejected-with-source-unchanged' , 'open-offer-bind-replan-requires-explicit-resume' , 'bound-cargo-reservation-removed-without-double-count' )
offer = ( await offerDetail ( dispatchers [ 0 ])). offer
const beforeClose = await Promise . all ( proof . orders . map ( row => latest ( row . bookingNo )))
const closed = await request ( 'POST' , ` ${ POOL } / ${ proof . offerNo } /close` , { tripRecordVersion : offer . tripRecordVersion ,
offerRecordVersion : offer . recordVersion , idempotencyKey : key ( 'CLOSE' ), reason : context . marker }, dispatchers [ 0 ])
check ( 'Offer closed without cancelling commitments' , closed . offer . status === 'CLOSED' )
2026-10-10 08:07:27 +08:00
const replay = await request ( 'POST' , ` ${ POOL } /inquiries/ ${ first . bookingNo } /save-and-confirm` , firstCommand , dispatchers [ 0 ])
2026-10-07 18:11:55 +08:00
assertSingleCommercial ( firstConfirmed . inquiry , replay . inquiry )
check ( 'Successful confirmation still replays after close' , replay . replayed === true )
for ( const row of beforeClose ) check ( 'Old confirmation replay cannot revert bound source' , inquiryFact ( row ) === inquiryFact ( await latest ( row . bookingNo )))
const history = await request ( 'GET' , ` ${ POOL } /inquiries?page=1&size=100&scope=MINE` , undefined , sales [ 0 ])
check ( 'Sales retains its own history after close' , history . records . some ( row => row . bookingNo === first . bookingNo )
&& history . records . every ( row => row . salesOwnerUserId === first . salesOwnerUserId ))
proof . covered . push ( 'close-and-successful-confirmation-replay' )
2026-10-07 16:46:56 +08:00
detail = await request ( 'GET' , tripPath )
for ( const binding of detail . dispatches ) detail = await request ( 'POST' , ` ${ tripPath } /dispatches/ ${ encode ( binding . dispatchNo ) } /review` , { recordVersion : detail . trip . recordVersion , decision : 'APPROVED' })
const members = [... fixture . members , ... poolMembers ]
2026-10-07 17:21:58 +08:00
proof . pickScopes = []
2026-10-07 16:46:56 +08:00
for ( const member of members ) {
2026-10-07 17:21:58 +08:00
const namespace = await pickPoolDispatch ( context , fixture . pdaToken , member )
proof . pickScopes . push ({ customerId : member . customerId , dispatchNo : member . dispatchNo , namespace })
2026-10-07 16:46:56 +08:00
const task = await request ( 'GET' , `/api/pda/v1/last-leg/dispatches/ ${ encode ( member . dispatchNo ) } ` , undefined , fixture . pdaToken )
check ( 'Every owned and external pallet was actually picked' , task . items . every ( row => Number ( row . pickedPackages ) === Number ( row . scheduledPackages )))
}
const loading = await request ( 'GET' , ` ${ tripPath } /loading` )
check ( 'All four original physical pallets present for actual loading' , loading . cargo . length === 4 && ! loading . blockReason )
const loaded = await request ( 'POST' , ` ${ tripPath } /loading` , { recordVersion : loading . recordVersion , manifestHash : loading . manifestHash ,
pallets : controlledPalletMeasurements ( loading . cargo , detail . trip . stops ), reason : 'TEST受控实测每实托1托位;不同于之前18托位接单计划' })
detail = await request ( 'POST' , ` ${ tripPath } /loading-review` , { recordVersion : loaded . recordVersion , decision : 'APPROVED' })
const departBody = { recordVersion : detail . trip . recordVersion , idempotencyKey : key ( 'DEPART' ), remark : context . marker }
const departPath = `/api/pda/v1/last-leg/dispatches/ ${ encode ( fixture . members [ 0 ]. dispatchNo ) } /depart`
const departed = await request ( 'POST' , departPath , departBody , fixture . pdaToken )
check ( 'One whole-trip actual departure includes exactly four dispatches' , departed . status === 'IN_TRANSIT' && Number ( departed . shippedPackages ) === 4
&& departed . dispatches . length === 4 && members . every ( member => departed . dispatches . some ( row => row . dispatchNo === member . dispatchNo )))
check ( 'Departure original-key replay is explicit' , ( await request ( 'POST' , departPath , departBody , fixture . pdaToken )). replayed === true )
for ( const [ index , member ] of members . entries ()) {
const path = ` ${ API } /delivery-orders/ ${ encode ( member . deliveryNo ) } ?customerId= ${ encode ( member . customerId ) } `
const currentDelivery = await request ( 'GET' , path )
await request ( 'PUT' , path . replace ( '?' , '/status?' ), { status : 'DELIVERING' , expectedVersion : currentDelivery . order . version , remark : context . marker })
const signed = await saveReceiptAndComplete ( context , { deliveryNo : member . deliveryNo }, member . customerId , index )
check ( 'Receipt actually completes this delivery' , signed ? . order ? . status === 'DELIVERED' )
}
proof . covered . push ( 'shared-real-departure-and-signature' )
for ( const [ index , order ] of proof . orders . entries ()) {
const commercial = await request ( 'GET' , businessPath ( order ))
2026-10-07 19:38:56 +08:00
check ( 'Completed commercial order retains unknown container and palletizing facts' , assertPoolUnknownCommercialFacts ( order . businessType , commercial . summary ))
2026-10-07 16:46:56 +08:00
check ( 'Each business type completes from actual signature' , commercial . summary . status === 'COMPLETED'
&& commercial . summary . fleetArrangement ? . fulfillmentStage === 'COMPLETED' && Number ( commercial . summary . fleetArrangement . signedPackages ) === 1 )
const member = poolMembers [ index ], actual = await request ( 'GET' , ` ${ API } /delivery-orders/ ${ member . deliveryNo } ?customerId= ${ order . customerId } ` )
const fees = actual . fees . filter ( row => row . feeType === 'DELIVERY_FEE' && row . feeDirection === 'RECEIVABLE' )
check ( 'One agreed receivable remains, no order/receipt duplicate fee' , fees . length === 1 && Number ( fees [ 0 ]. amount ) === 30 && fees [ 0 ]. currency === 'CNY' )
const merchant = merchantSessions . find ( session => session . customerId === order . customerId )
const merchantPath = `/api/merchant/v1/last-leg/ ${ order . businessType === 'US_TRUCK' ? 'us-truck-orders' : 'local-looseboard-orders' } / ${ encode ( order . businessOrderNo ) } `
const own = await request ( 'GET' , merchantPath , undefined , merchant . accessToken )
2026-10-07 19:38:56 +08:00
check ( 'Client completed order retains unknown container and palletizing facts' , assertPoolUnknownCommercialFacts ( order . businessType , own . summary ))
2026-10-07 16:46:56 +08:00
check ( 'Client sees only own complete commercial arrangement' , own . summary . orderNo === order . businessOrderNo && own . summary . fleetArrangement ? . businessOrderNo === order . businessOrderNo )
}
2026-10-07 19:38:56 +08:00
proof . covered . push ( 'business-completion-and-fee-uniqueness' , 'pool-unspecified-container-and-palletizing-remain-unknown' )
2026-10-07 16:46:56 +08:00
proof . poolMembers = poolMembers ; proof . status = 'VERIFIED'
return proof
} catch ( error ) { proof . status = 'FAILED' ; throw error }
finally {
for ( const actor of proof . actors ) {
if ( actor . membershipId ) await request ( 'PUT' , ` ${ IAM } /users/ ${ actor . membershipId } /status` , { status : 'DISABLED' })
if ( actor . roleId ) await request ( 'PUT' , ` ${ IAM } /roles/ ${ actor . roleId } /status` , { status : 'DISABLED' })
}
// The outer runner tracks and logs out all successful login sessions, including these.
actorSessions . length = 0 ; merchantSessions . length = 0
}
}
2026-10-07 18:11:55 +08:00
async function receiveAndBindPoolOrder ( context , fixture , order , index , { request , reject , check , key , readInventory ,
2026-10-10 08:07:27 +08:00
latest , command , businessPath , offerDetail , dispatcherToken , fulfillmentToken , destinationKey , proof }) {
2026-10-07 16:46:56 +08:00
const tripPath = ` ${ API } /fleet/trips/ ${ encode ( fixture . tripNo ) } ` , bookingPath = ` ${ tripPath } /bookings/ ${ encode ( order . bookingNo ) } `
2026-10-10 08:07:27 +08:00
const poolPath = ` ${ POOL } /inquiries/ ${ encode ( order . bookingNo ) } `
2026-10-07 16:46:56 +08:00
let bookingList = await request ( 'GET' , ` ${ tripPath } /bookings` ), booking = bookingList . records . find ( row => row . bookingNo === order . bookingNo )
check ( 'Pool commercial association survived into existing booking workflow' , booking ? . status === 'CONFIRMED' )
const body = { tripRecordVersion : bookingList . tripRecordVersion , recordVersion : booking . recordVersion , estimatedArrivalAt : new Date ( Date . now () + 300000 ). toISOString (),
deliveryInstructions : ` ${ context . marker } TEST客户自送` , idempotencyKey : key ( `INCOMING- ${ index } ` ) }
2026-10-10 08:07:27 +08:00
await reject ( 'Pool management alone cannot arrange incoming' , 'POST' , ` ${ poolPath } /incoming` , body , dispatcherToken )
const created = await request ( 'POST' , ` ${ poolPath } /incoming` , body , fulfillmentToken ), incomingNo = created . booking . incomingNo
const repeated = await request ( 'POST' , ` ${ poolPath } /incoming` , body , fulfillmentToken )
2026-10-07 16:46:56 +08:00
check ( 'One incoming document on exact replay' , incomingNo && repeated . replayed === true && repeated . booking . incomingNo === incomingNo )
2026-10-07 18:11:55 +08:00
const incomingBeforeCancel = await request ( 'GET' , ` ${ bookingPath } /incoming` ), sourceBeforeCancel = await request ( 'GET' , businessPath ( order ))
const inquiryBeforeCancel = await latest ( order . bookingNo ), capacityBeforeCancel = ( await offerDetail ()). offer
await reject ( 'Incoming already registered cannot be cancelled from pool' , 'POST' , ` ${ POOL } /inquiries/ ${ order . bookingNo } /cancel` ,
{ ... command ( inquiryBeforeCancel , `CANCEL-AFTER-INCOMING- ${ index } ` ), reason : 'TEST已来货取消拒绝' }, dispatcherToken )
check ( 'Rejected incoming cancellation preserves incoming commercial booking and capacity' ,
JSON . stringify ( incomingBeforeCancel ) === JSON . stringify ( await request ( 'GET' , ` ${ bookingPath } /incoming` ))
&& JSON . stringify ( sourceBeforeCancel ) === JSON . stringify ( await request ( 'GET' , businessPath ( order )))
&& inquiryFact ( inquiryBeforeCancel ) === inquiryFact ( await latest ( order . bookingNo ))
&& capacitySnapshot ( capacityBeforeCancel ) === capacitySnapshot (( await offerDetail ()). offer ))
2026-10-07 16:46:56 +08:00
const pdaPath = `/api/pda/v1/last-leg/warehouse-transfers/ ${ encode ( incomingNo ) } `
const measured = await request ( 'POST' , ` ${ pdaPath } /pallets/drafts` , { idempotencyKey : key ( `MEASURE- ${ index } ` ), cartonCount : 1 ,
lengthCm : 100 , widthCm : 100 , heightCm : 100 , volumeCbm : 1 , grossWeightKg : 100 }, fixture . pdaToken )
check ( 'One actual measured draft only' , measured . pallets . length === 1 && measured . pallets [ 0 ]. status === 'DRAFT' )
const palletPath = ` ${ pdaPath } /pallets/ ${ measured . pallets [ 0 ]. id } `
const generated = await request ( 'POST' , ` ${ palletPath } /generate` , { idempotencyKey : key ( `GENERATE- ${ index } ` ) }, fixture . pdaToken )
const pallet = generated . pallets [ 0 ]
await request ( 'POST' , ` ${ palletPath } /bind` , { palletCode : pallet . palletNo , idempotencyKey : key ( `BIND-PALLET- ${ index } ` ) }, fixture . pdaToken )
const uploaded = await context . requestMultipart ( 'POST' , ` ${ pdaPath } /measurement-photos/files` , { name : ` ${ context . marker } -pool- ${ index } -TEST.png` , type : 'image/png' , content : PNG }, fixture . pdaToken )
check ( 'Real TEST measurement file exists' , success ( uploaded ) && uploaded . body . data ? . fileId )
const received = await request ( 'POST' , ` ${ pdaPath } /palletize` , { idempotencyKey : key ( `PALLETIZE- ${ index } ` ), photoFileIds : [ id ( uploaded . body . data . fileId )] }, fixture . pdaToken )
check ( 'Actual PDA receipt completed one original pallet' , received . status === 'WAIT_CUSTOMER_CONFIRMATION' && received . pallets . length === 1 && received . pallets [ 0 ]. status === 'COMPLETED' )
2026-10-10 08:07:27 +08:00
const evidence = ( await request ( 'GET' , ` ${ poolPath } /fulfillment` , undefined , fulfillmentToken )). preview
2026-10-07 18:11:55 +08:00
const beforeOverReceipt = await latest ( order . bookingNo ), sourceBeforeReceipt = await request ( 'GET' , businessPath ( order ))
const capacityBeforeReceipt = ( await offerDetail ()). offer , tripBeforeReceipt = await request ( 'GET' , tripPath )
const inventoryBeforeReceipt = await readInventory ({ marker : context . marker , customerId : order . customerId , warehouseId : fixture . warehouseId })
2026-10-10 08:07:27 +08:00
const overReceipt = await reject ( 'Actual received pallet spaces cannot exceed whole vehicle capacity' , 'POST' , ` ${ poolPath } /receipt-confirmation` ,
2026-10-07 18:11:55 +08:00
{ tripRecordVersion : evidence . tripRecordVersion , recordVersion : evidence . recordVersion , receiptHash : evidence . incoming . receiptHash ,
pallets : [{ warehousePalletId : id ( pallet . warehousePalletId ), palletSpaces : Number ( tripBeforeReceipt . trip . maxPalletSpaces ) + 1 }],
2026-10-10 08:07:27 +08:00
unitPrice : 30 , idempotencyKey : key ( `OVER-RECEIPT- ${ index } ` ) }, fulfillmentToken )
2026-10-07 18:11:55 +08:00
check ( 'Receipt overrun is refused by actual capacity calculation' , /超过.*限制|容量|载重|容积/ . test ( id ( overReceipt . body ? . message )))
check ( 'Rejected overreceipt leaves source receipt commercial booking and reservations unchanged' ,
JSON . stringify ( evidence ) === JSON . stringify ( await request ( 'GET' , ` ${ bookingPath } /incoming` ))
&& inquiryFact ( beforeOverReceipt ) === inquiryFact ( await latest ( order . bookingNo ))
&& JSON . stringify ( sourceBeforeReceipt ) === JSON . stringify ( await request ( 'GET' , businessPath ( order )))
&& capacitySnapshot ( capacityBeforeReceipt ) === capacitySnapshot (( await offerDetail ()). offer ))
check ( 'Rejected overreceipt leaves complete customer warehouse inventory unchanged' , JSON . stringify ( inventoryBeforeReceipt )
=== JSON . stringify ( await readInventory ({ marker : context . marker , customerId : order . customerId , warehouseId : fixture . warehouseId })))
2026-10-10 08:07:27 +08:00
const receiptBody = { tripRecordVersion : evidence . tripRecordVersion , recordVersion : evidence . recordVersion ,
receiptHash : evidence . incoming . receiptHash , pallets : [{ warehousePalletId : id ( pallet . warehousePalletId ), palletSpaces : 1 }], unitPrice : 30 , idempotencyKey : key ( `RECEIPT- ${ index } ` ) }
await reject ( 'Pool management alone cannot confirm receipt' , 'POST' , ` ${ poolPath } /receipt-confirmation` , receiptBody , dispatcherToken )
await request ( 'POST' , ` ${ poolPath } /receipt-confirmation` , receiptBody , fulfillmentToken )
2026-10-07 16:46:56 +08:00
const inventory = await readInventory ({ marker : context . marker , customerId : order . customerId , warehouseId : fixture . warehouseId })
check ( 'Immutable actual incoming STOCK_IN is unique' , inventory . scope ? . coverage === 'CUSTOMER_WAREHOUSE_COMPLETE'
&& inventory . ledger . filter ( row => row . businessOrderNo === incomingNo && row . businessType === 'STOCK_IN' && Number ( row . deltaPackages ) === 1 ). length === 1 )
const dispatchBase = ` ${ API } /dispatches` , candidates = await request ( 'GET' , ` ${ dispatchBase } /candidates? ${ new URLSearchParams ({ sourceStage : 'RECEIVED' , orderNo : incomingNo } )}` )
check ( 'Received candidate exactly belongs to this commercial incoming' , candidates . length === 1 && candidates [ 0 ]. inboundCode === incomingNo
&& id ( candidates [ 0 ]. customerId ) === order . customerId && id ( candidates [ 0 ]. warehousePalletId ) === id ( pallet . warehousePalletId ))
bookingList = await request ( 'GET' , ` ${ tripPath } /bookings` ); booking = bookingList . records . find ( row => row . bookingNo === order . bookingNo )
2026-10-10 08:07:27 +08:00
const arrangeBody = { tripRecordVersion : bookingList . tripRecordVersion , recordVersion : booking . recordVersion ,
idempotencyKey : key ( `ARRANGE-DISPATCH- ${ index } ` ) }
await reject ( 'Pool management alone cannot generate and bind dispatch' , 'POST' , ` ${ poolPath } /arrange-dispatch` , arrangeBody , dispatcherToken )
const arranged = await request ( 'POST' , ` ${ poolPath } /arrange-dispatch` , arrangeBody , fulfillmentToken )
const arrangeReplay = await request ( 'POST' , ` ${ poolPath } /arrange-dispatch` , arrangeBody , fulfillmentToken )
check ( 'One command generates and binds the actual receipt without a manually supplied number' , arranged . booking . status === 'BOUND'
&& arranged . booking . dispatchNo && arranged . booking . incomingNo === incomingNo && arranged . booking . customerId === order . customerId )
check ( 'Arrange replay returns the original dispatch and version' , arrangeReplay . replayed === true
&& arrangeReplay . booking . dispatchNo === arranged . booking . dispatchNo && arrangeReplay . tripRecordVersion === arranged . tripRecordVersion )
const dispatch = { dispatchNo : arranged . booking . dispatchNo }
if ( index === 0 ) proof . covered . push ( 'pool-fulfillment-explicit-write-permissions' , 'pool-arrange-dispatch-without-manual-number' )
2026-10-07 16:46:56 +08:00
let plan = await request ( 'GET' , ` ${ tripPath } /capacity-plan` ), detail = await request ( 'GET' , tripPath )
2026-10-07 18:11:55 +08:00
const actualMeasurements = controlledPalletMeasurements ( plan . cargo , detail . trip . stops )
const restored = await verifyPoolBindingRevalidation ({ offerDetail , check ,
blockedConfirm : async label => {
const before = await latest ( proof . revalidationDraftNo )
const rejected = await reject ( 'Stale OPEN publication cannot confirm another draft' , 'POST' , ` ${ POOL } /inquiries/ ${ before . bookingNo } /confirm` ,
command ( before , ` ${ label } - ${ index } ` ), dispatcherToken )
check ( 'Confirmation refused specifically for stale plan or publication commitment' , /计划|核对|恢复接单|承诺已变化/ . test ( id ( rejected . body ? . message )))
check ( 'Revalidation refusal keeps competing draft unchanged' , inquiryFact ( before ) === inquiryFact ( await latest ( before . bookingNo )))
},
replan : () => request ( 'POST' , ` ${ tripPath } /capacity-plan` , { recordVersion : plan . recordVersion , manifestHash : plan . manifestHash ,
pallets : actualMeasurements , reason : ` ${ context . marker } 实收后重新核对,不重复扣外接预留` }),
resume : async offer => ( await request ( 'POST' , ` ${ POOL } / ${ proof . offerNo } /resume` , { tripRecordVersion : offer . tripRecordVersion ,
offerRecordVersion : offer . recordVersion , idempotencyKey : key ( `RESUME-AFTER-BIND- ${ index } ` ), reason : 'TEST实际绑定核量后显式恢复接单' }, dispatcherToken )). offer })
const bound = await latest ( order . bookingNo )
check ( 'Bound booking points to this actual incoming and dispatch' , bound . status === 'BOUND' && bound . incomingNo === incomingNo && bound . dispatchNo === dispatch . dispatchNo )
const allBookings = await request ( 'GET' , ` ${ tripPath } /bookings` )
const load = rows => rows . reduce (( total , row ) => { for ( const dimension of [ 'palletSpaces' , 'weightKg' , 'volumeCbm' ]) total [ dimension ] += Number ( row [ dimension ]); return total },
{ palletSpaces : 0 , weightKg : 0 , volumeCbm : 0 })
const plannedLoad = load ( actualMeasurements )
const reservedLoad = load ( allBookings . records . filter ( row => [ 'CONFIRMED' , 'RECEIVED' ]. includes ( row . status )). map ( row => row . cargo ))
const capacity = { palletSpaces : detail . trip . maxPalletSpaces , weightKg : detail . trip . maxLoadKg , volumeCbm : detail . trip . maxVolumeCbm }
assertPoolBoundCapacity ( restored , destinationKey , plannedLoad , reservedLoad , capacity )
check ( 'BOUND reservation is removed exactly once from all capacity dimensions' , Number ( plannedLoad . palletSpaces ) === 3 + index
&& Number ( reservedLoad . palletSpaces ) === ( index === 0 ? 4 : 0 ))
;( proof . bindingCapacity ||= []). push ({ bookingNo : order . bookingNo , status : bound . status , planned : plannedLoad ,
reserved : reservedLoad , remaining : restored . destinations . find ( row => row . destinationKey === destinationKey ). remaining })
2026-10-07 16:46:56 +08:00
const path = ` ${ dispatchBase } / ${ encode ( dispatch . dispatchNo ) } ` , freight = await request ( 'GET' , ` ${ path } /external-freight` )
check ( 'The one pool commercial source owns the one agreed freight' , freight . requiresDeliveryFee === false && freight . quotes . length === 1
&& freight . quotes [ 0 ]. bookingNo === order . bookingNo && Number ( freight . quotes [ 0 ]. amount ) === 30 )
detail = await request ( 'GET' , tripPath )
const transfer = { idempotencyKey : key ( `TRANSFER- ${ index } ` ), plannedDeliveryAt : localTime ( 21600000 ), deliveryMethod : 'TRUCK_PALLET' , pickupMethod : 'WAREHOUSE_PICKUP' ,
loadingType : 'PALLET' , vehicleType : 'TRUCK' , vehicleSize : '30托' , vehiclePlate : detail . trip . vehiclePlate , driverName : detail . trip . driverName ,
driverPhone : detail . trip . driverPhone , appointmentAccount : 'SELF' , externalFreightHash : freight . snapshotHash , fees : [], remark : context . marker }
const converted = await request ( 'POST' , ` ${ path } /transfer-delivery` , transfer )
check ( 'Pool source produces exactly one outbound and delivery' , converted . outboundNo && converted . deliveries . length === 1 )
const replay = await request ( 'POST' , ` ${ path } /transfer-delivery` , transfer )
check ( 'Transfer replay reuses outbound and delivery' , replay . replayed === true && replay . outboundNo === converted . outboundNo && replay . deliveries [ 0 ]. deliveryNo === converted . deliveries [ 0 ]. deliveryNo )
return { customerId : order . customerId , bookingNo : order . bookingNo , businessOrderNo : order . businessOrderNo , incomingNo , dispatchNo : dispatch . dispatchNo ,
outboundNo : converted . outboundNo , deliveryNo : converted . deliveries [ 0 ]. deliveryNo , palletId : id ( pallet . warehousePalletId ), palletNo : pallet . palletNo }
}
export async function runPoolAcceptance ( config , { env = process . env , outputRoot = ROOT , runner = runCrossCustomer } = {}) {
if ( ! config . execute ) return poolPlan ( config )
const credentials = new Map (); let restoreWrites
try {
const result = await runner ( config , { env , outputRoot ,
contextFactory : options => {
const context = new AcceptanceRun ( options ), original = context . request
context . request = async function (... args ) {
const response = await original . apply ( this , args )
if ( args [ 0 ] === 'POST' && args [ 1 ] === ` ${ IAM } /tenants` && success ( response )) {
const data = response . body . data , customer = data . tenant || data . customer || data
credentials . set ( id ( customer . id || data . tenantId ), { username : data . administratorUsername , password : data . temporaryPassword })
}
return response
}
2026-10-08 17:07:57 +08:00
const directory = join ( outputRoot , 'reports' , poolReportName ( config ), 'pool-write-journal' )
2026-10-07 16:46:56 +08:00
mkdirSync ( directory )
restoreWrites = guardFleetWrites ( context , async entry => {
const file = openSync ( join ( directory , ` ${ entry . sequence } .json` ), 'w' )
try { writeFileSync ( file , JSON . stringify ( redactFleetWrite ( entry ), null , 2 ) + '\n' ); fsyncSync ( file ) } finally { closeSync ( file ) }
}, { guardFetch : true })
return context
},
verify : ( context , fixture , options ) => poolScenario ( context , fixture , { ... options , credentials }) })
2026-10-08 17:07:57 +08:00
result . scope = config . environment === 'GRAY' ? 'FLEET_CAPACITY_POOL_GRAY' : 'FLEET_CAPACITY_POOL_HTTP' ; result . fullAcceptance = false
2026-10-07 16:46:56 +08:00
result . remainingSeparateAcceptance = [... POOL_REMAINING ]
2026-10-08 17:07:57 +08:00
const output = join ( outputRoot , 'reports' , poolReportName ( config ))
const sources = [ 'scripts/fleet-capacity-pool-test-env.mjs' , ...( config . environment === 'GRAY' ? [ 'scripts/fleet-capacity-pool-gray.mjs' ] : [])]. map ( path => ({ path , sha256 : createHash ( 'sha256' ). update ( readFileSync ( join ( ROOT , path ))). digest ( 'hex' ) }))
2026-10-07 16:46:56 +08:00
writeFileSync ( join ( output , 'pool-scope.json' ), JSON . stringify ({ plan : poolPlan ( config ), sources , success : result . success ,
fullAcceptance : false , covered : result . createdResources ? . fleetCapacityPool ? . covered || [], remaining : POOL_REMAINING }, null , 2 ) + '\n' , { flag : 'wx' })
return result
} finally { restoreWrites ? .(); credentials . clear () }
}
if ( process . argv [ 1 ] && import . meta . url === pathToFileURL ( resolve ( process . argv [ 1 ])). href ) {
try {
const config = parsePoolArguments ( process . argv . slice ( 2 )), result = await runPoolAcceptance ( config )
console . log ( JSON . stringify ( config . execute ? { success : result . success , fullAcceptance : result . fullAcceptance , error : result . error ,
summary : result . summary , covered : result . createdResources ? . fleetCapacityPool ? . covered , remaining : result . remainingSeparateAcceptance ,
output : poolPlan ( config ). output } : result , null , 2 ))
if ( config . execute && ! result . success ) process . exitCode = 1
} catch ( error ) { console . error ( error . message ); process . exitCode = 1 }
}