2026-10-08 17:07:57 +08:00
|
|
|
import test from 'node:test'
|
|
|
|
|
import assert from 'node:assert/strict'
|
|
|
|
|
import { join } from 'node:path'
|
|
|
|
|
import { parseGrayUiFixtureArguments } from './fleet-capacity-pool-ui-fixture-gray.mjs'
|
|
|
|
|
import { PRIVATE_DIRECTORY, runGrayUiFixture, runUiFixture, uiActorPermissions, grayUiSchedule,
|
|
|
|
|
applyGrayUiTransportWindow, uiInquiryBody } from './fleet-capacity-pool-ui-fixture-test-env.mjs'
|
|
|
|
|
|
|
|
|
|
const args = ['--execute', '--environment', 'GRAY', '--run-id', 'ui-gray-offline', '--warehouse-id', '900',
|
|
|
|
|
'--destination-warehouse-id', '901', '--private-credentials-file', join(PRIVATE_DIRECTORY, 'ui-gray-offline.json')]
|
|
|
|
|
const env = { CHONGCHONG_ENV: 'prod', CHONGCHONG_ADMIN_API_BASE_URL: 'https://gray.example.invalid/api',
|
|
|
|
|
CHONGCHONG_ADMIN_USERNAME: 'offline', CHONGCHONG_ADMIN_PASSWORD: 'offline-only' }
|
|
|
|
|
|
|
|
|
|
test('GRAY UI default plan does not access network or private credential files and keeps only four drafts', async () => {
|
|
|
|
|
const original = globalThis.fetch; globalThis.fetch = () => assert.fail('no network')
|
|
|
|
|
try {
|
|
|
|
|
const plan = await runGrayUiFixture(parseGrayUiFixtureArguments([]), { env: {} })
|
|
|
|
|
assert.equal(plan.execute, false); assert.equal(plan.environment, 'GRAY'); assert.equal(plan.fullAcceptance, false)
|
|
|
|
|
assert.equal(plan.stopsAt, 'OPEN_AND_4_DRAFTS_READY_FOR_UI'); assert.equal(plan.retainedPda, true)
|
|
|
|
|
assert.equal(plan.output, 'reports/fleet-capacity-pool-ui-gray-plan')
|
|
|
|
|
assert.equal(plan.privateCredentialsFile, null)
|
|
|
|
|
assert.match(plan.boundary, /all six UI logins/)
|
|
|
|
|
} finally { globalThis.fetch = original }
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
test('GRAY UI CLI requires explicit scope and private exclusive file without arbitrary lifecycle switches', () => {
|
|
|
|
|
assert.equal(parseGrayUiFixtureArguments(args).environment, 'GRAY')
|
|
|
|
|
for (const bad of [args.slice(0, -2), args.map(value => value === 'GRAY' ? 'TEST' : value),
|
|
|
|
|
[...args, '--private-credentials-file', args.at(-1)], [...args, '--modules', 'orders'],
|
|
|
|
|
[...args, '--retain-pda', 'false'], [...args, '--draft-count', '24']]) assert.throws(() => parseGrayUiFixtureArguments(bad))
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
test('GRAY UI refuses TEST profile or evidence overrides and TEST runner rejects GRAY', async () => {
|
|
|
|
|
const config = parseGrayUiFixtureArguments(args)
|
|
|
|
|
await assert.rejects(runUiFixture(config, { env }), /TEST/)
|
|
|
|
|
await assert.rejects(runGrayUiFixture(config, { env: { ...env, CHONGCHONG_ENV: 'test' } }), /GRAY_FIXTURE_CONFIGURATION/)
|
|
|
|
|
for (const name of ['readers', 'prepare', 'contextFactory', 'pickerDisable']) {
|
|
|
|
|
await assert.rejects(runGrayUiFixture(config, { env, [name]: {} }), /CANNOT_OVERRIDE/)
|
|
|
|
|
}
|
|
|
|
|
await assert.rejects(runGrayUiFixture(config, { env }), /Missing gray evidence configuration/)
|
|
|
|
|
await assert.rejects(runGrayUiFixture(config, { env: { ...env, CHONGCHONG_ADMIN_API_BASE_URL: 'http://gray.example.invalid/api' } }), /INVALID_UI_API_BASE/)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
test('only sales A receives quotation-read while dispatcher and sales B remain minimal', () => {
|
|
|
|
|
const prefix = 'platform:fleet:capacity-offer:'
|
|
|
|
|
const sales = ['list', 'inquiry-create', 'inquiry-edit', 'inquiry-cancel'].map(value => prefix + value)
|
|
|
|
|
assert.deepEqual(uiActorPermissions('SALES_A', true), [...sales, 'platform:fleet:quote:list'])
|
|
|
|
|
assert.deepEqual(uiActorPermissions('SALES_B', true), sales)
|
|
|
|
|
assert.deepEqual(uiActorPermissions('MANAGER', true), [prefix + 'manage'])
|
|
|
|
|
assert.deepEqual(uiActorPermissions('SALES_A', false), sales)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
test('eight-hour cutoff has coherent later departure delivery and driver end without rewriting other requests', () => {
|
|
|
|
|
const now = Date.parse('2030-01-01T00:00:00Z'), schedule = grayUiSchedule(now)
|
|
|
|
|
assert.equal(Date.parse(schedule.cutoffAt), now + 8 * 3600000)
|
|
|
|
|
assert.equal(Date.parse(schedule.plannedDepartureAt), now + 9 * 3600000)
|
|
|
|
|
assert.equal(Date.parse(schedule.plannedDeliveryAt), now + 10 * 3600000)
|
|
|
|
|
assert.equal(Date.parse(schedule.expectedDriverEndAt), now + 12 * 3600000)
|
|
|
|
|
assert.deepEqual(grayUiSchedule(now + 999), schedule)
|
|
|
|
|
assert.match(schedule.cutoffAt, /Z$/); assert.match(schedule.plannedDepartureAt, /Z$/)
|
|
|
|
|
assert.doesNotMatch(schedule.plannedDeliveryAt, /Z$/)
|
2026-10-21 14:30:41 +08:00
|
|
|
const body = { idempotencyKey: 'stable', recordVersion: '9007199254740993' }
|
|
|
|
|
const applied = applyGrayUiTransportWindow('PUT', '/api/platform/v1/last-leg/dispatches/DS-1', body, schedule)
|
|
|
|
|
assert.equal(applied.estimatedArrivalAt, new Date(schedule.plannedDeliveryAt).toISOString())
|
2026-10-20 10:35:52 +08:00
|
|
|
assert.equal(applied.idempotencyKey, body.idempotencyKey); assert.equal(applied.recordVersion, body.recordVersion)
|
2026-10-21 14:30:41 +08:00
|
|
|
assert.equal(Object.hasOwn(body, 'estimatedArrivalAt'), false)
|
|
|
|
|
assert.throws(() => applyGrayUiTransportWindow('POST', '/api/platform/v1/last-leg/dispatches', body, null), /NATIVE_DELIVERY/)
|
2026-10-08 17:07:57 +08:00
|
|
|
assert.equal(applyGrayUiTransportWindow('POST', '/api/auth/login', body, schedule), body)
|
2026-10-21 14:30:41 +08:00
|
|
|
assert.equal(applyGrayUiTransportWindow('GET', '/api/platform/v1/last-leg/dispatches/DS-1', body, schedule), body)
|
2026-10-08 17:07:57 +08:00
|
|
|
assert.throws(() => grayUiSchedule(NaN), /CLOCK/)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
test('four GRAY drafts are complete for both commercial types without creating reservations or commercial orders', () => {
|
|
|
|
|
const offer = { status: 'OPEN', canCreateInquiry: true, tripRecordVersion: 1, recordVersion: 2, destinations: [{ destinationKey: 'FBA:ONT8' }] }
|
|
|
|
|
const bodies = Array.from({ length: 4 }, (_, index) => uiInquiryBody(offer, index < 2 ? 'A' : 'B', index, `key-${index}`, true))
|
|
|
|
|
assert.deepEqual(bodies.map(body => body.businessType), ['LOCAL_LOOSEBOARD', 'US_TRUCK', 'LOCAL_LOOSEBOARD', 'US_TRUCK'])
|
|
|
|
|
for (const body of bodies) {
|
|
|
|
|
assert.equal(body.expectedPalletCount, 1); assert.equal(body.cargo.packageCount, 1); assert.equal(body.cargo.palletSpaces, 1)
|
|
|
|
|
assert.equal('businessOrderNo' in body, false); assert.equal('status' in body, false)
|
|
|
|
|
}
|
|
|
|
|
})
|