From ba4179b28c88542c452ac7993064701a442da10a Mon Sep 17 00:00:00 2001 From: ouyangxq <775153912@qq.com> Date: Sun, 4 Oct 2026 10:37:43 +0800 Subject: [PATCH] fix(test): wait for database timeout before judging fleet rollback --- scripts/fleet-trip-rollback-test-env.mjs | 13 +++++++++---- scripts/fleet-trip-rollback-test-env.test.mjs | 8 +++++++- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/scripts/fleet-trip-rollback-test-env.mjs b/scripts/fleet-trip-rollback-test-env.mjs index f5328d947..ec436a09b 100644 --- a/scripts/fleet-trip-rollback-test-env.mjs +++ b/scripts/fleet-trip-rollback-test-env.mjs @@ -62,6 +62,11 @@ export function assertMidTransactionObservation(rows) { demand(dispatch.length===1&&dispatch[0].status==='DEPARTED'&&dispatch[0].shipped===1&&outbound.length===1&&outbound[0].status==='OUTBOUNDED'&&replay.length===1&&replay[0].count===1,'FIRST_CUSTOMER_UNCOMMITTED_WRITE_NOT_PROVED') return {leaseConnectionId:lease[0].connectionId,requestingConnectionId:wait[0].requester,lockId:lease[0].lockId,dirtyObservationOnly:true} } +export function requestRollbackDeparture(context,path,body,token) { + // The normal JSON request stops at 30 seconds. This deliberate 50-second + // database timeout needs the existing 60-second, single-attempt request path. + return context.requestWithBody('POST',path,body,token) +} async function remote(env,scope,mode) { const script=buildRollbackRemoteScript(scope,mode,env.FBA_CORE_JENKINS_AGENT) const response=await fetch(new URL('scriptText',env.JENKINS_URL.replace(/\/?$/,'/')),{method:'POST',redirect:'error',signal:AbortSignal.timeout(mode==='LEASE'?95000:45000),headers:{Authorization:'Basic '+Buffer.from(env.JENKINS_USER+':'+env.JENKINS_API_TOKEN).toString('base64'),'Content-Type':'application/x-www-form-urlencoded'},body:new URLSearchParams({script})}) @@ -90,16 +95,13 @@ export function createDepartureRollbackVerifier(env,config,runtime) { if(rows.filter(x=>x.collection==='lease').length===1)break demand(i!==11,'LIVE_LEASE_NOT_OBSERVED');await pause(250) } - pending=context.request('POST',departPath,departBody,fixture.pdaToken).then(value=>({value}),error=>({error:error.message})) + pending=requestRollbackDeparture(context,departPath,departBody,fixture.pdaToken).then(value=>({value}),error=>({error:error.message})) for(let i=0;i<20;i++) { const rows=(await remote(env,scope,'OBSERVE')).rows;report.samples.push({phase:'MID_TRANSACTION',rows});save() if(rows.some(x=>x.collection==='wait')) {report.observation=assertMidTransactionObservation(rows);save();break} demand(i!==19,'MID_TRANSACTION_NOT_OBSERVED');await pause(250) } report.response=await pending;save() - demand(!report.response.error,'DEPARTURE_OUTCOME_UNKNOWN_RETAIN_ORIGINAL_COMMAND') - const response=report.response.value - demand(response.http===500||(response.http===200&&String(response.body?.code)==='S0500'),'EXPECTED_DATABASE_FAILURE_RESPONSE_REQUIRED') report.lease=await leasePromise;save() demand(!report.lease.error&&report.lease.value.rows.at(-1)?.released===true,'LEASE_RELEASE_NOT_PROVED') const released=report.released=(await remote(env,scope,'RELEASED')).rows @@ -107,6 +109,9 @@ export function createDepartureRollbackVerifier(env,config,runtime) { const restored=await snapshot('ROLLBACK_AFTER_FAULT') report.consistentSnapshot={beforeSha256:createHash('sha256').update(canonical(ready)).digest('hex'),afterSha256:createHash('sha256').update(canonical(restored)).digest('hex')} demand(report.consistentSnapshot.beforeSha256===report.consistentSnapshot.afterSha256,'FULL_TWO_CUSTOMER_ROLLBACK_SNAPSHOT_CHANGED') + demand(!report.response.error&&report.response.value?.http!==0,'DEPARTURE_OUTCOME_UNKNOWN_RETAIN_ORIGINAL_COMMAND') + const response=report.response.value + demand(response.http===500||(response.http===200&&String(response.body?.code)==='S0500'),'EXPECTED_DATABASE_FAILURE_RESPONSE_REQUIRED') // The caller also checks restoration before sending the same original command. report.verified=true;return report }catch(error){report.error=error.message;throw error} diff --git a/scripts/fleet-trip-rollback-test-env.test.mjs b/scripts/fleet-trip-rollback-test-env.test.mjs index fbfb203be..bcd3bb62a 100644 --- a/scripts/fleet-trip-rollback-test-env.test.mjs +++ b/scripts/fleet-trip-rollback-test-env.test.mjs @@ -1,9 +1,15 @@ import test from 'node:test' import assert from 'node:assert/strict' -import { validateRollbackScope, rollbackSql, buildRollbackRemoteScript, assertMidTransactionObservation, runTripRollback } from './fleet-trip-rollback-test-env.mjs' +import { validateRollbackScope, rollbackSql, buildRollbackRemoteScript, assertMidTransactionObservation, runTripRollback, requestRollbackDeparture } from './fleet-trip-rollback-test-env.mjs' const scope={marker:'OWN_RUN',tripNo:'TR-OWN',firstCustomerId:'101',secondCustomerId:'102',firstDispatchId:'201',secondDispatchId:'202',firstOutboundId:'301',secondPalletId:'401',firstDispatchNo:'DS-OWN',expectedImage:'sha256:'+'a'.repeat(64)} const rows=()=>[{collection:'lease',connectionId:11,lockId:'99'},{collection:'wait',requester:12,blocker:11,lockId:'99'}, {collection:'firstDispatch',id:'201',status:'DEPARTED',shipped:1},{collection:'firstOutbound',id:'301',status:'OUTBOUNDED'},{collection:'firstReplay',count:1}] +test('fault request allows database timeout and never retries an unknown write',async()=>{ + const body={recordVersion:'11',idempotencyKey:'ORIGINAL'},calls=[] + const context={request:()=>{throw Error('30_SECOND_CHANNEL')},requestWithBody:async(...args)=>{calls.push(args);return {http:0,body:null}}} + assert.equal((await requestRollbackDeparture(context,'/owned/depart',body,'owned-token')).http,0) + assert.deepEqual(calls,[['POST','/owned/depart',body,'owned-token']]) +}) test('plan and import are network-free; execution rejects missing TEST profile before any network',async()=>{ const plan=await runTripRollback({execute:false},{env:{}}) assert.equal(plan.businessSqlWrites,false);assert.equal(plan.deployment,false)