test: adapt PDA loading completion contract
This commit is contained in:
@@ -237,33 +237,50 @@ export async function run(context) {
|
|||||||
const batchHeaders = { 'Idempotency-Key': `${context.marker}-pda-batch`.slice(0, 128) }
|
const batchHeaders = { 'Idempotency-Key': `${context.marker}-pda-batch`.slice(0, 128) }
|
||||||
const pickResponse = await request(context, 'POST', batchPath, batchPayload, batchHeaders)
|
const pickResponse = await request(context, 'POST', batchPath, batchPayload, batchHeaders)
|
||||||
const picked = context.apiSuccess(MODULE, '单次调用 PDA 原子 batch 完成拣货', pickResponse, '', OPTIONS)
|
const picked = context.apiSuccess(MODULE, '单次调用 PDA 原子 batch 完成拣货', pickResponse, '', OPTIONS)
|
||||||
const directPdaCompletion = picked?.taskStatus === 'COMPLETED'
|
const awaitingReview = picked?.taskStatus === 'LOADING'
|
||||||
&& picked?.workflowStatus === 'COMPLETED'
|
&& picked?.workflowStatus === 'AWAITING_REVIEW'
|
||||||
context.assert(MODULE, '最后一箱拣货后 PDA 任务按已部署契约直接完成', pickResponse,
|
context.assert(MODULE, '最后一箱拣货后 PDA 任务等待人工确认装柜完成', pickResponse,
|
||||||
directPdaCompletion
|
awaitingReview
|
||||||
&& 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', batchPath, batchPayload, batchHeaders)
|
const pickReplayResponse = await request(context, 'POST', batchPath, batchPayload, batchHeaders)
|
||||||
const pickReplay = context.apiSuccess(MODULE, 'PDA batch Idempotency-Key 幂等重放', pickReplayResponse, '', OPTIONS)
|
const pickReplay = context.apiSuccess(MODULE, 'PDA batch Idempotency-Key 幂等重放', pickReplayResponse, '', OPTIONS)
|
||||||
context.assert(MODULE, 'PDA batch 重放保持完成任务和拣货数', pickReplayResponse,
|
context.assert(MODULE, 'PDA batch 重放保持待确认任务和拣货数', pickReplayResponse,
|
||||||
pickReplay?.taskStatus === 'COMPLETED' && pickReplay?.workflowStatus === 'COMPLETED'
|
pickReplay?.taskStatus === 'LOADING' && pickReplay?.workflowStatus === 'AWAITING_REVIEW'
|
||||||
&& 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)
|
||||||
|
|
||||||
|
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 operationsResponse = await request(context, 'GET', `${PDA}/tasks/${taskId}/operations`)
|
||||||
const operations = context.apiSuccess(MODULE, '回查完成后的 PDA 操作审计', operationsResponse, '', OPTIONS)
|
const operations = context.apiSuccess(MODULE, '回查完成后的 PDA 操作审计', operationsResponse, '', OPTIONS)
|
||||||
context.assert(MODULE, 'PDA 完成审计保留领取和原子批次操作者记录', operationsResponse,
|
context.assert(MODULE, 'PDA 完成审计保留领取、原子批次和人工完成记录', operationsResponse,
|
||||||
Array.isArray(operations)
|
Array.isArray(operations)
|
||||||
&& operations.some((item) => item.actionType === 'CLAIM'
|
&& operations.some((item) => item.actionType === 'CLAIM'
|
||||||
&& Boolean(item.operatorId) && Boolean(item.createdAt))
|
&& Boolean(item.operatorId) && Boolean(item.createdAt))
|
||||||
&& operations.some((item) => item.actionType === 'BATCH'
|
&& operations.some((item) => item.actionType === 'BATCH'
|
||||||
&& Boolean(item.operatorId) && Boolean(item.createdAt))
|
&& 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'),
|
&& !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 reviewResponse = await request(context, 'POST', `${PDA}/tasks/${taskId}/review`, {
|
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,
|
remark: context.marker,
|
||||||
})
|
})
|
||||||
context.addStep(MODULE, 'PDA 已完成后拒绝重复复核', reviewResponse,
|
context.addStep(MODULE, 'PDA 已完成后拒绝重复复核', reviewResponse,
|
||||||
|
|||||||
@@ -73,8 +73,13 @@ test('container loading follows push, claim, real pick, completed sequence', ()
|
|||||||
"pushed?.status === 'EDITING'",
|
"pushed?.status === 'EDITING'",
|
||||||
"claimedPlan?.status === 'LOADING'",
|
"claimedPlan?.status === 'LOADING'",
|
||||||
"const pickResponse = await request",
|
"const pickResponse = await request",
|
||||||
|
"const completeResponse = await request",
|
||||||
"finalPlan?.status === 'COMPLETED'",
|
"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', () => {
|
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/)
|
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 container = source('./phase3-container-loading.mjs')
|
||||||
const convergence = source('./phase3-pda-convergence.mjs')
|
const convergence = source('./phase3-pda-convergence.mjs')
|
||||||
assert.match(container, /回查完成后的 PDA 操作审计/)
|
assert.match(container, /回查完成后的 PDA 操作审计/)
|
||||||
assert.match(container, /item\.actionType === 'BATCH'/)
|
assert.match(container, /item\.actionType === 'BATCH'/)
|
||||||
|
assert.match(container, /item\.actionType === 'COMPLETE_LOADING'/)
|
||||||
assert.match(container, /Boolean\(item\.operatorId\) && Boolean\(item\.createdAt\)/)
|
assert.match(container, /Boolean\(item\.operatorId\) && Boolean\(item\.createdAt\)/)
|
||||||
assert.match(convergence, /PDA 原子批次完成审计包含 BATCH 操作/)
|
assert.match(convergence, /PDA 原子批次完成审计包含 BATCH 操作/)
|
||||||
assert.match(convergence, /item\.actionType === 'BATCH'/)
|
assert.match(convergence, /item\.actionType === 'BATCH'/)
|
||||||
|
|||||||
Reference in New Issue
Block a user