2026-09-14 13:48:21 +08:00
import test from 'node:test'
import assert from 'node:assert/strict'
import { readFile } from 'node:fs/promises'
2026-10-02 21:33:39 +08:00
import vm from 'node:vm'
import { createHash } from 'node:crypto'
import { inflateRawSync , deflateRawSync } from 'node:zlib'
2026-09-14 13:48:21 +08:00
const source = await readFile ( new URL ( './client-test-env-acceptance.mjs' , import . meta . url ), 'utf8' )
test ( 'client registration distinguishes incomplete-registration replay from credential conflicts' , () => {
assert . match ( source , /same credentials replay the incomplete registration/ )
assert . match ( source , /\['accountId', 'tenantId', 'membershipId'\]/ )
assert . match ( source , /String\(json\.data\[field\]\), String\(registration\.json\.data\[field\]\)/ )
assert . match ( source , /duplicate username with different credentials is rejected/ )
assert . match ( source , /body: \{ \.\.\.registrationBody, password: `Different/ )
})
2026-10-02 21:33:39 +08:00
const helperSource = source . slice ( source . indexOf ( '// BEGIN ORDER ACCEPTANCE HELPERS' ), source . indexOf ( '// END ORDER ACCEPTANCE HELPERS' ))
const workflowSource = source . slice ( source . indexOf ( 'async function ownedOrderSnapshot(' ), source . indexOf ( 'async function apiStep(' ))
function runtime ( extra = {}) {
2026-10-03 00:46:00 +08:00
const context = vm . createContext ({ Buffer , createHash , inflateRawSync , deflateRawSync , structuredClone , Blob , FormData ,
2026-10-02 21:33:39 +08:00
assertEqual : ( actual , expected , label ) => assert . equal ( actual , expected , label ),
assertTrue : ( value , label ) => assert . ok ( value , label ),
assertPresent : ( value , label ) => assert . ok ( value !== null && value !== undefined && value !== '' , label ),
requireSuccess : json => assert . equal ( String ( json ? . code ), '200' ), ... extra })
vm . runInContext ( ` ${ helperSource } \n ${ workflowSource } ` , context )
return context
}
const plain = value => JSON . parse ( JSON . stringify ( value ))
2026-10-03 00:46:00 +08:00
function priceBookSource ( r ) {
const channels = [{ channelCode : 'MATSON_CLX_TRUCK' , sourceSheetName : '美森正班海卡' },
{ channelCode : 'HEDE_TRUCK' , sourceSheetName : '合德海卡' }]
const rates = channels . map ( channel =>({ channelCode : channel . channelCode , originWarehouseCode : 'YIWU' , originWarehouseId : '2' ,
destinationType : 'FBA' , destinationCode : 'ONT8' , unitPrice : 10 }))
const entries = new Map ([
[ 'xl/workbook.xml' , '<workbook><sheets><sheet name="美森正班海卡" sheetId="1" r:id="rId1"/><sheet name="合德海卡" sheetId="2" r:id="rId2"/></sheets></workbook>' ],
[ 'xl/_rels/workbook.xml.rels' , '<Relationships><Relationship Id="rId1" Target="worksheets/sheet1.xml"/><Relationship Id="rId2" Target="worksheets/sheet2.xml"/></Relationships>' ],
[ 'xl/worksheets/sheet1.xml' , '<worksheet><sheetData/></worksheet>' ],
[ 'xl/worksheets/sheet2.xml' , '<worksheet><sheetData/></worksheet>' ],
[ 'xl/media/image1.png' , Buffer . from ([ 0x89 , 0x50 , 0x4e , 0x47 , 0xff , 0x00 , 0x80 ])],
])
const bytes = r . orderZipWrite ( entries , true )
const version = { id : '100' , customerId : '0' , status : 'ACTIVE' , sourceSha256 : createHash ( 'sha256' ). update ( bytes ). digest ( 'hex' )}
return { bytes , entries , reference : { version , channels , rates }}
}
test ( 'private B removes target sheet only, preserves binary source parts and uses published mapping IDs' ,()=>{
const r = runtime (), source = priceBookSource ( r )
const b = r . channelGateWorkbook ( source . bytes , source . reference , 'MATSON_CLX_TRUCK' ), entries = r . orderZipRead ( b . bytes )
assert . deepEqual ( plain ( b . removedCodes ),[ 'MATSON_CLX_TRUCK' ])
assert . doesNotMatch ( entries . get ( 'xl/workbook.xml' ), /美森正班海卡/ )
assert . match ( entries . get ( 'xl/workbook.xml' ), /合德海卡/ )
for ( const [ name , original ] of source . entries ) if ( name !== 'xl/workbook.xml' ) assert . deepEqual ( entries . get ( name ), original )
assert . deepEqual ( plain ( r . channelGateMappings ({ rates : [ source . reference . rates [ 1 ]]}, source . reference )),{
originMappings : [{ originWarehouseCode : 'YIWU' , warehouseId : '2' }], overseasMappings : [],
})
assert . throws (()=> r . channelGateWorkbook ( source . bytes ,{... source . reference , channels : [ source . reference . channels [ 0 ]]}, 'MATSON_CLX_TRUCK' ), /retain another/ )
assert . throws (()=> r . channelGateMappings ({ rates : [ source . reference . rates [ 1 ]]},{ rates : [{... source . reference . rates [ 1 ], originWarehouseId : null }]}), /unique published/ )
assert . throws (()=> r . channelGateMappings ({ rates : [ source . reference . rates [ 1 ]]},{ rates : [ source . reference . rates [ 1 ],{... source . reference . rates [ 1 ], originWarehouseId : '9' }]}), /unique published/ )
})
test ( 'shared parcel source sheet removes all its channel codes and cannot leave an empty price book' ,()=>{
const r = runtime (), source = priceBookSource ( r )
source . reference . channels . push ({ channelCode : 'SAME_SHEET_SECOND_CODE' , sourceSheetName : '美森正班海卡' })
assert . deepEqual ( plain ( r . channelGateWorkbook ( source . bytes , source . reference , 'MATSON_CLX_TRUCK' ). removedCodes ),[ 'MATSON_CLX_TRUCK' , 'SAME_SHEET_SECOND_CODE' ])
})
function channelGateHarness ( fault = '' ) {
const r = runtime (), source = priceBookSource ( r ), calls = [], books = new Map (), publicVersions = [ structuredClone ( source . reference . version )]
const report = { resources : { tenantId : '20' , orderIds : []}, steps : [{ module : 'registration' , name : 'enterprise self-registration' , passed : true , evidence : { tenantId : '20' }}],
orderSubmissionGates : { balance : { restoration : 'RESTORED_WITH_AUDIT' }, remaining : [ 'Channel-offline requires fixture' , 'Other pending scope' ]}}
const order = { order : { id : '42' , tenantId : '20' , orderNo : 'FL42' , status : 'DRAFT' , version : 7 , channelCode : 'MATSON_CLX_TRUCK' },
cargoes : [{ skuNo : 'SKU' }], parties : [], internalNotes : [], statusHistory : []}
let nextId = 201 , nextKey = 0 , task = null
if ( fault === 'unowned-customer' ) report . steps [ 0 ]. evidence . tenantId = '21'
const base = '/api/platform/v1/fba-pricing/carrier-rates/price-books'
Object . assign ( r ,{ report , marker : 'channel-fixture' , merchantToken : 'owner' , platformToken : 'platform' , newIdempotencyKey : ()=> `key- ${ ++ nextKey } ` ,
orderAcceptanceFixture : { masterData : { receiving : { id : '2' }, channel : { priceBookVersionId : '100' }}, payload : { channelCode : 'MATSON_CLX_TRUCK' , cargoes : [{ skuNo : 'SKU' }]}},
apiStep : async ( _module , name , request , validate )=>{
calls . push ({ name , path : request . path , method : request . method , body : request . body , token : request . token , headers : request . headers })
const url = new URL ( request . path , 'https://offline.invalid' )
let data = null , bytes = null , code = '200' , message = 'success'
if ( url . pathname . endsWith ( '/tenant/profile' )) data = { id : '20' , status : 'ACTIVE' }
else if ( url . pathname === base && request . method !== 'POST' ) data = url . searchParams . get ( 'customerId' ) === '0' ? structuredClone ( publicVersions ) : [... books . values ()]. map ( book => structuredClone ( book . version ))
else if ( request . path === ` ${ base } /100` ) data = structuredClone ( source . reference )
else if ( request . path === ` ${ base } /100/source` ) bytes = source . bytes
else if ( url . pathname === base && request . method === 'POST' ) {
assert . equal ( url . searchParams . get ( 'customerId' ), '20' )
assert . equal ( request . token , 'platform' )
const file = request . body . get ( 'file' ), raw = Buffer . from ( await file . arrayBuffer ()), entries = r . orderZipRead ( raw )
const containsTarget = entries . get ( 'xl/workbook.xml' ). includes ( '美森正班海卡' )
const id = String ( nextId ++ ), channels = source . reference . channels . filter ( channel => containsTarget || channel . channelCode !== 'MATSON_CLX_TRUCK' )
data = { version : { id , customerId : fault === 'wrong-version-owner' ? '0' : '20' , status : 'PENDING_CONFIRMATION' , sourceSha256 : createHash ( 'sha256' ). update ( raw ). digest ( 'hex' )},
channels : structuredClone ( channels ), rates : structuredClone ( source . reference . rates . filter ( rate => channels . some ( channel => channel . channelCode === rate . channelCode )))}
books . set ( id , data )
} else if ( url . pathname . startsWith ( ` ${ base } /` )) {
const id = url . pathname . slice ( base . length + 1 ). split ( '/' )[ 0 ], book = books . get ( id )
assert . ok ( book , 'only this run uploaded version may be read/published' )
if ( request . method === 'POST' ) {
assert . ok ( request . path . endsWith ( '/publish' )); assert . equal ( book . version . customerId , '20' )
assert . deepEqual ( plain ( request . body ),{ originMappings : [{ originWarehouseCode : 'YIWU' , warehouseId : '2' }], overseasMappings : []})
for ( const previous of books . values ()) if ( previous . version . status === 'ACTIVE' ) previous . version . status = 'SUPERSEDED'
book . version . status = 'ACTIVE' ; book . version . blockingErrorCount = 0
if ( fault === 'public-drift' ) publicVersions [ 0 ]. status = 'SUPERSEDED'
}
data = structuredClone ( book )
} else if ( url . pathname . endsWith ( '/pricing/channels' )) {
const active = [... books . values ()]. find ( book => book . version . status === 'ACTIVE' )
let channels = active . channels
if ( fault === 'channel-retained' && active . version . id === '202' ) channels = source . reference . channels
data = channels . map ( channel =>({... channel , priceBookVersionId : active . version . id }))
} else if ( url . pathname === '/api/merchant/v1/orders/first-leg' && request . method === 'POST' ) data = '42'
else if ( url . pathname === '/api/merchant/v1/orders/first-leg/42' ) data = structuredClone ( order )
else if ( url . pathname . includes ( '/tasks/by-order/' )) data = structuredClone ( task )
else if ( url . pathname === '/api/platform/inbounds' ) data = { page : 1 , total : 0 , records : []}
else if ( url . pathname . endsWith ( '/submit' )) {
assert . equal ( request . body . expectedVersion , 7 )
code = 'A0409' ; message = fault === 'wrong-conflict' ? '订单版本已变化,请刷新后重试' : '所选渠道已下线,请重新选择'
if ( fault === 'order-drift' ) order . order . version ++
if ( fault === 'task-drift' ) task = { id : 'task' , orderId : '42' , tenantId : '20' }
} else throw new Error ( `unhandled offline request ${ request . path } ` )
const response = { status : 200 , json : { code , message , data }, bytes }; validate ( response ); return response
},
})
return { r , report , calls , books }
}
test ( 'channel gate publishes private A then B, proves client removal and rejects both original-version submits' , async ()=>{
const { r , report , calls , books } = channelGateHarness ()
await r . orderSubmissionChannelGateModule ()
assert . equal ( report . orderSubmissionGates . channel . status , 'REJECTIONS_VERIFIED' )
assert . equal ( report . orderSubmissionGates . channel . publicVersionsUnchanged , true )
assert . deepEqual ( report . orderSubmissionGates . remaining ,[ 'Other pending scope' ])
assert . equal ( books . get ( '201' ). version . status , 'SUPERSEDED' ); assert . equal ( books . get ( '202' ). version . status , 'ACTIVE' )
2026-10-03 00:50:52 +08:00
assert . equal ( JSON . stringify ( report . orderSubmissionGates . channel . versions . map ( version =>[ version . statusAtPublication , version . status ])), JSON . stringify ([[ 'ACTIVE' , 'SUPERSEDED' ],[ 'ACTIVE' , 'ACTIVE' ]]))
2026-10-03 00:46:00 +08:00
assert . equal ( calls . filter ( call => call . path . endsWith ( '/publish' )). length , 2 )
assert . equal ( calls . filter ( call => call . path . includes ( '/submit' )). length , 2 )
assert . ok ( calls . filter ( call => call . method === 'POST' ). every ( call => ! call . path . includes ( 'customerId=0' ) &&! call . path . includes ( '/100/' )))
})
for ( const fault of [ 'unowned-customer' , 'wrong-version-owner' , 'channel-retained' , 'wrong-conflict' , 'order-drift' , 'task-drift' , 'public-drift' ]) test ( `channel gate fails closed: ${ fault } ` , async ()=>{
const { r , calls } = channelGateHarness ( fault )
await assert . rejects ( r . orderSubmissionChannelGateModule ())
if ([ 'unowned-customer' , 'wrong-version-owner' ]. includes ( fault )) assert . equal ( calls . some ( call => call . path . endsWith ( '/publish' )), false )
})
2026-10-03 00:27:45 +08:00
function balanceGateHarness ( fault = '' ) {
const calls = [], ledger = [], report = { resources : { tenantId : '20' , orderIds : []}, steps : [{ module : 'registration' , name : 'enterprise self-registration' , passed : true , evidence : { tenantId : '20' }}]}
const wallet = { currencyCode : 'CNY' , version : 0 , creditActive : false , actualBalance : 0 , availableBalance : 0 , frozenBalance : 0 ,
creditLimit : 0 , creditUsed : 0 , creditFrozen : 0 , availableCredit : 0 }
const detail = { order : { id : '42' , tenantId : '20' , customerId : '20' , orderNo : 'FL42' , status : 'DRAFT' , version : 3 }, cargoes : [{ skuNo : 'SKU' }], parties : [], internalNotes : [], statusHistory : []}
let sequence = 0 , task = null
if ( fault === 'other-customer' ) report . steps [ 0 ]. evidence . tenantId = '21'
if ( fault === 'funded-account' ) wallet . actualBalance = 1
const r = runtime ({ report , marker : 'isolated-fixture' , merchantToken : 'owner' , platformToken : 'platform' , newIdempotencyKey : ()=> `key- ${ ++ sequence } ` ,
orderAcceptanceFixture : { payload : { cargoes : [{ skuNo : 'SKU' }]}},
apiStep : async ( _module , name , request , validate )=>{
calls . push ({ name , request : structuredClone ( request )})
let data = null , code = '200' , message = 'success'
if ( request . path . endsWith ( '/tenant/profile' )) data = { id : '20' , status : 'ACTIVE' }
else if ( request . path === '/api/client/finance/v1/account' ) data = structuredClone ( wallet )
else if ( request . path . includes ( '/account/ledger' )) data = { total : ledger . length , records : structuredClone ( ledger )}
else if ( request . path . endsWith ( '/adjustments' )) {
assert . equal ( request . path , '/api/platform/finance/v1/accounts/20/adjustments' )
assert . equal ( request . token , 'platform' ); assert . ok ( request . headers [ 'Idempotency-Key' ])
assert . equal ( request . body . amount , 0.01 ); assert . equal ( request . body . expectedAccountVersion , wallet . version )
const deduct = request . body . type === 'DEDUCTION'
if ( deduct && fault === 'deduction-not-applied' ) throw new Error ( 'not applied' )
wallet . actualBalance += deduct ?- 0.01 : 0.01 ; wallet . availableBalance = wallet . actualBalance ; wallet . version ++
ledger . push ({ sourceType : deduct ? 'MANUAL_DEDUCTION' : 'MANUAL_RECHARGE' , referenceNo : request . body . referenceNo , amount : deduct ?- 0.01 : 0.01 })
if ( deduct && fault === 'lost-deduction-response' ) throw new Error ( 'response lost' )
data = ledger . at ( - 1 )
} else if ( request . method === 'POST' && request . path === '/api/merchant/v1/orders/first-leg' ) data = '42'
else if ( request . path . includes ( '/submit' )) {
assert . equal ( wallet . actualBalance , - 0.01 )
assert . equal ( request . body . expectedVersion , 3 )
assert . ok ( request . headers [ 'Idempotency-Key' ])
code = 'A0403' ; message = fault === 'wrong-gate' ? '企业未审核' : '账户实际余额为负,请充值后再下单'
if ( fault === 'order-drift' ) detail . order . version ++
if ( fault === 'task-drift' ) task = { orderId : '42' , tenantId : '20' , id : 'unexpected' }
} else if ( request . path . includes ( '/tasks/by-order/' )) data = structuredClone ( task )
else if ( request . path . includes ( '/inbounds?' )) data = { page : 1 , total : 0 , records : []}
else if ( request . path === '/api/merchant/v1/orders/first-leg/42' ) data = structuredClone ( detail )
else throw new Error ( `unhandled offline request ${ request . path } ` )
const response = { status : 200 , json : { code , message , data }}; validate ( response ); return response
},
})
return { r , calls , report , wallet , ledger }
}
test ( 'negative actual funds fixture touches only the new registered customer and restores an audited cent' , async ()=>{
const { r , calls , report , wallet , ledger } = balanceGateHarness ()
await r . orderSubmissionBalanceGateModule ()
assert . equal ( wallet . actualBalance , 0 ); assert . equal ( wallet . version , 2 ); assert . equal ( ledger . length , 2 )
assert . equal ( report . orderSubmissionGates . balance . status , 'REJECTIONS_VERIFIED' )
assert . equal ( report . orderSubmissionGates . balance . restoration , 'RESTORED_WITH_AUDIT' )
assert . equal ( calls . filter ( call => call . request . path . includes ( '/submit' )). length , 2 )
assert . equal ( calls . filter ( call => call . request . path . endsWith ( '/adjustments' )). length , 2 )
assert . ok ( calls . every ( call => ! call . request . path . includes ( 'channels' ) &&! call . request . path . includes ( 'price-books' )))
})
for ( const fault of [ 'other-customer' , 'funded-account' ]) test ( `financial fixture refuses unsafe scope: ${ fault } ` , async ()=>{
const { r , calls } = balanceGateHarness ( fault )
await assert . rejects ( r . orderSubmissionBalanceGateModule ())
assert . equal ( calls . some ( call => call . request . method === 'POST' ), false )
})
for ( const fault of [ 'wrong-gate' , 'order-drift' , 'task-drift' , 'lost-deduction-response' ]) test ( `gate evidence fails and restores its proven deduction after ${ fault } ` , async ()=>{
const { r , wallet , ledger } = balanceGateHarness ( fault )
await assert . rejects ( r . orderSubmissionBalanceGateModule ())
assert . equal ( wallet . actualBalance , 0 ); assert . equal ( ledger . length , 2 )
})
test ( 'unapplied deduction does not trigger a blind restoring credit' , async ()=>{
const { r , wallet , ledger , calls } = balanceGateHarness ( 'deduction-not-applied' )
await assert . rejects ( r . orderSubmissionBalanceGateModule (), /not applied/ )
assert . equal ( wallet . actualBalance , 0 ); assert . equal ( ledger . length , 0 )
assert . equal ( calls . filter ( call => call . request . path . endsWith ( '/adjustments' )). length , 1 )
})
2026-10-02 21:33:39 +08:00
test ( 'frozen edit command preserves original version, key and nested payload through retries' , () => {
const r = runtime (), snapshot = { order : { version : 7 } }, payload = { draft : { cargoes : [{ skuNo : 'ORIGINAL' }] } }
const command = r . orderMutationCommand ( snapshot , payload , 'original-key' )
snapshot . order . version = 99 ; payload . draft . cargoes [ 0 ]. skuNo = 'CHANGED'
assert . deepEqual ( plain ( command ), { headers : { 'Idempotency-Key' : 'original-key' }, body : { expectedVersion : 7 , draft : { cargoes : [{ skuNo : 'ORIGINAL' }] } } })
assert . ok ( Object . isFrozen ( command . body . draft . cargoes [ 0 ]))
assert . throws (() => r . orderMutationCommand ({ order : {} }, payload , 'key' ), /version/ )
assert . throws (() => r . orderMutationCommand ( snapshot , payload , '' ), /key/ )
})
2026-10-02 23:14:24 +08:00
test ( 'submit command carries only frozen expectedVersion with the same original replay key' ,()=>{
const r = runtime (), detail = { order : { version : 4 }}
const command = r . orderMutationCommand ( detail ,{}, 'submit-original' )
detail . order . version = 9
assert . deepEqual ( plain ( command ),{ headers : { 'Idempotency-Key' : 'submit-original' }, body : { expectedVersion : 4 }})
assert . match ( source , /const submitCommand = orderMutationCommand\(submitBefore, \{\}, submitKey\)/ )
assert . match ( source , /submit replay keeps version and history unchanged/ )
assert . match ( source , /submit same key different version conflicts/ )
assert . match ( source , /submit new key cannot submit already submitted order/ )
for ( const name of [ 'pre-approval-submit' , 'incomplete-submit' , 'cancelled-submit' , 'cancelled-submit-proof' ]) {
assert . match ( source , new RegExp ( `orderMutationCommand\\([^\\n]+(?:\\n[^\\n]+)?newIdempotencyKey\\(' ${ name } '\\)` ))
}
})
2026-10-02 21:33:39 +08:00
test ( 'zero-drift validator detects changes in every aggregate and preserves array ordering' , () => {
const r = runtime (), before = { order : { version : 2 }, cargoes : [{ id : '1' , amount : 2 }], parties : [], internalNotes : [], statusHistory : [{ id : 'h1' }, { id : 'h2' }] }
r . assertOrderUnchanged ( before , structuredClone ( before ))
for ( const field of Object . keys ( before )) {
const changed = structuredClone ( before )
if ( field === 'order' ) changed . order . version ++
else changed [ field ]. push ({ id : 'new' })
assert . throws (() => r . assertOrderUnchanged ( before , changed ), /unchanged/ )
}
assert . throws (() => r . assertOrderUnchanged ( before , { ... before , statusHistory : [... before . statusHistory ]. reverse () }), /unchanged/ )
r . assertOrderUnchanged ({ b : 2 , a : 1 }, { a : 1 , b : 2 })
})
test ( 'partial import validates physical row, exact counts, failure details and identities' , () => {
const r = runtime (), data = { mode : 'PARTIAL' , batchNo : 'batch' , sourceFileId : 'f' , errorReportFileId : 'e' , result : {
total : 2 , succeeded : 1 , failed : 1 , items : [
{ index : 0 , clientRowId : 'excel-row-3' , success : false , orderId : null , errorCode : 'A0422' , errorMessage : 'cargo required' },
{ index : 1 , clientRowId : 'excel-row-4' , success : true , orderId : '99' , orderNo : 'FL99' },
],
} }
r . assertImportResult ( data , 'PARTIAL' , [ false , true ])
for ( const mutate of [
value => value . result . succeeded ++ , value => value . result . items [ 0 ]. clientRowId = 'excel-row-2' ,
value => value . result . items [ 0 ]. orderId = 'unexpected' , value => value . result . items [ 0 ]. errorCode = 'S0500' ,
value => value . result . items [ 1 ]. orderNo = '' , value => value . errorReportFileId = null ,
]) {
const changed = structuredClone ( data ); mutate ( changed )
assert . throws (() => r . assertImportResult ( changed , 'PARTIAL' , [ false , true ]))
}
})
2026-10-02 21:46:39 +08:00
test ( 'PARTIAL wire response accepts only null or omitted failed-row identifiers' , () => {
const r = runtime ()
// Shape captured from r1: Jackson omits all null fields on both failed and successful rows.
const wire = JSON . parse ( '{"mode":"PARTIAL","batchNo":"OBI-contract","sourceFileId":"101","errorReportFileId":"102","result":{"total":2,"succeeded":1,"failed":1,"items":[{"index":0,"clientRowId":"excel-row-3","success":false,"errorCode":"A0422","errorMessage":"货物名称不能为空"},{"index":1,"clientRowId":"excel-row-4","success":true,"orderId":"2106016937331458049","orderNo":"FL2106016937331458049"}]}}' )
r . assertImportResult ( wire , 'PARTIAL' , [ false , true ])
for ( const field of [ 'orderId' , 'orderNo' , 'inboundCode' ]) {
const explicitNull = structuredClone ( wire ); explicitNull . result . items [ 0 ][ field ] = null
r . assertImportResult ( explicitNull , 'PARTIAL' , [ false , true ])
for ( const value of [ '2106016937331458049' , 'FL-example' , 'NYEXX-example' , '' , 0 , false , NaN , [], {}]) {
const invalid = structuredClone ( wire ); invalid . result . items [ 0 ][ field ] = value
assert . throws (() => r . assertImportResult ( invalid , 'PARTIAL' , [ false , true ]), new RegExp ( `no ${ field } ` ))
}
}
for ( const field of [ 'orderId' , 'orderNo' ]) {
const missingSuccess = structuredClone ( wire ); delete missingSuccess . result . items [ 1 ][ field ]
assert . throws (() => r . assertImportResult ( missingSuccess , 'PARTIAL' , [ false , true ]), /created order/ )
}
})
2026-10-02 21:33:39 +08:00
function templateFixture ( r ) {
return new Map ([
[ 'xl/workbook.xml' , '<workbook><sheets><sheet name="订单导入"/><sheet name="选项数据"/></sheets></workbook>' ],
[ 'xl/sharedStrings.xml' , '<sst><si><t>运输方式 *</t></si><si><t>客户备注</t></si><si><t>仓库|CODE</t></si></sst>' ],
[ 'xl/worksheets/sheet1.xml' , '<worksheet><sheetData><row r="1"><c r="A1" t="inlineStr"><is><t>订单基础</t></is></c></row><row r="2"><c r="A2" t="s"><v>0</v></c><c r="B2"/><c r="AE2" t="s"><v>1</v></c></row><row r="3"><c r="A3" t="inlineStr"><is><t>old data</t></is></c></row></sheetData><dataValidations count="1"/></worksheet>' ],
[ 'xl/worksheets/sheet2.xml' , '<worksheet><sheetData><row r="1"><c r="A1" t="s"><v>2</v></c></row></sheetData></worksheet>' ],
])
}
test ( 'XLSX mutation preserves downloaded headers/options and writes 31 columns past Z' , () => {
const r = runtime (), template = templateFixture ( r ), row = Array . from ({ length : 31 }, ( _ , i ) => `column- ${ i } ` )
row [ 30 ] = '备注<&"\'中文'
const bytes = r . orderWorkbookRows ( template , [ row ]), parsed = r . orderZipRead ( bytes ), rows = r . orderSheetRows ( parsed )
assert . equal ( parsed . get ( 'xl/worksheets/sheet2.xml' ), template . get ( 'xl/worksheets/sheet2.xml' ))
assert . match ( parsed . get ( 'xl/worksheets/sheet1.xml' ), /dataValidations/ )
assert . deepEqual ( plain ( rows . find ( item => item . number === 2 ). cells . map ( cell => cell . text )), [ '运输方式 *' , '' , '客户备注' ])
assert . equal ( rows [ 2 ]. cells [ 30 ]. ref , 'AE' ); assert . equal ( rows [ 2 ]. cells [ 30 ]. text , row [ 30 ])
assert . doesNotMatch ( parsed . get ( 'xl/worksheets/sheet1.xml' ), /old data/ )
assert . deepEqual ( plain ( r . orderSheetRows ( parsed , 'xl/worksheets/sheet2.xml' )[ 0 ]. cells ), [{ ref : 'A' , text : '仓库|CODE' }])
const corrupted = Buffer . from ( bytes ); corrupted [ 80 ] ^= 1
assert . throws (() => r . orderZipRead ( corrupted ))
assert . throws (() => r . orderZipRead ( Buffer . from ( 'not zip' )))
})
test ( 'ZIP reader accepts independently compressed entries and rejects damaged checksums' , () => {
const r = runtime (), data = Buffer . from ( '<worksheet>中文</worksheet>' ), name = Buffer . from ( 'xl/worksheets/sheet1.xml' )
const compressed = deflateRawSync ( data )
let checksum = 0xffffffff
for ( const byte of data ) { checksum ^= byte ; for ( let i = 0 ; i < 8 ; i ++ ) checksum = checksum & 1 ? 0xedb88320 ^ ( checksum >>> 1 ) : checksum >>> 1 }
checksum = ( checksum ^ 0xffffffff ) >>> 0
const local = Buffer . alloc ( 30 ); local . writeUInt32LE ( 0x04034b50 ); local . writeUInt16LE ( 8 , 8 ); local . writeUInt16LE ( name . length , 26 )
const central = Buffer . alloc ( 46 ); central . writeUInt32LE ( 0x02014b50 ); central . writeUInt16LE ( 8 , 10 )
central . writeUInt32LE ( checksum , 16 ); central . writeUInt32LE ( compressed . length , 20 ); central . writeUInt32LE ( data . length , 24 ); central . writeUInt16LE ( name . length , 28 )
const end = Buffer . alloc ( 22 ); end . writeUInt32LE ( 0x06054b50 ); end . writeUInt16LE ( 1 , 10 ); end . writeUInt32LE ( local . length + name . length + compressed . length , 16 )
const archive = Buffer . concat ([ local , name , compressed , central , name , end ])
assert . equal ( r . orderZipRead ( archive ). get ( name . toString ()), data . toString ())
central . writeUInt32LE (( checksum ^ 1 ) >>> 0 , 16 )
assert . throws (() => r . orderZipRead ( Buffer . concat ([ local , name , compressed , central , name , end ])), /checksum/ )
})
test ( 'rollback plan changes only row 3 validity and row 4 hash without moving physical rows' , () => {
const r = runtime (), first = Array . from ({ length : 31 }, ( _ , index ) => `first- ${ index } ` ), second = Array . from ({ length : 31 }, ( _ , index ) => `second- ${ index } ` )
const plan = r . importRollbackRows ( first , second )
assert . equal ( plan . seed [ 0 ][ 10 ], '' ); assert . deepEqual ( plain ( plan . seed [ 1 ]), second )
assert . deepEqual ( plain ( plan . conflict [ 0 ]), first )
assert . deepEqual ( plain ( plan . conflict [ 1 ]. slice ( 0 , 30 )), second . slice ( 0 , 30 ))
assert . notEqual ( plan . conflict [ 1 ][ 30 ], second [ 30 ]); assert . deepEqual ( plain ( plan . retry ), [ first , second ])
const rows = r . orderSheetRows ( r . orderZipRead ( r . orderWorkbookRows ( templateFixture ( r ), plan . conflict )))
assert . deepEqual ( plain ( rows . slice ( 2 ). map ( row => row . number )), [ 3 , 4 ])
})
test ( 'actual Java path derives row keys without mode and joins both writes to the atomic transaction' , async () => {
const service = await readFile ( new URL ( '../order/src/main/java/com/baiyue/order/service/MerchantBatchDraftService.java' , import . meta . url ), 'utf8' )
const item = await readFile ( new URL ( '../order/src/main/java/com/baiyue/order/service/MerchantBatchDraftItemService.java' , import . meta . url ), 'utf8' )
const orders = await readFile ( new URL ( '../order/src/main/java/com/baiyue/order/service/OrderApplicationService.java' , import . meta . url ), 'utf8' )
assert . match ( service , /batchKey\.trim\(\) \+ "\\n" \+ rowId/ )
assert . match ( service , /@Transactional\s+public MerchantBatchDraftDtos.Response createAtomic/ )
assert . match ( service , /itemService.createJoined\(tenantId, userId, username, item.draft\(\),\s*derivedKey\(batchKey, item.clientRowId\(\)\)\)/ )
assert . match ( item , /@Transactional\(propagation = Propagation.MANDATORY\)/ )
assert . match ( orders , /requestHash\("CREATE_DRAFT", request\)/ )
})
function mutationHarness ( fault = '' ) {
let nextId = 100 , nextKey = 0 , nextHistory = 0
const draft = { customerRemark : 'original' , cargoes : [{ skuNo : 'SKU' , packageCount : 2 , model : 'M' , productImageFileId : 'img' }] }
const make = ( id , status , body ) => ({ order : { id , orderNo : `FL ${ id } ` , inboundCode : null , tenantId : 'tenant' , customerId : 'customer' , version : 0 , status , customerRemark : body . customerRemark },
cargoes : structuredClone ( body . cargoes ), parties : [], internalNotes : [], statusHistory : [] })
const orders = new Map ([[ 'submitted' , make ( 'submitted' , 'SUBMITTED' , draft )]]), commands = new Map (), requests = [], report = { resources : { orderIds : [] } }
const r = runtime ({ merchantToken : 'owner' , existingClientToken : 'other' , marker : 'local-contract' , report ,
orderAcceptanceFixture : { payload : draft , orderId : 'submitted' }, newIdempotencyKey : () => `key- ${ ++ nextKey } ` ,
apiStep : async ( module , name , request , validate ) => {
requests . push ({ name , request : structuredClone ( request ) })
const id = request . path . split ( '/' )[ 6 ], current = orders . get ( id )
let code = '200' , data = null
if ( ! request . method || request . method === 'GET' ) data = structuredClone ( current )
else if ( request . path . endsWith ( '/first-leg' )) {
data = String ( nextId ++ ); orders . set ( data , make ( data , 'DRAFT' , request . body ))
} else if ( request . token === 'other' ) code = 'A0404'
else if ( request . path . endsWith ( '/submit' )) code = 'A0409'
else {
const key = request . headers ? .[ 'Idempotency-Key' ], payload = request . body
const signature = JSON . stringify ( payload ), stored = commands . get ( key )
if ( ! key ) code = 'A0400'
else if ( payload . expectedVersion == null || ( request . method === 'POST' && ! payload . reason ? . trim ()) || payload . draft ? . cargoes [ 0 ]. productImageFileId === - 1 ) code = 'A0422'
else if ( stored ) {
code = stored === signature ? '200' : 'A0409'
if ( fault === 'replay-write' && code === '200' ) current . order . version ++
} else if ( payload . expectedVersion !== current . order . version || current . order . status !== 'DRAFT' ) code = 'A0409'
else {
commands . set ( key , signature ); current . order . version ++
const cancelled = request . method === 'POST'
if ( cancelled ) current . order . status = 'CANCELLED'
else { current . order . customerRemark = payload . draft . customerRemark ; current . cargoes = structuredClone ( payload . draft . cargoes ) }
current . statusHistory . push ({ id : `h ${ ++ nextHistory } ` , action : cancelled ? 'CANCELLED' : 'UPDATE' , fromStatus : 'DRAFT' , toStatus : current . order . status ,
reason : payload . reason ?? null , operatorId : 'owner' , occurredAt : '2026-10-02T12:00:00' })
}
}
if ( fault === 'rejection-drift' && code !== '200' && current ) current . parties . push ({ id : 'unexpected' })
const response = { status : 200 , json : { code , data } }; validate ( response ); return response
},
})
return { r , requests , report , orders }
}
test ( 'F06 actual orchestration covers frozen success replay, stale versions, missing protocol and isolation' , async () => {
const { r , requests , report } = mutationHarness ()
await r . orderMutationEvidenceModule ()
const firstEdit = requests . find ( item => item . name === 'versioned draft edit succeeds' ). request
const editReplay = requests . find ( item => item . name === 'original edit key and frozen payload replay successfully' ). request
assert . deepEqual ( editReplay , firstEdit )
const firstCancel = requests . find ( item => item . name === 'cancel owned draft with reason' ). request
const cancelReplay = requests . find ( item => item . name === 'original cancellation key payload and version replay successfully' ). request
assert . deepEqual ( cancelReplay , firstCancel )
const newCancel = requests . find ( item => item . name === 'repeat cancellation cannot add history' ). request
assert . notEqual ( newCancel . headers [ 'Idempotency-Key' ], firstCancel . headers [ 'Idempotency-Key' ])
assert . ok ( newCancel . body . expectedVersion > firstCancel . body . expectedVersion )
assert . ok ( report . orderMutationRequests . every ( item => ! ( 'token' in item )))
assert . ok ( requests . some ( item => item . name === 'edit missing version is rejected' ))
assert . ok ( requests . some ( item => item . name === 'cancel missing key is rejected' ))
})
for ( const fault of [ 'replay-write' , 'rejection-drift' ]) test ( `F06 refuses false success when ${ fault } ` , async () => {
const { r } = mutationHarness ( fault )
await assert . rejects (() => r . orderMutationEvidenceModule (), /unchanged/ )
})
test ( 'full order collection detects leaked rows, duplicated pages and truncated results' , async () => {
for ( const records of [[{ orderId : 'expected' }, { orderId : 'leaked' }], [{ orderId : 'expected' }, { orderId : 'expected' }]]) {
const r = runtime ({ merchantToken : 'owner' , apiStep : async ( _module , _name , _request , validate ) => {
validate ({ json : { code : '200' , data : { page : 1 , total : 2 , records } } })
} })
await assert . rejects (() => r . importOrderSet ( 'rollback evidence' , [ 'expected' ]))
}
const r = runtime ({ merchantToken : 'owner' , apiStep : async ( _module , _name , _request , validate ) => {
validate ({ json : { code : '200' , data : { page : 1 , total : 201 , records : [{ orderId : 'only-one' }] } } })
} })
await assert . rejects (() => r . importOrderSet ( 'complete pagination' ), /intermediate/ )
})
function importHarness ( fault = '' ) {
const report = { resources : { orderIds : [], tenantId : 'tenant' } }, orders = new Map (), requests = []
const masterData = { receiving : { id : 'domestic' , warehouseName : '国内仓' , warehouseCode : 'CN' },
destination : { id : 'fba' , warehouseName : '目的仓' , warehouseCode : 'US' }, channel : { channelName : '海运渠道' , channelCode : 'SEA' } }
let nextKey = 0 , r
2026-10-02 21:46:39 +08:00
const resultItem = ( index , success , orderId ) => Object . fromEntries ( Object . entries ({ index , clientRowId : `excel-row- ${ index + 3 } ` , success ,
2026-10-02 21:33:39 +08:00
orderId : success ? orderId : null , orderNo : success ? `FL ${ orderId } ` : null ,
2026-10-02 21:46:39 +08:00
errorCode : success ? null : 'A0422' , errorMessage : success ? null : 'cargo required' }). filter (([, value ]) => value !== null ))
2026-10-02 21:33:39 +08:00
const responseData = ( mode , items ) => ({ mode , batchNo : 'batch' , sourceFileId : 'source-file' , errorReportFileId : 'error-file' ,
result : { total : items . length , succeeded : items . filter ( item => item . success ). length , failed : items . filter ( item => ! item . success ). length , items } })
const addDraft = ( id , cells ) => orders . set ( id , { order : { id , orderNo : `FL ${ id } ` , version : 0 , status : 'DRAFT' , tenantId : 'tenant' ,
customerRemark : cells [ 30 ], referenceId : cells [ 27 ], receivingWarehouseId : 'domestic' , destinationWarehouseId : 'fba' , channelCode : 'SEA' },
cargoes : [{ skuNo : cells [ 18 ], packageCount : 2 }], parties : [], internalNotes : [], statusHistory : [] })
const cells = ( values , row ) => values . map (( value , index ) => `<c r=" ${ r . orderColumn ( index ) }${ row } " t="inlineStr"><is><t> ${ r . orderXmlEncode ( value ) } </t></is></c>` ). join ( '' )
r = runtime ({ report , marker : 'contract' , merchantToken : 'owner' , orderAcceptanceFixture : { masterData , payload : { pricingTaxMode : 'TAX_INCLUDED' } },
newIdempotencyKey : () => `key- ${ ++ nextKey } ` ,
apiStep : async ( _module , name , request , validate ) => {
requests . push ({ name , path : request . path , key : request . headers ? .[ 'Idempotency-Key' ] })
let code = '200' , data = null , bytes = null
if ( request . path . endsWith ( '/template' )) {
const template = templateFixture ( r )
const headers = Array . from ({ length : 31 }, ( _ , index ) => index === 0 ? '运输方式 *' : index === 30 ? '客户备注' : `字段 ${ index } ` )
template . set ( 'xl/worksheets/sheet1.xml' , `<worksheet><sheetData><row r="1"> ${ cells ([ '订单基础' ], 1 ) } </row><row r="2"> ${ cells ( headers , 2 ) } </row></sheetData></worksheet>` )
template . set ( 'xl/worksheets/sheet2.xml' , `<worksheet><sheetData><row r="1"> ${ cells ([ '国内仓|CN' , '目的仓|US' , '海运渠道|SEA' ], 1 ) } </row></sheetData></worksheet>` )
bytes = r . orderZipWrite ( template )
} else if ( request . path . includes ( '/error-reports/' )) {
2026-10-02 21:49:48 +08:00
const physicalRow = fault === 'wrong-error-row' ? '4.0' : '3.0'
bytes = r . orderZipWrite ( new Map ([[ 'xl/worksheets/sheet1.xml' , `<worksheet><sheetData><row r="1"> ${ cells ([ 'Excel行号' , '错误代码' , '错误说明' ], 1 ) } </row><row r="2"> ${ cells ([ '3' , 'A0422' , 'cargo required' ], 2 ). replace ( /<c r="A2"[\s\S]*?<\/c>/ , `<c r="A2" t="n"><v> ${ physicalRow } </v></c>` ) } </row></sheetData></worksheet>` ]]))
2026-10-02 21:33:39 +08:00
} else if ( request . method === 'POST' ) {
const file = request . body . get ( 'file' ), body = Buffer . from ( await file . arrayBuffer ())
if ( name . startsWith ( 'PARTIAL' )) {
const parsed = r . orderSheetRows ( r . orderZipRead ( body )). slice ( 2 ). map ( row => row . cells . map ( cell => cell . text ))
if ( ! orders . has ( 'second' )) addDraft ( 'second' , parsed [ 1 ])
data = responseData ( 'PARTIAL' , [ resultItem ( 0 , false ), resultItem ( 1 , true , 'second' )])
} else if ( name . startsWith ( 'ATOMIC retry' )) {
const parsed = r . orderSheetRows ( r . orderZipRead ( body )). slice ( 2 ). map ( row => row . cells . map ( cell => cell . text ))
addDraft ( 'first' , parsed [ 0 ]); data = responseData ( 'ATOMIC' , [ resultItem ( 0 , true , 'first' ), resultItem ( 1 , true , 'second' )])
} else if ( name . startsWith ( 'ATOMIC later' )) {
code = 'A0409'
if ( fault === 'leaked-first-row' ) {
const parsed = r . orderSheetRows ( r . orderZipRead ( body )). slice ( 2 ). map ( row => row . cells . map ( cell => cell . text ))
addDraft ( 'leaked-first' , parsed [ 0 ])
}
if ( fault === 'changed-existing-row' ) orders . get ( 'second' ). order . customerRemark = 'incorrectly-overwritten'
2026-10-02 21:53:29 +08:00
} else code = name === 'reject over five MiB' ? 'A0400' : 'A0422'
2026-10-02 21:33:39 +08:00
} else if ( request . path . includes ( '?page=' )) {
data = { page : 1 , total : orders . size , records : [... orders . keys ()]. map ( orderId => ({ orderId })) }
} else data = structuredClone ( orders . get ( request . path . split ( '/' ). at ( - 1 )))
2026-10-02 21:53:29 +08:00
const response = { status : 200 , headers : new Map ([[ 'content-type' , 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' ]]), bytes , json : { code , data , message : code === 'A0400' ? '上传文件超过服务允许大小' : undefined } }
2026-10-02 21:33:39 +08:00
validate ( response ); return response
},
})
return { r , report , requests }
}
test ( 'F04 runs actual import orchestration with the same seed/conflict/retry key and records limitations' , async () => {
const { r , report , requests } = importHarness ()
await r . orderExcelImportModule ()
const keys = requests . filter ( item => item . key && /PARTIAL preserves|PARTIAL same key|ATOMIC later|ATOMIC retry/ . test ( item . name )). map ( item => item . key )
assert . equal ( keys . length , 4 ); assert . equal ( new Set ( keys ). size , 1 )
assert . deepEqual ( report . resources . orderIds , [ 'second' , 'first' ])
assert . ok ( report . orderImportLimitations . some ( item => item . includes ( 'remote source uploads' )))
assert . ok ( requests . some ( item => item . name === 'forged option adds no orders: complete order page 1' ))
})
for ( const fault of [ 'leaked-first-row' , 'changed-existing-row' ]) test ( `F04 rejects apparent ATOMIC failure with ${ fault } ` , async () => {
const { r } = importHarness ( fault )
await assert . rejects (() => r . orderExcelImportModule (), /unchanged/ )
})
2026-10-02 21:49:48 +08:00
test ( 'F04 still rejects a numeric error row pointing to the successful row' , async () => {
const { r } = importHarness ( 'wrong-error-row' )
await assert . rejects (() => r . orderExcelImportModule (), /actual Excel error row/ )
})