test(fba): verify signed delivery ACK loss and original event recovery
This commit is contained in:
@@ -3,6 +3,8 @@
|
||||
import { readFileSync } from 'node:fs'
|
||||
import { createHash } from 'node:crypto'
|
||||
import { isDeepStrictEqual } from 'node:util'
|
||||
import { assertTestDeliveryFaultProof, deliveryFaultScopeKeys } from './test-delivery-fault-proof.mjs'
|
||||
export { assertTestDeliveryFaultProof } from './test-delivery-fault-proof.mjs'
|
||||
import { validateEvidenceConfig } from './fba-core-regression-evidence.mjs'
|
||||
|
||||
const runtime = readFileSync(new URL('./test-event-fault-runtime.py', import.meta.url), 'utf8')
|
||||
@@ -17,11 +19,11 @@ export function renderTestUnloadFaultAction(action, scope, agent) {
|
||||
return renderAction(action,scope,agent,'UNLOAD')
|
||||
}
|
||||
function renderAction(action,scope,agent,direction) {
|
||||
const keys=direction==='UNLOAD'?['marker','customerId','containerBookingId']:['marker','customerId','loadingPlanId','containerBookingId']
|
||||
const keys=direction==='DELIVERY'?deliveryFaultScopeKeys:direction==='UNLOAD'?['marker','customerId','containerBookingId']:['marker','customerId','loadingPlanId','containerBookingId']
|
||||
if (!['prepare', 'activate', 'proxy-ready', 'health', 'status', 'recover', 'proof', 'restore', 'finish'].includes(action)
|
||||
|| !/^[A-Za-z0-9_.-]{1,128}$/.test(agent) || !/^[A-Za-z0-9_-]{1,96}$/.test(scope?.marker || '')
|
||||
|| keys.filter(key=>key!=='marker').some(key => !/^[1-9][0-9]{0,19}$/.test(scope?.[key] || ''))
|
||||
|| (direction==='UNLOAD'&&Object.keys(scope).some(key=>!keys.includes(key)))) {
|
||||
|| (direction!=='LOADING'&&Object.keys(scope).some(key=>!keys.includes(key)))) {
|
||||
throw Error('Invalid TEST fault action or scope')
|
||||
}
|
||||
const config = encode(JSON.stringify({ action, scope, direction, ...(action === 'prepare' ? { proxySource } : {}) }))
|
||||
@@ -32,6 +34,9 @@ function renderAction(action,scope,agent,direction) {
|
||||
export function createTestFaultExecutor(env, fetcher = globalThis.fetch) {
|
||||
return createExecutor(env,fetcher,'LOADING')
|
||||
}
|
||||
export function createTestDeliveryFaultExecutor(env, fetcher = globalThis.fetch) {
|
||||
return createExecutor(env,fetcher,'DELIVERY')
|
||||
}
|
||||
export function createTestUnloadFaultExecutor(env, fetcher = globalThis.fetch) {
|
||||
return createExecutor(env,fetcher,'UNLOAD')
|
||||
}
|
||||
@@ -117,23 +122,23 @@ function createExecutor(env,fetcher,direction) {
|
||||
await action('restore')
|
||||
await wait('health', row => row.healthy)
|
||||
restoration=await action('finish')
|
||||
if(direction==='UNLOAD'&&!(/^sha256:[0-9a-f]{64}$/.test(preparedEvidence?.originalImageId||'')
|
||||
if(direction!=='LOADING'&&!(/^sha256:[0-9a-f]{64}$/.test(preparedEvidence?.originalImageId||'')
|
||||
&&/^[0-9a-f]{64}$/.test(preparedEvidence?.originalFingerprint||'')
|
||||
&&restoration?.restored===true&&restoration.healthy===true&&restoration.proxyStopped===true
|
||||
&&restoration.producerName==='firstleg-test-lastleg'&&restoration.originalImageId===preparedEvidence?.originalImageId
|
||||
&&restoration.currentImageId===preparedEvidence?.originalImageId&&restoration.originalFingerprint===preparedEvidence?.originalFingerprint
|
||||
&&restoration.currentFingerprint===preparedEvidence?.originalFingerprint))throw Error('Unload restoration evidence mismatch')
|
||||
&&restoration.currentFingerprint===preparedEvidence?.originalFingerprint))throw Error((direction==='DELIVERY'?'Delivery':'Unload')+' restoration evidence mismatch')
|
||||
finished = true
|
||||
return restoration
|
||||
}
|
||||
return {
|
||||
async prepare(next) {
|
||||
if (scope) {
|
||||
if(direction==='UNLOAD'&&!isDeepStrictEqual(scope,next))throw Error('TEST unload fault scope drift')
|
||||
if(direction!=='LOADING'&&!isDeepStrictEqual(scope,next))throw Error('TEST unload fault scope drift')
|
||||
return // Pin only the first actual current-run plan; other events pass through.
|
||||
}
|
||||
if(direction==='UNLOAD')renderAction('prepare',next,env.FBA_CORE_JENKINS_AGENT,direction)
|
||||
scope = direction==='UNLOAD'?{...next}:{ marker: next.marker, customerId: next.customerId,
|
||||
if(direction!=='LOADING')renderAction('prepare',next,env.FBA_CORE_JENKINS_AGENT,direction)
|
||||
scope = direction!=='LOADING'?{...next}:{ marker: next.marker, customerId: next.customerId,
|
||||
loadingPlanId: next.loadingPlanId, containerBookingId: next.containerBookingId }
|
||||
preparedEvidence=await action('prepare')
|
||||
await wait('proxy-ready', row => row.ready, 10, true)
|
||||
@@ -141,16 +146,17 @@ function createExecutor(env,fetcher,direction) {
|
||||
await wait('health', row => row.healthy)
|
||||
},
|
||||
async readProof(expected) {
|
||||
if (!scope || (direction==='UNLOAD'?!isDeepStrictEqual(scope,expected):Object.keys(scope).some(key => expected?.[key] !== scope[key]))) throw Error('TEST fault scope drift')
|
||||
if (!scope || (direction!=='LOADING'?!isDeepStrictEqual(scope,expected):Object.keys(scope).some(key => expected?.[key] !== scope[key]))) throw Error('TEST fault scope drift')
|
||||
try {
|
||||
await wait('status', row => row.producer?.publishStatus === 'DEAD' && row.producer.failedAttempts === 12, 180)
|
||||
await action('recover')
|
||||
await wait('status', row => row.producer?.publishStatus === 'PUBLISHED', 90)
|
||||
const proof = await action('proof')
|
||||
if(direction==='UNLOAD')assertTestUnloadFaultProof(proof,scope)
|
||||
if(direction==='DELIVERY')assertTestDeliveryFaultProof(proof,scope)
|
||||
await cleanup()
|
||||
return { ...proof, restored: true, ...(direction==='UNLOAD'?{restoration}:{}) }
|
||||
} finally { if(direction==='UNLOAD')await cleanup() }
|
||||
return { ...proof, restored: true, ...(direction!=='LOADING'?{restoration}:{}) }
|
||||
} finally { if(direction!=='LOADING')await cleanup() }
|
||||
},
|
||||
cleanup,
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user