88 lines
5.1 KiB
JavaScript
88 lines
5.1 KiB
JavaScript
import test from 'node:test'
|
|
import assert from 'node:assert/strict'
|
|
import { bindLooseboardDemand } from './looseboard-pool-binding.mjs'
|
|
|
|
// 有状态模拟保留原单与独立询价,重放返回原响应,故障必须在后续写入前停止。
|
|
function fixture(fault = '') {
|
|
const demand = { marker: 'fixture', orderNo: 'LB-1', customerId: '101', version: 2, finalStatus: 'PENDING_ACCEPTANCE' }
|
|
const tripProof = { tripNo: 'TR-1', warehouseId: '42', looseboardOffer: { marker: 'fixture', offerNo: 'OF-1' } }
|
|
const offer = { offerNo: 'OF-1', tripNo: 'TR-1', warehouseId: '42', status: 'OPEN', tripRecordVersion: 1, recordVersion: 0,
|
|
canCreateInquiry: true, canConfirm: true, capacityKnown: true,
|
|
destinations: [{ destinationKey: 'FBA:TEST', remaining: { palletSpaces: fault === 'capacity' ? 0 : 4, weightKg: 400, volumeCbm: 4 } }] }
|
|
const original = { remark: 'fixture-original', summary: { ...demand, status: 'PENDING_ACCEPTANCE', estimatedPalletCount: 2,
|
|
packageCount: 3, totalWeightKg: 80, totalVolumeCbm: 1.8, productName: 'TEST', currency: 'USD', originContactName: 'TEST', originContactPhone: '19900000000' } }
|
|
let created, confirmed, writes = 0
|
|
const calls = [], snapshots = []
|
|
const context = { marker: 'fixture', createdResources: { phase4OverseasBusiness: { looseboard: demand }, fleetTrips: [tripProof] },
|
|
assert(_m, name, _r, ok) { assert.ok(ok, name) }, apiSuccess() {},
|
|
async checkpoint() {
|
|
if (fault === 'disk') throw Error('disk unavailable')
|
|
snapshots.push(structuredClone(demand.poolBinding))
|
|
},
|
|
async request(method, path, body) {
|
|
calls.push({ method, path, body: structuredClone(body) })
|
|
if (method === 'POST') writes += 1
|
|
let data
|
|
if (path.includes('/local-looseboard-orders/')) {
|
|
data = structuredClone(original)
|
|
if (fault === 'customer') data.summary.customerId = '102'
|
|
if (confirmed) {
|
|
data.summary.status = 'WAITING_INBOUND'; data.summary.version += 1
|
|
data.summary.fleetArrangement = { businessOrderNo: 'LB-1', bookingNo: 'BK-1', tripNo: 'TR-1' }
|
|
if (fault === 'binding') data.summary.fleetArrangement.tripNo = 'OTHER'
|
|
}
|
|
} else if (method === 'GET' && path.endsWith('/OF-1')) data = { offer }
|
|
else if (method === 'GET' && path.endsWith('/BK-1')) data = created.inquiry
|
|
else if (method === 'POST' && path.endsWith('/OF-1/inquiries')) {
|
|
assert.equal(body.sourceOrderNo, 'LB-1'); assert.equal(body.sourceOrderVersion, 2)
|
|
assert.equal(body.customerId, '101'); assert.equal(body.expectedPalletCount, 2)
|
|
assert.equal(body.cargo.packageCount, 3)
|
|
if (fault === 'network') return { http: 0 }
|
|
if (!created) {
|
|
created = { inquiry: { ...body, offerNo: 'OF-1', tripNo: 'TR-1', bookingNo: 'BK-1', status: 'DRAFT', recordVersion: 0 }, offer }
|
|
data = created
|
|
} else {
|
|
data = { ...structuredClone(created), replayed: true }
|
|
if (fault === 'replay') data.inquiry.bookingNo = 'BK-2'
|
|
}
|
|
} else if (method === 'POST' && path.endsWith('/BK-1/confirm')) {
|
|
if (!confirmed) {
|
|
confirmed = { inquiry: { ...created.inquiry, status: 'CONFIRMED', recordVersion: 1, agreedAmount: fault === 'price' ? 25 : 50,
|
|
businessOrderNo: fault === 'new order' ? 'LB-2' : 'LB-1' }, offer }
|
|
data = confirmed
|
|
} else data = { ...confirmed, replayed: true }
|
|
} else throw Error(`Unexpected ${method} ${path}`)
|
|
return { http: 200, body: { code: '200', data: structuredClone(data) }, requestId: 'request', traceId: 'trace' }
|
|
} }
|
|
return { context, args: { demand, tripProof, offerNo: 'OF-1', destinationKey: 'FBA:TEST', unitPrice: 25 }, calls, snapshots, writes: () => writes }
|
|
}
|
|
|
|
test('pool binding preserves original demand and confirms only one commercial order', async () => {
|
|
const f = fixture()
|
|
const proof = await bindLooseboardDemand(f.context, f.args)
|
|
assert.equal(proof.status, 'CONFIRMED_ORIGINAL_ORDER')
|
|
assert.equal(f.args.demand.finalStatus, 'WAITING_INBOUND')
|
|
assert.equal(f.writes(), 4)
|
|
assert.equal(f.snapshots.at(-1).status, 'CONFIRMED_ORIGINAL_ORDER')
|
|
await assert.rejects(bindLooseboardDemand(f.context, f.args), /本轮原单/)
|
|
assert.equal(f.writes(), 4)
|
|
})
|
|
|
|
for (const [fault, maxWrites] of [['customer', 0], ['capacity', 0], ['network', 1], ['replay', 2], ['new order', 3], ['price', 3], ['binding', 4], ['disk', 0]]) {
|
|
test(`pool binding stops on ${fault} without another order or automatic retry`, async () => {
|
|
const f = fixture(fault)
|
|
await assert.rejects(bindLooseboardDemand(f.context, f.args))
|
|
assert.equal(f.args.demand.poolBinding.status, 'FAILED')
|
|
assert.equal(f.writes(), maxWrites)
|
|
assert.equal(f.calls.some(c => /cancel|\/iam\/|\/auth\/register/.test(c.path)), false)
|
|
})
|
|
}
|
|
|
|
test('foreign batch or unregistered trip is rejected before HTTP', async () => {
|
|
for (const alter of [f => { f.args.demand.marker = 'other' }, f => { f.context.createdResources.fleetTrips = [] }]) {
|
|
const f = fixture(); alter(f)
|
|
await assert.rejects(bindLooseboardDemand(f.context, f.args))
|
|
assert.equal(f.calls.length, 0)
|
|
}
|
|
})
|