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 { 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 starts through a real PDA batch before preload confirmation', () => { const value = source('./phase3-loading.mjs') assertOrder(value, [ '原子关联入库预装与柜次装柜计划', '无真实柜号拒绝推送柜次计划', '无真实柜号推送失败后不生成 PDA 任务', '绑定真实柜号后开放三期 PDA 推送门禁', '订舱 CONFIRMED 即使已绑定真实柜号也不生成 PDA 任务', '三期订舱携带已冻结运输快照并放舱', '三期订舱 RELEASED 后保持模拟柜关联身份', '真实柜号与 RELEASED 订舱就绪后推送并生成 PDA 任务', "pushedLoadingPlan?.status === 'EDITING'", "claimedPlan?.status === 'LOADING'", "const canonicalPick = await request", "pickedPlan?.status === 'COMPLETED'", "const confirming = await request", ]) assert.match(value, /confirmed\?\.mainStatus === 'LOADING' && confirmed\?\.preloadPlan\?\.status === 'CONFIRMING'/) assert.match(value, /confirmed\?\.preloadPlan\?\.status === 'CONFIRMED'/) assert.doesNotMatch(value, /关联柜次计划进入 LOADING/) assert.match(value, /confirmed\?\.mainStatus === 'LOADING'/) assert.match(value, /confirmed\?\.preloadPlan\?\.status === 'CONFIRMING'/) assert.match(value, /Number\(confirmed\?\.preloadPlan\?\.version\) === Number\(confirmingData\.preloadPlan\.version\)/) assert.match(value, /const missingContainerPushRejected = failure\(missingContainerPushResponse\)[\s\S]*if \(!missingContainerPushRejected\) return/) assert.match(value, /const missingContainerTaskAbsent =[\s\S]*if \(!missingContainerTaskAbsent\) return/) assert.match(value, /version: transportVersion/) assert.match(value, /Number\.isInteger\(transportVersion\)/) 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/) assertOrder(value, [ 'CONFIRMED 订舱回显完整且冻结五项运输快照', 'if (!frozenTransportMatches) return', '三期订舱携带已冻结运输快照并放舱', ]) assert.match(value, /\.\.\.frozenTransport, reason: context\.marker/) assert.doesNotMatch(value, /new Date\(Date\.now\(\)/) assert.match(value, /bookingReleased\?\.oceanBooking\?\.status === 'RELEASED'/) }) 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", "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, /version: Number\(linkedContainer\.version\), reason: context\.marker/) assert.doesNotMatch(value, /version: Number\(containerDetail\?\.version \?\? 0\), reason: 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("'PATCH', `${CONTAINERS}/${containerId}/transport")) assert.match(value, /在放舱前绑定本轮真实箱号/) assert.match(value, /订舱携带已冻结运输快照放舱并满足 PDA 门禁/) assert.match(value, /无真实柜号拒绝推送预装计划/) assert.match(value, /柜号与 RELEASED 订舱就绪后推送预装计划/) 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, [ '新增内部模拟柜', '新增真实异渠道模拟柜', '两个真实有效渠道之间拒绝跨渠道装柜', '人工分配本轮候选货物', '首次关联真实已确认订舱柜', '无真实柜号拒绝推送预装计划', '无真实柜号推送失败后不生成 PDA 任务', '在放舱前绑定本轮真实箱号', '订舱 CONFIRMED 即使已有真实柜号也不开放 PDA 任务', '订舱携带已冻结运输快照放舱并满足 PDA 门禁', '柜号与 RELEASED 订舱就绪后推送预装计划', '换柜号前读取 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, /Number\.isInteger\(smartPreloadVersion\) && smartPreloadVersion >= 0/) assert.match(loading, /Number\(link\?\.preloadVersion\) > bindVersions\.expectedPreloadVersion/) assert.match(loading, /Number\(link\?\.loadingPlanVersion\) > bindVersions\.expectedLoadingPlanVersion/) assert.match(loading, /Number\(confirmingData\?\.preloadPlan\?\.version\) > Number\(link\.preloadVersion\)/) assert.doesNotMatch(loading, /Number\((?:smartData|link|confirmingData|confirmed)[^\n]*version\) === [0-9]/) }) 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/) })