test: integrate reliable event recovery and receiving evidence

This commit is contained in:
2026-10-02 20:14:17 +08:00
parent d0df738bba
commit 4b531c7e22
17 changed files with 1527 additions and 37 deletions
@@ -2,10 +2,69 @@ import test from 'node:test'
import assert from 'node:assert/strict'
import { readFileSync } from 'node:fs'
import { resolve } from 'node:path'
import { AcceptanceRun, parseArguments, validateExecutionOptions } from './admin-test-env-acceptance.mjs'
import { AcceptanceRun, configureReadOnlyEvidence, unloadRecoveryWasVerified, parseArguments, validateExecutionOptions } from './admin-test-env-acceptance.mjs'
const source = readFileSync(resolve('scripts/admin-test-env-acceptance.mjs'), 'utf8')
test('full CLI evidence preflight attaches readers and fails before business writes when unavailable', async () => {
const run = { options: { modules: ['manual-state-matrix'] }, context: {} }
const readReceiving = () => {}, readCompletion = () => {}
let checked = false
await configureReadOnlyEvidence(run, {}, () => ({ readReceiving, readCompletion,
preflight: async () => { checked = true } }))
assert.equal(checked, true)
assert.equal(run.context.phase4ReceivingEvidenceReader, readReceiving)
assert.equal(run.context.f40ReadOnlyOutboxEvidence, readCompletion)
const unavailable = { options: run.options, context: {} }
await assert.rejects(configureReadOnlyEvidence(unavailable, {}, () => ({
preflight: async () => { throw Error('evidence unavailable') } })), /unavailable/)
assert.deepEqual(unavailable.context, {})
})
test('explicit unloading fault uses a separate first-container hook and restores both directions on failure', async () => {
const calls = []
const run = { marker: 'TEST-UNLOAD', options: { modules: ['phase4-overseas-pda', 'phase4-cross-region-events'],
verifyUnloadEventRecovery: true }, context: {} }
const fault = name => ({ prepare: async scope => calls.push([name, 'prepare', scope]),
readProof: async scope => ({ name, scope, restored: true }),
cleanup: async () => { calls.push([name, 'cleanup']); if (name === 'reverse') throw Error('recovery failed') } })
const cleanup = await configureReadOnlyEvidence(run, {}, () => ({ readReceiving() {}, readCompletion() {}, preflight: async () => {} }),
{ createTestFaultExecutor: () => fault('forward'), createTestUnloadFaultExecutor: () => fault('reverse') })
assert.equal(calls.length, 0)
await run.context.beforeReceivingComplete({ customerId: '7', bookingId: '66' })
await run.context.beforeReceivingComplete({ customerId: '8', bookingId: '77' })
assert.deepEqual(calls, [['reverse', 'prepare', { marker: 'TEST-UNLOAD', customerId: '7', containerBookingId: '66' }]])
assert.equal(await run.context.afterReceivingComplete({ customerId: '8', bookingId: '77' }), null)
const proof = await run.context.afterReceivingComplete({ customerId: '7', bookingId: '66' })
assert.equal(proof.name, 'reverse')
await assert.rejects(cleanup.cleanup(), /恢复失败/)
assert.deepEqual(calls.slice(-2), [['reverse', 'cleanup'], ['forward', 'cleanup']])
})
test('unloading recovery is opt-in and cannot run against unrelated modules', () => {
const args = ['--modules', 'phase4-overseas-return-followups', '--gateway', 'http://localhost:9000',
'--username', 'contract', '--password', 'contract']
assert.equal(parseArguments(args).verifyUnloadEventRecovery, false)
assert.throws(() => validateExecutionOptions(parseArguments([...args, '--verify-unload-event-recovery', 'true'])), /收货链/)
})
test('requested unloading fault cannot pass without exact current-run recovery and inventory proof', () => {
const run = { marker: 'CURRENT', createdResources: {} }
assert.equal(unloadRecoveryWasVerified(run), false)
const row = { status: 'VERIFIED', eventRecoveryInventoryUnchanged: true,
eventRecovery: { restored: true, marker: 'CURRENT', containerBookingId: '66' } }
run.createdResources.phase4OverseasPda = { receivingCompletionEvidence: { '66': row } }
assert.equal(unloadRecoveryWasVerified(run), true)
for (const change of [r => r.eventRecoveryInventoryUnchanged = false, r => r.status = 'UNPROVEN',
r => r.eventRecovery.restored = false, r => r.eventRecovery.marker = 'OLD', r => r.eventRecovery.containerBookingId = '77']) {
const changed = structuredClone(run)
change(changed.createdResources.phase4OverseasPda.receivingCompletionEvidence['66'])
assert.equal(unloadRecoveryWasVerified(changed), false)
}
run.createdResources.phase4OverseasPda.receivingCompletionEvidence['77'] = structuredClone(row)
assert.equal(unloadRecoveryWasVerified(run), false)
})
test('F40 optional chain always creates its customer and rejects absent domestic PDA credentials', () => {
const options = parseArguments(['--modules', 'phase5-root-fulfillment',
'--gateway', 'http://localhost:9000', '--username', 'contract-user', '--password', 'contract-password',