Files
baiyue-firstleg/scripts/admin-test-env-modules/phase4-carrier-nodes-contract.test.mjs
T
2026-08-21 10:38:28 +08:00

100 lines
4.3 KiB
JavaScript

import assert from 'node:assert/strict'
import fs from 'node:fs'
import path from 'node:path'
import test from 'node:test'
import { fileURLToPath } from 'node:url'
import { QUERY_STATUSES, STANDARD_NODES, run } from './phase4-carrier-nodes.mjs'
const here = path.dirname(fileURLToPath(import.meta.url))
const source = fs.readFileSync(path.join(here, 'phase4-carrier-nodes.mjs'), 'utf8')
function response(data) { return { http: 200, body: { code: '200', data } } }
function contextFor(routes) {
const calls = []
return {
calls,
request: async (method, requestPath) => {
calls.push({ method, path: requestPath })
assert.equal(method, 'GET')
assert.ok(Object.hasOwn(routes, requestPath), `unexpected path: ${requestPath}`)
return response(routes[requestPath])
},
apiSuccess: (_module, _name, result) => {
assert.equal(result.http, 200)
assert.equal(result.body.code, '200')
return result.body.data
},
assert: (_module, name, _result, condition, evidence) => {
assert.ok(condition, `${name}: ${evidence}`)
},
}
}
const task = {
id: '9007199254740993', queryNo: 'CSQ9007199254740993', customerId: '10001',
oceanBookingId: '20001', containerBookingId: '30001', carrierCode: 'COSCO',
trackingType: 'CONTAINER_NO', trackingNo: 'CSNU1234567', status: 'STOPPED',
latestStandardNode: 'PICKUP_READY', latestNodeObservedAt: '2026-08-20T12:00:00',
lastSuccessAt: '2026-08-20T12:00:00', sourceUpdatedAt: '2026-08-20T12:00:00',
expiresAt: null, stoppedAt: '2026-08-20T12:01:00', stopReason: '目的港可提柜后停止',
}
const node = {
id: '40001', providerCode: 'COSCO', providerNodeCode: 'PICKUP', standardNode: 'PICKUP_READY',
nodeSequence: 60, observedAt: '2026-08-20T12:00:00', estimatedAt: null,
actualAt: '2026-08-20T11:55:00', locationCode: 'USLAX', locationName: 'Los Angeles',
sourceReference: null, createdAt: '2026-08-20T12:00:01',
}
const history = {
id: '50001', action: 'STOP', fromVersion: 4, toVersion: 5,
reason: null, operatorId: null, operatorName: '系统', createdAt: '2026-08-20T12:01:00',
}
const carriers = [{
carrierCode: 'COSCO', carrierName: 'COSCO', officialUrl: 'https://example.invalid',
accessMode: 'INTERNAL_PROVIDER', automated: true, coverageGap: false,
}]
test('readonly module statically forbids writes and internal execution endpoints', () => {
assert.match(source, /context\.request\('GET'/)
assert.doesNotMatch(source, /\bfetch\s*\(/)
for (const method of ['POST', 'PUT', 'PATCH', 'DELETE']) {
assert.doesNotMatch(source, new RegExp(`context\\.request\\(['"]${method}['"]`))
}
for (const endpoint of ['/scheduler', '/attempts', '/manual-results', '/internal', '/pda']) {
assert.doesNotMatch(source, new RegExp(endpoint.replace('/', '\\/'), 'i'))
}
})
test('canonical task and node sets are locked to the phase-4 read contract', () => {
assert.deepEqual(QUERY_STATUSES, ['READY', 'MANUAL_REQUIRED', 'ARRIVED', 'PICKUP_READY', 'STOPPED'])
assert.deepEqual(STANDARD_NODES, [
'DEPARTED_WAREHOUSE', 'AT_ORIGIN_PORT', 'DEPARTED_ORIGIN_PORT',
'IN_TRANSIT', 'ARRIVED', 'PICKUP_READY',
])
})
test('empty page validates read-only pagination without detail calls', async () => {
const context = contextFor({
'/api/platform/v1/carrier-schedule-queries/carriers': carriers,
'/api/platform/v1/carrier-schedule-queries?page=1&size=20': { records: [], page: 1, size: 20, total: 0, pages: 0 },
})
await run(context)
assert.deepEqual(context.calls, [
{ method: 'GET', path: '/api/platform/v1/carrier-schedule-queries/carriers' },
{ method: 'GET', path: '/api/platform/v1/carrier-schedule-queries?page=1&size=20' },
])
})
test('existing task reads page, detail and history with nullable node times', async () => {
const base = '/api/platform/v1/carrier-schedule-queries'
const context = contextFor({
[`${base}/carriers`]: carriers,
[`${base}?page=1&size=20`]: { records: [{ query: task, expired: false }], page: 1, size: 20, total: 1, pages: 1 },
[`${base}/${task.id}`]: { query: { query: task, expired: false }, nodes: [node], histories: [history] },
[`${base}/${task.id}/history`]: [history],
})
await run(context)
assert.deepEqual(context.calls.map((item) => item.method), ['GET', 'GET', 'GET', 'GET'])
assert.equal(context.calls.at(-1).path, `${base}/${task.id}/history`)
})