90 lines
5.1 KiB
JavaScript
90 lines
5.1 KiB
JavaScript
import test from 'node:test'
|
|
import assert from 'node:assert/strict'
|
|
import { verifyCargoDiscardFulfillment } from './cargo-discard-fulfillment.mjs'
|
|
|
|
function harness(fault = '') {
|
|
const calls = [], persisted = []
|
|
let discarded = false, confirmed = false, repeats = 0
|
|
const proof = { approvedNo: 'CD202610220001', customerId: '101', warehouseId: '201', inboundCode: 'RK-TEST', skuCode: 'SKU-TEST',
|
|
status: 'APPROVAL_HTTP_VERIFIED_CARTON_SQL_PENDING' }
|
|
const task = { discardNo: proof.approvedNo, customerId: '101', overseasWarehouseId: '201', palletNo: 'PALLET-TEST',
|
|
status: 'PENDING', skus: [{ itemId: '301', skuCode: proof.skuCode, quantityUnit: '箱', plannedPackages: 1 }], cartonMarks: ['BOX-TEST'] }
|
|
const context = { marker: 'DISCARD-TEST', context: { merchantQualificationFixture: { tenantId: '101' } },
|
|
createdResources: { phase4OverseasClientServices: { marker: 'DISCARD-TEST' } },
|
|
assert(_module, name, _response, passed) { assert.ok(passed, name) },
|
|
async request(method, path, body, token) {
|
|
calls.push({ method, path, body, token })
|
|
assert.equal(persisted.at(-1)?.requests.at(-1)?.status, 'SENDING')
|
|
let data
|
|
if (method === 'GET') data = [{ ...task, customerId: fault === 'wrong customer' ? '999' : '101' }]
|
|
else if (path.endsWith('/resolve')) data = task
|
|
else if (path.endsWith('/confirm-completion')) { confirmed = true; data = {} }
|
|
else if (path.endsWith('/confirm')) {
|
|
if (fault === 'unknown confirm') throw new Error('network unknown')
|
|
if (discarded) repeats++
|
|
discarded = true
|
|
data = { ...task, status: 'COMPLETED', skus: [{ ...task.skus[0], actualPackages: 1 }] }
|
|
} else throw new Error(`unexpected ${path}`)
|
|
return { http: 200, body: { code: '200', data } }
|
|
},
|
|
async requestMultipart(method, path, form, token) {
|
|
calls.push({ method, path, token })
|
|
assert.ok(form.get('file'))
|
|
assert.equal(persisted.at(-1)?.requests.at(-1)?.status, 'SENDING')
|
|
return { http: 200, body: { code: '200', data: { fileId: '401', palletNo: task.palletNo } } }
|
|
},
|
|
}
|
|
const inputs = { proof, merchantToken: 'client-secret', pdaToken: 'pda-secret',
|
|
async checkpoint() {
|
|
if (fault === 'disk') throw new Error('disk full')
|
|
persisted.push(structuredClone(proof.fulfillment))
|
|
},
|
|
async snapshot() {
|
|
const quantity = discarded ? fault === 'no deduction' ? 4 : fault === 'replay deduction' && repeats ? 2 : 3 : 4
|
|
const ledger = discarded ? [{ businessOrderNo: proof.approvedNo, businessType: 'DISCARD', deltaPackages: -1,
|
|
skuCode: proof.skuCode, inboundCode: proof.inboundCode }] : []
|
|
if (discarded && fault === 'duplicate ledger') ledger.push({ ...ledger[0] })
|
|
return { balances: [{ inboundCode: proof.inboundCode, skuCode: proof.skuCode, totalPackages: quantity, availablePackages: quantity }],
|
|
ledger, detail: { summary: { discardNo: proof.approvedNo, customerId: proof.customerId, overseasWarehouseId: proof.warehouseId,
|
|
status: confirmed ? 'COMPLETED' : discarded ? 'PDA_COMPLETED' : 'APPROVED', totalPackages: 1,
|
|
actualPackages: discarded ? 1 : null, customerConfirmedAt: confirmed ? '2026-10-22T12:00:00' : null } } }
|
|
} }
|
|
return { context, inputs, calls, persisted }
|
|
}
|
|
|
|
test('discard executes same application through PDA and customer completion with zero-drift replays', async () => {
|
|
const h = harness()
|
|
const result = await verifyCargoDiscardFulfillment(h.context, h.inputs)
|
|
assert.equal(result.status, 'HTTP_VERIFIED')
|
|
assert.equal(h.inputs.proof.status, 'COMPLETED_HTTP_VERIFIED_CARTON_SQL_PENDING')
|
|
assert.equal(h.calls.filter(call => call.path.endsWith('/confirm')).length, 3)
|
|
assert.equal(h.calls.filter(call => call.path.endsWith('/confirm-completion')).length, 2)
|
|
assert.equal(h.calls.filter(call => call.path.endsWith('/photos/files')).length, 1)
|
|
assert.ok(!JSON.stringify(h.persisted).includes('client-secret'))
|
|
assert.ok(!JSON.stringify(h.persisted).includes('pda-secret'))
|
|
await assert.rejects(verifyCargoDiscardFulfillment(h.context, h.inputs), /已批准申请|不自动重跑/)
|
|
})
|
|
|
|
for (const fault of ['wrong customer', 'no deduction', 'duplicate ledger', 'replay deduction', 'unknown confirm', 'disk']) {
|
|
test(`discard stops on ${fault}`, async () => {
|
|
const h = harness(fault)
|
|
await assert.rejects(verifyCargoDiscardFulfillment(h.context, h.inputs))
|
|
assert.equal(h.inputs.proof.fulfillment.status, 'FAILED')
|
|
assert.equal(h.calls.some(call => call.path.endsWith('/confirm-completion')), false)
|
|
if (fault === 'wrong customer') assert.equal(h.calls.length, 1)
|
|
if (fault === 'disk') assert.equal(h.calls.length, 0)
|
|
if (fault === 'unknown confirm') {
|
|
assert.equal(h.calls.filter(call => call.path.endsWith('/confirm')).length, 1)
|
|
assert.equal(h.inputs.proof.fulfillment.requests.at(-1).status, 'UNKNOWN_RESULT')
|
|
}
|
|
})
|
|
}
|
|
|
|
test('foreign batch or customer fails before HTTP or checkpoint', async () => {
|
|
const h = harness()
|
|
h.inputs.proof.customerId = '999'
|
|
await assert.rejects(verifyCargoDiscardFulfillment(h.context, h.inputs), /本轮已批准申请/)
|
|
assert.equal(h.calls.length, 0)
|
|
assert.equal(h.persisted.length, 0)
|
|
})
|