完成单机TEST统一入口与可靠投递回归收尾
This commit is contained in:
+49
-104
@@ -1,110 +1,55 @@
|
||||
import assert from 'node:assert/strict'
|
||||
import { readFileSync } from 'node:fs'
|
||||
import test from 'node:test'
|
||||
|
||||
import {
|
||||
EVENT_BRIDGE_RECOVERY_FIXTURE,
|
||||
INTERNAL_TOKEN_COHORT_SERVICES,
|
||||
recoveredEventBridgeFacts,
|
||||
TEST_EVENT_BRIDGE_URLS,
|
||||
validInternalTokenCohort,
|
||||
validTestEventBridgeEnvironment,
|
||||
} from './event-bridge-pending-recovery.mjs'
|
||||
|
||||
const read = (relativeUrl) => readFileSync(new URL(relativeUrl, import.meta.url), 'utf8')
|
||||
const cnCompose = read('../../deploy/docker-compose.yml')
|
||||
const overseasCompose = read('../../deploy/docker-compose.overseas.yml')
|
||||
const deployBuilt = read('../../deploy/scripts/deploy-built-service.sh')
|
||||
const deployServices = read('../../deploy/scripts/deploy-services.sh')
|
||||
const crossRegionHttp = read('phase4-cross-region-events.mjs')
|
||||
|
||||
test('test bridge URLs use stable Compose DNS and internal port 9312 only', () => {
|
||||
assert.deepEqual(TEST_EVENT_BRIDGE_URLS, {
|
||||
LASTLEG_EVENT_BRIDGE_URL: 'http://event-bridge-overseas:9312',
|
||||
FIRSTLEG_EVENT_BRIDGE_URL: 'http://event-bridge-cn:9312',
|
||||
})
|
||||
assert.equal(validTestEventBridgeEnvironment(TEST_EVENT_BRIDGE_URLS), true)
|
||||
assert.equal(validTestEventBridgeEnvironment({
|
||||
...TEST_EVENT_BRIDGE_URLS, LASTLEG_EVENT_BRIDGE_URL: 'http://172.31.198.8:9313',
|
||||
}), false)
|
||||
import { TEST_DIRECT_DELIVERY_URLS, INTERNAL_TOKEN_COHORT_SERVICES, validDirectDeliveryEnvironment,
|
||||
validInternalTokenCohort, validDirectDeliveryEvidence } from './event-bridge-pending-recovery.mjs'
|
||||
const read=p=>readFileSync(new URL(p,import.meta.url),'utf8')
|
||||
const compose=read('../../deploy/docker-compose.yml')
|
||||
test('all three direct targets use Compose DNS and current internal ports',()=>{
|
||||
assert.equal(validDirectDeliveryEnvironment(TEST_DIRECT_DELIVERY_URLS),true)
|
||||
for(const [key,url] of Object.entries(TEST_DIRECT_DELIVERY_URLS)){
|
||||
assert.ok(compose.includes(`${key}: ${url}`))
|
||||
assert.equal(validDirectDeliveryEnvironment({...TEST_DIRECT_DELIVERY_URLS,[key]:'http://localhost:9312'}),false)
|
||||
}
|
||||
assert.doesNotMatch(compose,/EVENT_BRIDGE_URL|event-bridge-service:/)
|
||||
})
|
||||
test('the three producers and consumers share internal token revision without exposing secrets',()=>{
|
||||
assert.deepEqual(INTERNAL_TOKEN_COHORT_SERVICES,['firstleg','lastleg-service','finance'])
|
||||
assert.match(compose,/INTERNAL_SERVICE_TOKEN: \$\{INTERNAL_SERVICE_TOKEN\}/)
|
||||
assert.equal(validInternalTokenCohort({firstleg:'r1','lastleg-service':'r1',finance:'r1'}),true)
|
||||
assert.equal(validInternalTokenCohort({firstleg:'r1','lastleg-service':'r1',finance:'r2'}),false)
|
||||
assert.equal(validInternalTokenCohort({}),false)
|
||||
})
|
||||
const attempt=(extra={})=>({eventId:'event-fixture',payloadHash:'hash-fixture',at:'2026-09-29T00:00:00Z',requestId:'request-fixture',traceId:'trace-fixture',...extra})
|
||||
const facts=(attempts,extra={})=>({marker:'run-fixture',eventId:'event-fixture',payloadHash:'hash-fixture',maxAttempts:12,sqlMutationCount:0,status:'SENT',inboxEventCount:1,businessEffectCount:1,attempts,...extra})
|
||||
test('DNS, invalid token and timeout recovery require original event identity and actual duplicate ACK',()=>{
|
||||
for(const failure of ['DNS_UNAVAILABLE','TOKEN_REJECTED','TIMEOUT']){
|
||||
const evidence=facts([attempt({failure}),attempt({outcome:'APPLIED',ackLost:true}),attempt({outcome:'DUPLICATE'})],{authorizedDeadRetry:failure==='TOKEN_REJECTED'})
|
||||
assert.equal(validDirectDeliveryEvidence(evidence),true)
|
||||
assert.equal(validDirectDeliveryEvidence({...evidence,businessEffectCount:2}),false)
|
||||
assert.equal(validDirectDeliveryEvidence({...evidence,sqlMutationCount:1}),false)
|
||||
assert.equal(validDirectDeliveryEvidence({...evidence,attempts:[...evidence.attempts,attempt({eventId:'new-id',outcome:'DUPLICATE'})]}),false)
|
||||
}
|
||||
assert.equal(validDirectDeliveryEvidence(facts([attempt({outcome:'APPLIED',ackLost:true}),attempt({outcome:'APPLIED',ackLost:true})])),false)
|
||||
})
|
||||
test('retry budget exhaustion stays DEAD and never schedules an automatic retry',()=>{
|
||||
const evidence=facts(Array.from({length:12},()=>attempt({failure:'TIMEOUT'})),{status:'DEAD',automaticRetryScheduled:false})
|
||||
assert.equal(validDirectDeliveryEvidence(evidence),true)
|
||||
assert.equal(validDirectDeliveryEvidence({...evidence,automaticRetryScheduled:true}),false)
|
||||
assert.equal(validDirectDeliveryEvidence({...evidence,attempts:[...evidence.attempts,attempt({failure:'TIMEOUT'})]}),false)
|
||||
})
|
||||
test('GET stability does not claim real event replay coverage',()=>{
|
||||
const source=read('./phase4-cross-region-events.mjs')
|
||||
assert.match(source,/非事件重放证据/)
|
||||
assert.match(source,/missing-direct-delivery-replay-evidence/)
|
||||
assert.match(source,/event-outbox-operations\.mjs/)
|
||||
assert.doesNotMatch(source,/经Bridge|不能访问.*Audience/)
|
||||
})
|
||||
|
||||
test('Compose wires both producers to the stable service aliases used by test env', () => {
|
||||
assert.match(cnCompose, /LASTLEG_EVENT_BRIDGE_URL:\s*\$\{LASTLEG_EVENT_BRIDGE_URL\}/)
|
||||
assert.match(overseasCompose,
|
||||
/FIRSTLEG_EVENT_BRIDGE_URL:\s*\$\{CN_EVENT_BRIDGE_TARGET_URL:-http:\/\/event-bridge-cn:9312\}/)
|
||||
assert.match(cnCompose, /aliases:\s*\[event-bridge-cn\]/)
|
||||
assert.match(cnCompose,
|
||||
/FINANCE_FEE_EVENT_CONSUMER_URL:\s*\$\{FINANCE_FEE_EVENT_CONSUMER_URL:-http:\/\/finance:9203\/internal\/finance\/v1\/business-fee-source-events\}/)
|
||||
assert.match(overseasCompose, /aliases:\s*\[event-bridge-overseas\]/)
|
||||
for (const source of [cnCompose, overseasCompose]) {
|
||||
assert.doesNotMatch(source, /(?:LASTLEG|FIRSTLEG|CN)_EVENT_BRIDGE_URL:[^\n]*(?:\d{1,3}\.){3}\d{1,3}/)
|
||||
assert.doesNotMatch(source, /(?:LASTLEG|FIRSTLEG|CN)_EVENT_BRIDGE_URL:[^\n]*:9313/)
|
||||
}
|
||||
})
|
||||
|
||||
test('all four recovery services inherit INTERNAL_SERVICE_TOKEN and deploy from one env file', () => {
|
||||
assert.deepEqual(INTERNAL_TOKEN_COHORT_SERVICES, [
|
||||
'firstleg', 'event-bridge-cn', 'event-bridge-overseas', 'lastleg-service',
|
||||
])
|
||||
assert.match(cnCompose, /x-common:\s*&common[\s\S]*INTERNAL_SERVICE_TOKEN:\s*\$\{INTERNAL_SERVICE_TOKEN\}/)
|
||||
assert.match(overseasCompose,
|
||||
/x-overseas-common:\s*&overseas-common[\s\S]*INTERNAL_SERVICE_TOKEN:\s*\$\{INTERNAL_SERVICE_TOKEN\}/)
|
||||
assert.match(cnCompose, /firstleg:[\s\S]*<<:\s*\*common/)
|
||||
assert.match(cnCompose, /event-bridge-service:[\s\S]*<<:\s*\*common/)
|
||||
assert.match(overseasCompose, /lastleg-service:[\s\S]*<<:\s*\*overseas-common/)
|
||||
assert.match(overseasCompose, /event-bridge-service:[\s\S]*<<:\s*\*overseas-common/)
|
||||
for (const source of [deployBuilt, deployServices]) {
|
||||
assert.match(source, /--env-file\s+"\$ENV_FILE"/)
|
||||
}
|
||||
assert.equal(validInternalTokenCohort({
|
||||
firstleg: 'revision-20260827',
|
||||
'event-bridge-cn': 'revision-20260827',
|
||||
'event-bridge-overseas': 'revision-20260827',
|
||||
'lastleg-service': 'revision-20260827',
|
||||
}), true)
|
||||
assert.equal(validInternalTokenCohort({
|
||||
firstleg: 'revision-20260827',
|
||||
'event-bridge-cn': 'revision-20260827',
|
||||
'event-bridge-overseas': 'stale-revision',
|
||||
'lastleg-service': 'revision-20260827',
|
||||
}), false)
|
||||
})
|
||||
|
||||
test('incident fixture records DNS recovery followed by retry81 token rejection', () => {
|
||||
assert.deepEqual(EVENT_BRIDGE_RECOVERY_FIXTURE, {
|
||||
customerCode: 'C10000000',
|
||||
eventId: 'b569c7f6-3493-4a0f-a944-dd42698f183b',
|
||||
initialOutboxStatus: 'PENDING',
|
||||
initialRetryCount: 62,
|
||||
failedTarget: '172.31.198.8:9313',
|
||||
currentOutboxStatus: 'PENDING',
|
||||
currentRetryCount: 81,
|
||||
currentFailureCode: 'A0401',
|
||||
currentFailureMessage: '内部服务令牌无效',
|
||||
currentRequestId: '200edca3-428e-446d-977a-ca98a84dd704',
|
||||
currentTraceId: 'e8e6d620c1cff40caa765e6968f89024',
|
||||
dockerDnsProven: true,
|
||||
})
|
||||
})
|
||||
|
||||
test('post-rebuild convergence uses the original pending event with no replay or SQL repair', () => {
|
||||
assert.equal(recoveredEventBridgeFacts({
|
||||
firstlegOutboxStatus: 'SENT', overseasBridgeEventCount: 1,
|
||||
lastlegInboxEventCount: 1, lastlegOrderCount: 1, cnBridgeDuplicateCount: 0,
|
||||
manualReplayCount: 0, sqlMutationCount: 0,
|
||||
}), true)
|
||||
assert.equal(recoveredEventBridgeFacts({
|
||||
firstlegOutboxStatus: 'SENT', overseasBridgeEventCount: 1,
|
||||
lastlegInboxEventCount: 1, lastlegOrderCount: 2, cnBridgeDuplicateCount: 0,
|
||||
manualReplayCount: 0, sqlMutationCount: 0,
|
||||
}), false)
|
||||
assert.equal(recoveredEventBridgeFacts({
|
||||
firstlegOutboxStatus: 'SENT', overseasBridgeEventCount: 1,
|
||||
lastlegInboxEventCount: 1, lastlegOrderCount: 1, cnBridgeDuplicateCount: 0,
|
||||
manualReplayCount: 1, sqlMutationCount: 0,
|
||||
}), false)
|
||||
assert.match(crossRegionHttp, /const delivered = await poll/)
|
||||
assert.match(crossRegionHttp, /deliveredRows\.length > 0/)
|
||||
assert.match(crossRegionHttp, /sameIds\(delivered\.response\.body\?\.data, deliveredAgain\.body\?\.data\)/)
|
||||
test('permanent token failure stops immediately and recovery needs authorized DEAD retry', () => {
|
||||
const stopped=facts([attempt({failure:'TOKEN_REJECTED'})],{status:'DEAD',automaticRetryScheduled:false})
|
||||
assert.equal(validDirectDeliveryEvidence(stopped),true)
|
||||
const recovered=facts([attempt({failure:'TOKEN_REJECTED'}),attempt({outcome:'APPLIED',ackLost:true}),attempt({outcome:'DUPLICATE'})])
|
||||
assert.equal(validDirectDeliveryEvidence(recovered),false)
|
||||
assert.equal(validDirectDeliveryEvidence({...recovered,authorizedDeadRetry:true}),true)
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user