90 lines
3.1 KiB
JavaScript
90 lines
3.1 KiB
JavaScript
import assert from 'node:assert/strict'
|
|
import { mkdtemp, readFile, rm, writeFile } from 'node:fs/promises'
|
|
import os from 'node:os'
|
|
import path from 'node:path'
|
|
import { fileURLToPath } from 'node:url'
|
|
import { spawnSync } from 'node:child_process'
|
|
import test from 'node:test'
|
|
|
|
const testDir = path.dirname(fileURLToPath(import.meta.url))
|
|
const projectRoot = path.resolve(testDir, '..', '..')
|
|
const verifierPath = path.join(projectRoot, 'scripts', 'verify-aug1-content-candidates.mjs')
|
|
const candidatePath = path.join(
|
|
projectRoot,
|
|
'docs',
|
|
'content-candidates',
|
|
'aug1-life-advisor-content-candidates-v0.1.json',
|
|
)
|
|
const baseCandidate = JSON.parse(await readFile(candidatePath, 'utf8'))
|
|
|
|
const cloneCandidate = () => structuredClone(baseCandidate)
|
|
|
|
async function verifyCandidate(candidate) {
|
|
const tempDir = await mkdtemp(path.join(os.tmpdir(), 'aug1-content-'))
|
|
const fixturePath = path.join(tempDir, 'candidate.json')
|
|
try {
|
|
await writeFile(fixturePath, JSON.stringify(candidate), 'utf8')
|
|
return spawnSync(process.execPath, [verifierPath, fixturePath], {
|
|
cwd: projectRoot,
|
|
encoding: 'utf8',
|
|
})
|
|
} finally {
|
|
await rm(tempDir, { recursive: true, force: true })
|
|
}
|
|
}
|
|
|
|
test('accepts the reviewed disabled candidate structure through an explicit package path', async () => {
|
|
const result = await verifyCandidate(cloneCandidate())
|
|
assert.equal(result.status, 0, result.stderr)
|
|
assert.match(result.stdout, /verification passed/)
|
|
})
|
|
|
|
test('rejects sensitive locators, credentials and mobile numbers', async (t) => {
|
|
const cases = [
|
|
['precise room', (candidate) => {
|
|
candidate.scenarios[0].scenarioDraft.openingText = '住户住在3栋2单元501室。'
|
|
}],
|
|
['access credential', (candidate) => {
|
|
candidate.scenarios[0].scenarioDraft.openingText = '门禁口令 ABCD。'
|
|
}],
|
|
['mobile phone', (candidate) => {
|
|
candidate.policyQuestionCandidates[0].question = ['联系号码', '13', '9000', '00000'].join('')
|
|
}],
|
|
]
|
|
|
|
for (const [name, mutate] of cases) {
|
|
await t.test(name, async () => {
|
|
const candidate = cloneCandidate()
|
|
mutate(candidate)
|
|
const result = await verifyCandidate(candidate)
|
|
assert.notEqual(result.status, 0)
|
|
})
|
|
}
|
|
})
|
|
|
|
test('rejects publishable content, answers and duplicate IDs before formal sign-off', async (t) => {
|
|
const cases = [
|
|
['publishable root', (candidate) => {
|
|
candidate.publishable = true
|
|
}],
|
|
['embedded answer', (candidate) => {
|
|
candidate.policyQuestionCandidates[0].answer = 'not allowed before formal sourcing'
|
|
}],
|
|
['duplicate scenario ID', (candidate) => {
|
|
candidate.scenarios[1].candidateId = candidate.scenarios[0].candidateId
|
|
}],
|
|
['duplicate question ID', (candidate) => {
|
|
candidate.policyQuestionCandidates[1].id = candidate.policyQuestionCandidates[0].id
|
|
}],
|
|
]
|
|
|
|
for (const [name, mutate] of cases) {
|
|
await t.test(name, async () => {
|
|
const candidate = cloneCandidate()
|
|
mutate(candidate)
|
|
const result = await verifyCandidate(candidate)
|
|
assert.notEqual(result.status, 0)
|
|
})
|
|
}
|
|
})
|