import test from 'node:test' import assert from 'node:assert/strict' import { mkdtempSync, existsSync, readFileSync, rmSync } from 'node:fs' import { tmpdir } from 'node:os' import { join, dirname, basename, resolve } from 'node:path' import { parseFeeVoidArguments, runFeeVoid } from './fee-void-recovery-test-env.mjs' const config = () => parseFeeVoidArguments(['--execute', '--environment', 'TEST', '--run-id', 'offline', '--customer-id', '41', '--control-customer-id', '42', '--warehouse-id', '900']) const env = { CHONGCHONG_ENV: 'test', CHONGCHONG_ADMIN_USERNAME: 'offline-admin', CHONGCHONG_ADMIN_PASSWORD: 'private-password', CHONGCHONG_ADMIN_API_BASE_URL: 'https://test.example.invalid/api' } function harness(outputRoot, failure) { const calls = []; let fee, finished = false, cleanup = 0 const context = { marker: 'FEE-OFFLINE', async request(method, path, body) { calls.push({ method, path }) let data if (path === '/api/auth/login') data = { accessToken: 'private-access', refreshToken: 'private-refresh' } else if (path === '/api/auth/logout') { if (failure === 'logout') throw Error('LOGOUT_TRANSPORT'); data = {} } else if (path.includes('/iam/tenants/')) data = { id: path.split('/').at(-1), tenantName: 'Offline customer', status: 'ACTIVE' } else if (path.includes('/warehouse-admin/')) data = { warehouse: { id: '900', tenantId: '0', status: 'ACTIVE', warehouseType: 'OVERSEAS' } } else if (path.includes('/fee-pool?')) data = path.includes('customerId=41') && fee?.reviewStatus === 'APPROVED' && (!finished || failure === 'pool') ? [{ customerId: '41', sourceId: '81', sourceVersion: 1, amount: 17.31, currency: 'CNY' }] : [] else if (method === 'GET') data = path.includes('customerId=41') && fee ? [structuredClone(fee)] : finished && failure === 'isolation' ? [{ customerId: '42', sourceFeeId: '99' }] : [] else if (path.endsWith('/submit')) { fee.reviewStatus = 'PENDING'; data = fee } else if (path.endsWith('/review')) { Object.assign(fee, { reviewStatus: 'APPROVED', publicationStatus: 'PUBLISHED', sourceStatus: 'ACTIVE' }); data = fee } else if (path.endsWith('/void')) { fee.voidStatus = finished ? 'CONFIRMED' : 'PENDING_FINANCE' if (failure === 'early-confirm') fee.sourceStatus = 'VOIDED' data = fee } else { fee = { sourceFeeId: '81', sourceVersion: 1, customerId: body.customerId, reviewStatus: 'DRAFT' }; data = fee } return { http: 200, body: { code: '200', data: structuredClone(data) } } } } return { calls, get cleanup() { return cleanup }, options: { env, outputRoot, contextFactory: () => context, wait: async () => {}, readers: { preflight: async () => {}, readFleetInventory: async scope => ({ scope, balances: [], positions: [], ledger: [] }) }, faultFactory: () => ({ prepare: async () => { if (['prepare', 'preflight'].includes(failure)) throw Error('PREPARE_FAILED') }, readProof: async () => { finished = true; Object.assign(fee, { sourceStatus: 'VOIDED', voidStatus: 'CONFIRMED' }); return { status: 'VERIFIED' } }, cleanup: async () => { cleanup++; return failure === 'preflight' || failure === 'missing-restoration' ? { attempted: false } : { restored: failure !== 'restore', healthy: true, proxyStopped: true } } }) } } } test('fee void plan has no network and refuses implicit environment or ambiguous customer scope', async () => { assert.equal((await runFeeVoid(parseFeeVoidArguments([]), { env: {} })).execute, false) for (const args of [['--execute'], ['--environment', 'PROD'], ['--run-id', '../escape'], ['--plan', '--execute']]) assert.throws(() => parseFeeVoidArguments(args)) await assert.rejects(runFeeVoid({ ...config(), controlCustomerId: '41' }, { env }), /distinct/) await assert.rejects(runFeeVoid(config(), { env: { ...env, CHONGCHONG_ENV: 'prod' } }), /TEST/) }) for (const failure of [undefined, 'prepare', 'preflight', 'early-confirm', 'isolation', 'pool', 'restore', 'missing-restoration', 'logout']) { test(`fee void ${failure || 'success'} retains evidence and releases its lock after cleanup`, async () => { const root = mkdtempSync(join(tmpdir(), 'fee-void-contract-')) try { const h = harness(root, failure), report = await runFeeVoid(config(), h.options) assert.equal(report.success, !failure, JSON.stringify(report)) assert.equal(h.cleanup, 1) if (failure === 'preflight') assert.equal(report.stage, 'FAILED') if (failure === 'missing-restoration') assert.equal(report.stage, 'RESTORATION_FAILED') assert.equal(h.calls.filter(c => c.path === '/api/auth/logout').length, 1) assert.equal(existsSync(join(root, 'reports/.test-event-recovery.lock')), false) const path = join(root, 'reports/fee-void-offline/report.json'), saved = readFileSync(path, 'utf8') assert.doesNotMatch(saved, /private-password|private-access|private-refresh/) if (failure === 'logout') assert.equal(JSON.parse(saved).logoutError, 'LOGOUT_TRANSPORT') const previous = h.calls.length assert.equal((await runFeeVoid(config(), h.options)).success, false) assert.equal(h.calls.length, previous) assert.equal(readFileSync(path, 'utf8'), saved) } finally { assert.equal(dirname(resolve(root)), resolve(tmpdir())) assert.ok(basename(root).startsWith('fee-void-contract-')) rmSync(root, { recursive: true, force: true }) } }) }