test: adapt PDA loading completion contract

This commit is contained in:
2026-08-12 13:48:46 +08:00
parent 584b73faf5
commit 49967bbc28
2 changed files with 32 additions and 9 deletions
@@ -237,33 +237,50 @@ export async function run(context) {
const batchHeaders = { 'Idempotency-Key': `${context.marker}-pda-batch`.slice(0, 128) }
const pickResponse = await request(context, 'POST', batchPath, batchPayload, batchHeaders)
const picked = context.apiSuccess(MODULE, '单次调用 PDA 原子 batch 完成拣货', pickResponse, '', OPTIONS)
const directPdaCompletion = picked?.taskStatus === 'COMPLETED'
&& picked?.workflowStatus === 'COMPLETED'
context.assert(MODULE, '最后一箱拣货后 PDA 任务按已部署契约直接完成', pickResponse,
directPdaCompletion
const awaitingReview = picked?.taskStatus === 'LOADING'
&& picked?.workflowStatus === 'AWAITING_REVIEW'
context.assert(MODULE, '最后一箱拣货后 PDA 任务等待人工确认装柜完成', pickResponse,
awaitingReview
&& picked?.items?.every?.((item) => Number(item.pickedPackageCount) === Number(item.plannedPackageCount)),
`taskStatus=${picked?.taskStatus || 'missing'}, workflow=${picked?.workflowStatus || 'missing'}`, OPTIONS)
const pickReplayResponse = await request(context, 'POST', batchPath, batchPayload, batchHeaders)
const pickReplay = context.apiSuccess(MODULE, 'PDA batch Idempotency-Key 幂等重放', pickReplayResponse, '', OPTIONS)
context.assert(MODULE, 'PDA batch 重放保持完成任务和拣货数', pickReplayResponse,
pickReplay?.taskStatus === 'COMPLETED' && pickReplay?.workflowStatus === 'COMPLETED'
context.assert(MODULE, 'PDA batch 重放保持待确认任务和拣货数', pickReplayResponse,
pickReplay?.taskStatus === 'LOADING' && pickReplay?.workflowStatus === 'AWAITING_REVIEW'
&& pickReplay?.items?.every?.((item) => Number(item.pickedPackageCount) === Number(item.plannedPackageCount)),
`taskStatus=${pickReplay?.taskStatus || 'missing'}`, OPTIONS)
const completePath = `${PDA}/tasks/${taskId}/complete`
const completePayload = {
expectedTaskVersion: Number(picked?.version),
operationId: `${context.marker}-pda-complete`.slice(0, 128),
remark: context.marker,
}
const completeResponse = await request(context, 'POST', completePath, completePayload)
const completed = context.apiSuccess(MODULE, 'PDA 人工确认装柜完成', completeResponse, '', OPTIONS)
const directPdaCompletion = completed?.taskStatus === 'COMPLETED'
&& completed?.workflowStatus === 'COMPLETED'
context.assert(MODULE, '人工确认后 PDA 任务进入 COMPLETED', completeResponse,
directPdaCompletion
&& completed?.items?.every?.((item) => Number(item.pickedPackageCount) === Number(item.plannedPackageCount)),
`taskStatus=${completed?.taskStatus || 'missing'}, workflow=${completed?.workflowStatus || 'missing'}`, OPTIONS)
const operationsResponse = await request(context, 'GET', `${PDA}/tasks/${taskId}/operations`)
const operations = context.apiSuccess(MODULE, '回查完成后的 PDA 操作审计', operationsResponse, '', OPTIONS)
context.assert(MODULE, 'PDA 完成审计保留领取和原子批次操作者记录', operationsResponse,
context.assert(MODULE, 'PDA 完成审计保留领取、原子批次和人工完成记录', operationsResponse,
Array.isArray(operations)
&& operations.some((item) => item.actionType === 'CLAIM'
&& Boolean(item.operatorId) && Boolean(item.createdAt))
&& operations.some((item) => item.actionType === 'BATCH'
&& Boolean(item.operatorId) && Boolean(item.createdAt))
&& operations.some((item) => item.actionType === 'COMPLETE_LOADING'
&& Boolean(item.operatorId) && Boolean(item.createdAt))
&& !operations.some((item) => item.actionType === 'REVIEW'),
`actions=${Array.isArray(operations) ? operations.map((item) => item.actionType).join(',') : 'missing'}`, OPTIONS)
const reviewResponse = await request(context, 'POST', `${PDA}/tasks/${taskId}/review`, {
expectedTaskVersion: Number(picked?.version), operationId: `${context.marker}-pda-review`.slice(0, 128),
expectedTaskVersion: Number(completed?.version), operationId: `${context.marker}-pda-review`.slice(0, 128),
remark: context.marker,
})
context.addStep(MODULE, 'PDA 已完成后拒绝重复复核', reviewResponse,
@@ -73,8 +73,13 @@ test('container loading follows push, claim, real pick, completed sequence', ()
"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', () => {
@@ -119,11 +124,12 @@ test('PDA inventory snapshot gates batch and records the only safe mutation gap'
assert.match(convergence, /fixture\?\.inventoryMutationVerified !== true/)
})
test('PDA completion acceptance verifies immutable BATCH audit with operator metadata', () => {
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'/)