import test from 'node:test' import assert from 'node:assert/strict' import fs from 'node:fs' import path from 'node:path' import { fileURLToPath } from 'node:url' import { run } from './finance-full-fixtures.mjs' const here = path.dirname(fileURLToPath(import.meta.url)) const source = fs.readFileSync(path.join(here, 'finance-full-fixtures.mjs'), 'utf8') test('finance fixture module is independently exported and marker isolated', () => { assert.match(source, /export async function run\(context\)/) assert.match(source, /context\.marker = `\$\{originalMarker\}-\$\{suffix\}`/) assert.match(source, /createdResources\.financeFullFixtures/) }) test('fixture covers weight, volume, manual order, and container allocation contracts', () => { for (const basis of ['WEIGHT_KG', 'VOLUME_CBM', 'MANUAL_ORDER_FEE', 'CONTAINER_SHARED']) assert.match(source, new RegExp(basis)) assert.match(source, /request\(context, 'POST', '\/api\/platform\/finance\/v1\/cost-entries'/) assert.match(source, /no-marker-owned-container-snapshot/) }) test('fee writes use verified Firstleg paths and do not fabricate historical IDs', () => { assert.match(source, /listPath = `\$\{basePath\}\?accountId=/) assert.match(source, /const basePath = `\/api\/platform\/v1\/loading-plans\//) assert.match(source, /sourceType: detail\?\.sourceType \|\| 'MANUAL'/) assert.match(source, /created\?\.marker === context\.marker/) assert.match(source, /\/fees`/) assert.match(source, /chargeUnit === 'KG'/) assert.match(source, /chargeUnit === 'CBM'/) }) test('shared fixture source names the finance full acceptance contract', () => { for (const field of ['marker', 'customerId', 'orders', 'feeSources', 'container', 'allocations', 'supplier', 'voucherFileId', 'deploymentBlocker']) assert.match(source, new RegExp(`\\b${field}\\b`)) for (const field of ['feeSourceId', 'sourceType', 'direction', 'currency', 'amount', 'businessDate', 'billingBasis']) { assert.match(source, new RegExp(field)) } }) function fakeContext(overrides = {}) { const steps = [] const context = { marker: 'FIN-BEHAVIOR', token: 'test-token', options: { gateway: 'http://invalid.local' }, steps, scenarios: [], createdResources: {}, context: {}, addStep(module, name, response, passed, evidence, options = {}) { steps.push({ module, name, response, passed, evidence, deploymentBlocker: Boolean(options.deploymentBlocker && !passed) }) }, assert(module, name, response, passed, evidence, options) { this.addStep(module, name, response, passed, evidence, options) }, addCoverageGap(module, name, coverage, evidence, code) { this.addStep(module, name, null, false, `${code}: ${evidence}`, { deploymentBlocker: true }) }, apiSuccess(module, name, response) { this.addStep(module, name, response, true, 'fake'); return response?.body?.data }, async request() { throw new Error('behavior test must use hooks') }, financeFixtureHooks: {}, ...overrides, } return context } function hookSet({ missing = false } = {}) { let automaticIndex = 0 return { loadPricingEvidence: async () => ({ activeVersionIds: ['PRICE-1'] }), createOrder: async (context, suffix) => ({ marker: `${context.marker}-${suffix}`, tenantId: '701', orderId: `O-${suffix}`, orderNo: `NO-${suffix}`, waybillId: suffix === 'AUTO1' ? 'WB-1' : null, inboundId: `I-${suffix}` }), measureOrder: async (context, resource) => ({ ...resource, measurementId: `M-${resource.orderId}`, actualWeightKg: 12.5, totalVolumeCbm: 0.072 }), loadFeeItem: async (context, direction) => ({ id: direction === 'PAYABLE' ? '902' : '901', status: 'ACTIVE' }), loadSupplier: async () => missing ? null : ({ id: 'PARTNER-1', partnerName: '往来单位一', status: 'ACTIVE' }), uploadVoucher: async () => missing ? null : 'VOUCHER-1', verifyContainerCostSnapshotInput: async () => ({ valid: true, items: [{ accountId: '701', orderId: 'O-AUTO1', volumeCbm: 0.072 }] }), calculateAutomaticFee: async (context, resource) => { automaticIndex += 1 const weight = automaticIndex % 2 === 1 return { feeSourceId: `F-${automaticIndex}`, orderId: resource.orderId, inboundId: resource.inboundId, sourceType: 'FBA_PRICING', direction: 'RECEIVABLE', currency: 'CNY', amount: 100 + automaticIndex, businessDate: weight ? '2026-08-10' : '2026-08-11', billingBasis: weight ? 'WEIGHT_KG' : 'VOLUME_CBM', chargeUnit: weight ? 'KG' : 'CBM', chargeableQuantity: automaticIndex, actualWeightKg: 54.4, actualVolumeCbm: 0.748 } }, createManualFee: async (context, resource, item, direction, amount, basis, businessDate) => ({ feeSourceId: 'F-MANUAL', orderId: resource.orderId, inboundId: resource.inboundId, sourceType: 'MANUAL', direction, currency: 'CNY', amount, businessDate, billingBasis: basis, }), } } test('run behavior emits consumer shape, both billing bases, distinct dates, supplier and voucher', async () => { const context = fakeContext() context.financeFixtureHooks = hookSet() await run(context) const fixture = context.createdResources.financeFullFixtures assert.equal(fixture.customerId, '701') assert.equal(fixture.orders.length, 5) assert.equal(fixture.orders[0].orderNo, 'NO-AUTO1') assert.equal(fixture.orders[0].waybillId, 'WB-1') assert.equal(fixture.orders[0].actualWeightKg, 12.5) assert.equal(fixture.orders[0].totalVolumeCbm, 0.072) assert.deepEqual(new Set(fixture.feeSources.map((item) => item.billingBasis).filter((value) => ['WEIGHT_KG', 'VOLUME_CBM'].includes(value))), new Set(['WEIGHT_KG', 'VOLUME_CBM'])) assert.ok(new Set(fixture.feeSources.map((item) => item.businessDate)).size >= 2) assert.equal(fixture.supplier.supplierId, 'PARTNER-1') assert.equal(fixture.supplier.supplierName, '往来单位一') assert.equal(fixture.voucherFileId, 'VOUCHER-1') assert.ok(Array.isArray(fixture.allocations)) assert.ok(fixture.feeSources.filter((item) => item.direction === 'RECEIVABLE').length >= 4) assert.ok(new Set(fixture.feeSources.filter((item) => item.direction === 'RECEIVABLE').map((item) => item.businessDate)).size >= 2) }) test('run preserves real price-row profiles when creating automatic orders', async () => { const context = fakeContext() const profiles = [ { versionId: 'PRICE-1', rateId: 'RATE-KG', chargeUnit: 'KG', originWarehouseId: '11', channelCode: 'SEA-KG', pricingTaxMode: 'BUY_DECLARATION', destinationCode: 'USLAX' }, { versionId: 'PRICE-1', rateId: 'RATE-CBM', chargeUnit: 'CBM', originWarehouseId: '11', channelCode: 'SEA-CBM', pricingTaxMode: 'BUY_DECLARATION', destinationCode: 'USLAX' }, ] const seen = [] context.financeFixtureHooks = { ...hookSet(), loadPricingEvidence: async () => ({ activeVersionIds: ['PRICE-1'], profiles }), createOrder: async (ctx, suffix, profile) => { seen.push(profile) return { marker: `${ctx.marker}-${suffix}`, tenantId: '701', orderId: `O-${suffix}`, orderNo: `NO-${suffix}`, inboundId: `I-${suffix}` } }, } await run(context) assert.equal(seen[0].chargeUnit, 'KG') assert.equal(seen[1].chargeUnit, 'CBM') assert.deepEqual(context.createdResources.financeFullFixtures.pricingEvidence.profiles, profiles) }) test('run behavior preserves null IDs and deployment blockers when supplier, voucher and container are unavailable', async () => { const context = fakeContext() context.financeFixtureHooks = hookSet({ missing: true }) await run(context) const fixture = context.createdResources.financeFullFixtures assert.equal(fixture.supplier, null) assert.equal(fixture.voucherFileId, null) assert.deepEqual(fixture.container, { containerBookingId: null, containerNo: null }) assert.deepEqual(fixture.allocations, []) assert.ok(fixture.deploymentBlocker.length >= 3) assert.ok(context.steps.some((step) => step.deploymentBlocker && step.name.includes('往来单位'))) assert.ok(context.steps.some((step) => step.deploymentBlocker && step.name.includes('voucherFileId'))) }) test('run reports missing measurement prerequisites without throwing or borrowing historical fixtures', async () => { const context = fakeContext() context.financeFixtureHooks = { ...hookSet(), measureOrder: async () => null, } await run(context) const fixture = context.createdResources.financeFullFixtures assert.equal(fixture.orders.length, 0) assert.deepEqual(fixture.container, { containerBookingId: null, containerNo: null }) assert.deepEqual(fixture.allocations, []) assert.equal(fixture.prerequisites.container.category, 'container-prerequisite') assert.ok(fixture.deploymentBlocker.some((item) => item.includes('缺少已完成柜次'))) assert.ok(context.steps.some((step) => step.deploymentBlocker && step.name.includes('真实订单/运单分摊'))) }) test('manual fee approval path keeps accountId as a query parameter', () => { assert.match(source, /basePath = `\/api\/platform\/v1\/inbounds\/.*fee-details`/) assert.match(source, /\$\{basePath\}\/\$\{feeId\}\/approve\?accountId=/) assert.doesNotMatch(source, /fee-details\?accountId=\$\{encodeURIComponent\(resource\.tenantId\)\}`\)/) }) test('fixture creates four real receivable manual sources before payable source', () => { assert.match(source, /const receivableResources = measured\.slice\(0, 4\)/) assert.match(source, /feeItems\.RECEIVABLE, 'RECEIVABLE'/) assert.match(source, /'MANUAL_RECEIVABLE'/) assert.match(source, /dateOffset\(-1 - \(index % 2\)\)/) }) test('container cost uses loading plan identity and verifies snapshot fields before Finance', () => { assert.match(source, /containerId: String\(container\.loadingPlanId\)/) assert.doesNotMatch(source, /containerId: Number\(container\.loadingPlanId\)/) assert.match(source, /smart-loading\/plans\/\$\{encodeURIComponent\(container\.loadingPlanId\)\}/) assert.match(source, /id\(item\?\.accountId\) === id\(customerId\)/) assert.match(source, /Number\(item\?\.orderId\) > 0/) assert.match(source, /Number\(item\?\.volumeCbm\) > 0/) assert.match(source, /snapshotInput\.valid \? await createContainerCost\(context, customerId, container, voucherFileId, snapshotInput\)/) assert.match(source, /publicSnapshotValid=\$\{snapshotInput\?\.valid === true\}/) assert.match(source, /Finance -> Firstleg 内部快照服务部署\/契约/) })