68 lines
4.2 KiB
JavaScript
68 lines
4.2 KiB
JavaScript
import test from 'node:test'
|
|
import assert from 'node:assert/strict'
|
|
import { outboundWarehouseTransfer } from './warehouse-transfer-outbound.mjs'
|
|
|
|
// 替身返回独立的单据与库存证据,验证错误流水不能被成功状态掩盖。
|
|
function fixture(fault = '') {
|
|
const allocation = { inventoryPositionId: '301', palletId: '401', palletNo: 'P-1', packageCount: 1, skuCode: 'NEW', inboundCode: 'IN-1', status: 'RESERVED' }
|
|
const proof = { marker: 'BATCH', customerId: '101', warehouseId: '201', transferNo: 'WT-1', relabelNo: 'RL-1', relabelApplicationId: '501', inboundCode: 'IN-1',
|
|
status: 'ALLOCATED', outboundAllocations: [allocation], relabelFulfillment: { execution: { status: 'HTTP_VERIFIED_READY_FOR_TRANSFER_OUTBOUND' } } }
|
|
const calls = [], snapshots = []
|
|
let shipped = false, writes = 0
|
|
const context = { marker: 'BATCH', context: { merchantQualificationFixture: { tenantId: '101' } },
|
|
createdResources: { phase4OverseasClientServices: { transferFulfillment: proof } }, assert() {},
|
|
async request(method, path, body) {
|
|
calls.push({ method, path, body })
|
|
let data
|
|
if (method === 'POST') {
|
|
writes++
|
|
if (fault === 'network') throw Error('connection lost')
|
|
shipped = true
|
|
}
|
|
if (path.includes('/ledger?')) {
|
|
const rows = shipped || fault === 'existing shipment' ? [{ id: '601', palletNo: fault === 'wrong pallet' ? 'P-OTHER' : 'P-1', customerId: fault === 'foreign customer' ? '999' : '101',
|
|
overseasWarehouseId: '201', skuCode: fault === 'old sku' ? 'OLD' : 'NEW', inboundCode: 'IN-1', businessType: 'SHIP', businessOrderNo: 'WT-1', deltaPackages: -1 }] : []
|
|
if (fault === 'double shipment' && shipped) rows.push({ ...rows[0], id: '602' })
|
|
if (fault === 'replay ledger' && writes === 2) rows[0].id = '699'
|
|
data = { total: rows.length, records: rows }
|
|
} else if (path.includes('/relabel-applications/')) data = { application: { relabelNo: 'RL-1', customerOrderNo: 'WT-1', status: 'READY_FOR_OUTBOUND', fulfillmentStatus: fault === 'wrong relabel' ? 'COMPLETED' : 'OUTBOUNDED' } }
|
|
else data = { transferNo: 'WT-1', status: shipped ? 'OUTBOUNDED' : 'ALLOCATED', relabelNo: 'RL-1', relabelApplicationId: '501', operatingWarehouseId: '201',
|
|
packageCount: 1, fbaWarehouseCode: 'FBA-1', outboundAt: shipped ? '2026-10-22T12:00:00' : null,
|
|
allocations: [{ ...allocation, status: shipped ? 'SHIPPED' : 'RESERVED' }] }
|
|
return { http: 200, body: { code: '200', data } }
|
|
} }
|
|
const options = { proof, merchantToken: 'secret', checkpoint: async () => { snapshots.push(structuredClone(proof)); if (fault === 'disk') throw Error('disk full') } }
|
|
return { context, options, proof, calls, snapshots }
|
|
}
|
|
|
|
test('原转运出库验证同单新SKU扣库及重放唯一性', async () => {
|
|
const f = fixture()
|
|
await outboundWarehouseTransfer(f.context, f.options)
|
|
assert.equal(f.proof.status, 'OUTBOUNDED')
|
|
assert.equal(f.proof.outbound.status, 'OUTBOUNDED_HTTP_VERIFIED')
|
|
assert.equal(f.calls.filter(c => c.method === 'POST').length, 2)
|
|
assert.equal(JSON.stringify(f.snapshots).includes('secret'), false)
|
|
const count = f.calls.length
|
|
await assert.rejects(outboundWarehouseTransfer(f.context, f.options))
|
|
assert.equal(f.calls.length, count)
|
|
})
|
|
|
|
for (const fault of ['network', 'existing shipment', 'foreign customer', 'wrong pallet', 'old sku', 'double shipment', 'replay ledger', 'wrong relabel', 'disk']) {
|
|
test(`原转运出库拒绝 ${fault}`, async () => {
|
|
const f = fixture(fault)
|
|
await assert.rejects(outboundWarehouseTransfer(f.context, f.options))
|
|
assert.equal(f.proof.outbound.status, 'FAILED')
|
|
assert.equal(f.proof.status, 'ALLOCATED')
|
|
const writes = f.calls.filter(c => c.method === 'POST').length
|
|
assert.equal(writes, ['disk', 'existing shipment'].includes(fault) ? 0 : ['replay ledger', 'wrong relabel'].includes(fault) ? 2 : 1)
|
|
if (fault === 'network') assert.equal(f.proof.outbound.requests.at(-1).status, 'UNKNOWN_RESULT')
|
|
})
|
|
}
|
|
|
|
test('原转运未完成换标不得发出请求', async () => {
|
|
const f = fixture()
|
|
f.proof.relabelFulfillment.execution.status = 'FAILED'
|
|
await assert.rejects(outboundWarehouseTransfer(f.context, f.options))
|
|
assert.equal(f.calls.length, 0)
|
|
})
|