import assert from 'node:assert/strict' import test from 'node:test' import { AcceptanceRun, parseArguments, buildAcceptanceSummary, finalizeAcceptanceReport } from './admin-test-env-acceptance.mjs' import { mkdtemp, writeFile, unlink, rmdir } from 'node:fs/promises' import { tmpdir } from 'node:os' import { join } from 'node:path' test('登录失败且未执行业务模块不能报告全量覆盖', async () => { const run = new AcceptanceRun({ environment: 'test', fixedCustomerId: '123', quiet: true, modules: ['dashboard'], requestedModules: ['dashboard'] }) run.request = async () => ({ http: 200, body: { code: 'A0401' } }) const report = await run.run() assert.equal(report.success, false) assert.equal(report.fullCoverage, false) assert.equal(report.coverageStatus, 'FAILED') assert.deepEqual(report.executionScope.unexecutedModules, ['dashboard']) }) function completeReport() { return { fatal: null, executionScope: { plannedModules: ['orders', 'finance'], defaultModules: ['orders', 'finance'] }, steps: [{ module: 'orders', passed: true }, { module: 'finance', passed: true }], modules: ['orders', 'finance'].map(name => ({ name, success: true, passed: 1, failed: 0 })), coverageGaps: [], deploymentBlockers: [] } } test('全量覆盖要求计划全部实际完成;定向成功单独保留通过结果', () => { const full = finalizeAcceptanceReport(completeReport()) assert.equal(full.success, true) assert.equal(full.fullCoverage, true) assert.equal(full.coverageStatus, 'FULL') const focused = completeReport() focused.executionScope.plannedModules = ['orders'] focused.modules.pop(); focused.steps.pop() finalizeAcceptanceReport(focused) assert.equal(focused.success, true) assert.equal(focused.fullCoverage, false) assert.equal(focused.executionScope.planCompleted, true) assert.equal(focused.coverageStatus, 'PARTIAL') }) for (const [label, mutate, failed] of [ ['模块未执行', report => { report.modules.pop() }, true], ['模块没有断言', report => { report.modules[1].passed = 0 }, true], ['模块失败但没有失败步骤', report => { report.modules[1].success = false }, true], ['中止', report => { report.executionScope.stoppedByRequest = true }, true], ['阻断覆盖缺口', report => { report.coverageGaps.push({ blocking: true }) }, true], ['外部部署阻断', report => { report.deploymentBlockers.push({ module: 'orders' }) }, true], ['跳过模块', report => { Object.assign(report.modules[1], { skipped: true, success: false, passed: 0 }) }, false], ['只读生命周期缺口', report => { report.coverageGaps.push({ blocking: false }) }, false], ['仅准备数据', report => { report.executionScope.preparationOnly = true }, false], ['退货仅主链', report => { report.executionScope.returnMainChainOnly = true }, false], ['显式局部验收', report => { report.fullAcceptance = false }, false], ]) test(`${label}不允许标记全量覆盖`, () => { const report = completeReport(); mutate(report); finalizeAcceptanceReport(report) assert.equal(report.success, !failed) assert.equal(report.fullCoverage, false) }) test('清理失败后重新计算报告,不能保留先前的成功与覆盖率', () => { const report = finalizeAcceptanceReport(completeReport()) report.steps.push({ module: 'cleanup', passed: false }) finalizeAcceptanceReport(report) assert.equal(report.success, false) assert.equal(report.fullCoverage, false) assert.equal(report.summary.failed, 1) }) test('混柜专项隔离上下文与报告资源,失败仍保留本轮独立证据', async () => { const run = new AcceptanceRun({ environment: 'test', fixedCustomerId: '123', quiet: true }) run.context.orderDestinationType = 'FBA' run.createdResources.orders = { orderNo: 'MAIN-ORDER' } const marker = run.marker await assert.rejects(run.runIsolatedModule('mixed-container-receiving', async child => { assert.equal(child.fixedPdaLeases, run.fixedPdaLeases) child.context.orderDestinationType = 'PRIVATE' child.options.prepareDropshipManualCount = true child.createdResources.orders = { orderNo: 'ISOLATED-ORDER' } child.token = 'rotated-session' child.refreshToken = 'rotated-refresh' child.expiresAt = 123 child.marker = 'CHANGED' throw Error('fixture failure') }), /fixture failure/) assert.equal(run.marker, marker) assert.equal(run.context.orderDestinationType, 'FBA') assert.equal(run.options.prepareDropshipManualCount, undefined) assert.equal(run.token, 'rotated-session') assert.equal(run.refreshToken, 'rotated-refresh') assert.equal(run.expiresAt, 123) assert.equal(run.createdResources.orders.orderNo, 'MAIN-ORDER') assert.equal(run.createdResources.isolatedModules['mixed-container-receiving'].orders.orderNo, 'ISOLATED-ORDER') }) test('真实总入口调度混柜模块必须经过隔离处理器', async () => { const run = new AcceptanceRun({ environment: 'test', fixedCustomerId: '123', quiet: true, modules: ['mixed-container-receiving'], requestedModules: ['mixed-container-receiving'] }) run.request = async (method, path) => { assert.equal(method, 'POST'); assert.equal(path, '/api/auth/login') return { http: 200, body: { code: '200', data: { accessToken: 'mock-session', refreshToken: 'mock-refresh' } } } } let isolated = false run.runIsolatedModule = async (name, handler) => { assert.equal(name, 'mixed-container-receiving') assert.equal(typeof handler, 'function') isolated = true run.addStep(name, 'offline isolated workflow', null, true) } const report = await run.run() assert.equal(isolated, true) assert.equal(report.success, true) assert.equal(report.fullCoverage, false) assert.deepEqual(report.executionScope.completedModules, ['mixed-container-receiving']) }) test('explicit stop file waits for a module boundary and records an interrupted failure once', async () => { const directory = await mkdtemp(join(tmpdir(), 'acceptance-stop-')) const stopFile = join(directory, 'stop') const run = new AcceptanceRun({ stopFile, quiet: true, modules: [] }) try { assert.equal(parseArguments(['--stop-file', stopFile]).stopFile, stopFile) assert.equal(await run.stopAtBoundary(), false) await writeFile(stopFile, 'stop') assert.equal(await run.stopAtBoundary(), true) assert.equal(await run.stopAtBoundary(), true) assert.match(run.fatal, /中止结果/) assert.equal(run.steps.length, 1) assert.equal(run.steps[0].passed, false) assert.equal(buildAcceptanceSummary(run.steps, [], []).failed, 1) assert.deepEqual(run.createdResources, {}) } finally { await unlink(stopFile).catch(error => { if (error.code !== 'ENOENT') throw error }); await rmdir(directory) } }) test('report summary separates assertion, module-run and unique-request metrics', () => { const sharedResponse = { method: 'GET', path: '/api/platform/v1/orders?page=1', requestId: 'request-1', } const steps = [ { module: 'orders', name: '订单列表成功', passed: true, request: { method: sharedResponse.method, path: sharedResponse.path }, requestId: sharedResponse.requestId }, { module: 'orders', name: '订单列表数据结构正确', passed: true, request: { method: sharedResponse.method, path: sharedResponse.path }, requestId: sharedResponse.requestId }, { module: 'orders', name: '订单列表成功', passed: false, request: { method: 'GET', path: '/api/platform/v1/orders?page=2' }, requestId: 'request-2' }, { module: 'report-security', name: '报告敏感信息扫描', passed: true, request: null, requestId: null }, ] const modules = [ { name: 'orders' }, { name: 'orders' }, { name: 'finance', skipped: 'owned by finance full chain' }, ] const summary = buildAcceptanceSummary(steps, modules, []) assert.deepEqual(summary, { total: 4, passed: 3, failed: 1, moduleRuns: 2, uniqueModules: 1, uniqueRequests: 2, uniqueStepNames: 3, skippedModules: 1, coverageGaps: { total: 0, blocking: 0, nonBlocking: 0, byCategory: {} }, }) }) test('request evidence without requestId is deduplicated by method and path', () => { const summary = buildAcceptanceSummary([ { name: 'CSV 状态', passed: true, request: { method: 'GET', path: '/api/export' }, requestId: null }, { name: 'CSV 表头', passed: true, request: { method: 'GET', path: '/api/export' }, requestId: null }, { name: 'CSV POST', passed: true, request: { method: 'POST', path: '/api/export' }, requestId: null }, ]) assert.equal(summary.uniqueRequests, 2) }) test('coverage gaps are counted by category and blocking status without copying details', () => { const summary = buildAcceptanceSummary([], [], [ { category: 'device', blocking: false, reason: 'camera', secretEvidence: 'must-not-copy' }, { category: 'device', blocking: true, reason: 'scanner' }, { category: 'external-third-party', blocking: false, reason: 'captcha' }, { blocking: true, reason: 'fixture' }, ]) assert.deepEqual(summary.coverageGaps, { total: 4, blocking: 2, nonBlocking: 2, byCategory: { device: { total: 2, blocking: 1, nonBlocking: 1 }, 'external-third-party': { total: 1, blocking: 0, nonBlocking: 1 }, uncategorized: { total: 1, blocking: 1, nonBlocking: 0 }, }, }) assert.equal(JSON.stringify(summary).includes('must-not-copy'), false) })