import test from 'node:test' import assert from 'node:assert/strict' import { domesticCommands, domesticSuccess, runDomesticStage } from './prod-release-domestic-stage.mjs' const journal = () => { const state = {}, saves = [] return { state, saves, async save(patch) { saves.push(structuredClone(patch)); Object.assign(state, patch) } } } test('uncertain writes retain original intent and cannot replay', async () => { const checkpoint = journal(), run = domesticCommands(checkpoint, 'bound') let count = 0 const execute = async () => { count++; assert.equal(checkpoint.state.commands.create.status, 'INTENT'); throw Error('network') } await assert.rejects(run('create', { path: '/create' }, execute), /network/) await assert.rejects(run('create', { path: '/create' }, execute), /结果未知/) assert.equal(count, 1) }) test('completed writes reuse exact saved result and reject another binding', async () => { const checkpoint = journal(), run = domesticCommands(checkpoint, 'bound') const first = await run('create', { path: '/create' }, async () => ({ id: '17' })) assert.deepEqual(await run('create', { path: '/create' }, () => { throw Error('must not run') }), first) assert.throws(() => domesticCommands(checkpoint, 'other'), /绑定/) }) test('business rejection retains diagnostic evidence and cannot count as success', () => { assert.throws(() => domesticSuccess({ http: 200, body: { code: 'A0409', message: 'version' }, requestId: 'r', traceId: 't' }), error => error.evidence.code === 'A0409' && error.evidence.requestId === 'r' && error.evidence.traceId === 't') assert.throws(() => domesticSuccess({ http: 503, body: { code: '200' } })) }) function fixture() { const input = { batch: 'release-test-001', customer: { id: '1', name: 'TEST' }, origin: { id: '2' }, payload: { taxMode: 'BUY_DECLARATION', channelCode: 'MATSON_CLX', cargoes: [{ skuNo: 'TEST-SKU', packageCount: 2, lengthCm: 30, widthCm: 20, heightCm: 10, grossWeightKg: 8 }] }, domestic: { palletId: '3', assigneeId: '4', bookingAgent: { id: '5', name: 'AGENT' }, carrier: { id: '6', name: 'CARRIER', carrierCode: 'MATSON' }, terminalFleet: { id: '7', name: 'FLEET' }, estimatedSailingAt: '2026-10-12T08:00:00', estimatedArrivalAt: '2026-10-20T08:00:00' } } const order = { status: 'ORDER_APPROVED', orderId: '10', orderNo: 'TEST-ORDER', inboundCode: 'TEST-INBOUND' } const checkpoint = journal(), calls = [] const context = { token: 'platform-test', merchantToken: 'merchant-test', pdaToken: 'pda-test', pollAttempts: 1, sleep: async () => {} } let containerNo, carrierNode = 'DEPARTED_WAREHOUSE', carrierVersion = 0, latestActualAt = '2026-10-12T10:00:00' let measured = false const carrier = () => ({ query: { query: { latestActualNode: carrierNode, version: carrierVersion, latestActualAt } } }) context.request = async (method, path, body, token, headers) => { calls.push({ method, path, body, token, headers }) let data if (path === '/api/platform/v1/orders/10?tenantId=1') data = { summary: { inboundId: '20' } } else if (path === '/api/platform/inbounds/20?tenantId=1') data = { summary: { tenantId: '1', orderId: '10', inboundCode: 'TEST-INBOUND', version: 1 } } else if (path.includes('/measurement?')) data = { measurementId: '30', version: 2, items: [{ id: '31', inboundDetailId: '32', itemNo: 1 }], processStatus: measured ? 'WAIT_SPLIT' : 'MEASURING', measurementStatus: 'APPROVED', measureReviewStatus: 'APPROVED', actualPackageCount: 2, actualNetWeightKg: 8 } else if (path.includes('/measurements/approve')) { measured = true; data = {} } else if (path.includes('/weight-results?')) data = { id: '40', version: 0, palletTareWeightKg: 2 } else if (path.includes('/fees/send')) data = { feeStatus: 'SENT', feeVersion: 1 } else if (path.includes('/inbound-billing/confirm')) data = { customerConfirmationStatus: 'CONFIRMED' } else if (path === '/api/platform/v1/containers') data = { id: '50', version: 0 } else if (path === '/api/platform/v1/ocean-bookings') data = { oceanBooking: { id: '60', version: 0 } } else if (path.includes('/ocean-bookings/60/release')) data = { oceanBooking: { status: 'RELEASED' }, containerRequirements: [{ id: '61', containerType: '40HQ' }] } else if (path.includes('/ocean-booking-link')) { containerNo = body.containerNo; data = {} } else if (path.includes('/smart-loading/candidates')) data = [{ orderId: '10', accountId: '1', inboundDetailId: '32' }] else if (path.includes('/plans/by-booking/')) data = { id: '70', version: 0 } else if (path.includes('/plans/70/assignments')) data = { items: [{ id: '71', packageCount: 2 }] } else if (path.includes('/loading-plans/70/confirm')) data = { confirmationStatus: 'CONFIRMED', readiness: { pdaTaskId: '80' } } else if (path.includes('/first-leg/loading-plans/70?')) data = { summary: { version: 1 } } else if (path.includes('/tasks/80/claim')) data = { items: [{ id: '81', version: 0 }] } else if (path.includes('/tasks/80/inventory-snapshot')) data = { taskVersion: 1, snapshotToken: 'snapshot' } else if (path.includes('/tasks/80/batch')) data = { version: 2 } else if (path.includes('/tasks/80?')) data = { taskStatus: 'COMPLETED', workflowStatus: 'COMPLETED' } else if (path === '/api/platform/v1/customs/loading-plans/70') data = { inbounds: [{ inboundId: '20', taxMode: 'BUY_DECLARATION' }], status: 'READY_TO_START', version: 0 } else if (path.endsWith('/customs/loading-plans/70/start')) data = { status: 'DECLARING', version: 1 } else if (path.endsWith('/complete-all-buy')) data = { status: 'COMPLETED', version: 2 } else if (path.includes('/outbound-dispatches/50')) data = { aggregate: { dispatchStatus: 'WAITING_FOR_LOADING', inbounds: [{ inboundId: '20' }] } } else if (path.includes('/carrier-schedule-queries?')) data = { records: [{ query: { id: '90', containerBookingId: '50', triggerSource: 'OUTBOUND_CONFIRM' } }] } else if (path.endsWith('/carrier-schedule-queries/90')) data = carrier() else if (path.endsWith('/manual-nodes')) { carrierNode = body.standardNode; carrierVersion++; latestActualAt = body.actualAt; data = carrier() } else if (path.endsWith('/terminal-drayages/50')) data = { containerBookingId: '50', containerNo, version: 0 } else if (path.endsWith('/terminal-drayages/50/fleet')) data = { version: 1 } else if (path.endsWith('/pickup-appointment')) data = { version: 2, pickupAppointmentAt: body.pickupAppointmentAt } else if (/\/(accept|start)\?/.test(path) || /confirmed-package-count|measurement\/draft|weight-results\/40\/confirm|measurement\/submit|fees\/latest|fees\/confirm|tasks\/80\/complete/.test(path)) data = {} else throw Error(`unhandled ${method} ${path}`) return { http: 200, body: { code: '200', data } } } context.requestMultipart = async (method, path, form, token, headers) => { assert.ok(form instanceof FormData) calls.push({ method, path, token, headers, multipart: true }) let data if (path.includes('/carton-scans')) data = { itemId: '31', measurementId: '30', measurementVersion: 0 } else if (path.includes('/files/measurements/')) data = { fileId: '41' } else if (path.includes('/documents/upload')) data = { bookingVersion: 1 } else if (path.includes('/tasks/80/evidence')) data = { taskId: '80' } else throw Error(`unhandled upload ${path}`) return { http: 200, body: { code: '200', data } } } context.requestBinary = async () => ({ http: 200, contentType: 'application/pdf', bytes: Buffer.from('%PDF-test') }) return { input, order, context, checkpoint, calls } } test('single-ticket positive HTTP orchestration conserves two cartons and stops at pickup appointment', async () => { const value = fixture(), result = await runDomesticStage(value) assert.equal(result.status, 'DOMESTIC_PICKUP_SCHEDULED') assert.equal(result.inboundId, '20') assert.equal(result.packageCount, 2) const batch = value.calls.find(call => call.path.endsWith('/tasks/80/batch')) assert.equal(batch.body.items[0].pickedPackageCount, 2) assert.equal(batch.token, value.context.pdaToken) const weight = value.calls.find(call => call.path.includes('/weight-results/40/confirm')) assert.equal(weight.body.adjustedWeightValue, 10) assert.equal(value.calls.filter(call => call.path.endsWith('/manual-nodes')).length, 7) assert.equal(value.calls.filter(call => call.path.includes('/evidence?')).length, 5) assert.ok(value.calls.every(call => !/pickup-email|\/suppliers|\/users|\/customers/.test(call.path))) assert.ok(Object.values(value.checkpoint.state.commands).every(command => command.status === 'DONE')) }) test('tax mode and customer identity fail closed before unrelated business writes', async () => { const value = fixture() value.input.payload.taxMode = 'SELF_DECLARATION' await assert.rejects(runDomesticStage(value), /买单报关/) assert.equal(value.calls.length, 0) }) test('a timeout at first business write preserves intent and stops all later actions', async () => { const value = fixture(), request = value.context.request value.context.request = async (...args) => args[0] === 'POST' ? Promise.reject(Error('timeout')) : request(...args) await assert.rejects(runDomesticStage(value), /timeout/) assert.equal(value.checkpoint.state.commands.accept.status, 'INTENT') assert.equal(Object.keys(value.checkpoint.state.commands).length, 1) })