From df0f05c74dc2ef3ed128c5463058a1006190508c Mon Sep 17 00:00:00 2001 From: ouyangxq <775153912@qq.com> Date: Tue, 11 Aug 2026 14:07:16 +0800 Subject: [PATCH] test(admin): keep phase3 producer fixtures aligned --- .../admin-test-env-modules/phase3-customs.mjs | 22 ++++++-- .../phase3-loading-finalize.mjs | 21 ++++++-- .../phase3-producer-chain-contract.test.mjs | 52 +++++++++++++++++++ 3 files changed, 88 insertions(+), 7 deletions(-) create mode 100644 scripts/admin-test-env-modules/phase3-producer-chain-contract.test.mjs diff --git a/scripts/admin-test-env-modules/phase3-customs.mjs b/scripts/admin-test-env-modules/phase3-customs.mjs index 17d75bbca..daf923696 100644 --- a/scripts/admin-test-env-modules/phase3-customs.mjs +++ b/scripts/admin-test-env-modules/phase3-customs.mjs @@ -67,11 +67,25 @@ async function detail(context, caseId) { return { response, data } } +export function selectCustomsFlow(createdResources) { + const fixtures = [ + createdResources?.orchestrationFixtures?.inboundMeasurement, + createdResources?.orchestrationFixtures?.phase3Container, + ].filter(Boolean) + const containers = [ + createdResources?.phase3Loading, + createdResources?.phase3ContainerLoading, + ].filter(Boolean) + for (const container of containers) { + const fixture = fixtures.find((candidate) => id(candidate?.tenantId) === id(container?.tenantId) + && id(candidate?.inboundId) === id(container?.inboundId)) + if (fixture) return { fixture, container } + } + return { fixture: null, container: null } +} + export async function run(context) { - const fixture = context.createdResources?.orchestrationFixtures?.inboundMeasurement - || context.createdResources?.orchestrationFixtures?.phase3Container - const container = context.createdResources?.phase3Loading - || context.createdResources?.phase3ContainerLoading + const { fixture, container } = selectCustomsFlow(context.createdResources) const tenantId = id(fixture?.tenantId) const parentOrderId = id(fixture?.orderId) const inboundId = id(fixture?.inboundId) diff --git a/scripts/admin-test-env-modules/phase3-loading-finalize.mjs b/scripts/admin-test-env-modules/phase3-loading-finalize.mjs index 329716879..f6c3a61c2 100644 --- a/scripts/admin-test-env-modules/phase3-loading-finalize.mjs +++ b/scripts/admin-test-env-modules/phase3-loading-finalize.mjs @@ -2,6 +2,23 @@ const MODULE = 'phase3-loading-finalize' const OPTIONS = { scenario: 'phase3-loading-hard-gate-finalize', deploymentBlocker: true } function id(value) { return value == null ? '' : String(value) } +function codeOf(response) { return response?.body?.code == null ? 'missing' : String(response.body.code) } + +export function buildFinalizeResource(fixture, finalData, completeResponse) { + const observedStatus = finalData?.summary?.processStatus || fixture?.inboundStatus || 'UNKNOWN' + const observedVersion = Number(finalData?.summary?.version) + const loaded = observedStatus === 'LOADED' + const version = Number.isInteger(observedVersion) ? observedVersion : fixture?.inboundVersion + return { + ...fixture, + ...(Number.isInteger(version) ? { inboundVersion: version } : {}), + inboundStatus: observedStatus, + finalStatus: observedStatus, + finalizeSucceeded: loaded, + notCleanedReason: loaded ? null + : `装柜完成未成功,保留真实状态 ${observedStatus};completeCode=${codeOf(completeResponse)}`, + } +} async function request(context, method, path, body, headers = {}) { try { @@ -69,7 +86,5 @@ export async function run(context) { && item.operatorName && item.fromStatus === 'LOADING' && item.toStatus === 'LOADED'), `status=${finalData?.summary?.processStatus || 'missing'}, version=${finalData?.summary?.version ?? 'missing'}, histories=${finalData?.statusHistory?.length ?? 'n/a'}`, OPTIONS) - context.createdResources.phase3Loading = { - ...fixture, inboundVersion: Number(finalData?.summary?.version), inboundStatus: 'LOADED', finalStatus: 'LOADED', - } + context.createdResources.phase3Loading = buildFinalizeResource(fixture, finalData, completeResponse) } diff --git a/scripts/admin-test-env-modules/phase3-producer-chain-contract.test.mjs b/scripts/admin-test-env-modules/phase3-producer-chain-contract.test.mjs new file mode 100644 index 000000000..e2772146b --- /dev/null +++ b/scripts/admin-test-env-modules/phase3-producer-chain-contract.test.mjs @@ -0,0 +1,52 @@ +import assert from 'node:assert/strict' +import test from 'node:test' + +import { selectCustomsFlow } from './phase3-customs.mjs' +import { buildFinalizeResource } from './phase3-loading-finalize.mjs' + +test('pairs customs source fixture with the matching loading container', () => { + const resources = { + orchestrationFixtures: { + inboundMeasurement: { tenantId: 10001, orderId: 101, inboundId: 201 }, + phase3Container: { tenantId: 10001, orderId: 102, inboundId: 202 }, + }, + phase3Loading: { tenantId: 10001, inboundId: 201, containerBookingId: 301 }, + phase3ContainerLoading: { tenantId: 10001, inboundId: 202, containerBookingId: 302 }, + } + const selected = selectCustomsFlow(resources) + assert.equal(selected.fixture.orderId, 101) + assert.equal(selected.container.containerBookingId, 301) +}) + +test('falls back to the matching container fixture without crossing source identities', () => { + const resources = { + orchestrationFixtures: { phase3Container: { tenantId: 10001, orderId: 102, inboundId: 202 } }, + phase3Loading: { tenantId: 10001, inboundId: 201, containerBookingId: 301 }, + phase3ContainerLoading: { tenantId: 10001, inboundId: 202, containerBookingId: 302 }, + } + const selected = selectCustomsFlow(resources) + assert.equal(selected.fixture.orderId, 102) + assert.equal(selected.container.containerBookingId, 302) +}) + +test('records LOADING and a cleanup reason when finalize returns S0500', () => { + const result = buildFinalizeResource( + { inboundStatus: 'LOADING', inboundVersion: 5, finalStatus: 'COMPLETED' }, + { summary: { processStatus: 'LOADING', version: 5 } }, + { http: 200, body: { code: 'S0500' } }, + ) + assert.equal(result.finalStatus, 'LOADING') + assert.equal(result.finalizeSucceeded, false) + assert.match(result.notCleanedReason, /S0500/) +}) + +test('records LOADED and clears the reason only after a real LOADED reread', () => { + const result = buildFinalizeResource( + { inboundStatus: 'LOADING', inboundVersion: 5, finalStatus: 'COMPLETED' }, + { summary: { processStatus: 'LOADED', version: 6 } }, + { http: 200, body: { code: '200' } }, + ) + assert.equal(result.finalStatus, 'LOADED') + assert.equal(result.finalizeSucceeded, true) + assert.equal(result.notCleanedReason, null) +})