Files

82 lines
7.3 KiB
JavaScript
Raw Permalink Normal View History

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))
})
test('SHA-bound current artifact does not require optional trip remarks in the DEPART snapshot', () => {
const f = sample(); delete f.immutableDeparture.snapshot.trip.remark
assert.equal(convert(f).tripNo, f.tripNo)
f.costs.marker = 'OLD'
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, '')
})