Scans test files at startup and warns when vi.mock() targets internal modules (relative paths or @multica/* packages). Reports file paths and line numbers for all violations without interfering with test execution. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
72 lines
2.2 KiB
TypeScript
72 lines
2.2 KiB
TypeScript
/**
|
|
* Vitest global setup — statically scans test files for internal mock violations.
|
|
*
|
|
* Rule: only mock third-party / external dependencies.
|
|
* Internal modules (relative imports, @multica/* packages) must NOT be mocked.
|
|
*
|
|
* This runs once at startup and logs warnings for violations without
|
|
* interfering with vitest's mock hoisting mechanism.
|
|
*/
|
|
import { readFileSync, readdirSync, statSync } from "node:fs";
|
|
import { join, resolve } from "node:path";
|
|
|
|
const INTERNAL_MOCK_PATTERN =
|
|
/vi\.mock\(\s*["'](\.\/|\.\.\/|@multica\/)[^"']*["']/g;
|
|
|
|
function scanDirectory(dir: string): string[] {
|
|
const files: string[] = [];
|
|
try {
|
|
for (const entry of readdirSync(dir)) {
|
|
const full = join(dir, entry);
|
|
const stat = statSync(full, { throwIfNoEntry: false });
|
|
if (!stat) continue;
|
|
if (stat.isDirectory() && entry !== "node_modules") {
|
|
files.push(...scanDirectory(full));
|
|
} else if (entry.endsWith(".test.ts")) {
|
|
files.push(full);
|
|
}
|
|
}
|
|
} catch {
|
|
// ignore unreadable directories
|
|
}
|
|
return files;
|
|
}
|
|
|
|
function checkMockPolicy(): void {
|
|
const srcDir = resolve(process.cwd(), "src");
|
|
const testFiles = scanDirectory(srcDir);
|
|
const violations: Array<{ file: string; line: number; match: string }> = [];
|
|
|
|
for (const file of testFiles) {
|
|
const content = readFileSync(file, "utf-8");
|
|
const lines = content.split("\n");
|
|
for (let i = 0; i < lines.length; i++) {
|
|
const line = lines[i];
|
|
INTERNAL_MOCK_PATTERN.lastIndex = 0;
|
|
const match = INTERNAL_MOCK_PATTERN.exec(line);
|
|
if (match) {
|
|
violations.push({
|
|
file: file.replace(process.cwd() + "/", ""),
|
|
line: i + 1,
|
|
match: match[0],
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
if (violations.length > 0) {
|
|
console.warn("\n[mock-policy] Internal module mock violations detected:");
|
|
console.warn(
|
|
" Rule: Only mock third-party dependencies. See CLAUDE.md Testing Guidelines.\n",
|
|
);
|
|
for (const v of violations) {
|
|
console.warn(` ${v.file}:${v.line}`);
|
|
console.warn(` ${v.match}\n`);
|
|
}
|
|
console.warn(
|
|
` Total: ${violations.length} violation(s) in ${new Set(violations.map((v) => v.file)).size} file(s)\n`,
|
|
);
|
|
}
|
|
}
|
|
|
|
checkMockPolicy();
|