Two CLAUDE.md conventions had no mechanical gate, so both drifted: entity models shipped without sibling tests, and feature test files imported src modules via `../` instead of the `@/` alias. - `entity-must-have-test` — every entities/models/<x>.ts needs a sibling <x>.test.ts (errors and barrels excluded). - `no-relative-parent-import-in-tests` — feature test files must import src via `@/`, not `../`. Scoped to feature packages; core packages are governed by their own generator templates. Both register at warn level, bringing the conformance rule count to 15.
75 lines
2.2 KiB
JavaScript
75 lines
2.2 KiB
JavaScript
import { describe, it } from "vitest";
|
|
import { RuleTester } from "eslint";
|
|
import path from "node:path";
|
|
import os from "node:os";
|
|
import fs from "node:fs";
|
|
import rule from "./entity-must-have-test.js";
|
|
|
|
function makeModelsDir() {
|
|
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "emht-"));
|
|
const modelsDir = path.join(dir, "src", "entities", "models");
|
|
fs.mkdirSync(modelsDir, { recursive: true });
|
|
return modelsDir;
|
|
}
|
|
|
|
function makeEntityFixture({ withTest }) {
|
|
const modelsDir = makeModelsDir();
|
|
const entity = path.join(modelsDir, "cookie.ts");
|
|
fs.writeFileSync(entity, `export const cookie = {};`);
|
|
if (withTest) {
|
|
fs.writeFileSync(
|
|
path.join(modelsDir, "cookie.test.ts"),
|
|
`import { it } from "vitest"; it("works", () => {});`,
|
|
);
|
|
}
|
|
return { entity };
|
|
}
|
|
|
|
const tester = new RuleTester({
|
|
languageOptions: { ecmaVersion: "latest", sourceType: "module" },
|
|
});
|
|
|
|
describe("entity-must-have-test", () => {
|
|
it("passes when a sibling .test.ts exists", () => {
|
|
const { entity } = makeEntityFixture({ withTest: true });
|
|
tester.run("entity-must-have-test", rule, {
|
|
valid: [{ filename: entity, code: fs.readFileSync(entity, "utf8") }],
|
|
invalid: [],
|
|
});
|
|
});
|
|
|
|
it("fires when no sibling test file exists", () => {
|
|
const { entity } = makeEntityFixture({ withTest: false });
|
|
tester.run("entity-must-have-test", rule, {
|
|
valid: [],
|
|
invalid: [
|
|
{
|
|
filename: entity,
|
|
code: fs.readFileSync(entity, "utf8"),
|
|
errors: [{ messageId: "missingTest" }],
|
|
},
|
|
],
|
|
});
|
|
});
|
|
|
|
it("ignores files outside entities/models", () => {
|
|
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "emht-"));
|
|
const other = path.join(dir, "helper.ts");
|
|
fs.writeFileSync(other, `export const x = 1;`);
|
|
tester.run("entity-must-have-test", rule, {
|
|
valid: [{ filename: other, code: "export const x = 1;" }],
|
|
invalid: [],
|
|
});
|
|
});
|
|
|
|
it("ignores the index.ts barrel inside entities/models", () => {
|
|
const modelsDir = makeModelsDir();
|
|
const index = path.join(modelsDir, "index.ts");
|
|
fs.writeFileSync(index, `export {};`);
|
|
tester.run("entity-must-have-test", rule, {
|
|
valid: [{ filename: index, code: "export {};" }],
|
|
invalid: [],
|
|
});
|
|
});
|
|
});
|