2026-08-17 16:17:20 +08:00
|
|
|
import assert from 'node:assert/strict'
|
|
|
|
|
import { readFileSync } from 'node:fs'
|
|
|
|
|
import { dirname, resolve } from 'node:path'
|
|
|
|
|
import test from 'node:test'
|
|
|
|
|
import { fileURLToPath } from 'node:url'
|
|
|
|
|
|
|
|
|
|
import { buildPendingActions } from './phase3-pda-convergence.mjs'
|
|
|
|
|
|
|
|
|
|
const here = dirname(fileURLToPath(import.meta.url))
|
|
|
|
|
const read = (path) => readFileSync(resolve(here, path), 'utf8')
|
|
|
|
|
const convergence = read('./phase3-pda-convergence.mjs')
|
|
|
|
|
const mapper = read('../../firstleg/src/main/java/com/baiyue/firstleg/mapper/LoadingPickTaskMapper.java')
|
|
|
|
|
const openApi = read('../../firstleg/src/main/resources/openapi/phase3-pda-convergence-v1.yaml')
|
|
|
|
|
|
|
|
|
|
test('AWAITING_REVIEW derives zero client actions and never needs a repeated batch', () => {
|
|
|
|
|
assert.deepEqual(buildPendingActions({
|
|
|
|
|
workflowStatus: 'AWAITING_REVIEW',
|
|
|
|
|
items: [{ id: 1, itemStatus: 'PICKED', pickedPackageCount: 1 }],
|
|
|
|
|
}), [])
|
|
|
|
|
assert.deepEqual(buildPendingActions({
|
|
|
|
|
workflowStatus: 'IN_PROGRESS',
|
|
|
|
|
items: [{ id: 1, itemStatus: 'PICKED' }, { id: 2, itemStatus: 'PENDING' }],
|
|
|
|
|
}), [{ itemId: '2', action: 'PICK' }])
|
|
|
|
|
assert.match(convergence, /pending 与 detail 使用同一 workflowStatus 和未完成数量事实/)
|
|
|
|
|
assert.match(convergence, /AWAITING_REVIEW 已全拣任务 actions 为空且不再提交 batch/)
|
|
|
|
|
assert.match(convergence, /AWAITING_REVIEW 可进入装柜取证只读入口/)
|
2026-09-01 14:48:51 +08:00
|
|
|
assert.match(convergence, /\$\{taskPath\}\/evidence\?warehouseId=\$\{encodeURIComponent\(warehouseId\)\}/)
|
|
|
|
|
assert.doesNotMatch(convergence, /api\/platform\/v1\/inbounds\/\$\{inboundId\}\/loading\/evidence/)
|
2026-08-17 16:17:20 +08:00
|
|
|
assert.match(convergence, /filter\(\(item\) => item\.actionType === 'BATCH'/)
|
2026-09-04 17:17:08 +08:00
|
|
|
assert.match(convergence, /expectedBatchCount = fixture\?\.fulfillmentType === 'ONE_CLICK_DROPSHIP' \? 1 : 4/)
|
|
|
|
|
assert.match(convergence, /expectedBatchOperationIds\.length === expectedBatchCount/)
|
|
|
|
|
assert.match(convergence, /actualBatchOperationIds\.length === expectedBatchCount/)
|
|
|
|
|
assert.match(convergence, /new Set\(expectedBatchOperationIds\)\.size === expectedBatchCount/)
|
|
|
|
|
assert.match(convergence, /new Set\(actualBatchOperationIds\)\.size === expectedBatchCount/)
|
2026-08-25 13:59:58 +08:00
|
|
|
assert.match(convergence, /expectedBatchOperationIds\.every[\s\S]*actualBatchOperationIds\.every/)
|
|
|
|
|
assert.doesNotMatch(convergence, /dynamicIncrementVerified === true \? 2 : 1/)
|
2026-08-24 19:58:35 +08:00
|
|
|
assert.match(convergence, /LOADING 增量重开、刷新续拣与已拣不可删减/)
|
2026-08-17 16:17:20 +08:00
|
|
|
assert.doesNotMatch(convergence, /pendingActions[\s\S]{0,240}request\(context, 'POST'/)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
test('pending backend contract derives workflow from task items instead of assignment alone', () => {
|
|
|
|
|
assert.match(openApi,
|
2026-09-01 11:11:43 +08:00
|
|
|
/PendingTask:[\s\S]*workflowStatus: \{type: string, enum: \[UNASSIGNED, IN_PROGRESS, AWAITING_REVIEW, PROCESSING, COMPLETION_FAILED\][^\r\n]*}/)
|
2026-08-17 16:17:20 +08:00
|
|
|
assert.match(mapper,
|
2026-09-20 22:18:59 +08:00
|
|
|
/WHEN COUNT\(DISTINCT CASE WHEN pick_item\.item_status IN \('PENDING', 'PICKING'\)[\s\S]*= 0[\s\S]*THEN 'AWAITING_REVIEW'/)
|
2026-08-17 16:17:20 +08:00
|
|
|
assert.doesNotMatch(mapper,
|
|
|
|
|
/CASE WHEN assignment\.pick_task_id IS NULL THEN 'UNASSIGNED' ELSE 'IN_PROGRESS' END AS workflowStatus/)
|
|
|
|
|
})
|