From 011c30cc09f5e4501373abb2ad6fd2bec6e098f0 Mon Sep 17 00:00:00 2001 From: ouyangxq <775153912@qq.com> Date: Fri, 14 Aug 2026 22:30:28 +0800 Subject: [PATCH] test: align container flow acceptance identities --- .../phase3-container-overview.mjs | 10 ++++++++-- .../phase3-container-regression-contract.test.mjs | 7 ++++++- scripts/admin-test-env-modules/phase3-loading.mjs | 8 ++++---- .../phase3-producer-chain-contract.test.mjs | 5 +++++ scripts/admin-test-env-modules/service-center-flow.mjs | 9 ++++++--- 5 files changed, 29 insertions(+), 10 deletions(-) diff --git a/scripts/admin-test-env-modules/phase3-container-overview.mjs b/scripts/admin-test-env-modules/phase3-container-overview.mjs index 1deeaebff..04df5e4c0 100644 --- a/scripts/admin-test-env-modules/phase3-container-overview.mjs +++ b/scripts/admin-test-env-modules/phase3-container-overview.mjs @@ -72,11 +72,17 @@ export async function run(context) { ['evidenceId', 'fileId', 'status', 'downloadPath'].every((field) => photo[field] !== undefined), `fileId=${id(photo.fileId)}`, OPTIONS) const downloadPath = String(photo.downloadPath || '') + if (photo.status === 'VOID') { + context.assert(MODULE, `overview PDA 作废照片 ${index + 1} 无下载入口`, overviewResponse, + !downloadPath, + `fileId=${id(photo.fileId)}, status=${photo.status}, downloadPath=${downloadPath || 'none'}`, OPTIONS) + continue + } const controlledPath = downloadPath.startsWith('/api/') && !downloadPath.startsWith('//') && !downloadPath.includes('://') - if (!controlledPath) { + if (photo.status !== 'ACTIVE' || !controlledPath) { context.assert(MODULE, `overview PDA 照片 ${index + 1} 下载路径受控`, overviewResponse, false, - `fileId=${id(photo.fileId)}, downloadPath=${downloadPath || 'missing'}`, OPTIONS) + `fileId=${id(photo.fileId)}, status=${photo.status || 'missing'}, downloadPath=${downloadPath || 'missing'}`, OPTIONS) continue } const download = await context.requestBinary('GET', downloadPath) diff --git a/scripts/admin-test-env-modules/phase3-container-regression-contract.test.mjs b/scripts/admin-test-env-modules/phase3-container-regression-contract.test.mjs index ba932a097..74a5e1e3c 100644 --- a/scripts/admin-test-env-modules/phase3-container-regression-contract.test.mjs +++ b/scripts/admin-test-env-modules/phase3-container-regression-contract.test.mjs @@ -39,9 +39,14 @@ test('overview regression requires workflowStatus and distinguishes 100 percent assert.doesNotMatch(matrix, /PDA overview `workflowStatus` 缺失[\s\S]*BLOCKED_CONTRACT/) }) -test('every overview photo uses its server-provided controlled downloadPath', () => { +test('active overview photos download through controlled paths while void history has no download entry', () => { assert.match(overviewOpenApi, /\/api\/platform\/v1\/containers\/\{id\}\/pda\/photos\/\{fileId\}\/download/) assert.match(overviewAcceptance, /context\.requestBinary\('GET', downloadPath\)/) + assert.match(overviewAcceptance, /photo\.status === 'VOID'/) + assert.match(overviewAcceptance, /overview PDA 作废照片 \$\{index \+ 1\} 无下载入口/) + assert.match(overviewAcceptance, /photo\.status !== 'ACTIVE' \|\| !controlledPath/) + assert.match(overviewOpenApi, /status: \{type: string, enum: \[ACTIVE, VOID\]\}/) + assert.match(overviewOpenApi, /downloadPath: \{type: string, nullable: true, description: '仅 ACTIVE 照片提供鉴权下载路径;VOID 历史照片为空'\}/) assert.equal(controlledPhotoDownload('/api/platform/v1/containers/7/pda/photos/23/download', { http: 200, bytes: new Uint8Array([1]), contentType: 'image/png', }), true) diff --git a/scripts/admin-test-env-modules/phase3-loading.mjs b/scripts/admin-test-env-modules/phase3-loading.mjs index 75a2638f5..b32803209 100644 --- a/scripts/admin-test-env-modules/phase3-loading.mjs +++ b/scripts/admin-test-env-modules/phase3-loading.mjs @@ -393,7 +393,7 @@ export async function run(context) { `HTTP=${staleBatch.http}, code=${codeOf(staleBatch)}`, OPTIONS) const beforeInvalidTaskResponse = await context.request('GET', - `${pdaBase}/tasks?containerNo=${encodeURIComponent(containerNo)}&warehouseId=${encodeURIComponent(warehouseId)}`) + `${pdaBase}/tasks?loadingPlanId=${encodeURIComponent(loadingPlanId)}&warehouseId=${encodeURIComponent(warehouseId)}`) const beforeInvalidTask = context.apiSuccess(MODULE, '非法混合批次前读取 PDA 全量任务基线', beforeInvalidTaskResponse, '', OPTIONS) const beforeInvalidOperationsResponse = await context.request('GET', `${pdaBase}/tasks/${canonicalTaskId}/operations`) @@ -409,7 +409,7 @@ export async function run(context) { invalidBatch.http === 200 && codeOf(invalidBatch) === 'A0409', `HTTP=${invalidBatch.http}, code=${codeOf(invalidBatch)}`, OPTIONS) const unchangedAfterInvalidResponse = await context.request('GET', - `${pdaBase}/tasks?containerNo=${encodeURIComponent(containerNo)}&warehouseId=${encodeURIComponent(warehouseId)}`) + `${pdaBase}/tasks?loadingPlanId=${encodeURIComponent(loadingPlanId)}&warehouseId=${encodeURIComponent(warehouseId)}`) const unchangedAfterInvalid = context.apiSuccess(MODULE, '回查失败批次后的 PDA 任务快照', unchangedAfterInvalidResponse, '', OPTIONS) const afterInvalidOperationsResponse = await context.request('GET', `${pdaBase}/tasks/${canonicalTaskId}/operations`) const afterInvalidOperations = context.apiSuccess(MODULE, '非法混合批次后回查 PDA 操作历史', @@ -492,7 +492,7 @@ export async function run(context) { } } const beforeMutationTaskResponse = await context.request('GET', - `${pdaBase}/tasks?containerNo=${encodeURIComponent(containerNo)}&warehouseId=${encodeURIComponent(warehouseId)}`) + `${pdaBase}/tasks?loadingPlanId=${encodeURIComponent(loadingPlanId)}&warehouseId=${encodeURIComponent(warehouseId)}`) const beforeMutationTask = context.apiSuccess(MODULE, '库存变化前读取 PDA 任务基线', beforeMutationTaskResponse, '', OPTIONS) const beforeMutationOperationsResponse = await context.request('GET', `${pdaBase}/tasks/${canonicalTaskId}/operations`) const beforeMutationOperations = context.apiSuccess(MODULE, '库存变化前读取 PDA 操作历史基线', @@ -519,7 +519,7 @@ export async function run(context) { `HTTP=${staleInventoryBatch.http}, code=${codeOf(staleInventoryBatch)}, message=${staleInventoryBatch.body?.message || 'missing'}`, OPTIONS) const afterStaleTaskResponse = await context.request('GET', - `${pdaBase}/tasks?containerNo=${encodeURIComponent(containerNo)}&warehouseId=${encodeURIComponent(warehouseId)}`) + `${pdaBase}/tasks?loadingPlanId=${encodeURIComponent(loadingPlanId)}&warehouseId=${encodeURIComponent(warehouseId)}`) const afterStaleTask = context.apiSuccess(MODULE, '旧库存快照冲突后回查 PDA 任务', afterStaleTaskResponse, '', OPTIONS) const afterStaleOperationsResponse = await context.request('GET', `${pdaBase}/tasks/${canonicalTaskId}/operations`) const afterStaleOperations = context.apiSuccess(MODULE, '旧库存快照冲突后回查 PDA 操作历史', 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 699c1e32a..7028668b5 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 @@ -301,6 +301,8 @@ test('phase3 loading proves mixed-line rollback across task, all items and opera ]) 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', () => { @@ -362,6 +364,9 @@ test('downstream producers prefer the phase3Loading container fixture', () => { 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.doesNotMatch(serviceCenter, /customerName: `服务中心链路客户-/) const schedules = source('./phase3-port-schedules.mjs') assert.match(schedules, /allocatedContainerBookingIds: \(detail\.containerAllocations \|\| \[\]\)/) assert.match(schedules, /ocean\.allocatedContainerBookingIds\?\.includes\(containerBookingId\)/) diff --git a/scripts/admin-test-env-modules/service-center-flow.mjs b/scripts/admin-test-env-modules/service-center-flow.mjs index 181d33732..37da9e2db 100644 --- a/scripts/admin-test-env-modules/service-center-flow.mjs +++ b/scripts/admin-test-env-modules/service-center-flow.mjs @@ -49,8 +49,11 @@ export async function run(context) { const oceanResponse = await context.request('GET', `${OCEAN}/${ocean.id}`) const oceanDetail = context.apiSuccess(MODULE, '读取跨服务链路订舱真实客户归属', oceanResponse, `oceanBookingId=${ocean.id}`, OPTIONS) const customerId = id(oceanDetail?.oceanBooking?.customerId) - context.assert(MODULE, '跨服务链路订舱客户归属有效', oceanResponse, Boolean(customerId), `customerId=${customerId || 'missing'}`, OPTIONS) - if (!customerId) return + const customerName = String(oceanDetail?.oceanBooking?.customerName || '').trim() + context.assert(MODULE, '跨服务链路订舱客户归属有效', oceanResponse, + Boolean(customerId) && Boolean(customerName), + `customerId=${customerId || 'missing'}, customerName=${customerName || 'missing'}`, OPTIONS) + if (!customerId || !customerName) return const linkContainer = (containerFlow?.finalStatus === 'COMPLETED' || containerFlow?.finalStatus === 'LOADED') && id(containerFlow?.tenantId) === customerId && Boolean(containerFlow?.containerBookingId) @@ -75,7 +78,7 @@ export async function run(context) { warehouseId: linkContainer ? id(containerFlow.warehouseId) : id(context.createdResources?.warehouse?.id || context.context?.warehouseId || '193000000000000001'), - customerId, customerName: `服务中心链路客户-${suffix}`, carrierId: null, carrierName: `链路承运商-${suffix}`, + customerId, customerName, carrierId: null, carrierName: `链路承运商-${suffix}`, fleetContact: '链路车队联系人', fleetPhone: '13800000007', driverName: '链路司机', driverPhone: '13800000008', driverIdCard: null, vehicleNo: `粤D-${suffix.slice(-6)}`, storeName: `链路门店-${suffix}`, storeContact: '链路联系人', storePhone: '13800000009', storeAddress: '深圳市南山区链路地址', storeCity: '深圳市', storeZipcode: '518000',