Files
baiyue-firstleg/scripts/admin-test-env-modules/phase3-producer-chain-contract.test.mjs
T

628 lines
36 KiB
JavaScript

import assert from 'node:assert/strict'
import { readFileSync } from 'node:fs'
import test from 'node:test'
import { selectCustomsFlow } from './phase3-customs.mjs'
import { buildFinalizeResource } from './phase3-loading-finalize.mjs'
import { selectContainerLoadingFixture, selectPlatformChannelIsolationPair } from './phase3-container-loading.mjs'
import { diffPushPreloadStability, projectPushPreloadStability } from './push-preload-stability.mjs'
import { AcceptanceRun, normalizeModuleOrder, parseArguments } from '../admin-test-env-acceptance.mjs'
function source(name) {
return readFileSync(new URL(name, import.meta.url), 'utf8')
}
function assertOrder(value, snippets) {
let cursor = -1
for (const snippet of snippets) {
const next = value.indexOf(snippet)
assert.ok(next > cursor, `${snippet} must appear after the previous production step`)
cursor = next
}
}
test('pairs customs source fixture with the matching loading container', () => {
const resources = {
orders: { tenantId: 10001, orderId: 101, inboundId: 201 },
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, 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)
})
test('container loading follows push, claim, real pick, completed sequence', () => {
const value = source('./phase3-container-loading.mjs')
assert.match(value, /\['EDITING', 'WAIT_LOADING'\]\.includes\(containerDetail\?\.planStatus\)/)
assertOrder(value, [
"pushed?.status === 'EDITING'",
"claimedPlan?.status === 'LOADING'",
"const pickResponse = await request",
"const completeResponse = await request",
"finalPlan?.status === 'COMPLETED'",
])
assert.match(value, /picked\?\.workflowStatus === 'AWAITING_REVIEW'/)
assert.match(value, /`\$\{PDA\}\/tasks\/\$\{taskId\}\/complete`/)
assert.match(value, /expectedTaskVersion: Number\(picked\?\.version\)/)
assert.match(value, /operationId: `\$\{context\.marker\}-pda-complete`\.slice\(0, 128\)/)
})
test('bridge loading confirms preload before creating and claiming the PDA task', () => {
const value = source('./phase3-loading.mjs')
assertOrder(value, [
'无真实柜号推送预装成功并冻结柜次明细',
'无号 push 成功后 PDA 查询明确 A0404',
'push exact replay 对计划版本历史关联和任务零漂移',
'预装 EDITING 清单绑定真实柜号',
'已补号但未关联订舱仍无 PDA 任务',
'预装补号后关联三期 CONFIRMED 订舱',
'订舱已关联但未 RELEASED 仍无 PDA 任务',
'三期订舱携带已冻结运输快照并放舱',
'订舱 RELEASED 但预装未 CONFIRMED 仍无 PDA 任务',
'计划级确认前读取即时版本与就绪门禁',
'计划级原子确认整份预装并触发唯一 PDA 任务',
'计划确认后 task 待领取且 claim 前仍 WAIT_SPLIT/EDITING',
'读取柜次级 PDA 唯一事实任务',
"claimedPlan?.status === 'LOADING'",
"const canonicalPick = await request",
"pickedPlan?.status === 'COMPLETED'",
])
assert.match(value, /const confirmPath = `\/api\/platform\/v1\/first-leg\/loading-plans\/\$\{loadingPlanId\}\/confirm`/)
assert.match(value, /confirmed\?\.confirmationStatus === 'CONFIRMED'/)
assert.match(value, /confirmed\?\.readiness\?\.readyForPda === true/)
assert.match(value, /canonicalTaskId === confirmedTaskId/)
assert.match(value, /计划级确认同 key 异参返回冲突/)
assert.match(value, /已确认计划使用新 key 重复确认返回冲突/)
assert.doesNotMatch(value, /\/preload\/confirming|\/preload\/confirm\?tenantId=/)
assert.match(value, /loading-plans\/\$\{loadingPlanId\}\/transport\?warehouseId=/)
assert.match(value, /missingContainerTaskResponse\.http === 200 && codeOf\(missingContainerTaskResponse\) === 'A0404'/)
assert.doesNotMatch(value, /无真实柜号拒绝推送柜次计划|PDA complete后补确认/)
assert.match(value, /billOfLadingNo:[\s\S]*vesselName:[\s\S]*voyageNo:[\s\S]*estimatedSailingAt:[\s\S]*estimatedArrivalAt:/)
assert.doesNotMatch(value, /P3-SHIPPING|P3-AGENT/)
assert.match(value, /const frozenTransport = Object\.fromEntries\(transportFields\.map/)
assert.match(value, /const frozenTransportMatches = frozenTransportValid[\s\S]*transportFields\.every/)
assert.match(value, /CONFIRMED 订舱回显完整且冻结五项运输快照[\s\S]*if \(!bookingId \|\| !requirement\?\.id \|\| !frozenTransportMatches\) return[\s\S]*三期订舱携带已冻结运输快照并放舱/)
assert.match(value, /\.\.\.frozenTransport, reason: context\.marker/)
assert.doesNotMatch(value, /new Date\(Date\.now\(\)/)
assert.match(value, /bookingReleased\?\.oceanBooking\?\.status === 'RELEASED'/)
})
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)
assert.match(value, /const pushRequest = Object\.freeze\([\s\S]*body: Object\.freeze\(\{ expectedVersion:/)
assert.match(value, /push exact replay[\s\S]*零漂移/)
assert.match(value, /同 key 异参[\s\S]*新 key 重复 push/)
assert.match(value, /非法 push 后回查柜次计划[\s\S]*非法 push 后回查预装历史[\s\S]*非法 push 后回查 bridge/)
assert.match(value, /两类非法 push 后计划版本历史关联(?:和)?任务仍?零漂移/)
assert.match(value, /codeOf\(rejectedTaskResponse\) === 'A0404'/)
assert.match(value, /projectPushPreloadStability/)
assert.match(value, /diffPushPreloadStability/)
assert.match(value, /首次 push 后读取事务完成态柜次计划/)
assert.match(value, /事务完成态计划版本与 bridge 一致且保持 EDITING/)
assert.match(value, /plan: postPushPlan, detail:/)
assert.match(value, /expectedVersion: Number\(postPushPlan\.version\)/)
assert.match(value, /changed=\$\{(?:pushReplayDiff|replayDiff|rejectedDiff)\.join/)
assert.doesNotMatch(value, /baselineBytes=|replayBytes=|rejectedBytes=/)
}
})
test('push stability projection sorts unordered facts and reports persisted field drift', () => {
const baseline = projectPushPreloadStability({
plan: { id: '10', bookingId: '20', status: 'EDITING', version: 2,
items: [{ id: '2', inboundId: '42', packageCount: 1, version: 1 },
{ id: '1', inboundId: '41', packageCount: 1, version: 1 }] },
detail: { summary: { planId: '10', containerBookingId: '20', status: 'EDITING', version: 2,
readiness: { preloadPushed: true, preloadLinkCount: 2, confirmationStatus: 'PENDING',
blockingReasons: ['CONTAINER_NO_MISSING', 'OCEAN_BOOKING_NOT_LINKED'] } },
items: [{ itemId: '2', inboundId: '42', packageCount: 1 }, { itemId: '1', inboundId: '41', packageCount: 1 }],
histories: [{ historyId: '2', actionType: 'PUSH' }, { historyId: '1', actionType: 'CREATE' }] },
links: [{ id: '2', inboundId: '42', loadingPlanId: '10', allocatedPackageCount: 1 },
{ id: '1', inboundId: '41', loadingPlanId: '10', allocatedPackageCount: 1 }],
task: { http: 200, code: 'A0404' },
})
const reordered = projectPushPreloadStability({
plan: { id: '10', bookingId: '20', status: 'EDITING', version: 2,
items: [{ id: '1', inboundId: '41', packageCount: 1, version: 1 },
{ id: '2', inboundId: '42', packageCount: 1, version: 1 }], updatedAt: 'ignored' },
detail: { summary: { planId: '10', containerBookingId: '20', status: 'EDITING', version: 2,
readiness: { preloadPushed: true, preloadLinkCount: 2, confirmationStatus: 'PENDING',
blockingReasons: ['OCEAN_BOOKING_NOT_LINKED', 'CONTAINER_NO_MISSING'] } },
items: [{ itemId: '1', inboundId: '41', packageCount: 1 }, { itemId: '2', inboundId: '42', packageCount: 1 }],
histories: [{ historyId: '1', actionType: 'CREATE' }, { historyId: '2', actionType: 'PUSH' }] },
links: [{ id: '1', inboundId: '41', loadingPlanId: '10', allocatedPackageCount: 1 },
{ id: '2', inboundId: '42', loadingPlanId: '10', allocatedPackageCount: 1 }],
task: { http: 200, code: 'A0404' },
})
assert.deepEqual(diffPushPreloadStability(baseline, reordered), [])
const drifted = structuredClone(reordered)
drifted.plan.version = 3
drifted.links[0].preloadVersion = 2
assert.deepEqual(diffPushPreloadStability(baseline, drifted), [
'links[0].preloadVersion:null->2', 'plan.version:2->3',
])
})
test('push orchestration is transactional and a bridge failure cannot reach PDA creation', () => {
const service = source('../../firstleg/src/main/java/com/baiyue/firstleg/service/SmartLoadingService.java')
const serviceTest = source('../../firstleg/src/test/java/com/baiyue/firstleg/service/SmartLoadingServiceTest.java')
const push = service.slice(service.indexOf('public Plan pushPreload'), service.indexOf('private void requirePushReplay'))
assert.match(service, /@Transactional[\s\S]*public Plan pushPreload/)
assert.match(push, /repository\.push[\s\S]*history\([\s\S]*loadingModelBridge\.bindAllocatedBeforeLoading/)
assert.doesNotMatch(push, /loadingPickService\.beginIfReady/)
assert.match(serviceTest, /bindingFailureStopsBeforePdaAndPropagatesForTransactionRollback/)
assert.match(serviceTest, /verify\(loadingPickService, never\(\)\)\.beginIfReady/)
})
test('finalize rechecks the same unified order detail and list as LOADED', () => {
const value = source('./phase3-loading-finalize.mjs')
assertOrder(value, [
'入库单终态 LOADED、版本递增且审计历史存在',
'LOADED 后回查同一统一订单详情',
'统一订单详情投影同一订单为 LOADED',
'LOADED 后回查同一统一订单列表',
'统一订单列表投影同一订单为 LOADED',
])
assert.match(value, /summary\?\.mainStatus === 'LOADED'/)
assert.match(value, /unifiedRow\?\.mainStatus === 'LOADED'/)
assert.match(source('./phase3-loading.mjs'), /tenantId, orderId: id\(source\?\.orderId\), inboundId/)
})
test('PDA inventory snapshot gates batch and records the only safe mutation gap', () => {
const loading = source('./phase3-loading.mjs')
assertOrder(loading, [
'读取 PDA 提交前实时库存快照',
'未发生库存变化时重复读取实时库存快照',
'客户身份无权读取平台 PDA 实时库存快照',
'const overflowResource =',
'通过公开 A0 换绑接口制造真实库存变化',
'真实库存变化后旧 snapshot token 提交返回冲突',
'旧 token 冲突对任务、明细、已拣数量和操作历史零写入',
'库存变化后刷新 PDA 实时库存快照',
'const canonicalPick = await request',
'const canonicalBatchReplay = await request',
])
assert.match(loading, /inventorySnapshotToken: refreshedInventory\.snapshotToken/)
assert.match(loading, /warehouseId, loadingPlanId, containerBookingId,/)
assert.match(loading, /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)/)
assert.match(loading, /inventorySnapshotVerified: inventorySnapshotVerified && directPdaCompletion && atomicBatchReplayVerified/)
assert.match(loading, /inventoryMutationVerified, inventoryMutationCleanup/)
assert.match(loading, /当前唯一平台代理库存变更入口只允许 A0 临时占用换绑/)
const convergence = source('./phase3-pda-convergence.mjs')
assert.doesNotMatch(convergence, /未公开实时库存查询或库存版本快照端点/)
assert.match(convergence, /fixture\?\.inventorySnapshotVerified !== true/)
assert.match(convergence, /fixture\?\.inventoryMutationVerified !== true/)
})
test('container loading arranges a simulation before linking a confirmed real booking', () => {
const value = source('./phase3-container-loading.mjs')
assert.match(value, /candidates\.find\(\(candidate\) => candidate\.marker === context\.marker\)/)
assertOrder(value, [
"const simulationResponse = await request(context, 'POST', CONTAINERS",
"const candidateResponse = await context.request('GET', `${SMART}/candidates?bookingId=",
"const planResponse = await request(context, 'POST', `${SMART}/plans`",
"const assignResponse = await request",
'无真实柜号推送预装计划成功',
'预装 EDITING 清单绑定本轮真实柜号',
"const bookingCreate = await request",
"const linkResponse = await request(context, 'POST', `${CONTAINERS}/${containerId}/ocean-booking-link`",
])
assert.match(value, /channelCode, warehouseId, warehouseName/)
assert.match(value, /publishPlatformLogisticsChannels\(context, warehouseId, MODULE\)/)
assert.match(value, /warehouseId, warehouseName/)
const draft = value.slice(value.indexOf('const bookingDraft = {'), value.indexOf('context.assert(MODULE, \'订舱渠道与仓库快照完整\''))
for (const field of ['billOfLadingNo', 'vesselName', 'voyageNo', 'estimatedSailingAt', 'estimatedArrivalAt']) {
assert.match(draft, new RegExp(`${field}:`))
}
assert.doesNotMatch(value, /loading-booking-(?:submit|confirm)|ocean-bookings\/.*\/(?:submit|confirm)/)
assert.match(value, /containerAllocations\.length === 0/)
assert.match(value, /expectedVersion: Number\(postPushPlan\.version\), operationReason: context\.marker/)
assert.match(value, /function responseEvidence\(response\)[\s\S]*HTTP=\$\{response\?\.http \?\? 'missing'\}[\s\S]*code=\$\{codeOf\(response\)\}[\s\S]*requestId=\$\{response\?\.requestId \|\| 'missing'\}[\s\S]*traceId=\$\{response\?\.traceId \|\| 'missing'\}/)
assert.match(value, /emptySimulation = context\.apiSuccess\(MODULE, '空模拟柜首次关联真实订舱', emptyLink,\s+responseEvidence\(emptyLink\), OPTIONS\)\s+if \(!emptySimulation\) return\s+const emptyUnlink = await request/)
assert.match(value, /emptySimulation = context\.apiSuccess\(MODULE, '空模拟柜允许解除关联', emptyUnlink,\s+responseEvidence\(emptyUnlink\), OPTIONS\)\s+if \(!emptySimulation\) return\s+const emptyRelink = await request/)
assert.match(value, /emptySimulation = context\.apiSuccess\(MODULE, '空模拟柜解绑后允许重新关联', emptyRelink,\s+responseEvidence\(emptyRelink\), OPTIONS\)\s+if \(!emptySimulation\) return\s+const emptyFinalUnlink = await request/)
assert.match(value, /const emptyReleased = context\.apiSuccess\(MODULE, '空模拟柜最终解绑释放真实订舱容量', emptyFinalUnlink,\s+responseEvidence\(emptyFinalUnlink\), OPTIONS\)\s+if \(!emptyReleased\) return\s+const beforeLinkItems/)
assertOrder(value, [
"const linkedContainer = context.apiSuccess(MODULE, '首次关联真实已确认订舱柜', linkResponse,",
'if (!linkedContainer) return',
"const afterLinkPlanResponse = await context.request('GET'",
"const duplicateSimulationResponse = await request(context, 'POST', CONTAINERS",
])
assert.equal(value.match(/responseEvidence\((?:emptyLink|emptyUnlink|emptyRelink|emptyFinalUnlink|linkResponse)\)/g)?.length, 5)
assert.match(value, /关联前后内部柜次、计划和排货明细身份稳定/)
assert.match(value, /bookingId=\$\{encodeURIComponent\(containerId\)\}&warehouseId=/)
assert.match(value, /tasks\?loadingPlanId=\$\{encodeURIComponent\(planId\)\}&warehouseId=/)
assert.match(value, /loadingPlanId: planId, containerBookingId: containerId/)
assert.ok(value.includes('`${FIRST_LEG}/loading-plans/${planId}/transport?warehouseId='))
assert.match(value, /预装 EDITING 清单绑定本轮真实柜号/)
assert.match(value, /订舱携带已冻结运输快照放舱并满足 PDA 门禁/)
assert.match(value, /无真实柜号推送预装计划成功/)
assert.match(value, /订舱 RELEASED 但预装未 CONFIRMED 不生成 PDA 任务/)
assert.match(value, /计划级原子确认整份预装并触发唯一 PDA 任务/)
assert.match(value, /confirmationStatus === 'CONFIRMED'/)
assert.match(value, /readiness\?\.readyForPda === true/)
assert.match(value, /taskId === confirmedTaskId/)
assert.match(value, /计划级确认同 key 异参返回冲突/)
assert.match(value, /已确认计划使用新 key 重复确认返回冲突/)
assert.doesNotMatch(value, /\/preload\/confirming|\/preload\/confirm\?tenantId=/)
assert.match(value, /const confirmedTaskAbsent =[\s\S]*if \(!confirmedTaskAbsent\) return/)
assert.match(value, /POST', CONTAINERS/)
})
test('container loading selects the current marker instead of a stale CAB fixture', () => {
const selected = selectContainerLoadingFixture({
marker: 'ADMIN-RUN-AUTO1',
createdResources: {
orchestrationFixtures: {
phase3Container: { marker: 'ADMIN-RUN-CAB', inboundId: 'CAB-INBOUND' },
},
inboundMeasurement: { marker: 'ADMIN-RUN-AUTO1', inboundId: 'FINANCE-INBOUND' },
orders: { marker: 'ADMIN-RUN-AUTO1', inboundId: 'FINANCE-ORDER-INBOUND' },
},
})
assert.equal(selected.inboundId, 'FINANCE-INBOUND')
assert.equal(selected.marker, 'ADMIN-RUN-AUTO1')
})
test('container loading keeps an unmatched candidate so marker failure stays observable', () => {
const selected = selectContainerLoadingFixture({
marker: 'ADMIN-RUN-AUTO1',
createdResources: {
orchestrationFixtures: {
phase3Container: { marker: 'ADMIN-RUN-CAB', inboundId: 'CAB-INBOUND' },
},
},
})
assert.equal(selected.marker, 'ADMIN-RUN-CAB')
})
test('channel isolation accepts only two active channels from the current platform warehouse catalog', () => {
const selected = selectPlatformChannelIsolationPair({
marker: 'ADMIN-RUN-AUTO1', originWarehouseId: '41',
sourcePath: '/api/platform/v1/frozen-channel-contract?originWarehouseId=41',
channels: [
{ channelCode: 'SEA-A', channelName: '海运 A', status: 'ACTIVE' },
{ channelCode: 'SEA-B', channelName: '海运 B', status: 'ACTIVE' },
{ channelCode: 'SEA-C', channelName: '海运 C', status: 'DISABLED' },
],
}, 'ADMIN-RUN-AUTO1', '41', 'SEA-A')
assert.deepEqual(selected.pair, {
primary: { channelCode: 'SEA-A', channelName: '海运 A' },
foreign: { channelCode: 'SEA-B', channelName: '海运 B' },
})
assert.equal(selected.reason, null)
})
test('channel isolation reports BLOCKED_FIXTURE instead of inventing a second channel', () => {
for (const catalog of [
{ marker: 'OLD-RUN', originWarehouseId: '41', sourcePath: '/api/platform/v1/channels', channels: [] },
{ marker: 'ADMIN-RUN-AUTO1', originWarehouseId: '42', sourcePath: '/api/platform/v1/channels', channels: [] },
{ marker: 'ADMIN-RUN-AUTO1', originWarehouseId: '41', sourcePath: '/api/merchant/v1/first-leg/pricing/channels', channels: [] },
{ marker: 'ADMIN-RUN-AUTO1', originWarehouseId: '41', sourcePath: '/api/platform/v1/channels',
channels: [{ channelCode: 'SEA-A', channelName: '海运 A', status: 'ACTIVE' }] },
]) {
const selected = selectPlatformChannelIsolationPair(catalog, 'ADMIN-RUN-AUTO1', '41', 'SEA-A')
assert.equal(selected.pair, null)
assert.match(selected.reason, /^BLOCKED_FIXTURE:/)
}
const value = source('./phase3-container-loading.mjs')
assert.doesNotMatch(value, /foreignChannelCode|\$\{channelCode\}-OTHER|异渠道-\$\{suffix\}/)
assert.match(value, /context\.createdResources\?\.platformLogisticsChannels/)
})
test('container loading enforces channel isolation and stable PDA identity across container number changes', () => {
const value = source('./phase3-container-loading.mjs')
assertOrder(value, [
'新增内部模拟柜',
'新增真实异渠道模拟柜',
'两个真实有效渠道之间拒绝跨渠道装柜',
'人工分配本轮候选货物',
'无真实柜号推送预装计划成功',
'无号 push 成功后 PDA 查询 A0404',
'预装 EDITING 清单绑定本轮真实柜号',
'已补号但未关联订舱不生成 PDA 任务',
'首次关联真实已确认订舱柜',
'订舱已关联但未 RELEASED 不生成 PDA 任务',
'订舱携带已冻结运输快照放舱并满足 PDA 门禁',
'订舱 RELEASED 但预装未 CONFIRMED 不生成 PDA 任务',
'计划级确认前读取即时版本与就绪门禁',
'计划级原子确认整份预装并触发唯一 PDA 任务',
'计划确认后 task 待领取且 claim 前仍 WAIT_SPLIT/EDITING',
'换柜号前读取 PDA 稳定库存快照',
'PDA 任务创建后替换真实柜号',
'PDA 任务创建后清空真实柜号失败',
'换柜号后按稳定计划身份回查 PDA 任务',
'换柜号不改变任务计划柜次柜次号和库存快照身份',
])
assert.match(value, /containerDetail\?\.channelCode === channelCode/)
assert.match(value, /id\(replacedTask\?\.cabinetNo\) === stableIdentity\.cabinetNo/)
assert.match(value, /afterReplacementSnapshot\?\.snapshotToken === beforeReplacementSnapshot\?\.snapshotToken/)
assert.match(value, /pdaIdentityStable: true/)
assert.match(value, /shippingCompanyId: null, shippingCompanyName: null/)
assert.match(value, /bookingAgentId: null, bookingAgentName: null/)
assert.doesNotMatch(value, /shippingCompanyName: `验收船公司-/)
assert.doesNotMatch(value, /bookingAgentName: `验收代理-/)
assert.match(value, /const frozenTransport = Object\.fromEntries\(transportFields\.map/)
assert.match(value, /if \(!frozenTransportMatches\) return/)
assert.match(value, /expectedVersion: Number\(confirmedBooking\?\.oceanBooking\?\.version\),\s*\.\.\.frozenTransport, reason: context\.marker/s)
assert.doesNotMatch(value, /const sailingAt = new Date\(Date\.now\(\)/)
const batch = value.slice(value.indexOf('const staleBatchResponse'), value.indexOf('const completePath'))
assert.doesNotMatch(batch, /containerNo:/)
})
test('preload and loading versions use immediate operation baselines and exact push replay', () => {
const container = source('./phase3-container-loading.mjs')
assert.match(container, /const pushRequest = Object\.freeze\(\{[\s\S]*path:[\s\S]*body: Object\.freeze[\s\S]*headers: Object\.freeze/)
assert.equal((container.match(/request\(context, 'POST', pushRequest\.path, pushRequest\.body, pushRequest\.headers\)/g) || []).length, 2)
assert.match(container, /const beforeClaimPlanVersion = Number\(beforeClaimPlan\?\.version\)/)
assert.match(container, /Number\(claimedPlan\?\.version\) > beforeClaimPlanVersion/)
const loading = source('./phase3-loading.mjs')
assert.match(loading, /const pushRequest = Object\.freeze\(\{[\s\S]*body: Object\.freeze[\s\S]*headers: Object\.freeze/)
assert.equal((loading.match(/request\(context, 'POST', pushRequest\.path, pushRequest\.body, pushRequest\.headers\)/g) || []).length, 2)
assert.match(loading, /push exact replay 对计划版本历史关联和任务零漂移/)
assert.match(loading, /Number\(claimedPlan\?\.version\) > beforeClaimPlanVersion/)
})
test('PDA completion acceptance verifies immutable BATCH and COMPLETE_LOADING audit with operator metadata', () => {
const container = source('./phase3-container-loading.mjs')
const convergence = source('./phase3-pda-convergence.mjs')
assert.match(container, /回查完成后的 PDA 操作审计/)
assert.match(container, /item\.actionType === 'BATCH'/)
assert.match(container, /item\.actionType === 'COMPLETE_LOADING'/)
assert.match(container, /Boolean\(item\.operatorId\) && Boolean\(item\.createdAt\)/)
assert.match(convergence, /PDA 原子批次完成审计包含 BATCH 操作/)
assert.match(convergence, /item\.actionType === 'BATCH'/)
})
test('phase3 loading proves mixed-line rollback across task, all items and operations', () => {
const loading = source('./phase3-loading.mjs')
assertOrder(loading, [
'非法混合批次前读取 PDA 全量任务基线',
'PDA 原子批次任一非法明细时整批拒绝',
'回查失败批次后的 PDA 任务快照',
'非法混合批次后回查 PDA 操作历史',
'合法明细加非法明细失败后任务、全部明细和操作历史零写入',
])
assert.match(loading, /taskItemsSignature\(unchangedAfterInvalid\) === taskItemsSignature\(beforeInvalidTask\)/)
assert.match(loading, /operationsSignature\(afterInvalidOperations\) === operationsSignature\(beforeInvalidOperations\)/)
assert.doesNotMatch(loading, /tasks\?containerNo=/)
assert.equal((loading.match(/tasks\?loadingPlanId=/g) || []).length >= 7, true)
})
test('phase3 loading proves stale inventory zero-write for all items and completion audit', () => {
const loading = source('./phase3-loading.mjs')
assert.match(loading, /taskItemsSignature\(afterStaleTask\) === taskItemsSignature\(beforeMutationTask\)/)
assertOrder(loading, [
'旧 token 冲突对任务、明细、已拣数量和操作历史零写入',
'库存变化后刷新 PDA 实时库存快照',
'原子提交柜次 PDA 整批拣货',
'PDA 原子批次同参同键重放保持待人工确认',
'人工确认柜次 PDA 装柜完成',
'回查原子 Batch 完成审计',
'人工完成后保留 CLAIM/BATCH/COMPLETE_LOADING 审计',
])
})
test('phase3 loading fixture forces A0 through reversible public location status APIs', () => {
const entry = source('../admin-test-env-acceptance.mjs')
const measurement = source('./inbound-measurement.mjs')
const orders = source('./orders.mjs')
assertOrder(entry, [
'disableAvailableLocationsForA0(warehouseId)',
'this.context.phase3LoadingRequireSharedOverflow = true',
"executeModule('inbound-measurement-phase3-loading-fixture'",
'restoreAvailableLocationsAfterA0(warehouseId, disabledLocations)',
])
assert.match(entry, /\/locations\/\$\{encodeURIComponent\(location\.id\)\}\/status/)
assert.match(entry, /\{ status: 'DISABLED', version: Number\(location\.version\) \}/)
assert.match(entry, /\{ status: 'AVAILABLE', version: location\.version \}/)
assert.match(measurement, /phase3LoadingRequireSharedOverflow !== true/)
assert.match(orders, /receivingWarehouseId: id\(master\.receiving\.id\)/)
})
test('customs provisions released booking and completed truck for the selected loading container', () => {
const value = source('./phase3-customs.mjs')
assertOrder(value, [
'selectCustomsFlow(context.createdResources)',
'if (!await ensureServiceCenterFlow(context, container)) return',
'const slicesPath =',
])
assert.match(value, /oceanBookings\.run\(context\)/)
assert.match(value, /serviceCenter\.run\(context\)/)
assert.match(value, /request\('POST', '\/api\/platform\/v1\/suppliers\?tenantId=0'/)
assert.match(value, /category: 'CUSTOMS'/)
assert.match(value, /context\.createdResources\.phase3CustomsSupplier/)
assert.match(value, /const batchPayload = \{ customsAgentId,/)
assert.doesNotMatch(value, /customsAgentName: `代理-/)
})
test('downstream producers prefer the phase3Loading container fixture', () => {
const ocean = source('./ocean-bookings.mjs')
const customs = source('./phase3-customs.mjs')
const serviceCenter = source('./service-center-flow.mjs')
assert.match(ocean, /context\.createdResources\?\.phase3Loading\s*\|\|\s*context\.createdResources\?\.phase3ContainerLoading/)
assert.match(customs, /createdResources\?\.phase3Loading,\s*\n\s*createdResources\?\.phase3ContainerLoading/)
assert.match(serviceCenter, /context\.createdResources\?\.phase3Loading\s*\|\|\s*context\.createdResources\?\.phase3ContainerLoading/)
assert.match(ocean, /request\(context, 'POST', `\$\{path\}\/release`/)
assert.match(ocean, /flowOceanBookingId = id\(containerFlow\?\.oceanBookingId\)/)
assert.match(ocean, /ocean-flow-release/)
assert.match(ocean, /existing\?\.marker === context\.marker && existing\?\.finalStatus === 'RELEASED'/)
assert.match(ocean, /const reconciled = await releaseLoadingBooking\(context, containerFlow, suffix\)/)
assert.match(ocean, /containerBookingId: id\(containerFlow\.containerBookingId\), loadingPlanId: id\(containerFlow\.loadingPlanId\)/)
assert.match(source('./phase3-loading.mjs'), /oceanBookingId: bookingId, loadingPlanId, containerBookingId/)
assert.doesNotMatch(ocean, /`\$\{context\.marker\}-\$\{endpoint\}`/)
assert.match(ocean, /`\$\{context\.marker\}-ocean-\$\{endpoint\}`/)
assert.match(serviceCenter, /if \(!allocationMatches\) return/)
assert.match(serviceCenter, /const customerName = String\(oceanDetail\?\.oceanBooking\?\.customerName \|\| ''\)\.trim\(\)/)
assert.match(serviceCenter, /customerId, customerName, carrierId: null/)
assert.match(serviceCenter, /carrierId: null, carrierName: null/)
assert.doesNotMatch(serviceCenter, /链路承运商-/)
assert.doesNotMatch(serviceCenter, /customerName: `服务中心链路客户-/)
const schedules = source('./phase3-port-schedules.mjs')
assert.match(schedules, /allocatedContainerBookingIds: \(detail\.containerAllocations \|\| \[\]\)/)
assert.match(schedules, /ocean\.allocatedContainerBookingIds\?\.includes\(containerBookingId\)/)
assert.match(schedules, /if \(containerBookingId && !knownAllocation\)/)
})
test('default acceptance order verifies loading evidence before finalizing LOADED', () => {
const entry = source('../admin-test-env-acceptance.mjs')
assertOrder(entry, [
"'phase3-loading'",
"'phase3-loading-model-bridge'",
"'phase3-pda-convergence'",
"'phase3-evidence-gate'",
"'phase3-customs'",
"'phase3-loading-finalize'",
])
})
test('container overview always follows phase3 loading in default and custom module selections', () => {
const reordered = normalizeModuleOrder(['phase3-container-overview', 'orders', 'phase3-loading', 'finance'])
assert.deepEqual(reordered, ['orders', 'phase3-loading', 'phase3-container-overview', 'finance'])
assert.deepEqual(parseArguments(['--modules', 'phase3-container-overview,phase3-loading']).modules,
['phase3-loading', 'phase3-container-overview'])
const entry = source('../admin-test-env-acceptance.mjs')
assert.match(entry, /const DEFAULT_MODULES = normalizeModuleOrder\(CONFIGURED_MODULES\)/)
assert.match(entry, /modules: normalizeModuleOrder\(/)
})
test('overview does not fall back to a container fixture when loading evidence was requested', () => {
const overview = source('./phase3-container-overview.mjs')
assert.match(overview, /loadingRequested[\s\S]*\? context\.createdResources\?\.phase3Loading/)
assert.match(overview, /source=\$\{loadingRequested \? 'phase3Loading-required'/)
})
test('phase3 loading provisions its own marker-isolated WAIT_SPLIT fixture', async () => {
const run = new AcceptanceRun({ gateway: 'http://127.0.0.1', modules: [], username: '', password: '', output: '' })
const entry = source('../admin-test-env-acceptance.mjs')
assert.match(entry, /module === 'phase3-loading'/)
assert.match(entry, /preparePhase3LoadingFixture\(\)/)
assert.match(entry, /orders-phase3-loading-fixture/)
assert.match(entry, /inbound-measurement-phase3-loading-fixture/)
assert.match(entry, /marker: originalMarker/)
assert.match(entry, /sourceMarker: fixtureMarker/)
assert.match(entry, /phase3Loading: loadingFixture,\s*\n\s*inboundMeasurement: loadingFixture/)
assert.equal(run.marker.includes('-LOAD'), false)
})
test('container fixture isolates order idempotency while publishing the run marker', () => {
const entry = source('../admin-test-env-acceptance.mjs')
assert.match(entry, /const fixtureMarker = `\$\{originalMarker\}-CAB`/)
assert.match(entry, /marker: originalMarker,\s*\n\s*sourceMarker: fixtureMarker/)
assert.match(entry, /fixture\.finalStatus !== 'APPROVED'/)
assert.match(entry, /禁止回退使用 WAIT_PRICING 订单/)
})
test('measurement fixture polls asynchronous completion before exporting loading identity', () => {
const measurement = source('./inbound-measurement.mjs')
assert.match(measurement, /waitForCompletedMeasurement/)
assert.match(measurement, /measurement\?\.processStatus === 'WAIT_SPLIT'/)
assert.match(measurement, /setTimeout\(resolve, 1_000\)/)
})
test('customs and finalize consume the same loading inbound identity', () => {
const loadingFixture = { marker: 'RUN-1', tenantId: 10001, orderId: 101, inboundId: 201 }
const loading = {
marker: 'RUN-1', tenantId: 10001, inboundId: 201,
containerBookingId: 301, loadingPlanId: 401, finalStatus: 'COMPLETED',
}
const selected = selectCustomsFlow({
orchestrationFixtures: {
phase3Loading: loadingFixture,
inboundMeasurement: loadingFixture,
},
phase3Loading: loading,
})
assert.equal(selected.fixture.marker, loading.marker)
assert.equal(selected.fixture.inboundId, loading.inboundId)
assert.equal(selected.container.inboundId, loading.inboundId)
const finalized = buildFinalizeResource(loading, {
summary: { processStatus: 'LOADED', version: 6 },
}, { http: 200, body: { code: '200' } })
assert.equal(finalized.marker, loading.marker)
assert.equal(finalized.inboundId, loading.inboundId)
assert.equal(finalized.finalStatus, 'LOADED')
})
test('loading evidence acceptance covers idempotency, reversible missing-type gate and scope isolation', () => {
const loading = source('./phase3-loading.mjs')
assertOrder(loading, [
'装柜证据绑定同键同参幂等重放',
'装柜证据绑定同键异参返回冲突',
'装柜证据作废旧版本返回冲突',
'临时作废唯一 PHOTO_STEP_4 证据',
'证据作废同键同参重放保持同一 VOID 结果',
'证据作废同键异参返回冲突',
'已作废证据使用新请求重复作废返回冲突',
'作废唯一证据类型后 LOADED 门禁拒绝',
'补传恢复 PHOTO_STEP_4 有效证据',
'客户身份无权访问平台装柜证据',
'错误客户作用域不可读取本轮装柜证据',
'错误入库单不可读取本轮装柜证据',
])
assert.match(loading, /missingTypeVoidedEvidenceId/)
assert.match(loading, /replacementEvidenceFileId/)
})
test('loaded boundary acceptance proves evidence and transport immutability with zero drift', () => {
const finalize = source('./phase3-loading-finalize.mjs')
assertOrder(finalize, [
'LOADED 后使用新键重复 complete 幂等保持终态',
'LOADED complete 同键异参返回冲突',
'LOADED 后新增装柜证据被拒绝',
'LOADED 后作废装柜证据被拒绝',
'LOADED 后运输信息修改被拒绝',
'回查 LOADED 高风险边界后的入库快照',
'回查 LOADED 高风险边界后的证据快照',
'回查 LOADED 高风险边界后的计划快照',
'LOADED 后拒绝与幂等请求保持状态、版本、证据、计划和审计零漂移',
])
assert.match(finalize, /evidenceSignature\(afterEvidence\) === immutableEvidenceSignature/)
assert.match(finalize, /afterHistorySignature === historySignature/)
assert.match(finalize, /afterPlanSignature === immutablePlanSignature/)
})