import { mkdirSync, openSync, closeSync, writeFileSync, writeSync, ftruncateSync, fsyncSync, unlinkSync, readFileSync, realpathSync } from 'node:fs' import { resolve, dirname, join, isAbsolute, extname } from 'node:path' import { homedir } from 'node:os' import { fileURLToPath, pathToFileURL } from 'node:url' import { createHash, randomUUID } from 'node:crypto' import { AcceptanceRun, parseArguments, validateExecutionOptions, buildAcceptanceSummary, createFleetPoolGrayOptions } from './admin-test-env-acceptance.mjs' import { parseCrossCustomerArguments } from './fleet-cross-customer-test-env.mjs' import { createFleetCrossCustomerFixture } from './admin-test-env-modules/fleet-cross-customer-fixture.mjs' import { controlledPalletMeasurements } from './admin-test-env-modules/fleet-trip-fulfillment.mjs' import { createTestEvidenceReaders } from './fba-core-regression-evidence.mjs' import { createGrayEvidenceReaders } from './gray-regression-evidence.mjs' import { guardFleetWrites, redactFleetWrite, findFleetActorPhone } from './fleet-test-env-acceptance.mjs' import { createPicker, disablePicker, resolvePickerAccess } from './admin-test-env-modules/phase4-overseas-pda.mjs' const ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..') export const PRIVATE_DIRECTORY = resolve(homedir(), '.codex', 'env', 'chongchong-global') const API = '/api/platform/v1/last-leg', POOL = `${API}/fleet/capacity-offers`, IAM = '/api/platform/v1/iam' const PREFIX = 'platform:fleet:capacity-offer:' const id = value => String(value ?? '') const demand = (condition, message) => { if (!condition) throw Error(message) } const ok = response => response?.http === 200 && id(response?.body?.code) === '200' export function privateCredentialsPath(value, privateDirectory = PRIVATE_DIRECTORY) { demand(typeof value === 'string' && isAbsolute(value), 'EXPLICIT_ABSOLUTE_PRIVATE_CREDENTIALS_FILE_REQUIRED') const file = resolve(value), directory = resolve(privateDirectory) const same = (a, b) => process.platform === 'win32' ? a.toLowerCase() === b.toLowerCase() : a === b demand(same(dirname(file), directory) && extname(file).toLowerCase() === '.json', 'CREDENTIALS_FILE_MUST_BE_JSON_DIRECTLY_IN_PRIVATE_ENV_DIRECTORY') return file } export function parseUiFixtureArguments(args) { const ordinary = []; let credentialsFile for (let index = 0; index < args.length; index++) { if (args[index] !== '--private-credentials-file') { ordinary.push(args[index]); continue } demand(credentialsFile === undefined && args[index + 1] && !args[index + 1].startsWith('--'), 'INVALID_DUPLICATE_PRIVATE_CREDENTIALS_FILE') credentialsFile = privateCredentialsPath(args[++index]) } const config = parseCrossCustomerArguments(ordinary) demand(!config.execute || credentialsFile, 'EXPLICIT_ABSOLUTE_PRIVATE_CREDENTIALS_FILE_REQUIRED') return { ...config, privateCredentialsFile: credentialsFile || null } } export function uiFixturePlan(config) { if (config.environment === 'GRAY') return { ...uiFixturePlan({ ...config, environment: 'TEST' }), environment: 'GRAY', output: `reports/fleet-capacity-pool-ui-gray-${config.runId}`, purpose: 'GRAY UI fixture only: one OPEN 30/18-space trip, four complete non-reserving drafts, two customer logins, sales A with quote:list, sales B without quote permission, manage dispatcher and one warehouse-bound PDA for H5.', permissions: { salesA: uiActorPermissions('SALES_A', true), salesB: uiActorPermissions('SALES_B', true), manager: uiActorPermissions('MANAGER', true) }, stopsAt: 'OPEN_AND_4_DRAFTS_READY_FOR_UI', retainedPda: true, schedule: 'From preparation: cutoff +8h, departure +9h, delivery ETA +10h, driver expected end +12h; no actual departure.', boundary: 'No automatic rerun/resume, SQL mutations, deployment, pool confirmation, PDA picking, departure or receipt signature. Retain business samples and all six UI logins including the warehouse-bound PDA; logout script sessions. Actual profile remains prod/gray; production-identity read-only evidence required.' } return { execute: Boolean(config.execute), environment: 'TEST', runId: config.runId, output: `reports/fleet-capacity-pool-ui-${config.runId}`, privateCredentialsFile: config.privateCredentialsFile, purpose: 'UI fixture only: one OPEN 30-space trip with valid 18-space owned plan and 24 non-reserving drafts, two minimal sales logins, one manage login, two qualified customer logins.', permissions: { sales: ['list', 'inquiry-create', 'inquiry-edit', 'inquiry-cancel'].map(name => PREFIX + name), manager: [PREFIX + 'manage'] }, stopsAt: 'OPEN_AND_24_DRAFTS_READY_FOR_UI', fullAcceptance: false, credentials: 'Exclusive private JSON creation before any writes; every generated password is persisted and fsynced there only. Reports and console redact passwords and tokens.', boundary: 'No automatic rerun/resume, SQL mutations, deployment, pool confirmation, PDA picking, departure, receipt signature or completed-order fabrication. Retain business samples and UI accounts; disable the fixture-only PDA and logout script sessions.' } } export function uiActorPermissions(kind, gray = false) { demand(['SALES_A', 'SALES_B', 'MANAGER'].includes(kind), 'UI_ACTOR_KIND_REQUIRED') if (kind === 'MANAGER') return [PREFIX + 'manage'] return [...['list', 'inquiry-create', 'inquiry-edit', 'inquiry-cancel'].map(name => PREFIX + name), ...(gray && kind === 'SALES_A' ? ['platform:fleet:quote:list'] : [])] } export function grayUiSchedule(now = Date.now()) { demand(Number.isFinite(now), 'VALID_GRAY_UI_CLOCK_REQUIRED') // Persisted business DATETIME columns have second precision. const base = Math.floor(now / 1000) * 1000 const instant = hours => new Date(base + hours * 3600000).toISOString() const delivery = new Date(base + 10 * 3600000) return { cutoffAt: instant(8), plannedDepartureAt: instant(9), expectedDriverEndAt: instant(12), plannedDeliveryAt: new Date(delivery.getTime() - delivery.getTimezoneOffset() * 60000).toISOString().slice(0, 19) } } export function applyGrayUiTransportWindow(method, path, body, schedule) { if (method !== 'POST' || !/^\/api\/platform\/v1\/last-leg\/dispatches\/[^/]+\/transfer-delivery$/.test(path)) return body demand(body && schedule?.plannedDeliveryAt, 'GRAY_UI_DELIVERY_WINDOW_REQUIRED') return { ...body, plannedDeliveryAt: schedule.plannedDeliveryAt } } /** Private directory override is for isolated offline tests, never exposed as a CLI option. */ export function createPrivateCredentialStore(file, initial, { privateDirectory = PRIVATE_DIRECTORY } = {}) { const target = privateCredentialsPath(file, privateDirectory) demand(realpathSync(dirname(target)) === realpathSync(privateDirectory), 'PRIVATE_DIRECTORY_RESOLUTION_MISMATCH') const descriptor = openSync(target, 'wx', 0o600) let closed = false const save = value => { demand(!closed, 'PRIVATE_CREDENTIAL_STORE_CLOSED') const text = JSON.stringify(value, null, 2) + '\n' writeSync(descriptor, text, 0, 'utf8'); ftruncateSync(descriptor, Buffer.byteLength(text)); fsyncSync(descriptor) } try { save(initial) } catch (error) { closeSync(descriptor); throw error } return { save, close() { if (!closed) { closeSync(descriptor); closed = true } } } } export function publicUiEvidence(value, secrets = []) { const stripped = item => { if (Array.isArray(item)) return item.map(stripped) if (item && typeof item === 'object') return Object.fromEntries(Object.entries(item).map(([key, child]) => [key, /^(?:base64|bytes|content)$/i.test(key) ? '[BINARY OMITTED]' : stripped(child)])) return item } return stripped(redactFleetWrite(value, secrets)) } export function uiInquiryBody(offer, customerId, index, key, complete = false) { const destination = offer.destinations?.find(row => row.destinationKey?.startsWith('FBA:')) demand(offer.status === 'OPEN' && offer.canCreateInquiry && destination, 'OPEN_FBA_OFFER_REQUIRED') return { tripRecordVersion: offer.tripRecordVersion, offerRecordVersion: offer.recordVersion, customerId, businessType: index % 2 === 0 ? 'LOCAL_LOOSEBOARD' : 'US_TRUCK', deliveryMode: 'SELF_DELIVERY', expectedPalletCount: index === 0 && !complete ? null : 1, cargo: { destinationKey: destination.destinationKey, cargoName: `UI验收意向-${index + 1}`, packageCount: index === 0 && !complete ? null : 1, palletSpaces: index === 0 && !complete ? null : 1, weightKg: 100, volumeCbm: 1, priceBasis: 'PALLET', unitPrice: 30, currency: 'CNY', contactName: 'UI验收联系人', contactPhone: '19900000000', remark: '独立UI验收草稿,不占位' }, idempotencyKey: key } } export async function runUiFixture(config, dependencies = {}) { demand(config.environment === 'TEST', 'EXPLICIT_TEST_FIXTURE_CONFIGURATION_REQUIRED') return runUiFixtureCore(config, dependencies, false) } export async function runGrayUiFixture(config, dependencies = {}) { demand(config.environment === 'GRAY', 'EXPLICIT_GRAY_UI_FIXTURE_CONFIGURATION_REQUIRED') demand(Object.keys(dependencies).every(key => ['env', 'outputRoot'].includes(key)), 'GRAY_UI_CANNOT_OVERRIDE_EVIDENCE_OR_LIFECYCLE') return runUiFixtureCore(config, dependencies, true) } async function runUiFixtureCore(config, { env = process.env, outputRoot = ROOT, readers, prepare = createFleetCrossCustomerFixture, contextFactory = options => new AcceptanceRun(options) } = {}, gray = false) { if (!config.execute) return uiFixturePlan(config) demand((gray ? ['prod', 'gray'].includes(env.CHONGCHONG_ENV) : env.CHONGCHONG_ENV === 'test') && /^[A-Za-z0-9][A-Za-z0-9_-]{0,47}$/.test(config.runId) && config.runId !== 'plan' && /^[1-9]\d*$/.test(config.warehouseId) && /^[1-9]\d*$/.test(config.destinationWarehouseId) && env.CHONGCHONG_ADMIN_USERNAME && env.CHONGCHONG_ADMIN_PASSWORD, `EXPLICIT_${gray ? 'GRAY' : 'TEST'}_FIXTURE_CONFIGURATION_REQUIRED`) const privateFile = privateCredentialsPath(config.privateCredentialsFile) const endpoint = new URL(env.CHONGCHONG_ADMIN_API_BASE_URL) demand((gray ? endpoint.protocol === 'https:' : ['http:', 'https:'].includes(endpoint.protocol)) && ['/api', '/api/'].includes(endpoint.pathname) && !endpoint.username && !endpoint.password && !endpoint.hash && !endpoint.search, 'INVALID_UI_API_BASE') if (gray) readers = createGrayEvidenceReaders(env) else readers ||= createTestEvidenceReaders(env) const reports = join(outputRoot, 'reports'), output = resolve(outputRoot, uiFixturePlan(config).output) const transport = { gateway: endpoint.origin, username: env.CHONGCHONG_ADMIN_USERNAME, password: env.CHONGCHONG_ADMIN_PASSWORD, output, markerNamespace: createHash('sha256').update(`${config.environment}:${config.runId}`).digest('hex').slice(0, 12) } const options = gray ? createFleetPoolGrayOptions(transport) : validateExecutionOptions({ ...parseArguments(['--modules', 'merchant-qualification-fixture', '--quiet', 'true']), ...transport, environment: 'test' }) const secretDocument = { schemaVersion: 1, environment: config.environment, runtimeProfile: env.CHONGCHONG_ENV, runId: config.runId, status: 'PREPARING', createdAt: new Date().toISOString(), gatewayOrigin: endpoint.origin, accounts: [], business: {} } const vault = createPrivateCredentialStore(privateFile, secretDocument) const schedule = gray ? grayUiSchedule() : null, draftCount = gray ? 4 : 24, perSales = draftCount / 2 const secrets = Object.entries(env).filter(([key]) => /PASSWORD|TOKEN|SECRET/.test(key)).map(([, value]) => value).filter(Boolean) const sessions = new Map(), kinds = new Map() let context, picker, lock, restore, initialized = false, requestNumber = 0, inventoryNumber = 0, report const save = (name, value) => writeFileSync(join(output, name), JSON.stringify(publicUiEvidence(value, secrets), null, 2) + '\n') const writePrivate = () => vault.save(secretDocument) const remember = account => { if (account.password) secrets.push(account.password) const existing = secretDocument.accounts.find(row => row.username === account.username) if (existing) Object.assign(existing, account); else secretDocument.accounts.push(account) writePrivate() } try { mkdirSync(reports, { recursive: true }) lock = openSync(join(reports, '.fleet-acceptance.lock'), 'wx') writeFileSync(lock, JSON.stringify({ runId: config.runId, pid: process.pid, kind: 'UI_FIXTURE' })) mkdirSync(output); initialized = true; mkdirSync(join(output, 'requests')); mkdirSync(join(output, 'write-journal')); mkdirSync(join(output, 'evidence')) save('manifest.json', { ...uiFixturePlan(config), sourceSha256: createHash('sha256').update(readFileSync(fileURLToPath(import.meta.url))).digest('hex') }) await readers.preflight() context = contextFactory(options) secretDocument.marker = context.marker; writePrivate() const proof = context.createdResources.fleetCapacityPoolUi = { marker: context.marker, status: 'PREPARING', actors: [], drafts: [], fullAcceptance: false } for (const method of ['request', 'requestWithBody']) { const original = context[method] context[method] = async function (...args) { const sequence = ++requestNumber, [verb, path] = args if (gray) args[2] = applyGrayUiTransportWindow(verb, path, args[2], schedule) const body = args[2] if (verb === 'POST' && path === `${IAM}/users` && body?.username && body.password) { remember({ kind: kinds.get(body.username) || (body.username.startsWith('pda_overseas_') ? 'FIXTURE_PDA' : 'FIXTURE_USER'), username: body.username, password: body.password, roleIds: body.roleIds, warehouseId: body.overseasWarehouseId || null, status: 'PREPARED' }) } save(`requests/${sequence}.json`, { stage: 'INTENT', sequence, method: verb, path, body: body instanceof FormData ? '[MULTIPART]' : body }) const response = await original.apply(this, args) if (ok(response) && verb === 'POST' && path === `${IAM}/tenants`) { const data = response.body.data, customer = data.tenant || data.customer || data remember({ kind: 'CUSTOMER', customerId: id(customer.id || data.tenantId), customerName: customer.tenantName || customer.customerName, username: data.administratorUsername, password: data.temporaryPassword, status: 'CREATED' }) } if (ok(response) && verb === 'POST' && path === `${IAM}/users`) { const data = response.body.data remember({ kind: kinds.get(body.username) || (body.username.startsWith('pda_overseas_') ? 'FIXTURE_PDA' : 'FIXTURE_USER'), username: body.username, password: body.password, membershipId: id(data.membershipId), roleIds: data.roleIds, status: 'ACTIVE' }) } if (ok(response) && path === '/api/auth/login' && response.body.data?.accessToken) { const auth = response.body.data sessions.set(auth.accessToken, auth.refreshToken); secrets.push(auth.accessToken, auth.refreshToken) } save(`requests/${sequence}.json`, { stage: 'RESPONSE', sequence, method: verb, path, body: body instanceof FormData ? '[MULTIPART]' : body, response }) save('progress.json', { marker: context.marker, createdResources: context.createdResources, steps: context.steps }) return response } } restore = guardFleetWrites(context, async entry => { const file = openSync(join(output, 'write-journal', `${entry.sequence}.json`), 'w') try { writeFileSync(file, JSON.stringify(publicUiEvidence(entry, secrets), null, 2) + '\n'); fsyncSync(file) } finally { closeSync(file) } }, { guardFetch: true }) const check = (label, condition, response = null) => { context.assert('fleet-capacity-pool-ui', label, response, Boolean(condition), '', { scenario: 'UI_FIXTURE', deploymentBlocker: true }) demand(condition, label) } const request = async (method, path, body, token = context.token) => { const response = await context.request(method, path, body, token) check(`${method} ${path}`, ok(response) && response.requestId && response.traceId, response) return response.body.data } const login = await request('POST', '/api/auth/login', { loginId: options.username, password: options.password }, '') context.token = login.accessToken; context.refreshToken = login.refreshToken const access = await resolvePickerAccess(context) check('Fixture PDA role exists', Boolean(access)) picker = await createPicker(context, 93, config.warehouseId, access) check('Fixture PDA authenticated', Boolean(picker?.token)) const readInventory = async scope => { const snapshot = await readers.readFleetInventory(scope); save(`evidence/inventory-${++inventoryNumber}.json`, snapshot); return snapshot } const fixture = await prepare(context, { execute: true, warehouseId: config.warehouseId, destinationWarehouseId: config.destinationWarehouseId, pdaToken: picker.token, readInventory }) save('fixture.json', fixture) const trip = context.createdResources.fleetTrips.find(row => row.tripNo === fixture.tripNo), tripPath = `${API}/fleet/trips/${encodeURIComponent(fixture.tripNo)}` check('Existing producer created exactly two untouched customer dispatches', fixture.members.length === 2 && trip) let detail = await request('GET', tripPath), plan = await request('GET', `${tripPath}/capacity-plan`) check('Owned fixture vehicle has actual 30-space capacity', Number(detail.trip.maxPalletSpaces) === 30 && plan.cargo.length === 2) // GRAY delivery ETA was set on the producer's real transfer command, so all four times stay ordered. detail = await request('PUT', `${tripPath}/plan`, { recordVersion: detail.trip.recordVersion, plannedDepartureAt: schedule?.plannedDepartureAt || new Date(Date.now() + 4 * 3600000).toISOString(), expectedDriverEndAt: schedule?.expectedDriverEndAt || detail.trip.expectedDriverEndAt, stops: detail.trip.stops, reason: gray ? '灰度UI验收:统一截止、发车、到站与司机结束时间,不执行发车' : 'UI验收窗口:四小时后计划发车,不执行发车' }) if (gray) { check('GRAY trip departure and driver end follow the explicit UI window', Date.parse(detail.trip.plannedDepartureAt) === Date.parse(schedule.plannedDepartureAt) && Date.parse(detail.trip.expectedDriverEndAt) === Date.parse(schedule.expectedDriverEndAt)) for (const member of fixture.members) { const delivery = await request('GET', `${API}/delivery-orders/${member.deliveryNo}?customerId=${member.customerId}`) check('Original customer delivery ETA follows the same valid UI window', delivery.order.plannedDeliveryAt === schedule.plannedDeliveryAt) } } plan = await request('GET', `${tripPath}/capacity-plan`) await request('POST', `${tripPath}/capacity-plan`, { recordVersion: plan.recordVersion, manifestHash: plan.manifestHash, pallets: controlledPalletMeasurements(plan.cargo, detail.trip.stops).map(row => ({ ...row, palletSpaces: 9 })), reason: '仅UI验收:自有计划18/总30,不作为实际发车计量' }) const permissions = await request('GET', `${IAM}/permissions`), actorTokens = [] const key = suffix => createHash('sha256').update(`${context.marker}:UI:${suffix}`).digest('hex') for (const kind of ['SALES_A', 'SALES_B', 'MANAGER']) { const suffix = key(kind).slice(0, 16), username = `pool_ui_${kind.toLowerCase()}_${suffix}` const expected = uiActorPermissions(kind, gray) const candidates = expected.map(code => permissions.filter(row => row.permissionCode === code && row.status === 'ACTIVE')) check('Exact fixture permissions are installed', candidates.every(rows => rows.length === 1 && rows[0].id)) const role = await request('POST', `${IAM}/roles`, { roleCode: `POOL_UI_${kind}_${suffix}`.toUpperCase(), roleName: `${context.marker}-${kind}`, description: '仅本轮UI验收账号' }) proof.actors.push({ kind, roleId: id(role.id) }) await request('PUT', `${IAM}/roles/permissions`, { roleId: id(role.id), permissionIds: candidates.map(rows => id(rows[0].id)) }) kinds.set(username, kind) const password = `Ui${randomUUID().replaceAll('-', '')}A9`; remember({ kind, username, password, roleIds: [id(role.id)], status: 'PREPARED' }) const user = await request('POST', `${IAM}/users`, { username, password, displayName: `${context.marker}-同名销售`, phone: await findFleetActorPhone(context, suffix), email: `${username}@example.com`, tenantId: '0', roleIds: [id(role.id)], status: 'ACTIVE' }) proof.actors.at(-1).membershipId = id(user.membershipId) const auth = await request('POST', '/api/auth/login', { loginId: username, password }, '') const effective = await request('GET', `${IAM}/authorization/permissions`, undefined, auth.accessToken) check('UI actor has only its exact minimal permissions', JSON.stringify([...effective].sort()) === JSON.stringify([...expected].sort())) actorTokens.push(auth.accessToken) } detail = await request('GET', tripPath) const draft = await request('POST', POOL, { tripNo: fixture.tripNo, tripRecordVersion: detail.trip.recordVersion, cutoffAt: schedule?.cutoffAt || new Date(Math.min(Date.now() + 3 * 3600000, Date.parse(detail.trip.plannedDepartureAt) - 60000)).toISOString(), contactName: 'UI验收调度', contactPhone: '19900000000', remark: context.marker, idempotencyKey: key('OFFER') }, actorTokens[2]) let offer = (await request('POST', `${POOL}/${draft.offer.offerNo}/publish`, { tripRecordVersion: draft.offer.tripRecordVersion, offerRecordVersion: draft.offer.recordVersion, idempotencyKey: key('PUBLISH') }, actorTokens[2])).offer check('OPEN UI offer is available with exactly 12 planned spaces', offer.status === 'OPEN' && offer.canCreateInquiry && offer.canConfirm && offer.destinations.length === 1 && Number(offer.destinations[0].remaining.palletSpaces) === 12) proof.offerNo = offer.offerNo; proof.tripNo = fixture.tripNo for (let index = 0; index < draftCount; index++) { const owner = index < perSales ? 0 : 1, member = fixture.members[owner] const body = uiInquiryBody(offer, member.customerId, index, key(`DRAFT-${index}`), gray) if (gray) body.estimatedArrivalAt = new Date(Date.now() + 3600000).toISOString() const created = await request('POST', `${POOL}/${offer.offerNo}/inquiries`, body, actorTokens[owner]) check('UI inquiry is DRAFT with no commercial order or reservation', created.inquiry.status === 'DRAFT' && !created.inquiry.businessOrderNo && !created.inquiry.incomingNo && !created.inquiry.dispatchNo && Number(created.offer.destinations[0].remaining.palletSpaces) === 12) proof.drafts.push({ bookingNo: created.inquiry.bookingNo, customerId: member.customerId, owner: owner === 0 ? 'SALES_A' : 'SALES_B' }) offer = created.offer } const firstPage = await request('GET', `${POOL}/inquiries?page=1&size=20&scope=ALL&offerNo=${offer.offerNo}`, undefined, actorTokens[2]) if (gray) check('Manager sees exactly four current GRAY UI drafts', Number(firstPage.total) === 4 && firstPage.records.length === 4) else { const secondPage = await request('GET', `${POOL}/inquiries?page=2&size=20&scope=ALL&offerNo=${offer.offerNo}`, undefined, actorTokens[2]) check('Manager can exercise real page two of 24 drafts', Number(firstPage.total) === 24 && firstPage.records.length === 20 && secondPage.records.length === 4 && new Set([...firstPage.records, ...secondPage.records].map(row => row.bookingNo)).size === 24) } for (let owner = 0; owner < 2; owner++) { const mine = await request('GET', `${POOL}/inquiries?page=1&size=20&scope=MINE&offerNo=${offer.offerNo}`, undefined, actorTokens[owner]) check(`Each sales login sees exactly its own ${perSales} drafts`, Number(mine.total) === perSales && mine.records.length === perSales && mine.records.every(row => proof.drafts.some(draft => draft.bookingNo === row.bookingNo && draft.owner === (owner === 0 ? 'SALES_A' : 'SALES_B')))) } for (const member of fixture.members) { const account = secretDocument.accounts.find(row => row.kind === 'CUSTOMER' && row.customerId === member.customerId) check('Customer UI credentials safely preserved', account?.username && account.password) const auth = await request('POST', '/api/auth/login', { loginId: account.username, password: account.password }, '') const profile = await request('GET', '/api/merchant/v1/iam/tenant/profile', undefined, auth.accessToken) check('Customer login belongs to this fixture customer', id(profile.id) === member.customerId) for (const type of ['local-looseboard-orders', 'us-truck-orders']) { const page = await request('GET', `/api/merchant/v1/last-leg/${type}?page=1&size=20`, undefined, auth.accessToken) check('Fresh customer commercial order page provides real empty state before UI confirmation', Number(page.total) === 0 && page.records.length === 0) } account.status = 'ACTIVE'; writePrivate() } detail = await request('GET', tripPath) check('UI fixture has not picked departed or signed cargo', detail.trip.status === 'PLANNED') for (const member of fixture.members) { const dispatch = await request('GET', `${API}/dispatches/${member.dispatchNo}`) check('Original real cargo remains unpicked', Number(dispatch.pickedPackageCount) === 0 && Number(dispatch.shippedPackageCount) === 0) } const final = await request('GET', `${POOL}/${offer.offerNo}`, undefined, actorTokens[2]) check(`UI handoff keeps publication OPEN and ${draftCount} drafts untouched`, final.offer.status === 'OPEN' && final.offer.canCreateInquiry && Number(final.inquiries.total) === draftCount) proof.status = `OPEN_AND_${draftCount}_DRAFTS_READY_FOR_UI` secretDocument.business = { offerNo: proof.offerNo, tripNo: fixture.tripNo, warehouseId: fixture.warehouseId, destinationWarehouseId: config.destinationWarehouseId, cutoffAt: final.offer.cutoffAt, plannedDepartureAt: final.offer.plannedDepartureAt, ...(gray ? { uiSchedule: schedule } : {}), customers: fixture.members.map(row => ({ customerId: row.customerId, incomingNo: row.incomingNo, dispatchNo: row.dispatchNo })) } secretDocument.status = 'READY_FOR_UI'; writePrivate() report = { success: true, fixtureReady: true, fullAcceptance: false, proof } } catch (error) { report = { success: false, fixtureReady: false, fullAcceptance: false, error: publicUiEvidence(id(error.message), secrets) } secretDocument.status = 'FAILED_RETAINED'; writePrivate() } finally { try { if (context) { const pdaAccount = secretDocument.accounts.find(row => row.kind === 'FIXTURE_PDA' && row.membershipId) if (gray && pdaAccount) { pdaAccount.status = 'RETAINED_FOR_UI'; pdaAccount.purpose = 'GRAY_WAREHOUSE_BOUND_PDA_H5_ACCEPTANCE' pdaAccount.warehouseId = config.warehouseId report.retainedPda = { membershipId: pdaAccount.membershipId, warehouseId: config.warehouseId, purpose: pdaAccount.purpose } } else if (!gray && (picker?.membershipId || pdaAccount?.membershipId)) { try { const previousSteps = context.steps.length await disablePicker(context, picker?.membershipId ? picker : pdaAccount, 93) demand(context.steps.length > previousSteps && context.steps.at(-1)?.passed === true, 'PDA_DISABLE_NOT_CONFIRMED') if (pdaAccount) { pdaAccount.status = 'DISABLED_AFTER_FIXTURE'; writePrivate() } } catch { if (pdaAccount) pdaAccount.status = 'DISABLE_REQUIRES_REVIEW' report.success = false; report.cleanupError = 'PDA_DISABLE_REQUIRES_REVIEW' } } let sessionsClosed = true for (const [token, refreshToken] of sessions) { try { const response = await context.request('POST', '/api/auth/logout', { refreshToken }, token) if (!ok(response) && response.http !== 401 && id(response.body?.code) !== 'A0401') sessionsClosed = false } catch { sessionsClosed = false } } report.sessionsClosed = sessionsClosed; report.summary = buildAcceptanceSummary(context.steps, context.modules, context.coverageGaps) report.success &&= sessionsClosed && report.summary.failed === 0 report.fixtureReady &&= report.success report.marker = context.marker; report.createdResources = context.createdResources; report.steps = context.steps } if (!report.success) secretDocument.status = 'FAILED_RETAINED' writePrivate() report.privateCredentialsFile = privateFile; report.requestCount = requestNumber; report.inventorySnapshots = inventoryNumber report.environment = config.environment; report.runtimeProfile = env.CHONGCHONG_ENV report.output = output if (initialized) save('report.json', report) } finally { restore?.(); vault.close() if (lock !== undefined) { closeSync(lock); unlinkSync(join(reports, '.fleet-acceptance.lock')) } } } return report } if (process.argv[1] && import.meta.url === pathToFileURL(resolve(process.argv[1])).href) { try { const config = parseUiFixtureArguments(process.argv.slice(2)), result = await runUiFixture(config) console.log(JSON.stringify(config.execute ? { success: result.success, fixtureReady: result.fixtureReady, fullAcceptance: false, error: result.error, summary: result.summary, output: result.output, privateCredentialsFile: result.privateCredentialsFile } : result, null, 2)) if (config.execute && !result.success) process.exitCode = 1 } catch { console.error('UI fixture preparation failed; preserve existing private credentials/report and inspect locally.'); process.exitCode = 1 } }