test: extend eight-business acceptance flows and offline contracts

This commit is contained in:
2026-10-22 15:41:50 +08:00
parent 86889d7e0e
commit 080ca600e6
33 changed files with 1285 additions and 101 deletions
@@ -2,7 +2,7 @@ import test from 'node:test'
import assert from 'node:assert/strict'
import { verifyClientRelabelFulfillment } from './client-relabel-fulfillment.mjs'
function harness(fault = '') {
function harness(fault = '', transfer = false) {
const calls = [], saved = [], commands = new Set()
const proof = { marker: 'RELABEL-TEST', relabelNo: 'TB202610220001', customerId: '101', warehouseId: '201',
sourcePositionId: '301', sourcePalletNo: 'SOURCE', sourceOrderNo: 'WC202610220001' }
@@ -10,8 +10,9 @@ function harness(fault = '') {
const item = { itemId: '401', expectedPackages: 1, cartonMark: 'BOX-TEST', fbaCartonMark: 'FBA-TEST',
operationType: 'CARTON_AND_SKU', skuItems: [{ skuCode: 'SKU', newSkuCode: 'NEW', quantity: 1 }] }
const detail = () => ({ application: { relabelNo: proof.relabelNo, customerOrderNo: proof.sourceOrderNo,
lastlegOrderId: '1001', reviewChannel: transfer ? 'RELABEL_ORDER' : 'TAIL_ORDER',
overseasWarehouseId: fault === 'wrong warehouse' ? '999' : '201', status,
walletStatus: fault === 'no funds' ? 'PENDING' : 'FROZEN', fulfillmentStatus: status === 'READY_FOR_OUTBOUND' ? 'WAIT_PRE_DISPATCH' : null },
walletStatus: fault === 'no funds' ? 'PENDING' : 'FROZEN', fulfillmentStatus: status === 'READY_FOR_OUTBOUND' ? transfer && fault !== 'wrong fulfillment' ? 'WAIT_TRANSFER_OUTBOUND' : 'WAIT_PRE_DISPATCH' : null },
items: [{ sourceInventoryPositionId: '301', cartonMark: 'BOX-TEST' }] })
const task = () => ({ relabelNo: proof.relabelNo, taskStatus, version, applicationStatus: status,
relabelledPackages: scanned ? 1 : 0, skippedPackages: 0, exceptionPackages: 0, items: [item] })
@@ -24,7 +25,10 @@ function harness(fault = '') {
if (body?.idempotencyKey) commands.add(body.idempotencyKey)
let data
if (path.endsWith('/tenant/profile')) data = { id: fault === 'wrong customer' ? '999' : '101', status: 'ACTIVE' }
else if (path.endsWith('/status')) { status = 'PENDING_PROCESSING'; data = detail() }
else if (path.endsWith('/status') || path.includes('/relabel-review?')) {
assert.equal(path.endsWith('/status'), transfer)
status = 'PENDING_PROCESSING'; data = detail()
}
else if (path.endsWith('/claim')) { taskStatus = 'PROCESSING'; if (!repeated) version++; data = task() }
else if (path.endsWith('/cartons/scan')) { if (!repeated) version++; data = task() }
else if (path.endsWith('/units')) {
@@ -40,6 +44,9 @@ function harness(fault = '') {
else if (path.endsWith('/confirm-completion')) { status = 'READY_FOR_OUTBOUND'; data = detail() }
else if (path.endsWith('/complete')) { status = 'CUSTOMER_CONFIRM_PENDING'; taskStatus = 'WAIT_REVIEW'; data = task() }
else if (path.includes('/relabel-tasks/')) data = task()
else if (path.includes('/warehouse-transfers/')) data = { transferNo: proof.sourceOrderNo, status: 'ALLOCATED', relabelNo: proof.relabelNo,
relabelApplicationId: '801', allocations: [{ status: 'RESERVED', packageCount: 1, palletId: fault === 'wrong target' ? '999' : '501',
inboundCode: 'IN-1', inventoryPositionId: '901', skuCode: 'NEW' }] }
else data = detail()
return { http: 200, body: { code: '200', data } }
},
@@ -50,6 +57,11 @@ function harness(fault = '') {
} }
const inputs = { proof, merchantToken: 'client-secret', pdaToken: 'pda-secret', delay: async () => {},
checkpoint: async () => { if (fault === 'disk') throw new Error('disk full'); saved.push(structuredClone(proof.execution)) } }
if (transfer) {
inputs.transferProof = { ...proof, transferNo: proof.sourceOrderNo, status: 'WAIT_RELABEL', inboundCode: 'IN-1',
relabelApplicationId: '801', allocation: { status: 'ALLOCATED_WAIT_RELABEL' }, relabelFulfillment: proof }
context.createdResources = { phase4OverseasClientServices: { transferFulfillment: inputs.transferProof } }
}
return { context, inputs, calls, saved }
}
@@ -88,3 +100,27 @@ test('foreign-batch relabel is rejected before any HTTP', async () => {
await assert.rejects(verifyClientRelabelFulfillment(h.context, h.inputs), /本轮客户申请/)
assert.equal(h.calls.length, 0)
})
test('已有库存转运原关联换标完成后回到原转运新SKU预留', async () => {
const h = harness('', true)
await verifyClientRelabelFulfillment(h.context, h.inputs)
assert.equal(h.inputs.transferProof.status, 'ALLOCATED')
assert.equal(h.inputs.proof.execution.status, 'HTTP_VERIFIED_READY_FOR_TRANSFER_OUTBOUND')
assert.equal(h.inputs.transferProof.outboundAllocations[0].palletId, '501')
})
for (const fault of ['wrong fulfillment', 'wrong target']) {
test(`转运关联换标拒绝 ${fault}`, async () => {
const h = harness(fault, true)
await assert.rejects(verifyClientRelabelFulfillment(h.context, h.inputs))
assert.equal(h.inputs.transferProof.status, 'WAIT_RELABEL')
assert.equal(h.inputs.proof.execution.status, 'FAILED')
})
}
test('未登记转运分配证据不得操作关联换标', async () => {
const h = harness('', true)
h.inputs.transferProof = { ...h.inputs.transferProof }
await assert.rejects(verifyClientRelabelFulfillment(h.context, h.inputs), /原分配证据/)
assert.equal(h.calls.length, 0)
})