test(admin): align phase3 PDA direct completion fixtures

This commit is contained in:
2026-08-11 11:11:57 +08:00
parent 166ebfb7ef
commit fe0f1665e5
3 changed files with 34 additions and 26 deletions
@@ -207,15 +207,17 @@ export async function run(context) {
} }
const pickResponse = await request(context, 'POST', `${PDA}/tasks/${taskId}/items/${id(pickItem.id)}/pick`, pickPayload) const pickResponse = await request(context, 'POST', `${PDA}/tasks/${taskId}/items/${id(pickItem.id)}/pick`, pickPayload)
const picked = context.apiSuccess(MODULE, '执行本轮 PDA 全量拣货', pickResponse, '', OPTIONS) const picked = context.apiSuccess(MODULE, '执行本轮 PDA 全量拣货', pickResponse, '', OPTIONS)
context.assert(MODULE, '最后一箱拣货后 PDA 任务等待复核', pickResponse, const directPdaCompletion = picked?.taskStatus === 'COMPLETED'
picked?.taskStatus === 'LOADING' && picked?.workflowStatus === 'AWAITING_REVIEW' && picked?.workflowStatus === 'COMPLETED'
context.assert(MODULE, '最后一箱拣货后 PDA 任务按已部署契约直接完成', pickResponse,
directPdaCompletion
&& picked?.items?.every?.((item) => Number(item.pickedPackageCount) === Number(item.plannedPackageCount)), && picked?.items?.every?.((item) => Number(item.pickedPackageCount) === Number(item.plannedPackageCount)),
`taskStatus=${picked?.taskStatus || 'missing'}, workflow=${picked?.workflowStatus || 'missing'}`, OPTIONS) `taskStatus=${picked?.taskStatus || 'missing'}, workflow=${picked?.workflowStatus || 'missing'}`, OPTIONS)
const pickReplayResponse = await request(context, 'POST', `${PDA}/tasks/${taskId}/items/${id(pickItem.id)}/pick`, pickPayload) const pickReplayResponse = await request(context, 'POST', `${PDA}/tasks/${taskId}/items/${id(pickItem.id)}/pick`, pickPayload)
const pickReplay = context.apiSuccess(MODULE, 'PDA operationId 幂等重放', pickReplayResponse, '', OPTIONS) const pickReplay = context.apiSuccess(MODULE, 'PDA operationId 幂等重放', pickReplayResponse, '', OPTIONS)
context.assert(MODULE, 'PDA 重放保持待复核任务和拣货数', pickReplayResponse, context.assert(MODULE, 'PDA 重放保持完成任务和拣货数', pickReplayResponse,
pickReplay?.taskStatus === 'LOADING' && pickReplay?.workflowStatus === 'AWAITING_REVIEW' pickReplay?.taskStatus === 'COMPLETED' && pickReplay?.workflowStatus === 'COMPLETED'
&& pickReplay?.items?.every?.((item) => Number(item.pickedPackageCount) === Number(item.plannedPackageCount)), && pickReplay?.items?.every?.((item) => Number(item.pickedPackageCount) === Number(item.plannedPackageCount)),
`taskStatus=${pickReplay?.taskStatus || 'missing'}`, OPTIONS) `taskStatus=${pickReplay?.taskStatus || 'missing'}`, OPTIONS)
@@ -223,11 +225,9 @@ export async function run(context) {
expectedTaskVersion: Number(picked?.version), operationId: `${context.marker}-pda-review`.slice(0, 128), expectedTaskVersion: Number(picked?.version), operationId: `${context.marker}-pda-review`.slice(0, 128),
remark: context.marker, remark: context.marker,
}) })
const reviewed = context.apiSuccess(MODULE, '复核本轮柜次 PDA 任务', reviewResponse, '', OPTIONS) context.addStep(MODULE, 'PDA 已完成后拒绝重复复核', reviewResponse,
context.assert(MODULE, '复核后任务与柜次计划进入真实完成态', reviewResponse, directPdaCompletion && reviewResponse.http === 200 && codeOf(reviewResponse) === 'A0409',
reviewed?.taskStatus === 'COMPLETED' && reviewed?.workflowStatus === 'COMPLETED' `HTTP=${reviewResponse.http}, code=${codeOf(reviewResponse)}`, OPTIONS)
&& reviewed?.review?.status === 'APPROVED' && Boolean(reviewed?.review?.reviewedBy),
`taskStatus=${reviewed?.taskStatus || 'missing'}, review=${reviewed?.review?.status || 'missing'}`, OPTIONS)
const finalPlanResponse = await context.request('GET', `${SMART}/plans/${planId}`) const finalPlanResponse = await context.request('GET', `${SMART}/plans/${planId}`)
const finalPlan = context.apiSuccess(MODULE, '回查柜次装柜计划终态', finalPlanResponse, '', OPTIONS) const finalPlan = context.apiSuccess(MODULE, '回查柜次装柜计划终态', finalPlanResponse, '', OPTIONS)
@@ -263,7 +263,7 @@ export async function run(context) {
context.createdResources.phase3ContainerLoading = { context.createdResources.phase3ContainerLoading = {
marker: context.marker, tenantId, inboundId, warehouseId, containerBookingId: containerId, marker: context.marker, tenantId, inboundId, warehouseId, containerBookingId: containerId,
loadingPlanId: planId, pdaTaskId: taskId, pdaReviewVersion: reviewed?.review?.version, loadingPlanId: planId, pdaTaskId: taskId,
finalStatus: 'COMPLETED', finalStatus: 'COMPLETED',
} }
} }
@@ -249,7 +249,9 @@ export async function run(context) {
operationId: `${context.marker}-bridge-review`.slice(0, 128), remark: context.marker, operationId: `${context.marker}-bridge-review`.slice(0, 128), remark: context.marker,
}) })
context.addStep(MODULE, '柜次 PDA 已完成后拒绝重复复核', canonicalReview, context.addStep(MODULE, '柜次 PDA 已完成后拒绝重复复核', canonicalReview,
directPdaCompletion ? failure(canonicalReview) : canonicalReview.http === 200 && codeOf(canonicalReview) === '200', directPdaCompletion
? canonicalReview.http === 200 && codeOf(canonicalReview) === 'A0409'
: canonicalReview.http === 200 && codeOf(canonicalReview) === '200',
`HTTP=${canonicalReview.http}, code=${codeOf(canonicalReview)}`, OPTIONS) `HTTP=${canonicalReview.http}, code=${codeOf(canonicalReview)}`, OPTIONS)
const forgedPickingStart = await request(context, 'POST', `${base}/loading/picking/start?tenantId=${encodeURIComponent(tenantId)}`, const forgedPickingStart = await request(context, 'POST', `${base}/loading/picking/start?tenantId=${encodeURIComponent(tenantId)}`,
@@ -266,7 +268,9 @@ export async function run(context) {
: context.apiSuccess(MODULE, '开始本轮 PDA 拣货任务', pickingStart, '', OPTIONS) : context.apiSuccess(MODULE, '开始本轮 PDA 拣货任务', pickingStart, '', OPTIONS)
context.addStep(MODULE, directPdaCompletion context.addStep(MODULE, directPdaCompletion
? '柜次 PDA 已完成后拒绝旧入库级启动入口' : '开始本轮 PDA 拣货任务', pickingStart, ? '柜次 PDA 已完成后拒绝旧入库级启动入口' : '开始本轮 PDA 拣货任务', pickingStart,
directPdaCompletion ? failure(pickingStart) : Boolean(pickingStarted), directPdaCompletion
? pickingStart.http === 200 && codeOf(pickingStart) === 'A0409'
: Boolean(pickingStarted),
`HTTP=${pickingStart.http}, code=${codeOf(pickingStart)}, status=${pickingStarted?.status || 'n/a'}`, OPTIONS) `HTTP=${pickingStart.http}, code=${codeOf(pickingStart)}, status=${pickingStarted?.status || 'n/a'}`, OPTIONS)
if (!directPdaCompletion) { if (!directPdaCompletion) {
context.assert(MODULE, 'PDA 拣货任务处于 IN_PROGRESS 且计划数量正确', pickingStart, context.assert(MODULE, 'PDA 拣货任务处于 IN_PROGRESS 且计划数量正确', pickingStart,
@@ -289,7 +293,9 @@ export async function run(context) {
: context.apiSuccess(MODULE, '完成本轮 PDA 拣货复核', pickingComplete, '', OPTIONS) : context.apiSuccess(MODULE, '完成本轮 PDA 拣货复核', pickingComplete, '', OPTIONS)
context.addStep(MODULE, directPdaCompletion context.addStep(MODULE, directPdaCompletion
? '柜次 PDA 已完成后拒绝旧入库级完成入口' : '完成本轮 PDA 拣货复核', pickingComplete, ? '柜次 PDA 已完成后拒绝旧入库级完成入口' : '完成本轮 PDA 拣货复核', pickingComplete,
directPdaCompletion ? failure(pickingComplete) : Boolean(pickingCompleted), directPdaCompletion
? pickingComplete.http === 200 && codeOf(pickingComplete) === 'A0409'
: Boolean(pickingCompleted),
`HTTP=${pickingComplete.http}, code=${codeOf(pickingComplete)}, status=${pickingCompleted?.status || 'n/a'}`, OPTIONS) `HTTP=${pickingComplete.http}, code=${codeOf(pickingComplete)}, status=${pickingCompleted?.status || 'n/a'}`, OPTIONS)
if (!directPdaCompletion) { if (!directPdaCompletion) {
context.assert(MODULE, 'PDA 拣货任务 COMPLETED 且数量一致', pickingComplete, context.assert(MODULE, 'PDA 拣货任务 COMPLETED 且数量一致', pickingComplete,
@@ -19,20 +19,21 @@ export async function run(context) {
const fixture = context.createdResources?.phase3Loading const fixture = context.createdResources?.phase3Loading
const tenantId = id(fixture?.tenantId); const inboundId = id(fixture?.inboundId) const tenantId = id(fixture?.tenantId); const inboundId = id(fixture?.inboundId)
const taskId = id(fixture?.canonicalPdaTaskId); const containerNo = fixture?.containerNo const taskId = id(fixture?.canonicalPdaTaskId); const containerNo = fixture?.containerNo
const warehouseId = id(fixture?.warehouseId) const warehouseId = id(fixture?.warehouseId); const loadingPlanId = id(fixture?.loadingPlanId)
if (!tenantId || !inboundId || !taskId || !containerNo || !warehouseId || fixture?.marker !== context.marker) { if (!tenantId || !inboundId || !taskId || !containerNo || !warehouseId || !loadingPlanId
|| fixture?.marker !== context.marker) {
context.addCoverageGap(MODULE, OPTIONS.scenario, 'PDA 柜次拣货与入库单汇总拣货收敛', context.addCoverageGap(MODULE, OPTIONS.scenario, 'PDA 柜次拣货与入库单汇总拣货收敛',
'本轮未取得 marker 隔离的柜次 PDA 与入库汇总 fixture,未使用历史资源', 'fixture') '本轮未取得 marker 隔离的柜次 PDA 与入库汇总 fixture,未使用历史资源', 'fixture')
return return
} }
const taskResponse = await request(context, 'GET', const loadingListResponse = await request(context, 'GET',
`/api/platform/v1/first-leg/pda-loading/tasks?containerNo=${encodeURIComponent(containerNo)}&warehouseId=${encodeURIComponent(warehouseId)}`) `/api/platform/v1/first-leg/loading-lists/${loadingPlanId}?warehouseId=${encodeURIComponent(warehouseId)}`)
const task = context.apiSuccess(MODULE, '回查柜次 PDA 唯一事实任务', taskResponse, '', OPTIONS) const loadingList = context.apiSuccess(MODULE, '回查柜次 PDA 完成后的装柜清单', loadingListResponse, '', OPTIONS)
context.assert(MODULE, '柜次任务已领取、复核且明细数量一致', taskResponse, context.assert(MODULE, '装柜清单保留 PDA 完成终态和操作者审计', loadingListResponse,
id(task?.id) === taskId && task?.taskStatus === 'COMPLETED' && task?.workflowStatus === 'COMPLETED' id(loadingList?.summary?.planId) === loadingPlanId && loadingList?.summary?.status === 'COMPLETED'
&& Boolean(task?.assignment?.assigneeId) && task?.review?.status === 'APPROVED' && loadingList?.histories?.some?.((item) => item.actionType === 'PDA_COMPLETE_LOADING'
&& task?.items?.every?.((item) => Number(item.plannedPackageCount) === Number(item.pickedPackageCount)), && Boolean(item.operatorName) && Boolean(item.occurredAt)),
`taskStatus=${task?.taskStatus || 'missing'}, workflow=${task?.workflowStatus || 'missing'}`, OPTIONS) `planId=${id(loadingList?.summary?.planId) || 'missing'}, status=${loadingList?.summary?.status || 'missing'}`, OPTIONS)
const projectionResponse = await request(context, 'GET', const projectionResponse = await request(context, 'GET',
`/api/platform/v1/inbounds/${inboundId}/loading/picking?tenantId=${encodeURIComponent(tenantId)}`) `/api/platform/v1/inbounds/${inboundId}/loading/picking?tenantId=${encodeURIComponent(tenantId)}`)
const projection = context.apiSuccess(MODULE, '查询入库级 PDA 服务端聚合投影', projectionResponse, '', OPTIONS) const projection = context.apiSuccess(MODULE, '查询入库级 PDA 服务端聚合投影', projectionResponse, '', OPTIONS)
@@ -43,9 +44,10 @@ export async function run(context) {
const operationsResponse = await request(context, 'GET', const operationsResponse = await request(context, 'GET',
`/api/platform/v1/first-leg/pda-loading/tasks/${taskId}/operations`) `/api/platform/v1/first-leg/pda-loading/tasks/${taskId}/operations`)
const operations = context.apiSuccess(MODULE, '查询柜次 PDA 不可变操作历史', operationsResponse, '', OPTIONS) const operations = context.apiSuccess(MODULE, '查询柜次 PDA 不可变操作历史', operationsResponse, '', OPTIONS)
context.assert(MODULE, '领取和复核操作历史含操作者与时间', operationsResponse, context.assert(MODULE, '直接完成契约保留领取操作且不伪造复核历史', operationsResponse,
Array.isArray(operations) && ['CLAIM', 'ITEM_CLAIM', 'REVIEW'].every((action) => operations.some((item) => item.actionType === action Array.isArray(operations) && operations.some((item) => item.actionType === 'CLAIM'
&& Boolean(item.operatorId) && Boolean(item.createdAt))), && Boolean(item.operatorId) && Boolean(item.createdAt))
&& !operations.some((item) => item.actionType === 'REVIEW'),
`actions=${Array.isArray(operations) ? operations.map((item) => item.actionType).join(',') : 'missing'}`, OPTIONS) `actions=${Array.isArray(operations) ? operations.map((item) => item.actionType).join(',') : 'missing'}`, OPTIONS)
const forged = await request(context, 'POST', const forged = await request(context, 'POST',
`/api/platform/v1/inbounds/${inboundId}/loading/picking/start?tenantId=${encodeURIComponent(tenantId)}`, `/api/platform/v1/inbounds/${inboundId}/loading/picking/start?tenantId=${encodeURIComponent(tenantId)}`,