2026-10-07 18:11:55 +08:00
|
|
|
import test from 'node:test'
|
|
|
|
|
import assert from 'node:assert/strict'
|
|
|
|
|
import { mkdtempSync, readFileSync, unlinkSync, rmdirSync } from 'node:fs'
|
|
|
|
|
import { join, resolve } from 'node:path'
|
|
|
|
|
import { tmpdir } from 'node:os'
|
|
|
|
|
import { PRIVATE_DIRECTORY, privateCredentialsPath, parseUiFixtureArguments, uiFixturePlan,
|
2026-10-20 10:35:52 +08:00
|
|
|
createPrivateCredentialStore, publicUiEvidence, uiInquiryBody, runUiFixture, applyGrayUiTransportWindow, grayUiSchedule } from './fleet-capacity-pool-ui-fixture-test-env.mjs'
|
2026-10-07 18:11:55 +08:00
|
|
|
|
|
|
|
|
const credentials = join(PRIVATE_DIRECTORY, 'offline-contract-example.json')
|
|
|
|
|
const execute = ['--execute', '--environment', 'TEST', '--run-id', 'ui-contract', '--warehouse-id', '10',
|
|
|
|
|
'--destination-warehouse-id', '11', '--private-credentials-file', credentials]
|
|
|
|
|
|
|
|
|
|
test('default and explicit plan cannot invoke network, private writes or fixture producers', async () => {
|
|
|
|
|
const original = globalThis.fetch
|
|
|
|
|
globalThis.fetch = async () => assert.fail('unexpected network')
|
|
|
|
|
const forbidden = () => assert.fail('unexpected dependency')
|
|
|
|
|
try {
|
|
|
|
|
for (const args of [[], ['--plan']]) {
|
|
|
|
|
const result = await runUiFixture(parseUiFixtureArguments(args), { env: {}, prepare: forbidden,
|
|
|
|
|
contextFactory: forbidden, readers: { preflight: forbidden } })
|
|
|
|
|
assert.equal(result.execute, false)
|
|
|
|
|
assert.equal(result.fullAcceptance, false)
|
|
|
|
|
assert.equal(result.stopsAt, 'OPEN_AND_24_DRAFTS_READY_FOR_UI')
|
|
|
|
|
assert.equal(result.privateCredentialsFile, null)
|
|
|
|
|
}
|
|
|
|
|
} finally { globalThis.fetch = original }
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
test('execute requires explicit TEST, fresh run, warehouse IDs and one private absolute file', () => {
|
|
|
|
|
const config = parseUiFixtureArguments(execute)
|
|
|
|
|
assert.equal(config.privateCredentialsFile, credentials)
|
|
|
|
|
assert.equal(config.execute, true)
|
|
|
|
|
for (const args of [execute.slice(0, -2), [...execute, '--private-credentials-file', credentials],
|
|
|
|
|
[...execute, '--resume', 'old'], execute.map(value => value === 'TEST' ? 'gray' : value),
|
|
|
|
|
execute.map(value => value === 'ui-contract' ? '../old' : value), ['--private-credentials-file']]) {
|
|
|
|
|
assert.throws(() => parseUiFixtureArguments(args))
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
test('private credential path is JSON directly within private env directory', () => {
|
|
|
|
|
assert.equal(privateCredentialsPath(credentials), resolve(credentials))
|
|
|
|
|
for (const file of ['password.json', join(PRIVATE_DIRECTORY, '..', 'password.json'),
|
|
|
|
|
join(PRIVATE_DIRECTORY, 'nested', 'password.json'), join(PRIVATE_DIRECTORY, 'password.txt')]) {
|
|
|
|
|
assert.throws(() => privateCredentialsPath(file))
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
test('private store exclusively creates, fsyncs updates and refuses overwriting existing credentials', () => {
|
|
|
|
|
const directory = mkdtempSync(join(tmpdir(), 'pool-ui-private-contract-')), file = join(directory, 'credentials.json')
|
|
|
|
|
let store
|
|
|
|
|
try {
|
|
|
|
|
store = createPrivateCredentialStore(file, { accounts: [{ password: 'offline-placeholder-long' }] }, { privateDirectory: directory })
|
|
|
|
|
assert.equal(JSON.parse(readFileSync(file, 'utf8')).accounts[0].password, 'offline-placeholder-long')
|
|
|
|
|
assert.throws(() => createPrivateCredentialStore(file, {}, { privateDirectory: directory }), { code: 'EEXIST' })
|
|
|
|
|
assert.equal(JSON.parse(readFileSync(file, 'utf8')).accounts[0].password, 'offline-placeholder-long')
|
|
|
|
|
store.save({ status: 'READY' })
|
|
|
|
|
assert.deepEqual(JSON.parse(readFileSync(file, 'utf8')), { status: 'READY' })
|
|
|
|
|
store.close(); store.close()
|
|
|
|
|
assert.throws(() => store.save({}), /CLOSED/)
|
|
|
|
|
} finally { store?.close(); unlinkSync(file); rmdirSync(directory) }
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
test('public evidence removes credentials and binary while retaining business identifiers and failed responses', () => {
|
|
|
|
|
const publicValue = publicUiEvidence({ requestId: 'req-1', traceId: 'trace-1', body: {
|
|
|
|
|
code: 'A0409', message: 'login offline-secret failed', data: { temporaryPassword: 'generated', accessToken: 'token',
|
|
|
|
|
users: [{ username: 'private-login', password: 'nested-password' }], content: 'binary', base64: 'binary', bookingNo: 'FB-1' },
|
|
|
|
|
} }, ['offline-secret'])
|
|
|
|
|
const text = JSON.stringify(publicValue)
|
|
|
|
|
for (const forbidden of ['offline-secret', 'generated', 'private-login', 'nested-password', 'binary']) assert.equal(text.includes(forbidden), false)
|
|
|
|
|
assert.equal(publicValue.body.data.accessToken, '[REDACTED]')
|
|
|
|
|
assert.equal(publicValue.body.data.bookingNo, 'FB-1')
|
|
|
|
|
assert.equal(publicValue.body.code, 'A0409')
|
|
|
|
|
assert.equal(publicValue.requestId, 'req-1')
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
test('24 intended drafts preserve source versions, both business types and unknown initial quantities', () => {
|
|
|
|
|
const offer = { status: 'OPEN', canCreateInquiry: true, tripRecordVersion: 5, recordVersion: 7,
|
|
|
|
|
destinations: [{ destinationKey: 'FBA:11' }] }
|
|
|
|
|
const drafts = Array.from({ length: 24 }, (_, index) => uiInquiryBody(offer, index < 12 ? 'A' : 'B', index, `KEY-${index}`))
|
|
|
|
|
assert.equal(drafts.filter(row => row.customerId === 'A').length, 12)
|
|
|
|
|
assert.equal(drafts.filter(row => row.customerId === 'B').length, 12)
|
|
|
|
|
assert.equal(drafts.filter(row => row.businessType === 'LOCAL_LOOSEBOARD').length, 12)
|
|
|
|
|
assert.equal(drafts.filter(row => row.businessType === 'US_TRUCK').length, 12)
|
|
|
|
|
assert.equal(drafts[0].expectedPalletCount, null)
|
|
|
|
|
assert.equal(drafts[0].cargo.packageCount, null)
|
|
|
|
|
assert.equal(drafts[0].cargo.palletSpaces, null)
|
|
|
|
|
for (const row of drafts) {
|
|
|
|
|
assert.equal(row.tripRecordVersion, 5)
|
|
|
|
|
assert.equal(row.offerRecordVersion, 7)
|
|
|
|
|
assert.equal(row.deliveryMode, 'SELF_DELIVERY')
|
|
|
|
|
assert.equal('businessOrderNo' in row, false)
|
|
|
|
|
assert.equal('status' in row, false)
|
|
|
|
|
}
|
|
|
|
|
assert.throws(() => uiInquiryBody({ ...offer, status: 'CLOSED' }, 'A', 1, 'K'), /OPEN_FBA/)
|
|
|
|
|
assert.throws(() => uiInquiryBody({ ...offer, canCreateInquiry: false }, 'A', 1, 'K'), /OPEN_FBA/)
|
|
|
|
|
assert.throws(() => uiInquiryBody({ ...offer, destinations: [] }, 'A', 1, 'K'), /OPEN_FBA/)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
test('execute rejects non-TEST and incomplete environment before invoking fixture or writing private file', async () => {
|
|
|
|
|
const config = parseUiFixtureArguments(execute)
|
|
|
|
|
for (const env of [{}, { CHONGCHONG_ENV: 'prod' }, { CHONGCHONG_ENV: 'test' }]) {
|
|
|
|
|
await assert.rejects(runUiFixture(config, { env, prepare: () => assert.fail('unexpected fixture') }), /CONFIGURATION_REQUIRED/)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
test('plan lists minimal independent identities and explicitly excludes full acceptance or business completion', () => {
|
|
|
|
|
const plan = uiFixturePlan(parseUiFixtureArguments([]))
|
|
|
|
|
assert.equal(plan.permissions.sales.length, 4)
|
|
|
|
|
assert.deepEqual(plan.permissions.manager, ['platform:fleet:capacity-offer:manage'])
|
|
|
|
|
assert.equal(plan.fullAcceptance, false)
|
|
|
|
|
assert.match(plan.boundary, /No automatic rerun\/resume/)
|
|
|
|
|
assert.match(plan.boundary, /completed-order fabrication/)
|
|
|
|
|
assert.match(plan.boundary, /Retain business samples and UI accounts/)
|
|
|
|
|
})
|
2026-10-20 10:35:52 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
test('UI fixture prepares one shared native main and an explicitly planned 18-of-30 capacity without fake deliveries or loading', () => {
|
|
|
|
|
const source = readFileSync(new URL('./fleet-capacity-pool-ui-fixture-test-env.mjs', import.meta.url), 'utf8')
|
|
|
|
|
assert.match(source, /fixture\.members\.every\(member => member\.dispatchNo === fixture\.dispatchNo\)/)
|
|
|
|
|
assert.match(source, /return-to-review/)
|
|
|
|
|
assert.match(source, /request\('PUT', dispatchPath, uiPlanBody\)/)
|
|
|
|
|
assert.match(source, /\['submit-review', 'dispatch-review', 'loading-review'\]/)
|
|
|
|
|
assert.match(source, /testPlannedCapacity\(plan\.cargo, detail\.trip\.stops\)\.map\(row => \(\{ \.\.\.row, palletSpaces: 9 \}\)\)/)
|
|
|
|
|
assert.match(source, /proof\.planEvidence = \{ actual: false/)
|
|
|
|
|
assert.match(source, /dispatch\.documents\?\.outbounds\?\.length === 0/)
|
|
|
|
|
assert.match(source, /dispatch\.documents\?\.deliveries\?\.length === 0/)
|
|
|
|
|
for (const old of ['controlledPalletMeasurements(', "request('PUT', `${tripPath}/plan`", "member.deliveryNo", "dispatch.pickedPackageCount", "dispatch.shippedPackageCount"]) assert.equal(source.includes(old), false, old)
|
|
|
|
|
assert.equal(source.includes('/loading-complete'), false)
|
|
|
|
|
assert.equal(source.includes('/depart`'), false)
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
2026-10-21 14:30:41 +08:00
|
|
|
test('GRAY dispatch resource update carries its ETA into automatic delivery without changing the command identity', () => {
|
2026-10-20 10:35:52 +08:00
|
|
|
const schedule = grayUiSchedule(Date.parse('2030-01-01T00:00:00Z'))
|
2026-10-21 14:30:41 +08:00
|
|
|
const body = { recordVersion: '0', idempotencyKey: 'same-command', vehicle: { plate: 'TEST' } }
|
|
|
|
|
const applied = applyGrayUiTransportWindow('PUT', '/api/platform/v1/last-leg/dispatches/DS-1', body, schedule)
|
|
|
|
|
assert.equal(applied.recordVersion, body.recordVersion); assert.equal(applied.idempotencyKey, body.idempotencyKey)
|
|
|
|
|
assert.equal(applied.estimatedArrivalAt, new Date(schedule.plannedDeliveryAt).toISOString()); assert.deepEqual(applied.vehicle, body.vehicle)
|
|
|
|
|
assert.equal(Object.hasOwn(body, 'estimatedArrivalAt'), false)
|
|
|
|
|
assert.throws(() => applyGrayUiTransportWindow('PUT', '/api/platform/v1/last-leg/dispatches/DS-1', body, null), /NATIVE_DELIVERY_WINDOW_REQUIRED/)
|
2026-10-20 10:35:52 +08:00
|
|
|
})
|