import test from 'node:test' import assert from 'node:assert/strict' import { readFile } from 'node:fs/promises' import vm from 'node:vm' import { getDispatchHandoverStatus, normalizeHandoverStatus } from '../src/api/trip-handover.js' import { session } from '../src/stores/session.js' const wire = () => ({ dispatchNo: 'PC-ONE', tripNo: 'TR-ONE', tripStatus: 'IN_TRANSIT', tripRecordVersion: '90071992547409931', originalDeparture: { vehiclePlate: 'ORIGINAL', driverName: '原司机' }, deliveries: [{ deliveryNo: 'DL-DONE', status: 'DELIVERED', packages: '10', remainingPackages: 0, terminalAt: '2026-10-03T01:00:00Z', transport: { vehiclePlate: 'ORIGINAL', driverName: '原司机' } }, { deliveryNo: 'DL-PENDING', status: 'WAIT_DELIVERY', packages: 20, remainingPackages: 16, terminalAt: null, transport: { vehiclePlate: 'NEW', driverName: '接替司机' } }], latestHandover: null }) test('PDA query stays a GET in the dedicated namespace and never supplies customer or warehouse', async () => { const calls = []; session.apiBase = '/gateway-api'; session.token = 'test-token' globalThis.uni = { request(options) { calls.push(options); options.success({ statusCode: 200, data: { code: '200', data: wire() }, header: {} }) } } const result = await getDispatchHandoverStatus('PC-ONE') assert.equal(calls.length, 1); assert.equal(calls[0].method, 'GET'); assert.match(calls[0].url, /\/pda\/v1\/last-leg\/dispatches\/PC-ONE\/handover-status$/) assert.ok(!calls[0].data?.customerId); assert.ok(!calls[0].data?.warehouseId) assert.equal(result.tripRecordVersion, '90071992547409931'); assert.equal(result.deliveries[0].transport.vehiclePlate, 'ORIGINAL'); assert.equal(result.deliveries[1].transport.vehiclePlate, 'NEW') }) test('normalization exposes only business references and does not replace missing fields with internal IDs', () => { const data = wire(); data.customerId = 'secret-customer'; data.originalDeparture = { vehicleId: 'secret-vehicle' }; data.deliveries[0].id = 'secret-id' const value = normalizeHandoverStatus(data) assert.doesNotMatch(JSON.stringify(value), /secret-/); assert.equal(value.originalDeparture.vehiclePlate, '') assert.equal(value.deliveries[0].packages, 10) assert.equal(value.deliveries[1].remainingPackages, 16) for (const remainingPackages of [null, '', -1, 21, '1.5']) assert.throws(() => normalizeHandoverStatus({ ...data, deliveries: [{ ...data.deliveries[1], remainingPackages }] }), /剩余箱数无效/) for (const packages of [null, '', -1, 0, '1.5']) assert.throws(() => normalizeHandoverStatus({ ...data, deliveries: [{ ...data.deliveries[0], packages }] }), /箱数无效/) }) test('a response for another dispatch is rejected', async () => { session.apiBase = '/gateway-api'; session.token = 'test-token' globalThis.uni = { request(options) { options.success({ statusCode: 200, data: { code: '200', data: wire() }, header: {} }) } } await assert.rejects(getDispatchHandoverStatus('PC-OTHER'), /不属于当前排车/) }) test('the component ignores stale responses after switching task and has no write commands', async () => { const file = await readFile(new URL('../src/components/DispatchHandoverStatus.vue', import.meta.url), 'utf8') const pending = [] const context = { getDispatchHandoverStatus: () => new Promise(resolve => pending.push(resolve)), toDispatchError: error => ({ message: error.message }) } const source = file.match(/