Files
baiyue-firstleg/scripts/client-test-env-acceptance-contract.test.mjs
T

506 lines
37 KiB
JavaScript

import test from 'node:test'
import assert from 'node:assert/strict'
import { readFile } from 'node:fs/promises'
import vm from 'node:vm'
import { createHash } from 'node:crypto'
import { inflateRawSync, deflateRawSync } from 'node:zlib'
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/)
})
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 = {}) {
const context = vm.createContext({ Buffer, createHash, inflateRawSync, deflateRawSync, structuredClone, Blob, FormData,
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))
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')
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)
})
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)
})
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/)
})
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}'\\)`))
}
})
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]))
}
})
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/)
}
})
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
const resultItem = (index, success, orderId) => Object.fromEntries(Object.entries({ index, clientRowId: `excel-row-${index + 3}`, success,
orderId: success ? orderId : null, orderNo: success ? `FL${orderId}` : null,
errorCode: success ? null : 'A0422', errorMessage: success ? null : 'cargo required' }).filter(([, value]) => value !== null))
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/')) {
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>`]]))
} 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'
} else code = name === 'reject over five MiB' ? 'A0400' : 'A0422'
} 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)))
const response = { status: 200, headers: new Map([['content-type', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet']]), bytes, json: { code, data, message: code === 'A0400' ? '上传文件超过服务允许大小' : undefined } }
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/)
})
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/)
})