Files
baiyue-firstleg/scripts/admin-test-env-modules/looseboard-pool-dispatch-contract.test.mjs
2026-10-22 14:37:59 +08:00

76 lines
4.7 KiB
JavaScript

import test from 'node:test'
import assert from 'node:assert/strict'
import { arrangeLooseboardPool } from './looseboard-pool-dispatch.mjs'
function fixture(fault = '') {
const tripProof = { tripNo: 'TR-1', dispatchNo: 'DP-1' }
const pallet = { warehousePalletId: '72', palletNo: 'P-2', packages: 3 }
const receipt = { status: 'RECEIVED_READY_FOR_DISPATCH', incomingNo: 'IN-1', pallets: [pallet],
acceptedReceipt: { source: { receiptHash: 'hash', pallets: [{ ...pallet, weightKg: 80, volumeCbm: 1.8 }] } } }
const demand = { marker: 'fixture', customerId: '101', receipt, poolBinding: {
bookingNo: 'BK-1', tripNo: 'TR-1', createRequest: { cargo: { destinationKey: 'FBA:TEST' } } } }
const oldRow = { dispatchItemId: '1', sourceKey: 'OLD', customerId: '101', warehousePalletId: '71', inboundCode: 'OLD', scheduledPackages: 2, pickedPackages: 0 }
const newRow = { dispatchItemId: '2', sourceKey: 'NEW', customerId: '101', ...pallet, inboundCode: 'IN-1', scheduledPackages: 3, pickedPackages: 0 }
const planPallet = { warehousePalletId: '71', palletSpaces: 1, weightKg: 100, volumeCbm: 1, deliveryIndex: 1 }
const booking = { bookingNo: 'BK-1', tripNo: 'TR-1', customerId: '101', incomingNo: 'IN-1', status: 'RECEIVED', recordVersion: 2, receipt: receipt.acceptedReceipt }
let stage = 'LOADING', arranged = false, arrangeCalls = 0, returnCalls = 0
const calls = []
const context = { marker: 'fixture', createdResources: { fleetTrips: [tripProof], phase4OverseasBusiness: { looseboard: demand } },
assert(_m, name, _r, ok) { assert.ok(ok, name) }, async checkpoint() {},
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, recordVersion: 1,
counts: { pickedPackageCount: fault === 'picked' ? 1 : 0, shippedPackageCount: 0 } }
else if (path.endsWith('/items')) {
data = { dispatchNo: 'DP-1', items: arranged ? [oldRow, newRow] : [oldRow] }
data = structuredClone(data)
if (arranged && fault === 'lost original') data.items.shift()
if (arranged && fault === 'wrong boxes') data.items[1].scheduledPackages = 4
} else if (path.endsWith('/return-to-review')) {
returnCalls += 1; stage = 'BEFORE_LOADING'
data = { dispatchNo: 'DP-1', action: 'RETURN_TO_REVIEW', replayed: returnCalls > 1 }
} else if (path.endsWith('/capacity-plan')) {
data = { planValid: !arranged, recordVersion: 1, manifestHash: 'hash', capacityPlan: { pallets: [planPallet] }, cargo: [{ dispatchNo: 'DP-1' }] }
if (method === 'POST') {
assert.deepEqual(body.pallets[0], planPallet)
assert.deepEqual(body.pallets[1], { warehousePalletId: '72', palletSpaces: 1, weightKg: 80, volumeCbm: 1.8, deliveryIndex: 1 })
data.planValid = true
}
} else if (path.endsWith('/bookings')) data = { tripRecordVersion: 2, records: [booking] }
else if (path.endsWith('/arrange-dispatch')) {
arrangeCalls += 1
assert.equal('dispatchNo' in body, false)
if (fault === 'network') return { http: 0 }
arranged = true
data = { tripRecordVersion: 3, booking: { ...booking, status: 'BOUND', dispatchNo: fault === 'wrong main' ? 'OTHER' : 'DP-1' }, replayed: arrangeCalls > 1 }
if (fault === 'replay' && arrangeCalls > 1) data.tripRecordVersion += 1
} else if (path.endsWith('/trips/TR-1')) data = { trip: { tripNo: 'TR-1', stops: [{ destinationKey: 'ORIGIN' }, { destinationKey: 'FBA:TEST' }] } }
else throw Error(`Unexpected ${method} ${path}`)
return { http: 200, body: { code: '200', data: structuredClone(data) }, requestId: 'req', traceId: 'trace' }
} }
return { context, args: { demand, tripProof }, calls, counts: () => arrangeCalls }
}
test('original looseboard receipt joins same main preserving existing rows and planning', async () => {
const f = fixture()
const result = await arrangeLooseboardPool(f.context, f.args)
assert.equal(result.status, 'BOUND_AWAITING_REVIEW')
assert.equal(f.counts(), 2)
await assert.rejects(arrangeLooseboardPool(f.context, f.args))
assert.equal(f.counts(), 2)
})
for (const fault of ['picked', 'network', 'wrong main', 'replay', 'lost original', 'wrong boxes']) {
test(`join original main rejects ${fault} and preserves evidence`, async () => {
const f = fixture(fault)
await assert.rejects(arrangeLooseboardPool(f.context, f.args))
assert.equal(f.args.demand.receipt.dispatch.status, 'FAILED')
assert.equal(f.calls.some(c => c.method === 'POST' && c.path.endsWith('/capacity-plan')), false)
if (fault === 'network') {
assert.equal(f.counts(), 1)
assert.equal(f.context.createdResources.dispatchWriteBoundary.stopped, true)
}
})
}