import test from 'node:test' import assert from 'node:assert/strict' import { overseasCommands, runOverseasStage, validateOverseasInput, finishSingleOrderTrip } from './prod-release-overseas-stage.mjs' const input = { batch: 'release-test-001', customerId: '1', warehouseId: '2', packageCount: 1, resources: { vehicleId: '3', driverId: '4', carrierName: '验收既有车队' } } const domestic = { containerBookingId: '5', sourceInboundId: '6', orderId: '7', containerNo: 'TEST0000001', inboundCode: 'IN-TEST', orderNo: 'ORDER-TEST', cartonMarks: ['CARTON-TEST'] } const ok = data => ({ http: 200, body: { code: '200', data } }) const journal = () => { const state = {}; return { state, save: async patch => Object.assign(state, patch) } } test('reject multi-ticket quantities and duplicate frozen cartons before HTTP', () => { assert.throws(() => validateOverseasInput({ ...input, packageCount: 3 }, domestic), /1~2/) assert.throws(() => validateOverseasInput({ ...input, packageCount: 2 }, { ...domestic, cartonMarks: ['a', 'a'] }), /箱码/) }) test('command records intent before HTTP; uncertain result blocks resend', async () => { const checkpoint = journal(); let calls = 0 const { command } = overseasCommands({ request: async () => { calls++; assert.equal(checkpoint.state.commands.create.status, 'INTENT'); throw new Error('timeout') } }, checkpoint) await assert.rejects(command('create', 'POST', '/example', { x: 1 }), /timeout/) await assert.rejects(command('create', 'POST', '/example', { x: 1 }), /结果未知/) assert.equal(calls, 1) }) test('completed command restores result without HTTP and rejects changed request', async () => { const checkpoint = journal(); let calls = 0 const { command } = overseasCommands({ request: async () => { calls++; return ok({ id: '99' }) } }, checkpoint) await command('create', 'POST', '/example', { x: 1 }) assert.deepEqual(await command('create', 'POST', '/example', { x: 1 }), { id: '99' }) await assert.rejects(command('create', 'POST', '/example', { x: 2 }), /不符/) assert.equal(calls, 1) }) test('unknown prior command blocks even read/other writes on continuation', async () => { const checkpoint = journal(); checkpoint.state.commands = { lost: { status: 'INTENT' } } await assert.rejects(runOverseasStage({ input, domestic, checkpoint, context: { overseasPdaToken: 'pda', merchantToken: 'merchant', request: () => assert.fail('must not call HTTP') } }), /结果未知/) }) test('only initial current receiving probe accepts HTTP 200 A0404 as no session', async () => { const calls = [] const helpers = overseasCommands({ request: async (method, path, body, token) => { calls.push({ method, path, token }); return { http: 200, body: { code: 'A0404' } } } }, journal()) assert.equal(await helpers.readCurrentReceivingSession('pda'), null) assert.deepEqual(calls[0], { method: 'GET', path: '/api/pda/v1/last-leg/receiving-containers/current', token: 'pda' }) await assert.rejects(helpers.read('/api/pda/v1/last-leg/receiving-containers/current', 'pda'), /A0404/) await assert.rejects(helpers.read('/api/platform/v1/last-leg/orders/1'), /A0404/) }) test('current receiving probe preserves auth, transport and other business errors', async () => { for (const [http, code] of [[200, 'A0403'], [200, 'A0500'], [401, 'A0404'], [404, 'A0404'], [503, 'A0404']]) { const helpers = overseasCommands({ request: async () => ({ http, body: { code, requestId: 'request-test' } }) }, journal()) await assert.rejects(helpers.readCurrentReceivingSession('pda'), /request-test/) } }) function fixture({ foreignSource = false, destinationType = 'OVERSEAS' } = {}) { const checkpoint = journal(), calls = [] const session = { containerNo: domestic.containerNo, overseasWarehouseId: '2', totalPackages: 1, pendingPackages: 0, inbounds: [{ inboundCode: domestic.inboundCode, destinationType }] } const trip = { trip: { tripNo: 'TRIP-TEST', recordVersion: 1, stops: [] } } const cargo = [{ warehousePalletId: '9', palletNo: 'PLT-TEST', destinationKey: 'DEST', destinationLabel: 'Destination' }] const context = { token: 'admin', overseasPdaToken: 'pda', merchantToken: 'merchant', wait: async () => {}, requestWithBody: async (method, path, form, token) => { calls.push({ method, path, token }); assert.equal(token, 'pda'); assert.ok(form instanceof FormData); return ok({ fileId: '10' }) }, request: async (method, path, body, token) => { calls.push({ method, path, body, token }) if (path.includes('/master-data/vehicles/')) return ok({ id: '3', region: 'OVERSEAS', status: 'IN_SERVICE', plateNumber: 'TEST' }) if (path.includes('/fleet/drivers/')) return ok({ id: '4', qualification: { canDispatch: true }, details: { name: 'Driver', phone: '10000000000' } }) if (path.includes('/orders?containerNo=')) return ok({ records: [{ sourceContainerBookingId: '5', customerId: foreignSource ? '99' : '1', sourceInboundId: '6', inboundCode: domestic.inboundCode, overseasWarehouseId: '2', id: '8' }] }) if (path.endsWith('/container-arrival-times/query')) return ok([{ sourceContainerBookingId: '5', version: 0 }]) if (path.includes('/container-preview?')) return ok({ ready: true, totalOrderCount: 1, pendingOrderCount: 1, snapshotVersion: 1, snapshotHash: 'hash' }) if (path.endsWith('/convert-container')) return ok([{ lines: [{ inboundCode: domestic.inboundCode }], pallets: [{ palletNo: 'PLT-TEST', warehousePalletId: '9' }] }]) if (path.endsWith('/current') && method === 'GET') return calls.filter(c => c.path.endsWith('/current')).length === 1 ? { http: 200, body: { code: 'A0404' } } : ok(session) if (path.includes('/receiving-containers/TEST0000001?')) return ok(session) if (path.endsWith('/pallets/resolve')) return ok({ compatible: true }) if (path.endsWith('/cartons/resolve')) return ok({ inboundCode: domestic.inboundCode }) if (path.endsWith('/terminal-drayages/5')) return ok({ containerBookingId: '5', status: 'UNLOADED', version: 1, unloadingCompletedAt: '2026-10-12T12:00:00', allowedActions: ['RECORD_EMPTY_RETURN'] }) if (path.endsWith('/empty-return')) return ok({ status: 'EMPTY_RETURNED' }) if (path.endsWith('/candidates?sourceStage=RECEIVED')) return ok([{ sourceStage: 'RECEIVED', inboundCode: domestic.inboundCode, customerId: '1', overseasWarehouseId: '2', packageCount: 1, planPalletId: '11' }]) if (path.endsWith('/pre-arrange')) return ok({ dispatchNo: 'DISPATCH-TEST' }) if (path.includes('/merchant/v1/last-leg/delivery-orders?')) return ok({ outboundNo: 'OUT-TEST', deliveries: [{ deliveryNo: 'DELIVERY-TEST' }] }) if (path.endsWith('/loading') && method === 'GET') return ok({ cargo, recordVersion: 1, manifestHash: 'manifest' }) if (path.endsWith('/loading') && method === 'POST') return ok({ recordVersion: 2 }) if (path.includes('/fleet/trips')) return ok(trip) if (path === '/api/pda/v1/last-leg/dispatches/DISPATCH-TEST') return ok({ status: 'COMPLETED', canDepart: true, items: [{ dispatchItemId: '12', scheduledPackages: 1, pickedPackages: 0, positions: [{ id: '13', availablePackages: 1 }] }] }) if (path === '/api/platform/v1/last-leg/dispatches/DISPATCH-TEST' && method === 'GET') return ok({ items: [{ id: '12', inboundCode: domestic.inboundCode }] }) if (path.includes('/outbound-orders/')) return ok({ outbound: { status: 'OUTBOUNDED' }, pallets: [{ inboundCode: domestic.inboundCode }] }) if (method === 'GET') assert.fail(`unexpected GET ${path}`) return ok({}) } } return { input, domestic, context, checkpoint, calls } } test('normal single-ticket HTTP chain reaches outbound with existing master resources and PDA identity', async () => { const f = fixture(), result = await runOverseasStage(f) assert.equal(result.status, 'OVERSEAS_OUTBOUNDED') assert.equal(result.fullFlowVerified, false) assert.equal(result.deliveryNo, 'DELIVERY-TEST') assert.ok(f.calls.some(row => row.path.endsWith('/receipts/carton') && row.token === 'pda')) assert.ok(f.calls.some(row => row.path.endsWith('/depart') && row.token === 'pda')) assert.equal(f.calls.filter(row => row.path.endsWith('/terminal-drayages/5')).length, 1) assert.equal(f.calls.filter(row => row.path.endsWith('/empty-return')).length, 1) assert.equal(f.calls.filter(row => row.method !== 'GET' && /\/master-data\/|\/fleet\/drivers|\/iam\//.test(row.path)).length, 0) assert.ok(Object.values(f.checkpoint.state.commands).every(row => row.status === 'DONE')) }) test('foreign customer source stops before plan conversion or receiving', async () => { const f = fixture({ foreignSource: true }) await assert.rejects(runOverseasStage(f), /来源不属于/) assert.equal(f.calls.filter(row => row.method !== 'GET').length, 0) }) test('FBA receives cumulative package count without fabricated or resolved carton marks', async () => { const f = fixture({ destinationType: 'FBA' }) f.domestic = { ...f.domestic }; delete f.domestic.cartonMarks const result = await runOverseasStage(f) assert.equal(result.status, 'OVERSEAS_OUTBOUNDED') const received = f.calls.filter(row => row.path.endsWith('/receipts/bulk')) assert.equal(received.length, 1) assert.equal(received[0].body.packageCount, 1) assert.equal(f.calls.filter(row => /\/cartons\/resolve|\/receipts\/carton/.test(row.path)).length, 0) }) test('OVERSEAS missing frozen carton marks fails before opening receiving session', async () => { const f = fixture(); f.domestic = { ...f.domestic }; delete f.domestic.cartonMarks await assert.rejects(runOverseasStage(f), /正式冻结箱码/) assert.equal(f.calls.filter(row => /\/prepare\?|\/current\/sign|\/receipts\//.test(row.path)).length, 0) }) test('signed single ticket closes its trip and releases only its existing resources', async () => { const checkpoint = journal(), writes = [] const overseas = { tripNo: 'TRIP-TEST', dispatchNo: 'DISPATCH-TEST', deliveryNo: 'DELIVERY-TEST' } let trip = { tripNo: overseas.tripNo, vehicleId: '3', driverId: '4', recordVersion: 1, stops: [{ name: 'Origin' }, { name: 'Destination' }] } const context = { request: async (method, path, body) => { if (path.includes('/delivery-orders/')) return ok({ order: { customerId: '1', status: 'DELIVERED', dispatchNo: overseas.dispatchNo } }) if (path.endsWith('/completion')) return ok({ canComplete: true, recordVersion: 1, outcomes: [{}] }) if (method === 'POST') { writes.push({ path, body }); trip = { ...trip, recordVersion: trip.recordVersion + 1 } if (path.endsWith('/complete')) trip = { ...trip, status: 'COMPLETED', completedAt: '2026-10-12T12:00:00Z' } else if (body.resourceKind === 'VEHICLE') trip.vehicleRelease = { ended: true } else trip.driverRelease = { ended: true } } return ok({ trip, dispatches: [{ dispatchNo: overseas.dispatchNo }] }) } } assert.equal((await finishSingleOrderTrip({ input, overseas, context, checkpoint })).status, 'CARGO_COMPLETED_AND_RESOURCES_RELEASED') assert.equal(writes.length, 3) assert.deepEqual(writes.map(row => row.body.recordVersion), [1, 2, 3]) })