import test from 'node:test' import assert from 'node:assert/strict' import { createHash } from 'node:crypto' import { operationsFixtureFromHandover } from './admin-test-env-modules/fleet-quotes-operations.mjs' import { parseOperationsArguments, restrictOperationsTransport, runOperationsSession } from './fleet-operations-test-env.mjs' import { AcceptanceRun } from './admin-test-env-acceptance.mjs' function sample() { const marker = 'CURRENT', tripNo = 'TR-CURRENT', departedAt = '2026-10-03T01:00:00Z' return { marker, runId: 'current-run', tripNo, warehouseId: '92', status: 'DEPART_AND_HANDOVER_VERIFIED', handoverNo: 'HO-CURRENT', departure: { tripNo, status: 'IN_TRANSIT', departedAt, shippedPackages: 14 }, immutableDeparture: { action: 'DEPART', recordVersion: '5', snapshot: { trip: { tripNo, warehouseId: '92', departedAt, remark: marker, stops: [{ name: 'origin' }, { destinationKey: 'FBA:A' }, { destinationKey: 'FBA:B' }] }, confirmedLoad: { manifestHash: 'original-load', pallets: [{ warehousePalletId: '1', palletSpaces: 1, deliveryIndex: 1 }, { warehousePalletId: '2', palletSpaces: 1, deliveryIndex: 1 }], cargo: [{ dispatchNo: 'DP-OWN', dispatchItemId: '1', customerId: '9', warehousePalletId: '1', packages: 10, destinationKey: 'FBA:A' }, { dispatchNo: 'DP-EXT', dispatchItemId: '2', customerId: '9', warehousePalletId: '2', packages: 4, destinationKey: 'FBA:A' }] }, externalBookings: [{ bookingNo: 'FB-EXT', status: 'BOUND', dispatchNo: 'DP-EXT', customerId: '9', agreedAmount: '30.00', cargo: { currency: 'CNY' }, receipt: { source: { receivedPackages: 4, pallets: [{ warehousePalletId: '2', packages: 4 }] } } }] } }, costs: { marker, tripNo, status: 'VERIFIED', expected: { finalCny: '100.03', usd: '13.07', allocationMethod: 'SAME_ROUTE_PALLET_SPACES', allocationStatus: 'AVAILABLE' }, shares: [ { businessNo: 'DP-OWN', sourceType: 'OWN', dispatchNo: 'DP-OWN', palletSpaces: 1, warehousePalletIds: ['1'] }, { businessNo: 'FB-EXT', sourceType: 'EXTERNAL', dispatchNo: 'DP-EXT', palletSpaces: 1, warehousePalletIds: ['2'] }] } } } function convert(value, overrides = {}) { const bytes = Buffer.from(JSON.stringify(value)) return operationsFixtureFromHandover(bytes, { sha256: createHash('sha256').update(bytes).digest('hex'), marker: 'CURRENT', runId: 'current-run', ...overrides }) } test('adapts actual C artifact with independent physical cargo and exact cent currency shares', () => { const f = convert(sample()) assert.deepEqual(f.expected, { physicalPallets: 2, palletSpaces: 2, ownPackages: 10, externalPackages: 4, externalAgreedAmounts: [{ currency: 'CNY', amount: 30 }], costs: [ { currency: 'CNY', directCost: 100.03, ownCost: 50.02, externalCost: 50.01 }, { currency: 'USD', directCost: 13.07, ownCost: 6.54, externalCost: 6.53 }] }) assert.equal(f.stage, 'CURRENT_RUN_DEPARTED'); assert.equal(f.departureVerified, true) }) test('multi-stop costs retain full trip amounts and never infer source shares', () => { const f = sample(), load = f.immutableDeparture.snapshot.confirmedLoad load.pallets[1].deliveryIndex = 2; load.cargo[1].destinationKey = 'FBA:B' Object.assign(f.costs.expected, { allocationMethod: null, allocationStatus: 'UNAVAILABLE', allocationBlockReason: '各票卸货站不同,暂不可按同路线托位自动分摊' }) assert.deepEqual(convert(f).expected.costs, [{ currency: 'CNY', directCost: 100.03, ownCost: null, externalCost: null }, { currency: 'USD', directCost: 13.07, ownCost: null, externalCost: null }]) f.costs.expected.allocationMethod = 'SAME_ROUTE_PALLET_SPACES' assert.throws(() => convert(f)) }) for (const [name, mutate] of [ ['stale marker', f => { f.marker = 'OLD' }], ['stale run', f => { f.runId = 'old' }], ['unverified costs', f => { f.costs.status = 'CHECKING' }], ['wrong currency amount', f => { f.costs.expected.usd = '0' }], ['not departed', f => { f.departure.status = 'PLANNED' }], ['replaced history', f => { f.immutableDeparture.action = 'HANDOVER' }], ['duplicate physical pallet', f => { f.immutableDeparture.snapshot.confirmedLoad.pallets.push(f.immutableDeparture.snapshot.confirmedLoad.pallets[0]) }], ['external over-attribution', f => { f.immutableDeparture.snapshot.externalBookings[0].receipt.source.pallets[0].packages = 99 }], ['wrong shares', f => { f.costs.shares[1].warehousePalletIds = ['1'] }], ['wrong departure total', f => { f.departure.shippedPackages = 15 }], ]) test(`rejects ${name} before any HTTP`, () => { const f = sample(); mutate(f); assert.throws(() => convert(f)) }) test('rejects mismatched file hash even when its contents look valid', () => assert.throws(() => convert(sample(), { sha256: '0'.repeat(64) }))) test('requires explicit report/hash/current marker/run and TEST readiness; denies write and resume flags', () => { const args = ['--ready-commit=a6fd0ba3', '--fixture=reports/fleet-current-run/handover-fixture.json', `--fixture-sha256=${'a'.repeat(64)}`, '--marker=CURRENT', '--run-id=current-run'] const runtime = ['--env-file=C:/Users/BY-OY/.codex/env/chongchong-global/test-acceptance.env'] assert.equal(parseOperationsArguments(args, runtime).runId, 'current-run') for (const bad of [args.slice(1), [...args, '--write-measured=true'], [...args, '--resume=true'], args.map(a => a.startsWith('--fixture=') ? '--fixture=../old/handover-fixture.json' : a)]) assert.throws(() => parseOperationsArguments(bad, runtime)) }) test('transport permits only current-trip/statistics reads plus independent session login/logout', async () => { const context = new AcceptanceRun({ environment: 'test' }), calls = [] for (const name of ['request', 'requestWithBody', 'requestBinary']) context[name] = async (...args) => { calls.push(args); return {} } restrictOperationsTransport(context, 'TR-CURRENT') await context.request('GET', '/api/platform/v1/last-leg/fleet/trips/TR-CURRENT') for (const [method, path] of [['PUT', '/api/platform/v1/last-leg/fleet/operations/TR-CURRENT'], ['POST', '/api/platform/v1/last-leg/fleet/trips'], ['GET', '/api/platform/v1/last-leg/fleet/trips/TR-OLD'], ['GET', '/api/platform/v1/iam/tenants']]) assert.throws(() => context.request(method, path)) assert.equal(calls.length, 1) }) test('new login and logout surround a rejected live immutable snapshot without business writes', async () => { const context = new AcceptanceRun({ environment: 'test', username: 'offline', password: 'offline', quiet: true }), calls = [] context.request = async (method, path) => { calls.push([method, path]) return { http: 200, body: { code: '200', data: path.endsWith('/login') ? { accessToken: 'offline-access', refreshToken: 'offline-refresh' } : path.includes('/history') ? { total: 0, records: [] } : null }, requestId: 'offline', traceId: 'offline' } } const report = await runOperationsSession(context, convert(sample())) assert.equal(report.success, false); assert.equal(report.loggedOut, true); assert.equal(report.measuredStatus, 'NOT_TESTED') assert.deepEqual(calls.map(c => c[0]), ['POST', 'GET', 'POST']) assert.equal(calls[0][1], '/api/auth/login'); assert.equal(calls.at(-1)[1], '/api/auth/logout') assert.equal(context.token, '') })