89 lines
4.9 KiB
JavaScript
89 lines
4.9 KiB
JavaScript
import test from 'node:test'
|
|
import assert from 'node:assert/strict'
|
|
import { AcceptanceRun, configureReadOnlyEvidence, parseArguments, validateExecutionOptions } from './admin-test-env-acceptance.mjs'
|
|
|
|
const args = ['--environment', 'gray', '--modules', 'phase5-root-fulfillment',
|
|
'--gateway', 'https://gray.example.invalid', '--username', 'contract', '--password', 'contract',
|
|
'--pda-username', 'contract-pda', '--pda-password', 'contract-pda',
|
|
'--f40-tax-mode', 'TAX_REFUND', '--output', 'reports/gray-contract.json']
|
|
|
|
test('gray is explicit, keeps real environment identity and creates its own customer', () => {
|
|
const options = validateExecutionOptions(parseArguments(args))
|
|
assert.equal(options.environment, 'gray')
|
|
assert.deepEqual(options.modules, ['merchant-qualification-fixture', 'phase5-root-fulfillment'])
|
|
const run = new AcceptanceRun(options)
|
|
assert.match(run.marker, /^GRAY-API-/)
|
|
assert.equal(run.context.f40TaxMode, 'TAX_REFUND')
|
|
})
|
|
|
|
test('gray cannot run unbounded full suite, extra modules, bulk data or TEST faults', () => {
|
|
assert.throws(() => parseArguments(['--environment', 'gray']), /显式指定/)
|
|
for (const extra of [
|
|
['--modules', 'phase4-cross-region-events'],
|
|
['--modules', 'phase5-root-fulfillment,customers'],
|
|
['--verify-unload-event-recovery', 'true'], ['--wait-split-count', '2'],
|
|
]) assert.throws(() => validateExecutionOptions(parseArguments([...args, ...extra])), /gray/)
|
|
assert.throws(() => validateExecutionOptions(parseArguments([...args, '--f40-tax-mode', 'UNKNOWN'])), /f40-tax-mode/)
|
|
})
|
|
|
|
test('gray endpoint must be a single HTTPS origin and needs a named report', () => {
|
|
for (const endpoint of ['http://gray.example.invalid', 'https://gray.example.invalid/api',
|
|
'https://user:secret@gray.example.invalid', 'https://gray.example.invalid?debug=1']) {
|
|
assert.throws(() => validateExecutionOptions(parseArguments([...args, '--gateway', endpoint])), /HTTPS/)
|
|
}
|
|
const options = parseArguments(args)
|
|
options.output = ''
|
|
assert.throws(() => validateExecutionOptions(options), /output/)
|
|
})
|
|
|
|
test('gray has no fallback to TEST account, gateway, customer or PDA environment variables', () => {
|
|
const keys = ['ADMIN_TEST_GATEWAY', 'ADMIN_TEST_USERNAME', 'ADMIN_TEST_PASSWORD', 'ADMIN_PDA_USERNAME',
|
|
'ADMIN_PDA_PASSWORD', 'MERCHANT_TEST_USERNAME', 'MERCHANT_TEST_PASSWORD', 'ADMIN_TEST_CUSTOMER_NAME',
|
|
'ADMIN_GRAY_GATEWAY', 'ADMIN_GRAY_USERNAME', 'ADMIN_GRAY_PASSWORD', 'ADMIN_GRAY_PDA_USERNAME',
|
|
'ADMIN_GRAY_PDA_PASSWORD', 'MERCHANT_GRAY_USERNAME', 'MERCHANT_GRAY_PASSWORD', 'ADMIN_GRAY_CUSTOMER_NAME']
|
|
const saved = new Map(keys.map(key => [key, process.env[key]]))
|
|
try {
|
|
for (const key of keys) {
|
|
if (key.includes('GRAY')) delete process.env[key]
|
|
else process.env[key] = 'must-not-be-used'
|
|
}
|
|
const options = parseArguments(['--environment', 'gray', '--modules', 'phase5-root-fulfillment'])
|
|
for (const key of ['gateway', 'username', 'password', 'pdaUsername', 'pdaPassword',
|
|
'merchantUsername', 'merchantPassword', 'customerName']) assert.equal(options[key], '')
|
|
process.env.ADMIN_GRAY_GATEWAY = 'https://gray.example.invalid'
|
|
assert.equal(parseArguments(['--environment', 'gray', '--modules', 'phase5-root-fulfillment']).gateway,
|
|
'https://gray.example.invalid')
|
|
} finally {
|
|
for (const [key, value] of saved) {
|
|
if (value === undefined) delete process.env[key]
|
|
else process.env[key] = value
|
|
}
|
|
}
|
|
})
|
|
|
|
test('gray evidence preflight attaches only read-only readers, never constructs fault executors', async () => {
|
|
const run = new AcceptanceRun(validateExecutionOptions(parseArguments(args)))
|
|
const readReceiving = () => {}, readCompletion = () => {}
|
|
let preflight = false
|
|
const faults = new Proxy({}, { get() { throw Error('TEST fault factory must not be read') } })
|
|
assert.equal(await configureReadOnlyEvidence(run, {}, () => ({ readReceiving, readCompletion,
|
|
preflight: async () => { preflight = true } }), faults), undefined)
|
|
assert.equal(preflight, true)
|
|
assert.equal(run.context.phase4ReceivingEvidenceReader, readReceiving)
|
|
assert.equal(run.context.f40ReadOnlyOutboxEvidence, readCompletion)
|
|
assert.equal(run.context.beforeLoadingComplete, undefined)
|
|
assert.equal(run.context.beforeReceivingComplete, undefined)
|
|
assert.equal(run.context.f40DeliveryEventFaultExecutor, undefined)
|
|
})
|
|
|
|
test('direct gray evidence use cannot bypass module/fault restrictions or failed preflight', async () => {
|
|
const options = validateExecutionOptions(parseArguments(args))
|
|
const bad = { options: { ...options, verifyUnloadEventRecovery: true }, context: {} }
|
|
await assert.rejects(configureReadOnlyEvidence(bad, {}, () => { throw Error('must not get here') }), /故障注入/)
|
|
const run = new AcceptanceRun(options)
|
|
await assert.rejects(configureReadOnlyEvidence(run, {}, () => ({
|
|
preflight: async () => { throw Error('gray SSH unavailable') },
|
|
})), /gray SSH unavailable/)
|
|
assert.equal(run.context.phase4ReceivingEvidenceReader, undefined)
|
|
})
|