397777089e
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
53 lines
2.3 KiB
JavaScript
53 lines
2.3 KiB
JavaScript
/**
|
|
* TDD-gate real-test verification — router-gate v4 spec §3.11.
|
|
* Pure ядро + тонкая fs-обёртка. Закрывает sentinel-gaming (touch test без expect/it).
|
|
*
|
|
* DEVIATION: spec §3.11 показывает @babel/parser AST. Babel не установлен —
|
|
* regex-based extraction per спек note "basic regex + import check; AST refinement future".
|
|
*/
|
|
import fs from 'node:fs';
|
|
import path from 'node:path';
|
|
|
|
export function extractImportsAndStrings(content) {
|
|
const out = [];
|
|
// import ... from '<x>'
|
|
for (const m of content.matchAll(/from\s+['"]([^'"]+)['"]/g)) out.push(m[1]);
|
|
// require('<x>')
|
|
for (const m of content.matchAll(/require\(\s*['"]([^'"]+)['"]\s*\)/g)) out.push(m[1]);
|
|
// any string literals (single or double quoted)
|
|
// NOTE: import paths captured above are also matched here; duplicates are harmless for the downstream .some() check.
|
|
for (const m of content.matchAll(/['"]([^'"\n]+)['"]/g)) out.push(m[1]);
|
|
return out;
|
|
}
|
|
|
|
export function verifyRealTestContent(content, editedFilesList) {
|
|
const src = String(content || '');
|
|
if (!/\bexpect\s*\(/.test(src)) return { valid: false, reason: 'no_expect_call' };
|
|
if (!/\b(test|it)\s*\(/.test(src)) return { valid: false, reason: 'no_test_block' };
|
|
|
|
const list = editedFilesList || [];
|
|
if (list.length === 0) return { valid: true }; // no prod files to cover
|
|
|
|
const strings = extractImportsAndStrings(src);
|
|
const hasRelevant = list.some((editedFile) => {
|
|
const base = path.basename(editedFile, path.extname(editedFile));
|
|
// KNOWN: basename substring match can false-positive for short basenames (e.g. 'User' matched inside 'CurrentUser').
|
|
// Acceptable at regex-approximation fidelity; AST refinement deferred (§3.11).
|
|
return strings.some((s) => s.includes(base));
|
|
});
|
|
if (!hasRelevant) return { valid: false, reason: 'no_coverage_of_edited_files' };
|
|
return { valid: true };
|
|
}
|
|
|
|
export function verifyRealTest(testFilePath, editedFilesList, opts = {}) {
|
|
const { readFile = fs.readFileSync } = opts;
|
|
let content;
|
|
try {
|
|
content = readFile(testFilePath, 'utf8');
|
|
} catch (err) {
|
|
const reason = err && err.code === 'ENOENT' ? 'test_file_not_found' : 'test_file_read_error';
|
|
return { valid: false, reason };
|
|
}
|
|
return verifyRealTestContent(content, editedFilesList);
|
|
}
|