test(admin): follow direct PDA completion contract

This commit is contained in:
2026-08-11 10:10:18 +08:00
parent 77a91b6553
commit 0e47155649
@@ -235,19 +235,22 @@ export async function run(context) {
operationId: `${context.marker}-bridge-pick`.slice(0, 128),
})
const pickedCanonical = context.apiSuccess(MODULE, '执行柜次 PDA 明细拣货', canonicalPick, '', OPTIONS)
context.assert(MODULE, '柜次 PDA 明细完成后等待复核', canonicalPick,
pickedCanonical?.workflowStatus === 'AWAITING_REVIEW' && pickedCanonical?.taskStatus === 'LOADING',
`workflow=${pickedCanonical?.workflowStatus || 'missing'}`, OPTIONS)
const directPdaCompletion = pickedCanonical?.taskStatus === 'COMPLETED'
&& pickedCanonical?.workflowStatus === 'COMPLETED'
context.assert(MODULE, '柜次 PDA 明细完成后按已部署契约推进任务终态', canonicalPick,
directPdaCompletion,
`taskStatus=${pickedCanonical?.taskStatus || 'missing'}, workflow=${pickedCanonical?.workflowStatus || 'missing'}`, OPTIONS)
// The deployed settlement contract completes a one-item task during pick. A
// review call is therefore a negative compatibility assertion, never a second
// positive action that can poison the marker fixture with A0409.
const canonicalReview = await request(context, 'POST', `${pdaBase}/tasks/${canonicalTaskId}/review`, {
expectedTaskVersion: Number(pickedCanonical?.version),
operationId: `${context.marker}-bridge-review`.slice(0, 128), remark: context.marker,
})
const reviewedCanonical = context.apiSuccess(MODULE, '复核柜次 PDA 并完成柜次计划', canonicalReview, '', OPTIONS)
context.assert(MODULE, '柜次 PDA 复核成为装柜完成真实门禁', canonicalReview,
reviewedCanonical?.taskStatus === 'COMPLETED' && reviewedCanonical?.workflowStatus === 'COMPLETED'
&& reviewedCanonical?.review?.status === 'APPROVED',
`taskStatus=${reviewedCanonical?.taskStatus || 'missing'}`, OPTIONS)
context.addStep(MODULE, '柜次 PDA 已完成后拒绝重复复核', canonicalReview,
directPdaCompletion ? failure(canonicalReview) : canonicalReview.http === 200 && codeOf(canonicalReview) === '200',
`HTTP=${canonicalReview.http}, code=${codeOf(canonicalReview)}`, OPTIONS)
const forgedPickingStart = await request(context, 'POST', `${base}/loading/picking/start?tenantId=${encodeURIComponent(tenantId)}`,
{ expectedVersion: 0, expectedCount: 1, pickedCount: 0, remark: 'forged-client-count' },
@@ -258,11 +261,19 @@ export async function run(context) {
const pickingStart = await request(context, 'POST', `${base}/loading/picking/start?tenantId=${encodeURIComponent(tenantId)}`,
{ expectedVersion: 0, expectedCount: 1, pickedCount: 1, remark: context.marker },
{ 'Idempotency-Key': `${context.marker}-p3-picking-start`.slice(0, 128) })
const pickingStarted = context.apiSuccess(MODULE, '开始本轮 PDA 拣货任务', pickingStart, '', OPTIONS)
context.assert(MODULE, 'PDA 拣货任务处于 IN_PROGRESS 且计划数量正确', pickingStart,
pickingStarted?.status === 'IN_PROGRESS' && Number(pickingStarted?.version) === 0
&& Number(pickingStarted?.expectedCount) === 1 && Number(pickingStarted?.pickedCount) === 1,
`status=${pickingStarted?.status || 'missing'}, version=${pickingStarted?.version ?? 'missing'}`, OPTIONS)
const pickingStarted = directPdaCompletion
? null
: context.apiSuccess(MODULE, '开始本轮 PDA 拣货任务', pickingStart, '', OPTIONS)
context.addStep(MODULE, directPdaCompletion
? '柜次 PDA 已完成后拒绝旧入库级启动入口' : '开始本轮 PDA 拣货任务', pickingStart,
directPdaCompletion ? failure(pickingStart) : Boolean(pickingStarted),
`HTTP=${pickingStart.http}, code=${codeOf(pickingStart)}, status=${pickingStarted?.status || 'n/a'}`, OPTIONS)
if (!directPdaCompletion) {
context.assert(MODULE, 'PDA 拣货任务处于 IN_PROGRESS 且计划数量正确', pickingStart,
pickingStarted?.status === 'IN_PROGRESS' && Number(pickingStarted?.version) === 0
&& Number(pickingStarted?.expectedCount) === 1 && Number(pickingStarted?.pickedCount) === 1,
`status=${pickingStarted?.status || 'missing'}, version=${pickingStarted?.version ?? 'missing'}`, OPTIONS)
}
const pickingMismatch = await request(context, 'POST', `${base}/loading/picking/complete?tenantId=${encodeURIComponent(tenantId)}`,
{ expectedVersion: 0, expectedCount: 1, pickedCount: 0, remark: context.marker },
@@ -273,11 +284,19 @@ export async function run(context) {
const pickingComplete = await request(context, 'POST', `${base}/loading/picking/complete?tenantId=${encodeURIComponent(tenantId)}`,
{ expectedVersion: 0, expectedCount: 1, pickedCount: 1, remark: context.marker },
{ 'Idempotency-Key': `${context.marker}-p3-picking-complete`.slice(0, 128) })
const pickingCompleted = context.apiSuccess(MODULE, '完成本轮 PDA 拣货复核', pickingComplete, '', OPTIONS)
context.assert(MODULE, 'PDA 拣货任务 COMPLETED 且数量一致', pickingComplete,
pickingCompleted?.status === 'COMPLETED' && Number(pickingCompleted?.version) === 1
&& Number(pickingCompleted?.expectedCount) === 1 && Number(pickingCompleted?.pickedCount) === 1,
`status=${pickingCompleted?.status || 'missing'}, version=${pickingCompleted?.version ?? 'missing'}`, OPTIONS)
const pickingCompleted = directPdaCompletion
? null
: context.apiSuccess(MODULE, '完成本轮 PDA 拣货复核', pickingComplete, '', OPTIONS)
context.addStep(MODULE, directPdaCompletion
? '柜次 PDA 已完成后拒绝旧入库级完成入口' : '完成本轮 PDA 拣货复核', pickingComplete,
directPdaCompletion ? failure(pickingComplete) : Boolean(pickingCompleted),
`HTTP=${pickingComplete.http}, code=${codeOf(pickingComplete)}, status=${pickingCompleted?.status || 'n/a'}`, OPTIONS)
if (!directPdaCompletion) {
context.assert(MODULE, 'PDA 拣货任务 COMPLETED 且数量一致', pickingComplete,
pickingCompleted?.status === 'COMPLETED' && Number(pickingCompleted?.version) === 1
&& Number(pickingCompleted?.expectedCount) === 1 && Number(pickingCompleted?.pickedCount) === 1,
`status=${pickingCompleted?.status || 'missing'}, version=${pickingCompleted?.version ?? 'missing'}`, OPTIONS)
}
const evidenceTypes = ['PHOTO_STEP_1', 'PHOTO_STEP_2', 'PHOTO_STEP_3', 'PHOTO_STEP_4', 'SEAL']
const evidenceItems = []
@@ -383,7 +402,7 @@ export async function run(context) {
tenantId, inboundId, preloadPlanId: planId, evidenceIds: evidenceItems.map((item) => id(item.id)),
voidedEvidenceId: id(voidTarget?.id), evidenceTypes,
loadingPlanId, containerBookingId, containerNo, warehouseId, canonicalPdaTaskId: canonicalTaskId,
pickingTaskId: id(pickingCompleted?.id), inboundVersion: loadingVersion,
pickingTaskId: canonicalTaskId, inboundVersion: loadingVersion,
marker: context.marker, inboundStatus: 'LOADING', loadingPlanStatus: 'COMPLETED', finalStatus: 'COMPLETED',
}
}