2026-10-03 14:52:04 +08:00
import assert from 'node:assert/strict'
import test from 'node:test'
import { run , runQuoteSession , verifyOperations , requireCurrentFixture , readCsv , fixtureContract } from './fleet-quotes-operations.mjs'
import { AcceptanceRun } from '../admin-test-env-acceptance.mjs'
const copy = value => structuredClone ( value )
function harness ( fault ) {
const marker = 'FLEET-CURRENT-RUN' , tripNo = 'TR-CURRENT' , departedAt = '2026-10-03T01:00:00Z'
const rows = new Map (), histories = new Map (), commands = new Map (), measuredKeys = new Map (), calls = []
let next = 0 , supplierStatus = 'ACTIVE'
2026-10-03 16:38:37 +08:00
let measured = { tripNo , recordVersion : '0' , details : { actualDistanceMiles : null , actualFuelLitres : null , remark : null , evidenceFiles : [] } }
2026-10-03 14:52:04 +08:00
const measuredHistory = []
const expected = { physicalPallets : 1 , palletSpaces : 2 , ownPackages : 11 , externalPackages : 4 , externalAgreedAmounts : [{ currency : 'USD' , amount : 0 }], costs : [] }
const response = ( data , code = '200' , http = 200 ) => ({ http , body : { code , data : copy ( data ) }, requestId : 'offline-request' , traceId : 'offline-trace' })
function snapshot ( row , action ) { histories . get ( row . quoteNo ). unshift ({ action , recordVersion : row . recordVersion , snapshot : copy ( row ) }) }
const natural = d => JSON . stringify ([ d . kind , d . partyId , d . originCity , d . destinationCode , d . distanceMiles , d . currency , d . validFrom , d . validTo ])
function create ( request , batchHash = '' ) {
const hash = batchHash + JSON . stringify ( request . details ), prior = commands . get ( request . idempotencyKey )
if ( prior ) return prior . hash === hash ? response ( fault === 'mutable creation replay' ? rows . get ( prior . row . quoteNo ) : prior . row ) : response ( null , 'A0409' )
const d = request . details
if ([ d . selfPickupPrice , d . fullDeliveryPrice , d . looseDeliveryPrice ]. every ( v => v == null )) return response ( null , 'A0400' )
if ([... rows . values ()]. some ( r => natural ( r . details ) === natural ( d ))) return response ( null , 'A0409' )
const row = { quoteNo : `FQ- ${ ++ next } ` , recordVersion : '0' , details : copy ( d ), partyName : '本轮往来单位' , status : 'ACTIVE' }
if ( fault === 'null price becomes zero' ) row . details . selfPickupPrice = 0
rows . set ( row . quoteNo , row ); histories . set ( row . quoteNo , []); snapshot ( row , 'CREATE' ); commands . set ( request . idempotencyKey , { hash , row : copy ( row ) }); return response ( row )
}
const context = {
marker , context : { fleetQuotesOperationsFixture : { marker , stage : fixtureContract . stage , departureVerified : true , tripNo , warehouseId : '92' , departedAt , expected } },
createdResources : { merchantQualificationFixture : { marker , tenantId : '101' } },
assert ( _module , label , _response , condition ) { assert . ok ( condition , label ) },
apiSuccess ( _module , label , result ) { assert . equal ( result . http , 200 , label ); assert . equal ( result . body . code , '200' , label ); return result . body . data },
async request ( method , path , body ) {
calls . push ({ method , path , body : copy ( body ) }); const u = new URL ( path , 'http://offline.invalid' ), p = u . pathname
if ( p === '/api/platform/v1/last-leg/fleet/trips/TR-CURRENT' ) return response ({ trip : { tripNo , warehouseId : '92' , departedAt , status : 'IN_TRANSIT' , remark : fault === 'historical trip' ? 'OLD' : marker } })
if ( p === '/api/platform/v1/suppliers' && method === 'POST' ) {
for ( const field of [ 'name' , 'region' , 'legalRepresentative' , 'contactName' , 'phone' , 'settlementPeriod' ]) assert . ok ( body [ field ], field )
assert . equal ( body . truckingScope , 'OVERSEAS' ); return response ( '901' )
}
if ( p === '/api/platform/v1/suppliers/901/status' ) { supplierStatus = body . status ; return response ( null ) }
if ( p === '/api/platform/v1/suppliers/901' ) return response ({ id : '901' , status : supplierStatus })
if ( p === '/api/platform/v1/last-leg/fleet/quotes/import' ) {
const backup = { rows : copy ( rows ), histories : copy ( histories ), commands : copy ( commands ) }, result = []
for ( let index = 0 ; index < body . rows . length ; index ++ ) {
const outcome = create ({ idempotencyKey : ` ${ body . idempotencyKey } : ${ index + 1 } ` , details : body . rows [ index ] }, JSON . stringify ( body . rows ))
if ( outcome . body . code !== '200' ) {
if ( fault !== 'partial import leak' ) { rows . clear (); histories . clear (); commands . clear (); backup . rows . forEach (( v , k ) => rows . set ( k , v )); backup . histories . forEach (( v , k ) => histories . set ( k , v )); backup . commands . forEach (( v , k ) => commands . set ( k , v )) }
return outcome
}
result . push ( outcome . body . data )
}
return response ({ importedRows : result . length , records : result })
}
if ( p === '/api/platform/v1/last-leg/fleet/quotes' ) {
if ( method === 'POST' ) return create ( body )
let records = [... rows . values ()]
for ( const field of [ 'kind' , 'status' ]) if ( u . searchParams . get ( field )) records = records . filter ( r => ( field === 'kind' ? r . details . kind : r . status ) === u . searchParams . get ( field ))
const keyword = u . searchParams . get ( 'keyword' ) || '' ; records = records . filter ( r => [ r . details . originCity , r . details . destinationCode , r . quoteNo ]. some ( v => v . includes ( keyword )))
if ( fault === 'inactive leaks into active list' && u . searchParams . get ( 'status' ) === 'ACTIVE' ) records . push ([... rows . values ()]. find ( r => r . status === 'INACTIVE' ))
return response ({ records , total : String ( records . length ), page : '1' , size : '200' , pages : '1' })
}
const quote = p . match ( /\/quotes\/(FQ-\d+)(?:\/(history|status))?$/ )
if ( quote ) {
const row = rows . get ( quote [ 1 ]); assert . ok ( row )
if ( method === 'GET' ) return response ( quote [ 2 ] === 'history' ? histories . get ( row . quoteNo ) : row )
if ( String ( body . recordVersion ) !== row . recordVersion ) {
if ( fault === 'stale write accepted' && method === 'PUT' ) { row . details = copy ( body . details ); return response ( row ) }
return response ( null , 'A0409' )
}
if ( method === 'PUT' ) row . details = copy ( body . details ); else row . status = body . status
row . recordVersion = String ( Number ( row . recordVersion ) + 1 ); snapshot ( row , method === 'PUT' ? 'UPDATE' : body . status ); return response ( row )
}
if ( p === '/api/platform/v1/last-leg/fleet/operations/TR-CURRENT/record/history' ) return response ({ records : measuredHistory , total : String ( measuredHistory . length ), page : '1' , size : '20' , pages : '1' })
if ( p === '/api/platform/v1/last-leg/fleet/operations/TR-CURRENT/record' ) {
if ( method === 'GET' ) return response ( measured )
const prior = measuredKeys . get ( body . idempotencyKey )
if ( prior ) { if ( fault === 'measured replay adds history' ) measuredHistory . push ( copy ( measured )); return response ( prior ) }
if ( String ( body . recordVersion ) !== measured . recordVersion ) return response ( null , 'A0409' )
2026-10-03 16:38:37 +08:00
assert . deepEqual ( body . details . evidenceFileIds , []); assert . equal ( Object . hasOwn ( body . details , 'evidenceFiles' ), false )
const { evidenceFileIds , ... values } = body . details
measured = { tripNo , recordVersion : String ( Number ( measured . recordVersion ) + 1 ), details : { ... copy ( values ), evidenceFiles : [] } }
2026-10-03 14:52:04 +08:00
measuredKeys . set ( body . idempotencyKey , copy ( measured )); measuredHistory . unshift ( copy ( measured )); return response ( measured )
}
if ( p === '/api/platform/v1/last-leg/fleet/operations' ) {
assert . equal ( u . searchParams . get ( 'keyword' ), tripNo ); assert . equal ( u . searchParams . get ( 'warehouseId' ), '92' )
const metrics = { physicalPallets : 1 , palletSpaces : 2 , ownPackages : 11 , externalPackages : 4 , externalAgreedAmounts : [{ currency : 'USD' , amount : '0.00' }] }
const row = { tripNo , ... metrics , measured : measured . details , financeStatus : 'NOT_QUERIED' , costs : { totals : [] }, costStatus : fault === 'unknown cost becomes zero' ? 'AVAILABLE' : 'NOT_RECORDED' }
return response ({ page : { records : [ row ], total : '1' }, summary : { trips : fault === 'counts destinations as trips' ? '2' : '1' , ... metrics , costs : { visible : true , unrecordedTrips : 1 } }, charts : { physicalPallets : [{ points : [{ value : 1 }] }] } })
}
throw new Error ( `Unexpected offline HTTP: ${ method } ${ path } ` )
},
async requestBinary ( method , path ) {
calls . push ({ method , path }); const u = new URL ( path , 'http://offline.invalid' )
const error = response ( null , 'A0400' )
if ( u . pathname . includes ( '/quotes/' ) && u . searchParams . get ( 'size' ) === '0' ) { const invalid = response ( null , 'A0422' ); return { ... invalid , contentType : 'application/json' , bytes : Buffer . from ( JSON . stringify ( invalid . body )) } }
if ( fault === 'json export masquerades as csv' || ( u . searchParams . has ( 'from' ) && Date . parse ( u . searchParams . get ( 'from' )) > Date . parse ( u . searchParams . get ( 'to' )))) return { ... error , contentType : fault ? 'text/csv' : 'application/json' , bytes : Buffer . from ( JSON . stringify ( error . body )) }
const text = u . pathname . includes ( '/quotes/' ) ? `\uFEFF报价号,金额\r\n ${ [... rows . keys ()]. join ( '\r\n' ) } ` : `\uFEFF车次号,实际里程(mile)\r\n ${ tripNo } ,25.5`
return { http : 200 , body : null , bytes : Buffer . from ( text ), contentType : 'text/csv;charset=UTF-8' , requestId : 'offline-request' , traceId : 'offline-trace' }
},
}
return { context , rows , calls , supplierStatus : () => supplierStatus }
}
test ( 'offline full flow checks quotes, atomic import, current departure and measured history without live network' , async () => {
const { context , rows , calls , supplierStatus } = harness (); await run ( context ); await verifyOperations ( context , { writeMeasured : true , idempotencyNamespace : 'OPERATIONS-INDEPENDENT-KEY' })
const proof = context . createdResources . fleetQuoteAcceptance
assert . equal ( proof . status , 'VERIFIED' ); assert . equal ( proof . financePayment , 'NOT_TESTED' ); assert . equal ( proof . upstreamDeparture , 'NOT_TESTED' )
assert . equal ( proof . operationsStatus , 'NOT_TESTED' ); assert . equal ( proof . quoteStatus , 'VERIFIED' )
assert . equal ( context . createdResources . fleetOperationsAcceptance . operationsStatus , 'VERIFIED' ); assert . equal ( context . createdResources . fleetOperationsAcceptance . quoteStatus , 'NOT_TESTED' )
assert . equal ( rows . size , 4 ); assert . ok ([... rows . values ()]. every ( r => r . status === 'INACTIVE' )); assert . equal ( supplierStatus (), 'INACTIVE' )
assert . ok ( ! calls . some ( call => call . method === 'DELETE' )); assert . ok ( ! JSON . stringify ( proof ). includes ( 'password' ))
})
for ( const fault of [ 'null price becomes zero' , 'mutable creation replay' , 'partial import leak' , 'inactive leaks into active list' , 'stale write accepted' , 'counts destinations as trips' , 'unknown cost becomes zero' , 'measured replay adds history' , 'json export masquerades as csv' , 'historical trip' ]) {
test ( `verifier fails closed for ${ fault } ` , async () => {
const { context } = harness ( fault )
const operations = [ 'counts destinations as trips' , 'unknown cost becomes zero' , 'measured replay adds history' , 'historical trip' ]. includes ( fault )
await assert . rejects ( operations ? verifyOperations ( context , { writeMeasured : true , idempotencyNamespace : 'INDEPENDENT-KEY' }) : run ( context ))
assert . equal ( context . createdResources [ operations ? 'fleetOperationsAcceptance' : 'fleetQuoteAcceptance' ]. status , 'FAILED' )
})
}
test ( 'missing or stale fixture fails before any HTTP and cannot be treated as skipped success' , async () => {
for ( const alter of [ c => delete c . context . fleetQuotesOperationsFixture , c => { c . context . fleetQuotesOperationsFixture . marker = 'OLD' }, c => { c . context . fleetQuotesOperationsFixture . departureVerified = false }]) {
const { context , calls } = harness (); alter ( context ); await assert . rejects ( verifyOperations ( context ), /current-run/ ); assert . equal ( calls . length , 0 ); assert . equal ( context . createdResources . fleetOperationsAcceptance . status , 'FAILED' )
}
const { context , calls } = harness (); context . createdResources . merchantQualificationFixture . marker = 'OLD'
await assert . rejects ( run ( context ), /current-run/ ); assert . equal ( calls . length , 0 )
})
test ( 'proof cannot be overwritten on rerun and CSV detector rejects misleading success content' , async () => {
const { context } = harness (); await run ( context ); const proof = context . createdResources . fleetQuoteAcceptance
await assert . rejects ( run ( context ), /Existing proof/ ); assert . equal ( context . createdResources . fleetQuoteAcceptance , proof )
assert . throws (() => readCsv ({ http : 200 , contentType : 'text/csv' , bytes : Buffer . from ( '{"code":"A0403"}' ) }, '报价号' ), /not a valid CSV/ )
assert . throws (() => requireCurrentFixture ({ marker : 'x' }), /current-run/ )
})
test ( 'unknown expected money is rejected before HTTP instead of comparing as zero' , async () => {
const { context , calls } = harness (); context . context . fleetQuotesOperationsFixture . expected . externalAgreedAmounts [ 0 ]. amount = null
await assert . rejects ( verifyOperations ( context ), /never convert unknown to zero/ ); assert . equal ( calls . length , 0 )
})
test ( 'quote acceptance is independent of DEPART and never requests operations, inventory or finance' , async () => {
const { context , calls } = harness (); delete context . context . fleetQuotesOperationsFixture
await run ( context )
assert . equal ( context . createdResources . fleetQuoteAcceptance . scope , 'QUOTES_ONLY' )
assert . equal ( context . createdResources . fleetQuoteAcceptance . operationsStatus , 'NOT_TESTED' )
assert . ok ( calls . every ( call => ! /operations|inventory|finance|\/trips\// . test ( call . path )))
})
test ( 'operations defaults to read-only and never creates customer, supplier or quote records' , async () => {
const { context , calls , rows } = harness (); delete context . createdResources . merchantQualificationFixture
await verifyOperations ( context )
assert . ok ( calls . every ( call => call . method === 'GET' )); assert . equal ( rows . size , 0 )
assert . equal ( context . createdResources . fleetOperationsAcceptance . measuredStatus , 'NOT_TESTED' )
})
test ( 'measured writes require an explicit independent idempotency namespace' , async () => {
const { context , calls } = harness (); await assert . rejects ( verifyOperations ( context , { writeMeasured : true }), /independent idempotency/ )
assert . equal ( calls . length , 0 )
})
test ( 'independent quote session logs out after a failed quote scenario and persists scoped failure without secrets' , async () => {
const offline = harness ( 'null price becomes zero' )
const context = new AcceptanceRun ({ environment : 'test' , gateway : 'http://offline.invalid' , username : 'test-user' , password : 'synthetic-password' , quiet : true })
const authCalls = []
context . request = async ( method , path , body ) => {
if ( path . startsWith ( '/api/auth/' )) {
authCalls . push ( path )
if ( path . endsWith ( '/logout' )) { assert . equal ( body . refreshToken , 'synthetic-refresh' ); return { http : 200 , body : { code : '200' , data : null } } }
return { http : 200 , body : { code : '200' , data : { accessToken : 'synthetic-access' , refreshToken : 'synthetic-refresh' } } }
}
return offline . context . request ( method , path , body )
}
context . requestBinary = offline . context . requestBinary
const report = await runQuoteSession ( context , { marker : 'FLEET-QUOTE-20261003' , prepareCustomer : async ( current , options ) => {
assert . equal ( options . publishEnvironment , false )
current . createdResources . merchantQualificationFixture = { marker : current . marker , tenantId : '101' }
} })
assert . deepEqual ( authCalls , [ '/api/auth/login' , '/api/auth/logout' ])
assert . equal ( report . success , false ); assert . equal ( report . loggedOut , true ); assert . equal ( report . operationsStatus , 'NOT_TESTED' )
assert . equal ( report . createdResources . fleetQuoteAcceptance . status , 'FAILED' )
assert . equal ( context . token , '' ); assert . equal ( context . refreshToken , '' )
assert . ok ( ! /synthetic-password|synthetic-access|synthetic-refresh/ . test ( JSON . stringify ( report )))
})
test ( 'session helper rejects a borrowed login before touching HTTP' , async () => {
const context = new AcceptanceRun ({ environment : 'test' , quiet : true }); context . token = 'already-logged-in'
context . request = () => { throw new Error ( 'must not run' ) }
await assert . rejects ( runQuoteSession ( context ), /fresh TEST context/ )
})