Files
baiyue-firstleg/scripts/fleet-trip-rollback-test-env.test.mjs
T

40 lines
2.9 KiB
JavaScript
Raw Normal View History

import test from 'node:test'
import assert from 'node:assert/strict'
import { validateRollbackScope, rollbackSql, buildRollbackRemoteScript, assertMidTransactionObservation, runTripRollback } 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('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)
await assert.rejects(runTripRollback({execute:true},{env:{CHONGCHONG_ENV:'prod'}}),/TEST/)
})
test('scope rejects reused customers, injection and unpinned runtime',()=>{
assert.equal(validateRollbackScope(scope),scope)
for(const changed of [{secondCustomerId:'101'},{secondDispatchId:'201'},{firstDispatchId:'1 OR 1=1'},{marker:"RUN';DELETE"},{expectedImage:'latest'}])assert.throws(()=>validateRollbackScope({...scope,...changed}))
})
test('lease contains only owned locking reads, fixed duration and rollback, without mutation or schema change',()=>{
const sql=rollbackSql(scope,'LEASE')
assert.match(sql,/dispatch_order_id=202/);assert.match(sql,/warehouse_pallet_id=401/);assert.match(sql,/FOR UPDATE;.*SLEEP\(65\)\);ROLLBACK;/)
assert.doesNotMatch(sql,/\b(?:INSERT|DELETE|ALTER|DROP|TRUNCATE|REPLACE|KILL|SET GLOBAL)\b/)
assert.equal((sql.match(/UPDATE/g)||[]).length,1)
assert.throws(()=>rollbackSql(scope,'CUSTOM_SQL'))
assert.throws(()=>buildRollbackRemoteScript(scope,'LEASE',"agent');evil()"))
})
test('dirty observation is separate from final consistent cleanup evidence',()=>{
assert.match(rollbackSql(scope,'OBSERVE'),/READ UNCOMMITTED/)
assert.match(rollbackSql(scope,'RELEASED'),/REPEATABLE READ/)
assert.match(rollbackSql(scope,'RELEASED'),/READ ONLY/)
assert.equal(assertMidTransactionObservation(rows()).dirtyObservationOnly,true)
})
for(const fault of ['foreign blocker','no wait','two waiters','not yet written','no outbound write','no replay write'])test(`refuses indirect mid-transaction evidence: ${fault}`,()=>{
const value=rows()
if(fault==='foreign blocker')value[1].blocker=77
if(fault==='no wait')value.splice(1,1)
if(fault==='two waiters')value.push({...value[1],requester:13})
if(fault==='not yet written')value[2].status='READY_TO_DEPART'
if(fault==='no outbound write')value[3].status='WAIT_OUTBOUND'
if(fault==='no replay write')value[4].count=0
assert.throws(()=>assertMidTransactionObservation(value))
})