2026-08-25 09:50:02 +08:00
|
|
|
import assert from 'node:assert/strict'
|
|
|
|
|
import test from 'node:test'
|
2026-10-23 04:48:02 +08:00
|
|
|
import { AcceptanceRun, parseArguments, buildAcceptanceSummary } 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('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) }
|
|
|
|
|
})
|
2026-08-25 09:50:02 +08:00
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
})
|