Files
baiyue-firstleg/scripts/fleet-test-env-acceptance.test.mjs
T

284 lines
21 KiB
JavaScript

import test from 'node:test'
import assert from 'node:assert/strict'
import { mkdtempSync, mkdirSync, readFileSync, existsSync, rmSync } from 'node:fs'
import { tmpdir } from 'node:os'
import { join, dirname, resolve } from 'node:path'
import { parseFleetArguments, fleetExecutionPlan, runFleetAcceptance, reviewedFullCheckpoint, verifyFleetTerminalState, runFleetHandoverStage, fleetCostShares, prepareFleetCostActors } from './fleet-test-env-acceptance.mjs'
test('fleet entry defaults to a no-side-effect plan and requires explicit TEST and unique run ID', () => {
const p = parseFleetArguments([])
assert.equal(p.execute, false); assert.equal(fleetExecutionPlan(p).execution, 'PLAN')
for (const args of [['--execute'], ['--execute', '--run-id', 'new'], ['--execute', '--environment', 'prod', '--run-id', 'new'],
['--execute', '--environment', 'TEST', '--run-id', '../old'], ['--plan', '--execute'], ['--force'], ['--run-id', 'a', '--run-id', 'b']]) {
assert.throws(() => parseFleetArguments(args))
}
assert.equal(parseFleetArguments(['--execute', '--environment', 'TEST', '--run-id', 'new']).execute, true)
const handover = parseFleetArguments(['--plan', '--environment', 'TEST', '--run-id', 'fleet-handover-20261003', '--output', 'reports/fleet-handover-live-20261003'])
assert.equal(fleetExecutionPlan(handover).outputDir, 'reports/fleet-handover-live-20261003')
for (const output of ['reports/../outside', 'C:/temp/output', 'reports', 'reports/nested/path']) assert.throws(() => parseFleetArguments(['--output', output]))
})
test('handover stage exports only a verified same-run departure fixture before the optional UI checkpoint', async () => {
const order = [], departure = { tripNo: 'TR-NEW', status: 'IN_TRANSIT' }
const context = { marker: 'CURRENT', context: { saveFleetHandoverFixture(value) { order.push('save'); assert.equal(value.marker, 'CURRENT') } },
createdResources: { fleetAcceptance: { departure } } }
const trip = { tripNo: 'TR-NEW', warehouseId: '92', costAcceptance: { status: 'VERIFIED', marker: 'CURRENT', expected: { finalCny: '100.03', usd: '13.07' } } }, details = [{ dispatchNo: 'PC-NEW' }]
const verified = { status: 'VERIFIED', marker: 'CURRENT', originalDeparture: { snapshot: { confirmedLoad: { pallets: [] } } },
browser: { tripNo: 'TR-NEW' }, handoverNo: 'HO-NEW' }
const result = await runFleetHandoverStage(context, trip, 'secret-picker-token', details, {
verify: async (ctx, module, proof, token, dispatches) => { order.push('verify'); assert.equal(ctx, context); assert.equal(proof, trip); assert.equal(token, 'secret-picker-token'); assert.equal(dispatches, details); return verified },
onReady: async fixture => { order.push('ui'); assert.equal(fixture.status, 'DEPART_AND_HANDOVER_VERIFIED'); assert.equal(fixture.departure, departure) },
})
assert.deepEqual(order, ['verify', 'save', 'ui']); assert.ok(!JSON.stringify(result).includes('secret-picker-token'))
assert.deepEqual(result.costs, trip.costAcceptance)
for (const invalid of [{ ...verified, status: 'FAILED' }, { ...verified, marker: 'OLD' }, { ...verified, originalDeparture: null }]) {
await assert.rejects(runFleetHandoverStage(context, trip, 'token', details, { verify: async () => invalid,
onReady: async () => assert.fail('invalid evidence must not reach the browser checkpoint') }), /current verified/)
}
})
test('cost shares use controlled physical pallets and the original external booking, never statistical totals', () => {
const trip = { externalReceipt: { status: 'VERIFIED', bookingNo: 'BK-ONE', warehousePalletId: '3' }, externalDispatch: { dispatchNo: 'EXT' } }
const details = [{ dispatchNo: 'OWN', items: [{ warehousePalletId: '1' }, { warehousePalletId: '1' }, { warehousePalletId: '2' }] },
{ dispatchNo: 'EXT', items: [{ warehousePalletId: '3' }] }]
assert.deepEqual(fleetCostShares(trip, details), [
{ businessNo: 'OWN', sourceType: 'OWN', dispatchNo: 'OWN', warehousePalletIds: ['1', '2'], palletSpaces: 2 },
{ businessNo: 'BK-ONE', sourceType: 'EXTERNAL', dispatchNo: 'EXT', warehousePalletIds: ['3'], palletSpaces: 1 },
])
assert.throws(() => fleetCostShares(trip, [{ ...details[0], items: [{ warehousePalletId: '3' }] }, details[1]]), /distinct/)
assert.throws(() => fleetCostShares(trip, [details[0], { ...details[1], items: [{ warehousePalletId: '4' }] }]), /receipt/)
})
test('missing isolated cost actor preparation rejects before readers or fixture creation', async () => {
let touched = false
await assert.rejects(runFleetAcceptance({ execute: true, environment: 'TEST' }, {
env: { CHONGCHONG_ENV: 'test' }, prepareCostActors: null, readers: { preflight() { touched = true } },
}), /actor preparation/)
assert.equal(touched, false)
})
for (const failLogin of [false, true]) test(`cost actors create exact isolated roles and disable only owned accounts; login failure=${failLogin}`, async () => {
const calls = [], saved = [], roles = [], users = []
const context = { marker: 'RUN', token: 'admin-secret', createdResources: {}, async findAvailablePhone() { return '19900000000' },
async checkpoint() { saved.push(structuredClone(context.createdResources)) },
async request(method, path, body, token) {
const pending = saved.at(-1).fleetCostActors.requests.at(-1)
assert.equal(pending.status, 'SENDING'); assert.equal(pending.path, path)
calls.push({ method, path, body, token })
let data
if (path.endsWith('/authorization/permissions')) data = token === 'session-1' ? ['platform:fleet:trip:list'] : []
else if (path.endsWith('/permissions') && method === 'GET') data = [{ id: '7', permissionCode: 'platform:fleet:trip:list', status: 'ACTIVE' }]
else if (path.endsWith('/roles') && method === 'POST') { data = { ...body, id: String(roles.length + 1), roleType: 'CUSTOM' }; roles.push(data) }
else if (path.endsWith('/roles/permissions')) data = body
else if (path.endsWith('/users') && method === 'POST') { data = { ...body, membershipId: String(users.length + 10) }; users.push(data) }
else if (path === '/api/auth/login') { if (failLogin) throw Error('login unavailable'); data = { accessToken: `session-${users.length}` } }
else if (path.endsWith('/status')) data = body
else assert.fail(path)
return { http: 200, body: { code: '200', data }, requestId: 'request', traceId: 'trace' }
} }
if (failLogin) await assert.rejects(prepareFleetCostActors(context), /login unavailable/)
else {
const actors = await prepareFleetCostActors(context)
assert.equal(actors.listOnlyToken, 'session-1'); assert.equal(actors.deniedToken, 'session-2')
assert.equal(calls.filter(row => row.path.endsWith('/roles/permissions'))[1].body.permissionIds.length, 0)
await actors.close()
const count = calls.length; await actors.close(); assert.equal(calls.length, count)
}
assert.equal(context.createdResources.fleetCostActors.status, 'DISABLED')
assert.equal(calls.filter(row => /\/users\/\d+\/status$/.test(row.path)).length, users.length)
assert.equal(calls.filter(row => /\/roles\/\d+\/status$/.test(row.path)).length, roles.length)
const evidence = JSON.stringify(saved)
assert.ok(!evidence.includes('session-1')); assert.ok(!evidence.includes('admin-secret'))
for (const user of users) assert.ok(!evidence.includes(user.password))
assert.ok(!calls.some(row => row.method === 'DELETE'))
})
test('main chain performs handover after actual DEPART and before receipts, and cleanup follows current-resource closure', async () => {
const source = readFileSync(new URL('./fleet-test-env-acceptance.mjs', import.meta.url), 'utf8')
const savedDeparture = source.indexOf('proof.departure = departed'), handover = source.indexOf("stage('IN_TRANSIT_HANDOVER')")
const receipt = source.indexOf("stage('RECEIPTS_AND_RESOURCE_RELEASE')"), closure = source.indexOf('proof.closure = await finishFleetTrip')
const cleanup = source.indexOf('await cleanupFleetHandoverResources(context, MODULE, trip)')
assert.ok(savedDeparture > 0 && savedDeparture < handover && handover < receipt && receipt < closure && closure < cleanup)
assert.match(source, /finally \{ await disablePicker\(context, picker, 71\) \}/)
})
async function executeFixture(failProducer = false) {
const temporary = mkdtempSync(join(tmpdir(), 'fleet-entry-contract-'))
const root = join(temporary, 'chongchong-global', 'baiyue-firstleg'); mkdirSync(root, { recursive: true })
const calls = [], secrets = 'never-print-this-secret'
const env = { CHONGCHONG_ENV: 'test', CHONGCHONG_ADMIN_API_BASE_URL: 'https://test.invalid', CHONGCHONG_ADMIN_USERNAME: 'fixture-admin',
CHONGCHONG_ADMIN_PASSWORD: secrets, ADMIN_PDA_USERNAME: 'fixture-pda', ADMIN_PDA_PASSWORD: secrets }
const config = parseFleetArguments(['--execute', '--environment', 'TEST', '--run-id', 'fixture'])
const createContext = options => ({ options, context: {}, createdResources: {}, steps: [], modules: [], coverageGaps: [], token: secrets,
async executeModule(name, handler) {
try { await handler(); this.modules.push({ name, success: true }) }
catch { this.modules.push({ name, success: false }); this.steps.push({ name: 'producer failed', passed: false }) }
},
async run() {
await this.executeModule('producer', async () => { calls.push('producer'); if (failProducer) throw Error('fixture failure') })
await this.executeModule('phase4-overseas-dispatch', async () => { calls.push('dispatch'); this.createdResources.current = { accessToken: secrets } })
return { success: true, steps: this.steps, modules: this.modules, createdResources: this.createdResources, summary: { failed: 0 } }
},
})
try {
const options = { root, env, createContext, readers: { async preflight() { calls.push('preflight') }, async readReceiving() { throw Error('not used') } },
scenario: async context => {
calls.push('fleet'); context.createdResources.fleetAcceptance = { status: 'HTTP_CHAIN_VERIFIED_WITH_ACK_GAP' }
const command = { requests: [{ method: 'POST', status: 'SENDING', body: { idempotencyKey: 'original-key' }, accessToken: secrets }] }
await context.context.checkpointFleetHandover(command)
const firstPath = join(root, 'reports/fleet-fixture/evidence/handover-000001.json')
const before = readFileSync(firstPath, 'utf8'), saved = JSON.parse(before)
assert.equal(saved.proof.requests[0].body.idempotencyKey, 'original-key')
assert.equal(saved.proof.requests[0].status, 'SENDING'); assert.ok(!before.includes(secrets))
command.requests[0].status = 'RESPONDED'
await context.context.checkpointFleetHandover(command)
assert.equal(readFileSync(firstPath, 'utf8'), before)
assert.equal(JSON.parse(readFileSync(join(root, 'reports/fleet-fixture/evidence/handover-000002.json'), 'utf8')).proof.requests[0].status, 'RESPONDED')
} }
const result = await runFleetAcceptance(config, options)
const report = JSON.parse(readFileSync(result.report, 'utf8'))
assert.ok(!readFileSync(result.report, 'utf8').includes(secrets))
assert.equal(existsSync(join(root, 'reports/.fleet-acceptance.lock')), false)
const oldReport = readFileSync(result.report, 'utf8')
const repeated = await runFleetAcceptance(config, options)
assert.equal(repeated.success, false)
assert.equal(readFileSync(result.report, 'utf8'), oldReport, 'existing run is never overwritten or resumed')
return { result, report, calls }
} finally {
assert.equal(dirname(resolve(temporary)), resolve(tmpdir()))
assert.ok(resolve(temporary).startsWith(join(resolve(tmpdir()), 'fleet-entry-contract-')))
rmSync(temporary, { recursive: true, force: true })
}
}
test('fresh entry runs dedicated fleet scenario after prerequisites without altering the default F40 chain', async () => {
const { result, report, calls } = await executeFixture()
assert.equal(result.success, true)
assert.deepEqual(calls, ['preflight', 'producer', 'dispatch', 'fleet'])
assert.equal(report.fleetGoalComplete, false)
assert.equal(report.createdResources.current.accessToken, '[REDACTED]')
})
test('failed prerequisite stops before further fixture creation and retains the failed report', async () => {
const { result, report, calls } = await executeFixture(true)
assert.equal(result.success, false)
assert.deepEqual(calls, ['preflight', 'producer'])
assert.equal(report.fatal, 'Producer stopped: producer')
})
test('environment mismatch rejects before invoking any readers or contexts', async () => {
let touched = false
await assert.rejects(() => runFleetAcceptance({ execute: true, environment: 'TEST', runId: 'new' }, {
env: { CHONGCHONG_ENV: 'production' }, readers: { preflight() { touched = true } },
}), /TEST/)
assert.equal(touched, false)
})
test('reviewed resume requires explicit paired checkpoint options', () => {
assert.throws(() => parseFleetArguments(['--resume-report', 'reports/old/report.json']))
assert.throws(() => parseFleetArguments(['--resume-report', 'reports/old/report.json', '--resume-stage', 'PICK_EACH_DISPATCH']))
const c = parseFleetArguments(['--execute', '--environment', 'TEST', '--run-id', 'reviewed', '--resume-report', 'reports/old/report.json', '--resume-stage', 'FULL_COVERAGE'])
assert.equal(c.resumeStage, 'FULL_COVERAGE')
})
test('checkpoint guard forbids repeating commands whose write outcome may be unknown', () => {
const report = { success: false, createdResources: {
fleetAcceptance: { marker: 'M', stage: 'FULL_COVERAGE', status: 'CHECKING', tripNo: 'T', originalDispatch: { dispatchNo: 'D' } },
fleetTrips: [{ tripNo: 'T', dispatchNo: 'D' }],
fleetFeeCoverageCases: [{ mode: 'FULL', marker: 'M', dispatchNo: 'D', proof: { status: 'CHECKING', requests: [{ method: 'GET' }] } }],
} }
assert.equal(reviewedFullCheckpoint(report).marker, 'M')
const mutated = structuredClone(report)
mutated.createdResources.fleetFeeCoverageCases[0].proof.requests.push({ method: 'POST' })
assert.throws(() => reviewedFullCheckpoint(mutated), /unknown outcomes/)
mutated.createdResources.fleetFeeCoverageCases[0].proof.requests.pop()
mutated.createdResources.fleetFeeCoverageCases[0].proof.outboundNo = 'OUT-1'
assert.throws(() => reviewedFullCheckpoint(mutated), /unknown outcomes/)
assert.throws(() => reviewedFullCheckpoint(mutated, 'FULL_COVERAGE_PERSISTED'), /exact successful command/)
Object.assign(mutated.createdResources.fleetFeeCoverageCases[0].proof, { deliveryNos: ['DEL-1'], confirmationRequest: { idempotencyKey: 'original-key' } })
mutated.createdResources.fleetFeeCoverageCases[0].proof.requests.push({ label: '正式保存本次原费用服务覆盖并转派送', method: 'POST', http: 200, code: '200' })
assert.equal(reviewedFullCheckpoint(mutated, 'FULL_COVERAGE_PERSISTED').persisted, true)
})
test('receipt-ready reconciliation cannot repeat a receipt that has already started', () => {
const report = { success: false, steps: [{ name: '创建海外PDA仓库操作员71', code: 'A0409', message: '手机号已存在' }], createdResources: {
fleetAcceptance: { marker: 'M', stage: 'RESERVATIONS', status: 'CHECKING', tripNo: 'T' },
fleetTrips: [{ tripNo: 'T', dispatchNo: 'D2', reservation: { status: 'VERIFIED' } }],
fleetFeeCoverageCases: [{ mode: 'FULL', proof: { status: 'VERIFIED' } }, { mode: 'PARTIAL', dispatchNo: 'D2', proof: { status: 'VERIFIED' } }],
} }
assert.equal(reviewedFullCheckpoint(report, 'EXTERNAL_RECEIPT_READY').receiptReady, true)
report.createdResources.fleetTrips[0].externalReceipt = { status: 'CHECKING' }
assert.throws(() => reviewedFullCheckpoint(report, 'EXTERNAL_RECEIPT_READY'), /no receipt writes/)
})
test('palletized checkpoint requires a saved successful command, owned operator, and no later confirmation', () => {
const report = { success: false, createdResources: {
fleetAcceptance: { marker: 'M', stage: 'EXTERNAL_RECEIPT', status: 'CHECKING', tripNo: 'T', picker: { membershipId: '71' }, pickerMarker: 'M-PDA' },
fleetTrips: [{ tripNo: 'T', dispatchNo: 'D2', reservation: { status: 'VERIFIED' }, externalReceipt: { status: 'CHECKING',
warehousePalletId: 'P1', palletizeRequest: { idempotencyKey: 'original' }, requests: [
{ label: 'PDA正式完成外接收货及库存入账', method: 'POST', http: 200, code: '200' },
] } }],
fleetFeeCoverageCases: [{ mode: 'FULL', proof: { status: 'VERIFIED' } }, { mode: 'PARTIAL', dispatchNo: 'D2', proof: { status: 'VERIFIED' } }],
} }
assert.equal(reviewedFullCheckpoint(report, 'EXTERNAL_PALLETIZED').palletized, true)
assert.throws(() => reviewedFullCheckpoint(report, 'EXTERNAL_RECEIPT_CONFIRMED'), /exact successful PDA command/)
const confirmed = structuredClone(report)
confirmed.createdResources.fleetTrips[0].externalReceipt.receiptRequest = { idempotencyKey: 'confirm-original' }
assert.throws(() => reviewedFullCheckpoint(confirmed, 'EXTERNAL_RECEIPT_CONFIRMED'), /exact successful PDA command/)
confirmed.createdResources.fleetTrips[0].externalReceipt.requests.push({ label: '运营按实际1托位和新单价30确认实收', method: 'POST', http: 200, code: '200' })
assert.equal(reviewedFullCheckpoint(confirmed, 'EXTERNAL_RECEIPT_CONFIRMED').receiptConfirmed, true)
assert.throws(() => reviewedFullCheckpoint(confirmed, 'EXTERNAL_PALLETIZED'), /exact successful PDA command/)
for (const mutate of [
r => { r.fleetTrips[0].externalReceipt.requests[0].code = 'A0409' },
r => { delete r.fleetTrips[0].externalReceipt.palletizeRequest.idempotencyKey },
r => { delete r.fleetAcceptance.picker },
r => { r.fleetTrips[0].externalReceipt.receiptRequest = { idempotencyKey: 'later' } },
r => { r.fleetTrips[0].externalDispatch = { status: 'CHECKING' } },
]) {
const invalid = structuredClone(report); mutate(invalid.createdResources)
assert.throws(() => reviewedFullCheckpoint(invalid, 'EXTERNAL_PALLETIZED'), /exact successful PDA command/)
}
})
function terminalFixture(fault = '') {
const finalTrip = { status: 'COMPLETED', completedAt: '2026-10-02T22:01:00Z', recordVersion: '30', vehicleRelease: { endLocation: 'ONT8' }, driverRelease: { endLocation: 'ONT8' } }
const closure = { status: 'CARGO_COMPLETED_AND_RESOURCES_RELEASED', finalTrip, probes: [{ tripNo: 'PROBE' }] }
const trip = { tripNo: 'T', closure, externalReceipt: { status: 'VERIFIED' }, reassignment: { status: 'VERIFIED' }, externalDispatch: { status: 'VERIFIED', dispatchNo: 'EXT', deliveryNo: 'DEL-EXT' } }
const proof = { marker: 'M', tripNo: 'T', customerId: '92', stage: 'RECEIPTS_AND_RESOURCE_RELEASE', closure,
receipts: ['DEL-OWN', 'DEL-EXT'].map(deliveryNo => ({ deliveryNo, signedAt: '2026-10-02T22:00:00' })),
departure: { shippedPackages: 6 }, finance: { status: 'CONSUMER_PERSISTED', source: { sourceFeeId: 'F1', sourceVersion: 1 } } }
const resources = { fleetAcceptance: proof, fleetTrips: [trip], fleetFeeCoverageCases: [{ mode: 'PARTIAL', dispatchNo: 'OWN', proof: { deliveryNos: ['DEL-OWN'] } }] }
const calls = []
const context = { createdResources: resources, assert() {}, async request(method, path) {
calls.push({ method, path }); assert.equal(method, 'GET')
let data
if (path.endsWith('/trips/T')) data = { trip: finalTrip, dispatches: ['OWN', 'EXT'].map(dispatchNo => ({ dispatchNo })) }
else if (path.endsWith('/trips/PROBE')) data = { trip: { status: 'CANCELLED' } }
else if (path.includes('/dispatches/')) { const number = path.split('/').at(-1), count = number === 'OWN' ? 5 : 1
data = { dispatchNo: number, status: 'DEPARTED', packageCount: count, pickedPackageCount: count, shippedPackageCount: fault === 'quantity' ? 0 : count } }
else if (path.includes('/delivery-orders/')) data = { order: { deliveryNo: path.split('/').at(-1).split('?')[0], status: fault === 'unsigned' ? 'WAIT_DELIVERY' : 'DELIVERED', signedAt: '2026-10-02T22:00:00' } }
else if (path.includes('/fee-pool?')) data = [{ customerId: '92', sourceId: 'F1', sourceVersion: fault === 'wrong-fee-version' ? 2 : 1, amount: 30, currency: 'CNY', sourceType: 'OVERSEAS_DELIVERY_FEE' }]
else throw Error('unexpected path')
return { http: 200, body: { code: '200', data: structuredClone(data) }, requestId: 'r', traceId: 't' }
} }
return { context, calls, report: { success: false, createdResources: resources, steps: [{ name: '本次车次货物结案及车辆司机释放完成', passed: false }] } }
}
test('terminal reconciliation reads actual completion, every dispatch/delivery and exact fee without replaying writes', async () => {
const f = terminalFixture()
assert.equal(reviewedFullCheckpoint(f.report, 'TERMINAL_READONLY').terminal, true)
const result = await verifyFleetTerminalState(f.context)
assert.equal(result.terminalReadback.status, 'VERIFIED')
assert.equal(f.calls.length, 7)
assert.ok(f.calls.every(row => row.method === 'GET'))
const invalid = terminalFixture().report
invalid.steps.push({ name: '真实业务失败', passed: false })
assert.throws(() => reviewedFullCheckpoint(invalid, 'TERMINAL_READONLY'), /known aggregate assertion/)
})
for (const fault of ['quantity', 'unsigned', 'wrong-fee-version']) test(`terminal readback rejects ${fault}`, async () => {
const f = terminalFixture(fault)
await assert.rejects(() => verifyFleetTerminalState(f.context))
assert.equal(f.context.createdResources.fleetAcceptance.terminalReadback.status, 'CHECKING')
})