2026-10-03 06:26:33 +08:00
import { mkdirSync , writeFileSync , openSync , closeSync , unlinkSync } from 'node:fs'
import { resolve , dirname , basename , relative , isAbsolute , join } from 'node:path'
import { fileURLToPath , pathToFileURL } from 'node:url'
import { createHash } from 'node:crypto'
import { AcceptanceRun , parseArguments , validateExecutionOptions } from './admin-test-env-acceptance.mjs'
import { createTestEvidenceReaders , validateEvidenceConfig , EVIDENCE_ENV_KEYS } from './fba-core-regression-evidence.mjs'
const repositoryRoot = resolve ( dirname ( fileURLToPath ( import . meta . url )), '..' )
export const F40_MODES = Object . freeze ([ 'ROOT_FIRST' , 'CHILD_FIRST' , 'ROOT_SIGNED_REMAINDER_CANCELLED' ])
export const F40_TAX_MODES = Object . freeze ([ 'BUY_DECLARATION' , 'TAX_REFUND' ])
export const REQUIRED_ENV_KEYS = Object . freeze ([ 'CHONGCHONG_ADMIN_API_BASE_URL' , 'CHONGCHONG_ADMIN_USERNAME' ,
'CHONGCHONG_ADMIN_PASSWORD' , 'ADMIN_PDA_USERNAME' , 'ADMIN_PDA_PASSWORD' , 'FBA_CORE_TEST_GATEWAY_ORIGIN' ,... EVIDENCE_ENV_KEYS ])
export function parseCoreArguments ( argv ) {
const values = {}, flags = new Set ()
for ( let i = 0 ; i < argv . length ; i ++ ) {
const arg = argv [ i ]
2026-10-03 08:13:04 +08:00
if ([ '--plan' , '--execute' , '--verify-delivery-event-recovery' ]. includes ( arg )) { if ( flags . has ( arg )) throw Error ( 'Duplicate execution flag' ); flags . add ( arg ); continue }
if ( ! [ '--environment' , '--run-id' , '--output-dir' , '--tax-mode' , '--signature-mode' ]. includes ( arg ) || values [ arg ] !== undefined ||! argv [ i + 1 ] || argv [ i + 1 ]. startsWith ( '--' )) throw Error ( 'Invalid FBA core argument' )
2026-10-03 06:26:33 +08:00
values [ arg ] = argv [ ++ i ]
}
2026-10-03 08:13:04 +08:00
if ( flags . has ( '--plan' ) && flags . has ( '--execute' )) throw Error ( 'Choose plan or execute' )
2026-10-03 06:26:33 +08:00
if ( values [ '--environment' ] && values [ '--environment' ] !== 'TEST' ) throw Error ( 'Only explicit TEST is supported' )
const execute = flags . has ( '--execute' ), runId = values [ '--run-id' ] || ( ! execute ? 'plan' : null )
if ( ! runId ||! /^[A-Za-z0-9][A-Za-z0-9_-]{0,47}$/ . test ( runId )) throw Error ( 'Fresh run-id is required' )
if ( execute && values [ '--environment' ] !== 'TEST' ) throw Error ( 'Execution requires --environment TEST' )
const taxMode = values [ '--tax-mode' ] || 'BUY_DECLARATION'
if ( ! F40_TAX_MODES . includes ( taxMode )) throw Error ( 'Unsupported F40 tax mode' )
2026-10-03 08:13:04 +08:00
const signatureMode = values [ '--signature-mode' ]
if ( signatureMode &&! F40_MODES . includes ( signatureMode )) throw Error ( 'Unsupported F40 signature mode' )
const verifyDeliveryEventRecovery = flags . has ( '--verify-delivery-event-recovery' )
if ( verifyDeliveryEventRecovery &&! signatureMode ) throw Error ( 'Delivery fault requires one explicit signature mode' )
return { execute , environment : 'TEST' , runId , taxMode , signatureMode , verifyDeliveryEventRecovery , outputDir : values [ '--output-dir' ] || `reports/fba-core- ${ runId } ` }
}
function selectedModes ( config ) {
if ( config . signatureMode &&! F40_MODES . includes ( config . signatureMode )) throw Error ( 'Unsupported F40 signature mode' )
return config . signatureMode ? [ config . signatureMode ] : F40_MODES
2026-10-03 06:26:33 +08:00
}
function taxScope ( taxMode ) {
if ( ! F40_TAX_MODES . includes ( taxMode )) throw Error ( 'Unsupported F40 tax mode' )
return taxMode === 'TAX_REFUND'
? 'F40 TAX_REFUND: real customs upload, receipt confirmation, declaration/release, final documents, signatures and post-COMPLETED customer document reads/downloads.'
: 'F40 BUY_DECLARATION: existing buy-declaration shipment, signatures and remainder cancellation.'
}
export function executionPlan ( config ) {
return { environment : 'TEST' , execution : config . execute ? 'EXECUTE' : 'PLAN' , runId : config . runId , outputDir : config . outputDir ,
2026-10-03 08:13:04 +08:00
taxMode : config . taxMode , scope : taxScope ( config . taxMode ), verifyDeliveryEventRecovery : config . verifyDeliveryEventRecovery === true ,
modes : selectedModes ( config ), module : 'phase5-root-fulfillment' , requiredEnvironmentKeys : REQUIRED_ENV_KEYS ,
2026-10-03 06:26:33 +08:00
optionalEnvironmentKeys : [ 'CHONGCHONG_MERCHANT_USERNAME' , 'CHONGCHONG_MERCHANT_PASSWORD' ],
2026-10-03 08:13:04 +08:00
behavior : config . verifyDeliveryEventRecovery
? 'One explicit mode, first FBA receipt only: scoped ACK loss, original DEAD retry and verified Lastleg image/configuration restoration. HTTP creates fresh fixtures; SQL is read-only. Do not overlap deployments or other shared-environment writers.'
: 'Serial fresh fixtures; stop on first failed mode. HTTP creates customers/orders/files and uses shared PDA/pallet resources. SQL is read-only; no deploy or database mutation.' ,
reports : [ 'manifest.json' , 'summary.json' ,... selectedModes ( config ). map ( mode => ` ${ mode . toLowerCase () } .json` ), 'evidence/*.json' ],
2026-10-03 06:26:33 +08:00
retention : 'Exclusive run directory and evidence files. Interrupted lock requires operator review; no automatic cleanup or resume.' ,
2026-10-03 08:13:04 +08:00
scopeLimit : config . verifyDeliveryEventRecovery ? 'Only the selected first delivery event and listed F40 mode; not all reverse events, fee-void recovery, or UI acceptance.'
: 'Only the listed F40 modes and receiving/completion evidence; not all F01-F40, UI acceptance or reverse event replay.' }
2026-10-03 06:26:33 +08:00
}
export function validateCoreEnvironment ( env , root = repositoryRoot ) {
if ( basename ( root ) !== 'baiyue-firstleg' || basename ( dirname ( root )) !== 'chongchong-global' ) throw Error ( 'TEST workspace required' )
if ( REQUIRED_ENV_KEYS . some ( key => typeof env [ key ] !== 'string' ||! env [ key ]. trim ())) throw Error ( 'Missing required TEST configuration' )
let base , expected
try { base = new URL ( env . CHONGCHONG_ADMIN_API_BASE_URL ); expected = new URL ( env . FBA_CORE_TEST_GATEWAY_ORIGIN )} catch { throw Error ( 'Invalid TEST gateway configuration' )}
if ( ! [ 'http:' , 'https:' ]. includes ( base . protocol ) || base . username || base . password || base . search || base . hash
|| expected . href !== expected . origin + '/' || base . origin !== expected . origin
|| /(?:^|[.-])(?:dev|prod|production)(?:[.-]|$)/i . test ( base . hostname )) throw Error ( 'TEST gateway origin mismatch' )
if ( Boolean ( env . CHONGCHONG_MERCHANT_USERNAME ) !== Boolean ( env . CHONGCHONG_MERCHANT_PASSWORD )) throw Error ( 'Incomplete optional merchant credentials' )
validateEvidenceConfig ( env )
return base . origin
}
export function redactEvidence ( value , secrets = []) {
if ( Array . isArray ( value )) return value . map ( item => redactEvidence ( item , secrets ))
if ( value && typeof value === 'object' ) return Object . fromEntries ( Object . entries ( value ). map (([ key , item ])=>[ key ,
/^(?:.*password|.*token|authorization|cookie|gateway|jenkinsUrl|privateKey)$/i . test ( key ) ? '[REDACTED]' : redactEvidence ( item , secrets )]))
if ( typeof value === 'string' ) return secrets . filter ( secret => typeof secret === 'string' && secret . length > 0 )
. reduce (( text , secret )=> text . split ( secret ). join ( '[REDACTED]' ), value )
return value
}
export function modePassed ( report ) {
const proof = report ? . createdResources ? . phase5RootFulfillment
return report ? . success === true && report ? . summary ? . failed === 0 && proof ? . finalStatus === 'VERIFIED'
&& ! ( report . deploymentBlockers || []). length && ! ( report . steps || []). some ( step => step . passed === false )
}
function safeFailure ( error ) {
const message = typeof error ? . message === 'string' ? error . message : ''
const mysql = /^MYSQL_([0-9]{3,5})$/ . exec ( message )
const http = /^TEST evidence HTTP ([1-5][0-9]{2})$/ . exec ( message )
return mysql ? { code : 'READ_ONLY_MYSQL_FAILED' , mysqlError : Number ( mysql [ 1 ])}
: http ? { code : 'READ_ONLY_HTTP_FAILED' , http : Number ( http [ 1 ])} : { code : 'EXECUTION_FAILED' }
}
function createAcceptanceContext ( mode , config , env , gateway ) {
const options = validateExecutionOptions ({... parseArguments ([ '--modules' , 'phase5-root-fulfillment' , '--quiet' , 'true' ]),
environment : 'test' , gateway , username : env . CHONGCHONG_ADMIN_USERNAME , password : env . CHONGCHONG_ADMIN_PASSWORD ,
pdaUsername : env . ADMIN_PDA_USERNAME , pdaPassword : env . ADMIN_PDA_PASSWORD ,
merchantUsername : env . CHONGCHONG_MERCHANT_USERNAME || '' , merchantPassword : env . CHONGCHONG_MERCHANT_PASSWORD || '' ,
markerNamespace : createHash ( 'sha256' ). update ( config . runId + ':' + mode ). digest ( 'hex' ). slice ( 0 , 12 )})
return new AcceptanceRun ( options )
}
2026-10-03 08:13:04 +08:00
export async function runFbaCore ( config ,{ env = process . env , readers , createContext = createAcceptanceContext , outputRoot = repositoryRoot , faultFactory } = {}) {
2026-10-03 06:26:33 +08:00
const scopeDescription = taxScope ( config . taxMode )
2026-10-03 08:13:04 +08:00
const modes = selectedModes ( config )
2026-10-03 06:26:33 +08:00
const gateway = validateCoreEnvironment ( env )
2026-10-03 08:13:04 +08:00
if ( config . verifyDeliveryEventRecovery && ( env . CHONGCHONG_ENV !== 'test' ||! config . signatureMode )) throw Error ( 'Delivery fault requires explicit TEST and one signature mode' )
2026-10-03 06:26:33 +08:00
const output = resolve ( outputRoot , config . outputDir ), reports = resolve ( outputRoot , 'reports' ), rel = relative ( reports , output )
if ( ! config . execute || config . environment !== 'TEST' ||! rel || rel . startsWith ( '..' ) || isAbsolute ( rel )) throw Error ( 'Fresh report directory inside reports required' )
mkdirSync ( reports ,{ recursive : true })
const lock = join ( reports , '.fba-core-regression.lock' )
const lockFd = openSync ( lock , 'wx' )
const secrets = [... REQUIRED_ENV_KEYS , 'CHONGCHONG_MERCHANT_USERNAME' , 'CHONGCHONG_MERCHANT_PASSWORD' ]
. filter ( key => / PASSWORD | TOKEN | USERNAME | JENKINS_USER / . test ( key )). map ( key => env [ key ])
2026-10-03 08:13:04 +08:00
const summary = { runId : config . runId , environment : 'TEST' , taxMode : config . taxMode , verifyDeliveryEventRecovery : config . verifyDeliveryEventRecovery === true , startedAt : new Date (). toISOString (), success : false , modes : [], scope : scopeDescription }
2026-10-03 06:26:33 +08:00
let initialized = false
const merchantBefore = { username : process . env . MERCHANT_TEST_USERNAME , password : process . env . MERCHANT_TEST_PASSWORD }
try {
writeFileSync ( lockFd , JSON . stringify ({ runId : config . runId , pid : process . pid , startedAt : summary . startedAt }))
mkdirSync ( output ,{ recursive : false }); initialized = true
mkdirSync ( join ( output , 'evidence' ))
const save = ( name , value )=> writeFileSync ( join ( output , name ), JSON . stringify ( redactEvidence ( value , secrets ), null , 2 ) + '\n' ,{ flag : 'wx' })
save ( 'manifest.json' , executionPlan ( config ))
readers ||= createTestEvidenceReaders ( env )
await readers . preflight ()
2026-10-03 08:13:04 +08:00
for ( const mode of modes ) {
2026-10-03 06:26:33 +08:00
const context = createContext ( mode , config , env , gateway )
let sequence = 0
const evidence = async ( kind , scope , read )=>{
const record = { mode , taxMode : config . taxMode , kind , scope , capturedAt : new Date (). toISOString (), captured : false }
try { record . evidence = await read ( scope ); record . captured = true ; return record . evidence }
catch ( error ) { record . error = 'READ_ONLY_EVIDENCE_FAILED' ; record . diagnostic = safeFailure ( error ); throw Error ( 'Read-only evidence failed' )}
finally { save ( `evidence/ ${ mode . toLowerCase () } - ${ String ( ++ sequence ). padStart ( 4 , '0' ) } - ${ kind } .json` , record )}
}
context . context . f40SignatureOrder = mode
context . context . f40TaxMode = config . taxMode
context . context . f40ReadOnlyOutboxEvidence = scope => evidence ( 'completion' , scope , readers . readCompletion )
context . context . phase4ReceivingEvidenceReader = scope => evidence ( 'receiving' , scope , readers . readReceiving )
2026-10-03 08:13:04 +08:00
let fault , faultPreparationAttempted = false
if ( config . verifyDeliveryEventRecovery ) {
if ( typeof readers . readFleetInventory !== 'function' ) throw Error ( 'Complete raw inventory reader required for delivery recovery' )
const factory = faultFactory || ( await import ( './test-event-fault-executor.mjs' )). createTestDeliveryFaultExecutor
const executor = factory ( env )
fault = {... executor , prepare : async scope =>{
save ( `evidence/ ${ mode . toLowerCase () } -delivery-scope.json` ,{ scope , stage : 'PREPARE_INTENT' })
faultPreparationAttempted = true ; return executor . prepare ( scope )
}}
context . context . f40DeliveryEventFaultExecutor = fault
context . context . f40ReadOnlyInventoryEvidence = scope => evidence ( 'inventory' , scope , readers . readFleetInventory )
}
2026-10-03 06:26:33 +08:00
let report
try { report = await context . run ()}
catch ( error ) { report = { success : false , fatal : 'FBA_CORE_MODE_EXCEPTION' , diagnostic : safeFailure ( error ), summary : { failed : 1 }, createdResources : context . createdResources }}
2026-10-03 08:13:04 +08:00
finally {
secrets . push ( context . token , context . pdaToken , context . refreshToken , context . context ? . merchantQualificationFixture ? . password )
if ( fault ) {
try {
const restoration = await fault . cleanup ()
save ( `evidence/ ${ mode . toLowerCase () } -delivery-restoration.json` ,{ attempted : faultPreparationAttempted , restoration : restoration ?? null })
if ( faultPreparationAttempted && ( restoration ? . restored !== true || restoration . healthy !== true || restoration . proxyStopped !== true )) throw Error ( 'Delivery restoration unproven' )
} catch ( error ) { report = {... report , success : false , deliveryRestorationFailed : true , restorationDiagnostic : safeFailure ( error )}}
}
}
if ( config . verifyDeliveryEventRecovery ) {
const proof = report ? . createdResources ? . phase5RootFulfillment ? . deliveryEventRecovery
if ( proof ? . status !== 'VERIFIED' ) report = {... report , success : false , deliveryRecoveryUnproven : true }
}
2026-10-03 06:26:33 +08:00
report = {... report , regressionScope : { taxMode : config . taxMode , signatureOrder : mode , description : scopeDescription }}
const name = ` ${ mode . toLowerCase () } .json` ; save ( name , report )
const passed = modePassed ( report )
summary . modes . push ({ mode , taxMode : config . taxMode , report : name , passed , summary : report . summary , evidenceFiles : sequence })
if ( ! passed ) break
}
2026-10-03 08:13:04 +08:00
summary . success = summary . modes . length === modes . length && summary . modes . every ( mode => mode . passed )
2026-10-03 06:26:33 +08:00
} catch ( error ) { summary . failure = 'FBA_CORE_PREFLIGHT_OR_RUN_FAILED' ; summary . diagnostic = safeFailure ( error )}
finally {
summary . finishedAt = new Date (). toISOString ()
try {
if ( initialized ) writeFileSync ( join ( output , 'summary.json' ), JSON . stringify ( redactEvidence ( summary , secrets ), null , 2 ) + '\n' ,{ flag : 'wx' })
} finally {
closeSync ( lockFd ); unlinkSync ( lock )
for ( const [ key , value ] of [[ 'MERCHANT_TEST_USERNAME' , merchantBefore . username ],[ 'MERCHANT_TEST_PASSWORD' , merchantBefore . password ]]) {
if ( value === undefined ) delete process . env [ key ]; else process . env [ key ] = value
}
}
}
return summary
}
if ( process . argv [ 1 ] && import . meta . url === pathToFileURL ( resolve ( process . argv [ 1 ])). href ) {
try {
const config = parseCoreArguments ( process . argv . slice ( 2 ))
const result = config . execute ? await runFbaCore ( config ) : executionPlan ( config )
console . log ( JSON . stringify ( result , null , 2 )); if ( config . execute &&! result . success ) process . exitCode = 1
} catch { console . error ( 'FBA core rejected configuration or existing run/lock; no automatic overwrite.' ); process . exitCode = 1 }
}