Files
baiyue-firstleg/scripts/admin-test-env-modules/phase4-cross-region-events-contract.test.mjs
T

137 lines
6.9 KiB
JavaScript

import assert from 'node:assert/strict'
import { readFileSync } from 'node:fs'
import test from 'node:test'
import { normalizeModuleOrder } from '../admin-test-env-acceptance.mjs'
import { run } from './phase4-cross-region-events.mjs'
const source = readFileSync(new URL('./phase4-cross-region-events.mjs', import.meta.url), 'utf8')
const system = readFileSync(new URL('./system.mjs', import.meta.url), 'utf8')
function response(path, data, http = 200, code = '200') {
return { method: 'GET', path, http, body: { code, data }, requestId: 'request-1', traceId: 'trace-1' }
}
function context(resources = {}) {
const steps = []
const gaps = []
const calls = []
return {
marker: 'MARKER-1', token: 'domestic-token', overseasToken: 'overseas-token',
createdResources: resources, steps, gaps, calls,
addCoverageGap(module, scenario, name, reason, category, metadata) {
gaps.push({ module, scenario, name, reason, category, ...metadata })
},
addStep(module, name, value, passed, evidence, options) {
steps.push({ module, name, passed: Boolean(passed), evidence, options })
return Boolean(passed)
},
assert(module, name, value, passed, evidence, options) {
return this.addStep(module, name, value, passed, evidence, options)
},
async requestOverseas(method, path, body, headers, token) {
calls.push({ region: 'OVERSEAS', method, path, token: token || this.overseasToken })
if (token === this.token) return response(path, null, 403, 'A0403')
if (path.includes('/last-leg/orders/9001')) {
return response(path, { order: { id: '9001', containerNo: 'MSCU1234567' } })
}
return response(path, { records: [{ id: '7001', containerNo: 'MSCU1234567' }] })
},
async request(method, path, body, token) {
calls.push({ region: 'CN', method, path, token: token || this.token })
if (token === this.overseasToken) return response(path, null, 403, 'A0403')
if (path.includes('/overview')) return response(path, { container: {
id: '3001', containerNo: 'MSCU1234567', unloadingCompletedAt: '2026-08-24T10:00:00Z',
unloadingPhotoUploadedAt: '2026-08-24T09:00:00Z', estimatedEmptyReportedAt: '2026-08-26T09:00:00Z',
actualEmptyReportedAt: null, emptyReportSourceVersion: 7,
} })
return response(path, { records: [{ id: '3001', containerNo: 'MSCU1234567' }] })
},
}
}
test('entry orders marker-owned prerequisites before cross-region acceptance', () => {
const modules = normalizeModuleOrder(['phase4-cross-region-events'])
const cross = modules.indexOf('phase4-cross-region-events')
for (const prerequisite of ['system', 'phase3-loading-finalize', 'phase4-overseas-pda']) {
assert.ok(modules.indexOf(prerequisite) >= 0 && modules.indexOf(prerequisite) < cross, prerequisite)
}
})
test('cross-region normalization keeps existing producers ahead of every phase-three consumer', () => {
const requested = [
'merchant-qualification-fixture', 'orders', 'inbound-measurement', 'phase3-container-loading',
'phase3-loading-model-bridge', 'phase3-loading', 'ocean-bookings', 'phase3-loading-finalize',
'phase3-customs', 'phase3-outbound-dispatch', 'phase3-outbound-aggregate',
'phase4-overseas-pda', 'phase4-cross-region-events',
]
const modules = normalizeModuleOrder(requested)
for (const [producer, consumer] of [
['merchant-qualification-fixture', 'orders'], ['orders', 'inbound-measurement'],
['inbound-measurement', 'phase3-container-loading'], ['phase3-container-loading', 'phase3-loading'],
['phase3-loading', 'phase3-loading-model-bridge'], ['phase3-loading', 'ocean-bookings'],
['phase3-loading-finalize', 'phase3-customs'], ['phase3-customs', 'phase3-outbound-dispatch'],
['phase3-outbound-dispatch', 'phase3-outbound-aggregate'],
['phase4-overseas-pda', 'phase4-cross-region-events'],
]) assert.ok(modules.indexOf(producer) < modules.indexOf(consumer), `${producer} -> ${consumer}`)
assert.deepEqual(normalizeModuleOrder(modules), modules)
})
test('phase-four producer variants restore the primary orchestration fixture after each matrix branch', async () => {
const source = await import('node:fs/promises').then((fs) => fs.readFile(
new URL('../admin-test-env-acceptance.mjs', import.meta.url), 'utf8'))
const start = source.indexOf('const createdKeys = [', source.indexOf('async runPhase4ProducerVariant'))
const end = source.indexOf(']', start)
const createdKeys = source.slice(start, end)
for (const key of [
'phase3Loading', 'phase3LoadingCompletedProducer', 'orchestrationFixtures', 'oceanBooking',
'phase3CustomsSupplier', 'phase3Customs', 'serviceCenterFlow', 'portSchedule',
'phase3OutboundDispatch', 'phase3CarrierScheduleQuery', 'phase4CarrierPickupReady',
]) assert.match(createdKeys, new RegExp(`'${key}'`))
})
test('cross-region producers do not repeat the destructive A0 snapshot mutation', async () => {
const source = await import('node:fs/promises').then((fs) => fs.readFile(
new URL('../admin-test-env-acceptance.mjs', import.meta.url), 'utf8'))
const variantStart = source.indexOf('async runPhase4ProducerVariant')
const fixtureCall = source.slice(variantStart, source.indexOf('const loading =', variantStart))
assert.match(fixtureCall, /preparePhase3LoadingFixture\('', false\)/)
})
test('contract covers dual audience, canonical 403, both event directions and diagnostics', () => {
for (const token of [
'context.token)', 'context.overseasToken)', 'cn-overseas-event-not-delivered',
'overseas-cn-event-not-delivered', 'requestId=', 'traceId=', 'pollRequests=',
'装柜完成事件重试保持建单幂等', '海外柜完成事件重试保持回写幂等',
]) assert.ok(source.includes(token), token)
assert.match(system, /缺少canonical海外订单权限必须403/)
assert.match(system, /restrictedOverseasToken/)
})
test('full observable cross-region flow performs requests and passes', async () => {
const ctx = context({
phase3Loading: { marker: 'MARKER-1', finalizeSucceeded: true, containerBookingId: '3001',
containerNo: 'MSCU1234567', loadingPlanId: '4001' },
phase4OverseasPda: { marker: 'MARKER-1', finalStatus: 'COMPLETED', customerId: '100', orderId: '9001' },
})
await run(ctx)
assert.ok(ctx.calls.length >= 8)
assert.equal(ctx.steps.every((step) => step.passed), true)
assert.equal(ctx.gaps.length, 0)
assert.ok(ctx.calls.some((call) => call.region === 'CN' && call.token === 'overseas-token'))
assert.ok(ctx.calls.some((call) => call.region === 'OVERSEAS' && call.token === 'domestic-token'))
})
test('missing marker fixture is a blocking coverage gap after real security request', async () => {
const ctx = context()
await assert.rejects(() => run(ctx), /blocking coverage gap/)
assert.ok(ctx.calls.length > 0)
assert.equal(ctx.gaps.length, 1)
assert.equal(ctx.gaps[0].deploymentBlocker, true)
assert.equal(ctx.steps.some((step) => !step.passed && step.options?.deploymentBlocker), true)
})