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) } })