121 lines
8.3 KiB
JavaScript
121 lines
8.3 KiB
JavaScript
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 })
|
||
|
|
}
|
||
|
|
})
|