import test from 'node:test' import assert from 'node:assert/strict' import { mkdtempSync, readdirSync, readFileSync, rmSync } from 'node:fs' import { tmpdir } from 'node:os' import { join, dirname, resolve, basename } from 'node:path' import { parseGrayPoolArguments, runGrayPoolAcceptance } from './fleet-capacity-pool-gray.mjs' import { parseCrossCustomerArguments, runCrossCustomer, runFleetPoolGrayCrossCustomer } from './fleet-cross-customer-test-env.mjs' import { parsePoolArguments, runPoolAcceptance, poolReportName, POOL_COVERAGE } from './fleet-capacity-pool-test-env.mjs' import { AcceptanceRun, parseArguments, validateExecutionOptions, createFleetPoolGrayOptions } from './admin-test-env-acceptance.mjs' const args = ['--execute', '--environment', 'GRAY', '--run-id', 'pool-gray-offline', '--warehouse-id', '900', '--destination-warehouse-id', '901'] const env = { CHONGCHONG_ENV: 'prod', CHONGCHONG_ADMIN_API_BASE_URL: 'https://gray.example.invalid/api', CHONGCHONG_ADMIN_USERNAME: 'offline-user', CHONGCHONG_ADMIN_PASSWORD: 'offline-secret' } const transport = { gateway: 'https://gray.example.invalid', username: 'offline-user', password: 'offline-secret', output: 'reports/fleet-pool-gray-offline', markerNamespace: 'gray-offline' } test('GRAY plan is zero-network and preserves exact existing pool coverage without F40', async () => { const original = globalThis.fetch; globalThis.fetch = () => assert.fail('network forbidden') try { for (const argv of [[], ['--plan']]) { const plan = await runGrayPoolAcceptance(parseGrayPoolArguments(argv), { env: {}, execute: () => assert.fail('execution forbidden') }) assert.equal(plan.environment, 'GRAY'); assert.equal(plan.execute, false) assert.equal(plan.output, 'reports/fleet-pool-gray-plan') assert.deepEqual(plan.coveredByThisEntry, POOL_COVERAGE) assert.equal(plan.fullAcceptance, false) assert.ok(plan.excluded.includes('old FBA order/container/customs flow')) } } finally { globalThis.fetch = original } }) test('GRAY CLI requires explicit environment fresh run and exact IDs and rejects all modules/fault switches', () => { assert.equal(parseGrayPoolArguments(args).execute, true) for (const bad of [['--execute'], args.filter((_, index) => index !== 1 && index !== 2), args.map(value => value === 'GRAY' ? 'TEST' : value), args.map(value => value === 'pool-gray-offline' ? '../escape' : value), [...args, '--modules', 'phase5-root-fulfillment'], [...args, '--verify-unload-event-recovery', 'true'], [...args, '--readers', 'test'], [...args, '--plan'], [...args, '--run-id', 'duplicate']]) { assert.throws(() => parseGrayPoolArguments(bad)) } }) test('existing TEST CLI and runner still refuse GRAY and production process profile', async () => { assert.throws(() => parseCrossCustomerArguments(args), /TEST/) assert.throws(() => parsePoolArguments(args), /TEST/) const config = parseGrayPoolArguments(args) await assert.rejects(runCrossCustomer(config, { env }), /TEST configuration/) await assert.rejects(runPoolAcceptance(config, { env }), /TEST configuration/) await assert.rejects(runGrayPoolAcceptance(config, { env: { ...env, CHONGCHONG_ENV: 'test' } }), /TEST is refused/) }) test('dedicated constructor factory is branded frozen gray and carries no generic modules', () => { const options = createFleetPoolGrayOptions(transport), context = new AcceptanceRun(options) assert.equal(context.options.environment, 'gray'); assert.match(context.marker, /^GRAY-API-/) assert.equal(options.executionScope, 'FLEET_CAPACITY_POOL_GRAY') assert.deepEqual(options.modules, []); assert.deepEqual(options.requestedModules, []) assert.equal(options.verifyUnloadEventRecovery, false) assert.throws(() => options.modules.push('phase5-root-fulfillment'), TypeError) assert.throws(() => new AcceptanceRun({ ...options }), /gray 仅支持/) assert.throws(() => createFleetPoolGrayOptions({ ...transport, modules: ['phase5-root-fulfillment'] }), /configuration/) assert.throws(() => createFleetPoolGrayOptions({ ...transport, verifyUnloadEventRecovery: true }), /configuration/) assert.throws(() => createFleetPoolGrayOptions({ ...transport, gateway: 'http://gray.example.invalid' }), /HTTPS/) assert.throws(() => createFleetPoolGrayOptions({ ...transport, gateway: 'https://gray.example.invalid/api' }), /HTTPS/) }) test('ordinary Admin gray CLI and string-forged scope cannot opt into dedicated empty modules', () => { assert.throws(() => validateExecutionOptions({ environment: 'gray', executionScope: 'FLEET_CAPACITY_POOL_GRAY', requestedModules: [], modules: [], waitSplitCount: 1 }), /gray 仅支持/) assert.throws(() => parseArguments(['--environment', 'gray']), /显式/) assert.throws(() => validateExecutionOptions(parseArguments(['--environment', 'gray', '--modules', 'merchant-qualification-fixture', '--execution-scope', 'FLEET_CAPACITY_POOL_GRAY'])), /gray 仅支持/) }) test('GRAY delegates unchanged pool config and preserves prod profile using only dedicated runner', async () => { const config = parseGrayPoolArguments(args), original = { ...env } const result = await runGrayPoolAcceptance(config, { env, execute: async (actual, dependencies) => { assert.equal(actual, config); assert.equal(actual.environment, 'GRAY') assert.equal(dependencies.env, env); assert.equal(dependencies.env.CHONGCHONG_ENV, 'prod') assert.equal(dependencies.runner, runFleetPoolGrayCrossCustomer) return { scope: 'FLEET_CAPACITY_POOL_GRAY', success: true } } }) assert.equal(result.success, true); assert.deepEqual(env, original) assert.equal(poolReportName(config), 'fleet-pool-gray-pool-gray-offline') assert.notEqual(poolReportName(config), poolReportName({ ...config, environment: 'TEST' })) }) test('GRAY rejects injected TEST evidence and alternate fixture or cleanup even before filesystem writes', async () => { const config = parseGrayPoolArguments(args), dependencies = { env, verify: async () => {}, contextFactory: () => {} } for (const field of ['readers', 'prepare', 'finish', 'pickerAccess', 'pickerCreate', 'pickerDisable']) { await assert.rejects(runFleetPoolGrayCrossCustomer(config, { ...dependencies, [field]: {} }), /cannot override/) } await assert.rejects(runFleetPoolGrayCrossCustomer(config, { env }), /dedicated scenario/) await assert.rejects(runFleetPoolGrayCrossCustomer(config, dependencies), /Missing gray evidence configuration/) await assert.rejects(runFleetPoolGrayCrossCustomer(config, { ...dependencies, env: { ...env, CHONGCHONG_ADMIN_API_BASE_URL: 'http://gray.example.invalid/api' } }), /Invalid GRAY gateway/) }) test('real gray reader is selected and failed production preflight blocks every business request', async () => { const root = mkdtempSync(join(tmpdir(), 'fleet-gray-contract-')), original = globalThis.fetch, calls = [] globalThis.fetch = async (url, init) => { calls.push(String(url)) assert.equal(String(url), 'https://jenkins.example.invalid/scriptText') assert.match(init.body.get('script'), /firstleg-production-ssh/) assert.doesNotMatch(init.body.get('script'), /jenkins-test-agent/) return { ok: false, status: 403 } } try { const report = await runFleetPoolGrayCrossCustomer(parseGrayPoolArguments(args), { env: { ...env, JENKINS_URL: 'https://jenkins.example.invalid', JENKINS_USER: 'offline-jenkins', JENKINS_API_TOKEN: 'offline-token' }, outputRoot: root, verify: () => assert.fail('no business verification before preflight'), contextFactory: () => assert.fail('no business context before preflight') }) assert.equal(report.success, false); assert.match(report.error, /Gray evidence HTTP 403/) assert.equal(report.environment, 'GRAY'); assert.equal(report.runtimeProfile, 'prod'); assert.equal(calls.length, 1) assert.deepEqual(readdirSync(join(root, 'reports')), ['fleet-pool-gray-pool-gray-offline']) const manifest = JSON.parse(readFileSync(join(root, 'reports/fleet-pool-gray-pool-gray-offline/manifest.json'), 'utf8')) assert.ok(manifest.sources.some(source => source.path === 'scripts/gray-regression-evidence.mjs')) assert.equal(manifest.sources.some(source => source.path === 'scripts/fba-core-regression-evidence.mjs'), false) } finally { globalThis.fetch = original assert.equal(dirname(resolve(root)), resolve(tmpdir())); assert.ok(basename(root).startsWith('fleet-gray-contract-')) rmSync(root, { recursive: true, force: true }) } })