feat:Strengthen the testing framework
This commit is contained in:
106
scripts/guards/changed-file-test-impact-guard.mjs
Normal file
106
scripts/guards/changed-file-test-impact-guard.mjs
Normal file
@@ -0,0 +1,106 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import { execSync } from 'node:child_process'
|
||||
import { pathToFileURL } from 'node:url'
|
||||
|
||||
const RULES = [
|
||||
{
|
||||
name: 'api',
|
||||
source: /^src\/app\/api\//,
|
||||
tests: [/^tests\/integration\/api\/contract\//, /^tests\/system\//, /^tests\/regression\//],
|
||||
message: 'changing src/app/api/** requires a matching contract, system, or regression test change',
|
||||
},
|
||||
{
|
||||
name: 'worker',
|
||||
source: /^src\/lib\/workers\//,
|
||||
tests: [/^tests\/unit\/worker\//, /^tests\/system\//, /^tests\/regression\//],
|
||||
message: 'changing src/lib/workers/** requires a matching worker, system, or regression test change',
|
||||
},
|
||||
{
|
||||
name: 'task',
|
||||
source: /^src\/lib\/task\//,
|
||||
tests: [/^tests\/unit\/task\//, /^tests\/system\//, /^tests\/regression\//],
|
||||
message: 'changing src/lib/task/** requires a matching task, system, or regression test change',
|
||||
},
|
||||
{
|
||||
name: 'media',
|
||||
source: /^src\/lib\/media\//,
|
||||
tests: [/^tests\/unit\//, /^tests\/system\//, /^tests\/regression\//],
|
||||
message: 'changing src/lib/media/** requires a matching unit, system, or regression test change',
|
||||
},
|
||||
{
|
||||
name: 'provider',
|
||||
source: /^src\/lib\/(generator-api|generators|model-gateway|lipsync|providers)\//,
|
||||
tests: [/^tests\/unit\/(providers|model-gateway|llm)\//, /^tests\/integration\/provider\//, /^tests\/system\//, /^tests\/regression\//],
|
||||
message: 'changing provider/gateway code requires provider contract, system, or regression test change',
|
||||
},
|
||||
]
|
||||
|
||||
function normalizeChangedFiles(rawFiles) {
|
||||
return rawFiles
|
||||
.flatMap((item) => item.split(/[\n,]/))
|
||||
.map((item) => item.trim())
|
||||
.filter(Boolean)
|
||||
}
|
||||
|
||||
function readGitChangedFiles() {
|
||||
try {
|
||||
const output = execSync('git diff --name-only --cached', {
|
||||
cwd: process.cwd(),
|
||||
encoding: 'utf8',
|
||||
stdio: ['ignore', 'pipe', 'ignore'],
|
||||
})
|
||||
return normalizeChangedFiles([output])
|
||||
} catch {
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
||||
export function inspectChangedFiles(changedFiles) {
|
||||
const changed = normalizeChangedFiles(changedFiles)
|
||||
const changedTests = changed.filter((file) => file.startsWith('tests/'))
|
||||
const violations = []
|
||||
|
||||
for (const rule of RULES) {
|
||||
const impactedSources = changed.filter((file) => rule.source.test(file))
|
||||
if (impactedSources.length === 0) continue
|
||||
const hasMatchingTestChange = changedTests.some((file) => rule.tests.some((pattern) => pattern.test(file)))
|
||||
if (!hasMatchingTestChange) {
|
||||
violations.push(`${rule.name}: ${rule.message}; sources=${impactedSources.join(',')}`)
|
||||
}
|
||||
}
|
||||
|
||||
return violations
|
||||
}
|
||||
|
||||
function fail(violations) {
|
||||
console.error('\n[changed-file-test-impact-guard] Missing matching test changes')
|
||||
for (const violation of violations) {
|
||||
console.error(` - ${violation}`)
|
||||
}
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
function runCli() {
|
||||
const inputFiles = process.argv.slice(2)
|
||||
const changedFiles = inputFiles.length > 0
|
||||
? normalizeChangedFiles(inputFiles)
|
||||
: normalizeChangedFiles([process.env.TEST_IMPACT_CHANGED_FILES || '', ...readGitChangedFiles()])
|
||||
|
||||
if (changedFiles.length === 0) {
|
||||
console.log('[changed-file-test-impact-guard] SKIP no changed files detected')
|
||||
process.exit(0)
|
||||
}
|
||||
|
||||
const violations = inspectChangedFiles(changedFiles)
|
||||
if (violations.length > 0) {
|
||||
fail(violations)
|
||||
}
|
||||
|
||||
console.log(`[changed-file-test-impact-guard] OK files=${changedFiles.length}`)
|
||||
}
|
||||
|
||||
const entryHref = process.argv[1] ? pathToFileURL(process.argv[1]).href : null
|
||||
if (entryHref && import.meta.url === entryHref) {
|
||||
runCli()
|
||||
}
|
||||
@@ -6,7 +6,10 @@ import path from 'path'
|
||||
const root = process.cwd()
|
||||
const targetDirs = [
|
||||
path.join(root, 'tests', 'integration', 'api', 'contract'),
|
||||
path.join(root, 'tests', 'integration', 'provider'),
|
||||
path.join(root, 'tests', 'integration', 'chain'),
|
||||
path.join(root, 'tests', 'system'),
|
||||
path.join(root, 'tests', 'regression'),
|
||||
]
|
||||
|
||||
function fail(title, details = []) {
|
||||
|
||||
Reference in New Issue
Block a user