diff --git a/scripts/admin-test-env-modules/README.md b/scripts/admin-test-env-modules/README.md index f46f6459f..57c81e7fb 100644 --- a/scripts/admin-test-env-modules/README.md +++ b/scripts/admin-test-env-modules/README.md @@ -120,6 +120,14 @@ node scripts/admin-test-env-acceptance.mjs --gateway --ove node scripts/admin-test-env-acceptance.mjs --modules merchant-qualification-fixture,orders,inbound-measurement,phase3-container-loading,phase3-loading,phase3-container-overview,phase3-pda-convergence,phase3-evidence-gate,phase3-customs,phase3-loading-finalize ``` +只验证真实集装箱号晚补门禁时,使用正式可选参数 `LATE_CONTAINER_GATE_ONLY=1` 并仍从总入口执行 `phase3-loading`。该模式使用本轮 marker 独立造数,固定验证无号 push、订舱关联、整份预装确认、PDA claim/batch、无号 complete `A0409`、LOADING 阶段首次补号和补号后 complete 受理;随后允许后台命令停在 `CABINET_EVIDENCE/A0409`,以证明柜号门禁已经消失且下一门禁来自未上传的五类柜级证据。该模式不继续完整报关或 LOADED 链,也不得与完整链同时解释为同一终态验收。 + +```powershell +$env:LATE_CONTAINER_GATE_ONLY='1' +node --env-file="$env:USERPROFILE\.codex\chongchong-global-test-acceptance.env" scripts/admin-test-env-acceptance.mjs --modules phase3-loading +Remove-Item Env:LATE_CONTAINER_GATE_ONLY +``` + 该命令会写测试环境,必须另行获得当前对话授权。离线入口合同为 `../full-regression-entry-contract.test.mjs`,生产者与状态合同为 `phase3-producer-chain-contract.test.mjs`。 ## 消息中心模块 diff --git a/scripts/admin-test-env-modules/phase3-loading.mjs b/scripts/admin-test-env-modules/phase3-loading.mjs index 7e3535425..2d4bec66f 100644 --- a/scripts/admin-test-env-modules/phase3-loading.mjs +++ b/scripts/admin-test-env-modules/phase3-loading.mjs @@ -925,17 +925,20 @@ export async function run(context) { terminalResponse = await context.request('GET', taskDetailPath) if (terminalResponse.http === 200 && codeOf(terminalResponse) === '200') terminalTask = terminalResponse.body?.data } - context.assert(MODULE, '补号后的下一完成门禁返回明确终态而非柜号冲突', terminalResponse, - terminalTask?.workflowStatus !== 'PROCESSING' - && !String(terminalTask?.completionCommand?.failureMessage || '').includes('集装箱号'), + const nextStage = terminalTask?.completionCommand?.failureStage || null + const nextCode = terminalTask?.completionCommand?.failureCode || null + const nextMessage = String(terminalTask?.completionCommand?.failureMessage || '') + context.assert(MODULE, '补号后已越过柜号门禁并允许进入柜级证据门禁', terminalResponse, + !nextMessage.includes('集装箱号') + && (terminalTask?.workflowStatus !== 'PROCESSING' + || (nextStage === 'CABINET_EVIDENCE' && nextCode === 'A0409')), `workflow=${terminalTask?.workflowStatus || 'missing'}, stage=${terminalTask?.completionCommand?.failureStage || 'none'}, code=${terminalTask?.completionCommand?.failureCode || 'none'}, message=${terminalTask?.completionCommand?.failureMessage || 'none'}`, OPTIONS) context.createdResources.phase3LateContainerGate = { marker: context.marker, tenantId, inboundId, containerBookingId, cabinetNo: simulation.cabinetNo, oceanBookingId: bookingId, loadingPlanId, taskId: canonicalTaskId, containerNo, missingComplete: responseEvidence(missingComplete), fill: responseEvidence(transport), acceptedComplete: responseEvidence(acceptedComplete), finalWorkflow: terminalTask?.workflowStatus || null, - nextFailureStage: terminalTask?.completionCommand?.failureStage || null, - nextFailureCode: terminalTask?.completionCommand?.failureCode || null, + nextFailureStage: nextStage, nextFailureCode: nextCode, } return } 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 index 8573d0854..de5bff40e 100644 --- a/scripts/admin-test-env-modules/phase3-producer-chain-contract.test.mjs +++ b/scripts/admin-test-env-modules/phase3-producer-chain-contract.test.mjs @@ -123,6 +123,25 @@ test('bridge loading confirms preload before creating and claiming the PDA task' assert.match(value, /bookingConfirmed\?\.oceanBooking\?\.status === 'RELEASED'/) }) +test('late container gate mode proves the final gate without requiring evidence completion', () => { + const value = source('./phase3-loading.mjs') + assert.match(value, /LATE_CONTAINER_GATE_ONLY = process\.env\.LATE_CONTAINER_GATE_ONLY === '1'/) + assertOrder(value, [ + '无号模拟柜原地关联三期 RELEASED 订舱需求', + '计划级原子确认整份预装并触发唯一 PDA 任务', + '领取柜次 PDA 任务', + '原子提交柜次 PDA 整批拣货', + '无号拣货完成后 PDA complete 精确返回 A0409', + 'LOADING 阶段通过预装运输信息首次补录真实柜号', + '补号后 complete 已越过柜号门禁并受理异步命令', + '补号后已越过柜号门禁并允许进入柜级证据门禁', + ]) + assert.match(value, /String\(missingComplete\.body\?\.message \|\| ''\)\.includes\('真实集装箱号'\)/) + assert.match(value, /nextStage === 'CABINET_EVIDENCE' && nextCode === 'A0409'/) + assert.match(value, /!nextMessage\.includes\('集装箱号'\)/) + assert.match(value, /context\.createdResources\.phase3LateContainerGate/) +}) + test('push replay and both conflict modes preserve the complete pre-PDA snapshot', () => { for (const file of ['./phase3-loading.mjs', './phase3-container-loading.mjs']) { const value = source(file) @@ -220,7 +239,8 @@ test('PDA inventory snapshot gates batch and records the only safe mutation gap' ]) assert.match(loading, /inventorySnapshotToken: refreshedInventory\.snapshotToken/) assert.match(loading, /warehouseId, loadingPlanId, containerBookingId,/) - assert.match(loading, /inventory\?\.currentContainerNo === containerNo/) + assert.match(loading, + /LATE_CONTAINER_GATE_ONLY \? inventory\?\.currentContainerNo == null : inventory\?\.currentContainerNo === containerNo/) const batchPayload = loading.slice(loading.indexOf('const batchPayload ='), loading.indexOf('const staleBatch =')) assert.doesNotMatch(batchPayload, /containerNo[,\s]/) assert.doesNotMatch(loading, /\/loading\/picking\/(?:start|complete)/)