2026-10-03 06:26:33 +08:00
|
|
|
import test from 'node:test'
|
|
|
|
|
import assert from 'node:assert/strict'
|
|
|
|
|
import { mkdtempSync, readFileSync, mkdirSync, writeFileSync, existsSync } from 'node:fs'
|
|
|
|
|
import { tmpdir } from 'node:os'
|
|
|
|
|
import { join } from 'node:path'
|
|
|
|
|
import { F40_MODES, parseCoreArguments, executionPlan, validateCoreEnvironment, redactEvidence, runFbaCore } from './fba-core-regression-test-env.mjs'
|
|
|
|
|
|
|
|
|
|
const env=()=>({CHONGCHONG_ADMIN_API_BASE_URL:'https://test.example.invalid/api',FBA_CORE_TEST_GATEWAY_ORIGIN:'https://test.example.invalid',
|
|
|
|
|
CHONGCHONG_ADMIN_USERNAME:'offline-admin',CHONGCHONG_ADMIN_PASSWORD:'offline-password',ADMIN_PDA_USERNAME:'offline-pda',
|
|
|
|
|
ADMIN_PDA_PASSWORD:'offline-pda-password',JENKINS_URL:'https://ci.example.invalid/',JENKINS_USER:'offline-ci',
|
|
|
|
|
JENKINS_API_TOKEN:'offline-api-secret',FBA_CORE_JENKINS_AGENT:'java-test-agent'})
|
|
|
|
|
const config=()=>parseCoreArguments(['--execute','--environment','TEST','--run-id','offline-check'])
|
|
|
|
|
const scope=()=>({customerId:'2105209721220861954',warehouseId:'193000000000200003',bookingId:'2105220190013755394',
|
|
|
|
|
stage:'BEFORE_COMPLETE',containerNo:'TEST-ONE',inboundCodes:['IN-1'],sourceInboundIds:['2105220155339444226'],
|
|
|
|
|
expectedPackagesByInbound:{'IN-1':1}})
|
|
|
|
|
const completion=()=>({customerId:'90',rootOrderId:'100',sourceOrderIds:['100','101'],eventType:'FIRST_LEG_ORDER_FULFILLMENT_COMPLETED'})
|
|
|
|
|
const root=()=>mkdtempSync(join(tmpdir(),'fba-core-contract-'))
|
|
|
|
|
const readers=()=>({preflight:async()=>{},readReceiving:async()=>({balances:[]}),readCompletion:async()=>({orders:[]})})
|
|
|
|
|
const pass=()=>({success:true,summary:{failed:0},steps:[],deploymentBlockers:[],createdResources:{phase5RootFulfillment:{finalStatus:'VERIFIED'}}})
|
|
|
|
|
|
|
|
|
|
test('default plan is offline and exposes exact serial modes, required keys and retained reports',()=>{
|
|
|
|
|
const plan=executionPlan(parseCoreArguments([]))
|
|
|
|
|
assert.equal(plan.execution,'PLAN');assert.deepEqual(plan.modes,F40_MODES)
|
|
|
|
|
assert.equal(plan.taxMode,'BUY_DECLARATION');assert.match(plan.scope,/BUY_DECLARATION/)
|
|
|
|
|
assert.ok(plan.requiredEnvironmentKeys.includes('ADMIN_PDA_PASSWORD'))
|
|
|
|
|
for(const args of [['--execute'],['--execute','--environment','PROD','--run-id','x'],['--plan','--execute'],['--mode','ROOT_FIRST']])assert.throws(()=>parseCoreArguments(args))
|
|
|
|
|
})
|
|
|
|
|
test('tax mode is explicit, defaults to BUY and rejects aliases, duplicate and unsupported values',()=>{
|
|
|
|
|
const tax=parseCoreArguments(['--plan','--tax-mode','TAX_REFUND'])
|
|
|
|
|
assert.equal(tax.taxMode,'TAX_REFUND')
|
|
|
|
|
const plan=executionPlan(tax)
|
|
|
|
|
assert.deepEqual(plan.modes,F40_MODES);assert.match(plan.scope,/post-COMPLETED customer document reads\/downloads/)
|
|
|
|
|
for(const args of [['--tax-mode','REFUND'],['--tax-mode','tax_refund'],['--tax-mode',''],['--taxMode','TAX_REFUND'],
|
|
|
|
|
['--tax-mode','TAX_REFUND','--tax-mode','BUY_DECLARATION']])assert.throws(()=>parseCoreArguments(args))
|
|
|
|
|
})
|
2026-10-03 08:13:04 +08:00
|
|
|
|
|
|
|
|
test('explicit single signature mode runs only that mode and reports the narrowed scope', async()=>{
|
|
|
|
|
const cfg=parseCoreArguments(['--execute','--environment','TEST','--run-id','single-mode','--signature-mode','ROOT_FIRST'])
|
|
|
|
|
assert.deepEqual(executionPlan(cfg).modes,['ROOT_FIRST'])
|
|
|
|
|
assert.doesNotMatch(executionPlan(cfg).reports.join(','),/child_first/)
|
|
|
|
|
const seen=[]
|
|
|
|
|
const summary=await runFbaCore(cfg,{env:env(),readers:readers(),outputRoot:root(),createContext:mode=>({context:{},async run(){seen.push(mode);return pass()}})})
|
|
|
|
|
assert.equal(summary.success,true);assert.deepEqual(seen,['ROOT_FIRST']);assert.equal(summary.modes.length,1)
|
|
|
|
|
for(const args of [['--signature-mode','ALL'],['--signature-mode','root_first'],['--signature-mode','ROOT_FIRST','--signature-mode','CHILD_FIRST']])assert.throws(()=>parseCoreArguments(args))
|
|
|
|
|
})
|
2026-10-03 06:26:33 +08:00
|
|
|
test('both tax modes pass unchanged to all three existing contexts and persist on all report surfaces',async()=>{
|
|
|
|
|
for(const taxMode of ['BUY_DECLARATION','TAX_REFUND']){
|
|
|
|
|
const outputRoot=root(),cfg={...config(),taxMode},seen=[]
|
|
|
|
|
const result=await runFbaCore(cfg,{env:env(),outputRoot,readers:readers(),createContext:mode=>({context:{},async run(){
|
|
|
|
|
seen.push({mode,taxMode:this.context.f40TaxMode})
|
|
|
|
|
await this.context.phase4ReceivingEvidenceReader(scope())
|
|
|
|
|
return pass()
|
|
|
|
|
}})})
|
|
|
|
|
assert.equal(result.success,true);assert.equal(result.taxMode,taxMode)
|
|
|
|
|
assert.deepEqual(seen,F40_MODES.map(mode=>({mode,taxMode})))
|
|
|
|
|
const read=name=>JSON.parse(readFileSync(join(outputRoot,cfg.outputDir,name),'utf8'))
|
|
|
|
|
assert.equal(read('manifest.json').taxMode,taxMode)
|
|
|
|
|
assert.equal(read('summary.json').taxMode,taxMode)
|
|
|
|
|
assert.ok(read('summary.json').modes.every(mode=>mode.taxMode===taxMode))
|
|
|
|
|
for(const mode of F40_MODES){
|
|
|
|
|
const report=read(mode.toLowerCase()+'.json')
|
|
|
|
|
assert.equal(report.regressionScope.taxMode,taxMode)
|
|
|
|
|
assert.equal(report.regressionScope.signatureOrder,mode)
|
|
|
|
|
assert.match(report.regressionScope.description,new RegExp(taxMode))
|
|
|
|
|
assert.equal(read(`evidence/${mode.toLowerCase()}-0001-receiving.json`).taxMode,taxMode)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
test('tax mode exception is retained with exact scope and does not start later fixtures',async()=>{
|
|
|
|
|
const outputRoot=root(),cfg={...config(),taxMode:'TAX_REFUND'},seen=[]
|
|
|
|
|
const result=await runFbaCore(cfg,{env:env(),outputRoot,readers:readers(),createContext:mode=>({context:{},createdResources:{},async run(){
|
|
|
|
|
seen.push(mode);throw Error('private failure')
|
|
|
|
|
}})})
|
|
|
|
|
assert.deepEqual(seen,['ROOT_FIRST']);assert.equal(result.success,false);assert.equal(result.taxMode,'TAX_REFUND')
|
|
|
|
|
const report=JSON.parse(readFileSync(join(outputRoot,cfg.outputDir,'root_first.json'),'utf8'))
|
|
|
|
|
assert.equal(report.regressionScope.taxMode,'TAX_REFUND');assert.equal(report.fatal,'FBA_CORE_MODE_EXCEPTION')
|
|
|
|
|
})
|
|
|
|
|
test('TEST configuration rejects cross environment origins, embedded credentials and wrong workspace',()=>{
|
|
|
|
|
assert.equal(validateCoreEnvironment(env()),'https://test.example.invalid')
|
|
|
|
|
for(const changed of [{FBA_CORE_TEST_GATEWAY_ORIGIN:'https://other.example.invalid'},
|
|
|
|
|
{CHONGCHONG_ADMIN_API_BASE_URL:'https://user:secret@test.example.invalid'},
|
|
|
|
|
{CHONGCHONG_ADMIN_API_BASE_URL:'https://prod.example.invalid',FBA_CORE_TEST_GATEWAY_ORIGIN:'https://prod.example.invalid'},
|
|
|
|
|
{ADMIN_PDA_PASSWORD:''},{FBA_CORE_JENKINS_AGENT:"test');println('bad"}])assert.throws(()=>validateCoreEnvironment({...env(),...changed}))
|
|
|
|
|
assert.throws(()=>validateCoreEnvironment(env(),'C:/wrong/chongchong-global-dev/baiyue-firstleg'))
|
|
|
|
|
})
|
|
|
|
|
test('serial runner injects both readers and emits all modes without overlap',async()=>{
|
|
|
|
|
const outputRoot=root(),order=[];let active=0
|
|
|
|
|
const summary=await runFbaCore(config(),{env:env(),outputRoot,readers:readers(),createContext:mode=>({context:{},async run(){
|
|
|
|
|
assert.equal(active++,0);order.push(mode)
|
|
|
|
|
assert.equal(this.context.f40SignatureOrder,mode)
|
|
|
|
|
await this.context.phase4ReceivingEvidenceReader(scope());await this.context.f40ReadOnlyOutboxEvidence(completion())
|
|
|
|
|
active--;return pass()
|
|
|
|
|
}})})
|
|
|
|
|
assert.equal(summary.success,true);assert.deepEqual(order,F40_MODES)
|
|
|
|
|
const saved=JSON.parse(readFileSync(join(outputRoot,config().outputDir,'summary.json'),'utf8'))
|
|
|
|
|
assert.equal(saved.modes.length,3);assert.equal(saved.modes[0].evidenceFiles,2)
|
|
|
|
|
})
|
|
|
|
|
test('first failed mode stops later fixtures and keeps report plus evidence',async()=>{
|
|
|
|
|
const outputRoot=root(),calls=[]
|
|
|
|
|
const result=await runFbaCore(config(),{env:env(),outputRoot,readers:readers(),createContext:mode=>({context:{},async run(){
|
|
|
|
|
calls.push(mode);await this.context.phase4ReceivingEvidenceReader(scope());return {...pass(),success:false}
|
|
|
|
|
}})})
|
|
|
|
|
assert.equal(result.success,false);assert.deepEqual(calls,['ROOT_FIRST'])
|
|
|
|
|
assert.ok(existsSync(join(outputRoot,config().outputDir,'root_first.json')))
|
|
|
|
|
assert.ok(existsSync(join(outputRoot,config().outputDir,'evidence/root_first-0001-receiving.json')))
|
|
|
|
|
})
|
|
|
|
|
test('reader exception journals failure, hides secret and prevents next mode',async()=>{
|
|
|
|
|
const outputRoot=root(),r=readers();r.readReceiving=async()=>{throw Error('offline-api-secret')}
|
|
|
|
|
const result=await runFbaCore(config(),{env:env(),outputRoot,readers:r,createContext:()=>({context:{},createdResources:{},async run(){
|
|
|
|
|
await this.context.phase4ReceivingEvidenceReader(scope());return pass()
|
|
|
|
|
}})})
|
|
|
|
|
assert.equal(result.modes.length,1);assert.equal(result.success,false)
|
|
|
|
|
const text=readFileSync(join(outputRoot,config().outputDir,'evidence/root_first-0001-receiving.json'),'utf8')
|
|
|
|
|
assert.match(text,/READ_ONLY_EVIDENCE_FAILED/);assert.doesNotMatch(text,/offline-api-secret/)
|
|
|
|
|
})
|
|
|
|
|
test('preflight failure creates summary but no HTTP fixture; existing run or active lock cannot overwrite',async()=>{
|
|
|
|
|
const outputRoot=root(),r=readers();let created=0;r.preflight=async()=>{throw Error('private')}
|
|
|
|
|
await runFbaCore(config(),{env:env(),outputRoot,readers:r,createContext:()=>{created++}})
|
|
|
|
|
assert.equal(created,0)
|
|
|
|
|
const summaryPath=join(outputRoot,config().outputDir,'summary.json'),before=readFileSync(summaryPath,'utf8')
|
|
|
|
|
await runFbaCore(config(),{env:env(),outputRoot,readers:readers(),createContext:()=>{created++}})
|
|
|
|
|
assert.equal(readFileSync(summaryPath,'utf8'),before);assert.equal(created,0)
|
|
|
|
|
const lock=join(outputRoot,'reports/.fba-core-regression.lock');writeFileSync(lock,'occupied')
|
|
|
|
|
await assert.rejects(runFbaCore({...config(),runId:'next',outputDir:'reports/next'},{env:env(),outputRoot,readers:readers()}))
|
|
|
|
|
assert.equal(readFileSync(lock,'utf8'),'occupied')
|
|
|
|
|
})
|
2026-10-04 15:00:24 +08:00
|
|
|
test('redaction scans the secret input once for the complete report tree',()=>{
|
|
|
|
|
let reads=0
|
|
|
|
|
const values=Array.from({length:80},()=> 'synthetic-private-value')
|
|
|
|
|
const secrets=new Proxy(values,{get(target,key,receiver){if(/^\d+$/.test(String(key)))reads++;return Reflect.get(target,key,receiver)}})
|
|
|
|
|
const report={steps:Array.from({length:40},()=>({message:'prefix synthetic-private-value suffix',nested:{token:'hidden',public:'kept'}}))}
|
|
|
|
|
const result=redactEvidence(report,secrets)
|
|
|
|
|
assert.ok(result.steps.every(row=>row.message==='prefix [REDACTED] suffix'&&row.nested.token==='[REDACTED]'&&row.nested.public==='kept'))
|
|
|
|
|
assert.equal(reads,values.length,'normalization must not repeat for every evidence string')
|
|
|
|
|
assert.equal(report.steps[0].message,'prefix synthetic-private-value suffix','input report is not modified')
|
|
|
|
|
})
|
|
|
|
|
|
2026-10-03 06:26:33 +08:00
|
|
|
test('reports redact dynamically created account secrets and environment/token values',()=>{
|
|
|
|
|
const value=redactEvidence({password:'x',pdaToken:'y',message:'failure private-secret',nested:{gateway:'url',value:'safe'}},['private-secret'])
|
|
|
|
|
assert.equal(value.password,'[REDACTED]');assert.equal(value.pdaToken,'[REDACTED]')
|
|
|
|
|
assert.equal(value.message,'failure [REDACTED]');assert.equal(value.nested.value,'safe')
|
|
|
|
|
})
|
2026-10-03 08:13:04 +08:00
|
|
|
|
|
|
|
|
test('delivery fault requires one explicit mode and verified proof plus restoration, even after caught run failures', async()=>{
|
|
|
|
|
assert.throws(()=>parseCoreArguments(['--plan','--verify-delivery-event-recovery']))
|
|
|
|
|
const cfg=parseCoreArguments(['--execute','--environment','TEST','--run-id','fault-one','--signature-mode','ROOT_FIRST','--verify-delivery-event-recovery'])
|
|
|
|
|
assert.equal(executionPlan(cfg).verifyDeliveryEventRecovery,true)
|
|
|
|
|
for(const failure of ['none','context','proof','restore']) {
|
|
|
|
|
const outputRoot=root();let cleanups=0,prepares=0
|
|
|
|
|
const r={...readers(),readFleetInventory:async scope=>({scope,balances:[],positions:[],ledger:[]})}
|
|
|
|
|
const summary=await runFbaCore(cfg,{env:{...env(),CHONGCHONG_ENV:'test'},readers:r,outputRoot,
|
|
|
|
|
faultFactory:()=>({prepare:async()=>{prepares++},cleanup:async()=>{
|
|
|
|
|
cleanups++;if(failure==='restore')throw Error('private failure')
|
|
|
|
|
return {restored:true,healthy:true,proxyStopped:true}
|
|
|
|
|
}}),createContext:()=>({context:{},createdResources:{},async run(){
|
|
|
|
|
await this.context.f40DeliveryEventFaultExecutor.prepare({marker:'F40',customerId:'90',deliveryId:'91'})
|
|
|
|
|
await this.context.f40ReadOnlyInventoryEvidence({marker:'F40',customerId:'90',warehouseId:'92'})
|
|
|
|
|
if(failure==='context')throw Error('private failure')
|
|
|
|
|
const report=pass()
|
|
|
|
|
if(failure!=='proof')report.createdResources.phase5RootFulfillment.deliveryEventRecovery={status:'VERIFIED'}
|
|
|
|
|
return report
|
|
|
|
|
}})})
|
|
|
|
|
assert.equal(summary.success,failure==='none')
|
|
|
|
|
assert.equal(cleanups,1);assert.equal(prepares,1)
|
|
|
|
|
const evidencePath=join(outputRoot,cfg.outputDir,'evidence')
|
|
|
|
|
assert.ok(existsSync(join(evidencePath,'root_first-delivery-scope.json')))
|
|
|
|
|
assert.ok(existsSync(join(evidencePath,'root_first-0001-inventory.json')))
|
|
|
|
|
const report=JSON.parse(readFileSync(join(outputRoot,cfg.outputDir,'root_first.json'),'utf8'))
|
|
|
|
|
if(failure==='restore')assert.equal(report.deliveryRestorationFailed,true)
|
|
|
|
|
}
|
|
|
|
|
})
|