import test from 'node:test' import assert from 'node:assert/strict' import { verifyFleetFeeCoverage } from './fleet-fee-coverage-acceptance.mjs' function fixture(mode = 'FULL', fault = '') { const source = { marker: 'CURRENT-TEST', mode, dispatchNo: 'D-CURRENT', customerId: '900719925474099301', transferBody: { loadingType: 'PALLET', appointmentAccount: 'SELF', fees: [ { direction: 'RECEIVABLE', feeType: 'DELIVERY_FEE', feeName: '派送费', amount: 17.5, currency: 'USD' }, ] } } const cargo = [1, 2].map(n => ({ dispatchItemId: `item-${n}`, warehousePalletId: `pallet-${n}`, orderId: `order-${n}`, sourceRegion: 'FIRSTLEG', sourceInboundId: `90071992547409940${n}`, inboundCode: `IN-${n}`, packages: 2 })) const fees = cargo.map((row, index) => ({ financeFeeId: `90071992547409950${index}`, sourceVersion: 1, sourceHash: `fee-hash-${index}`, sourceInboundId: row.sourceInboundId, customerId: source.customerId, sourceType: 'INBOUND_FEE', currency: 'CNY', amount: 200, status: 'AVAILABLE' })) if (fault === 'foreign-customer') fees[0].customerId = 'different-customer' if (fault === 'missing-source') fees.splice(1) const hash = 'a'.repeat(64), calls = [], assertions = [] let savedBody, replayed = false, dirtyOutbound = false const response = (data, message = '') => ({ http: 200, body: { code: message ? 'A0409' : '200', message, data }, requestId: 'req-test', traceId: 'trace-test' }) const context = { marker: source.marker, createdResources: { fleetFeeCoverageCases: [source] }, assert(module, name, res, passed, evidence, options) { assertions.push({ name, passed, options }) }, async request(method, path, body) { calls.push({ method, path, body }) if (method === 'GET' && path === '/api/platform/v1/last-leg/dispatches/D-CURRENT') return response({ dispatchNo: source.dispatchNo, pickedPackageCount: 0, shippedPackageCount: 0, items: cargo.map((row, index) => ({ id: row.dispatchItemId, packageCount: row.packages, destinationWarehouseCode: `FBA-${index + 1}`, fbaWarehouseCode: `ORIGINAL-FBA-${index + 1}` })) }) if (method === 'GET' && path.endsWith('/fee-coverage')) return response({ dispatchNo: source.dispatchNo, sourceComplete: true, cargoHash: hash, cargo, feeSources: fees.map(row => fault === 'mutated-original' && savedBody ? { ...row, amount: 201 } : row) }) if (method === 'GET' && path.includes('/outbound-orders?')) { const params = new URL(path, 'https://test.invalid').searchParams assert.equal(params.get('customerId'), source.customerId) assert.equal(params.has('dispatchNo'), false, 'outbound list does not support dispatchNo filtering') const rows = dirtyOutbound ? [{ outboundNo: 'OUT-WRONG', dispatchNo: source.dispatchNo }] : [{ outboundNo: 'OUT-OTHER', dispatchNo: 'DS-OTHER' }] return response({ records: rows, total: rows.length, page: 1, size: 200 }) } if (method === 'GET' && path === '/api/platform/v1/last-leg/outbound-orders/OUT-CURRENT') return response({ outbound: { outboundNo: 'OUT-CURRENT', dispatchNo: source.dispatchNo, status: 'WAIT_OUTBOUND' }, deliveries: [1, 2].map(n => ({ deliveryNo: `DEL-${n}`, status: 'WAIT_DELIVERY' })), }) if (method === 'GET' && path.endsWith('/external-freight')) return response({ snapshotHash: 'b'.repeat(64), quotes: [], requiresDeliveryFee: true }) if (method === 'POST' && path.endsWith('/transfer-delivery')) { if (body.feeCoverageCargoHash !== hash) { if (fault === 'failed-command-writes') dirtyOutbound = true return response(null, '原费用对应货物已变化,请重新核对覆盖范围') } if (body.feeCoverage[0].sourceVersion !== 1) return response(null, '原费用版本已变化,请重新确认服务覆盖范围') if (mode === 'FULL' && body.fees.length) return response(null, '本次货物的该服务已由原费用覆盖,请勿重复登记应收') if (mode === 'PARTIAL' && !body.fees.length) return response(null, '尚未有约定运费的货物须填写派送费') replayed = Boolean(savedBody) if (savedBody) assert.deepEqual(body, savedBody) savedBody = structuredClone(body) return response({ dispatchNo: source.dispatchNo, outboundNo: 'OUT-CURRENT', outboundStatus: 'WAIT_OUTBOUND', replayed, deliveries: [1, 2].map(n => ({ deliveryNo: `DEL-${n}`, deliveryStatus: 'WAIT_DELIVERY' })) }) } const detail = path.match(/^\/api\/platform\/v1\/last-leg\/delivery-orders\/DEL-([12])\?customerId=900719925474099301$/) assert.ok(detail, `Unexpected ${method} ${path}`) const index = Number(detail[1]) - 1 const selected = savedBody.feeCoverage.filter(selection => selection.financeFeeId === fees[index].financeFeeId) const references = selected.map(selection => ({ source: fees[index], services: selection.services })) const shouldCharge = mode === 'PARTIAL' && (fault === 'charge-covered-destination' ? index === 0 : index === 1) const newFee = { feeDirection: 'RECEIVABLE', feeType: 'DELIVERY_FEE', currency: 'USD', amount: 17.5, sourceStatus: 'ACTIVE' } return response({ order: { dispatchNo: source.dispatchNo, deliveryNo: `DEL-${index + 1}`, destinationWarehouseCode: `FBA-${index + 1}` }, fees: shouldCharge || (fault === 'duplicate-new-fee' && index === 0) || (fault === 'replay-adds-fee' && replayed) ? [newFee] : [], feeCoverage: { cargoHash: hash, operatorId: 'op-test', confirmedAt: replayed && fault === 'rewrite-confirmation' ? '2026-10-03T02:00:00Z' : '2026-10-03T01:00:00Z', cargo: fault === 'wrong-cargo' ? [cargo[0]] : [cargo[index]], references: fault === 'missing-reference' ? [] : references } }) }, } return { source, context, calls, assertions, clearFault() { fault = '' } } } test('persisted reconciliation reads exact original documents and only replays the original successful command', async () => { const f = fixture('FULL', 'wrong-cargo') await assert.rejects(() => verifyFleetFeeCoverage(f.context, 'coverage', f.source)) assert.equal(f.source.proof.outboundNo, 'OUT-CURRENT') const originalKey = f.source.proof.confirmationRequest.idempotencyKey const before = f.calls.length f.clearFault() const proof = await verifyFleetFeeCoverage(f.context, 'coverage', f.source, { reconcilePersisted: true }) assert.equal(proof.status, 'VERIFIED') const commands = f.calls.slice(before).filter(row => row.method === 'POST') assert.ok(commands.length > 0) assert.ok(commands.every(row => row.body.idempotencyKey === originalKey)) assert.ok(f.calls.slice(before).some(row => row.method === 'GET' && row.path.endsWith('/outbound-orders/OUT-CURRENT'))) }) for (const mode of ['FULL', 'PARTIAL']) { test(`${mode} fee coverage helper checks source scope, pre-write conflicts, each destination and replay`, async () => { const f = fixture(mode) const proof = await verifyFleetFeeCoverage(f.context, 'coverage', f.source) assert.equal(proof.status, 'VERIFIED') assert.equal(proof.newReceivable, mode === 'FULL' ? 0 : 17.5) assert.deepEqual(proof.deliveryNos, ['DEL-1', 'DEL-2']) assert.ok(f.assertions.every(row => row.passed && row.options.deploymentBlocker)) assert.ok(f.calls.every(call => !/cancel|receipt|departure/.test(call.path))) await assert.rejects(() => verifyFleetFeeCoverage(f.context, 'coverage', f.source), /未执行/) }) } for (const fault of ['foreign-customer', 'missing-source', 'failed-command-writes', 'wrong-cargo', 'missing-reference', 'duplicate-new-fee', 'rewrite-confirmation', 'mutated-original', 'charge-covered-destination', 'replay-adds-fee']) { test(`fee coverage HTTP proof cannot pass ${fault}`, async () => { const f = fixture(fault === 'charge-covered-destination' ? 'PARTIAL' : 'FULL', fault) await assert.rejects(() => verifyFleetFeeCoverage(f.context, 'coverage', f.source)) assert.equal(f.source.proof.status, 'CHECKING') assert.ok(f.assertions.some(row => !row.passed)) if (['foreign-customer', 'missing-source'].includes(fault)) assert.equal(f.calls.some(row => row.method === 'POST'), false) }) }