import test from 'node:test' import assert from 'node:assert/strict' import { prepareLooseboardPool } from './looseboard-pool-preparation.mjs' // 模拟原车资源及容量版本;记录所有写入以验证不会创建主档或自动补发。 function fixture(fault = '', validPlan = false) { const tripProof = { marker: 'fixture', preparation: 'VERIFIED', tripNo: 'TR-1', dispatchNo: 'DP-1', warehouseId: '42', vehicleId: '11', driverId: '12' } const demand = { marker: 'fixture', finalStatus: 'PENDING_ACCEPTANCE' } const trip = { ...tripProof, status: 'PLANNED', recordVersion: 1, stops: [{ destinationKey: 'ORIGIN' }, { destinationKey: 'FBA:TEST' }] } const plan = { planValid: validPlan, recordVersion: 1, manifestHash: 'a'.repeat(64), availableSegments: [{}], cargo: [{ dispatchNo: 'DP-1', warehousePalletId: '71', palletNo: 'P-1', destinationKey: 'FBA:TEST' }] } const offer = { offerNo: 'OF-1', tripNo: 'TR-1', status: 'DRAFT', recordVersion: 0, tripRecordVersion: 1 } const calls = [], checkpoints = []; let creates = 0, publishes = 0, bindCalls = 0 const context = { marker: 'fixture', createdResources: { fleetTrips: [tripProof], phase4OverseasBusiness: { looseboard: demand } }, assert(_m, name, _r, passed) { assert.ok(passed, name) }, apiSuccess() {}, async checkpoint() { if (fault === 'disk') throw Error('disk'); checkpoints.push(structuredClone(tripProof.looseboardOffer)) }, async request(method, path, body) { calls.push({ method, path, body }) let data if (path.endsWith('/dispatches/DP-1')) data = { dispatchNo: 'DP-1', status: 'ACTIVE', stage: 'LOADING', counts: { pickedPackageCount: fault === 'picked' ? 1 : 0, shippedPackageCount: 0 } } else if (path.endsWith('/trips/TR-1')) data = { trip: { ...trip, driverId: fault === 'driver' ? '13' : '12' }, dispatches: [{ dispatchNo: 'DP-1' }] } else if (path.endsWith('/capacity-plan')) { if (method === 'POST') { assert.equal(body.pallets[0].warehousePalletId, '71') assert.equal(body.pallets[0].deliveryIndex, 1) plan.planValid = true } data = structuredClone(plan) if (fault === 'foreign cargo') data.cargo[0].dispatchNo = 'OTHER' } else if (path.endsWith('/loading')) data = { confirmedLoad: fault === 'loaded' ? {} : null } else if (path.endsWith('/capacity-offers') && method === 'POST') { creates += 1 if (fault === 'network') return { http: 0 } data = { offer: { ...offer }, replayed: creates > 1 } if (fault === 'draft replay' && creates > 1) data.offer.offerNo = 'OF-2' } else if (path.endsWith('/OF-1/publish')) { publishes += 1 data = { offer: { ...offer, status: 'OPEN', capacityKnown: true, canCreateInquiry: true, canConfirm: true }, replayed: publishes > 1 } if (fault === 'publish replay' && publishes > 1) data.offer.recordVersion = 9 } else throw Error(`Unexpected ${method} ${path}`) return { http: 200, body: { code: '200', data: structuredClone(data) }, requestId: 'request', traceId: 'trace' } } } const bind = async (ctx, args) => { bindCalls += 1 assert.equal(ctx, context); assert.equal(args.demand, demand); assert.equal(args.tripProof, tripProof) assert.equal(args.offerNo, 'OF-1'); assert.equal(args.destinationKey, 'FBA:TEST') assert.equal(tripProof.looseboardOffer.status, 'OPEN') return { status: 'CONFIRMED_ORIGINAL_ORDER', tripNo: 'TR-1', offerNo: 'OF-1' } } return { context, args: { demand, tripProof, destinationKey: 'FBA:TEST', unitPrice: 25 }, bind, calls, checkpoints, counts: () => ({ creates, publishes, bindCalls }) } } for (const valid of [false, true]) test(`prepare owned pool and pass original demand to binding; existing plan=${valid}`, async () => { const f = fixture('', valid) await prepareLooseboardPool(f.context, f.args, f.bind) assert.deepEqual(f.counts(), { creates: 2, publishes: 2, bindCalls: 1 }) assert.equal(f.calls.filter(c => c.method === 'POST' && c.path.endsWith('/capacity-plan')).length, valid ? 0 : 1) assert.equal(f.args.tripProof.looseboardOffer.status, 'BOUND_ORIGINAL_DEMAND') await assert.rejects(prepareLooseboardPool(f.context, f.args, f.bind)) assert.equal(f.calls.some(c => /\/vehicles|\/drivers|\/iam/.test(c.path)), false) }) for (const encoded of ['0', '12', '', null, '01', '-1', '9007199254740992']) test(`pool source version uses bounded native Long encoding: ${String(encoded)}`, async () => { const f = fixture('', true) const request = f.context.request f.context.request = async (method, path, body) => { const response = await request(method, path, body) if (path.endsWith('/capacity-plan')) response.body.data.recordVersion = encoded return response } if (['0', '12'].includes(encoded)) await prepareLooseboardPool(f.context, f.args, f.bind) else { await assert.rejects(prepareLooseboardPool(f.context, f.args, f.bind), /容量依据/) assert.equal(f.counts().creates, 0) } }) for (const fault of ['picked', 'driver', 'foreign cargo', 'loaded', 'network', 'draft replay', 'publish replay', 'disk']) { test(`pool preparation fails closed on ${fault}`, async () => { const f = fixture(fault) await assert.rejects(prepareLooseboardPool(f.context, f.args, f.bind)) assert.equal(f.args.tripProof.looseboardOffer.status, 'FAILED') assert.equal(f.counts().bindCalls, 0) if (fault === 'network') assert.equal(f.counts().creates, 1) if (fault === 'disk') assert.equal(f.calls.length, 0) assert.equal(f.calls.some(c => /cancel|close|delete/.test(c.path)), false) }) }