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 "./usecase-must-be-wired.js"; function makeFixture({ manifestUseCases, binderBody, binderName = "bind-production.ts", }) { const repoRoot = fs.mkdtempSync(path.join(os.tmpdir(), "umbw-")); const featureDir = path.join(repoRoot, "packages", "demo"); fs.mkdirSync(path.join(featureDir, "src", "di"), { recursive: true }); const useCasesObj = Object.entries(manifestUseCases) .map( ([name, uc]) => ` ${name}: { mutates: ${uc.mutates}, audits: [${uc.audits.map((a) => `"${a}"`).join(", ")}], publishes: [], consumes: [] },`, ) .join("\n"); fs.writeFileSync( path.join(featureDir, "src", "feature.manifest.ts"), `export const demoManifest = defineFeature({ name: "demo", requiredCores: [], useCases: { ${useCasesObj} }, realtimeChannels: [], jobs: [], } as const);`, ); const binderFile = path.join(featureDir, "src", "di", binderName); fs.writeFileSync(binderFile, binderBody); return { repoRoot, binderFile }; } const tester = new RuleTester({ languageOptions: { parser: await import("@typescript-eslint/parser"), ecmaVersion: "latest", sourceType: "module", }, }); describe("usecase-must-be-wired", () => { it("passes when every manifest use case has a wireUseCase call", () => { const { repoRoot, binderFile } = makeFixture({ manifestUseCases: { signIn: { mutates: false, audits: [] }, signUp: { mutates: true, audits: ["user.created"] }, }, binderBody: ` export function bindProductionDemo(ctx) { wireUseCase({ name: "signIn", factory: signInUseCase }); wireUseCase({ name: "signUp", factory: signUpUseCase }); } `, }); tester.run("usecase-must-be-wired", rule, { valid: [ { filename: binderFile, code: fs.readFileSync(binderFile, "utf8"), options: [{ repoRoot }], }, ], invalid: [], }); }); it("fires when a manifest use case is missing a wireUseCase call", () => { const { repoRoot, binderFile } = makeFixture({ manifestUseCases: { signIn: { mutates: false, audits: [] }, signUp: { mutates: true, audits: [] }, }, binderBody: ` export function bindProductionDemo(ctx) { wireUseCase({ name: "signIn", factory: signInUseCase }); } `, }); tester.run("usecase-must-be-wired", rule, { valid: [], invalid: [ { filename: binderFile, code: fs.readFileSync(binderFile, "utf8"), options: [{ repoRoot }], errors: [ { messageId: "missing", data: { useCase: "signUp", feature: "demo" }, }, ], }, ], }); }); it("also applies to bind-dev-seed.ts", () => { const { repoRoot, binderFile } = makeFixture({ binderName: "bind-dev-seed.ts", manifestUseCases: { signIn: { mutates: false, audits: [] } }, binderBody: ` export async function bindDevSeedDemo(ctx) { // forgot to call wireUseCase } `, }); tester.run("usecase-must-be-wired", rule, { valid: [], invalid: [ { filename: binderFile, code: fs.readFileSync(binderFile, "utf8"), options: [{ repoRoot }], errors: [ { messageId: "missing", data: { useCase: "signIn", feature: "demo" }, }, ], }, ], }); }); it("is a no-op on files outside src/di/bind-*.ts", () => { const { repoRoot } = makeFixture({ manifestUseCases: { signIn: { mutates: false, audits: [] } }, binderBody: `// not a binder`, }); const otherFile = path.join( repoRoot, "packages", "demo", "src", "other.ts", ); fs.writeFileSync(otherFile, `export const x = 1;`); tester.run("usecase-must-be-wired", rule, { valid: [ { filename: otherFile, code: fs.readFileSync(otherFile, "utf8"), options: [{ repoRoot }], }, ], invalid: [], }); }); it("is a no-op when the feature manifest declares no use cases", () => { const { repoRoot, binderFile } = makeFixture({ manifestUseCases: {}, binderBody: `export function bindProductionDemo(ctx) {}`, }); tester.run("usecase-must-be-wired", rule, { valid: [ { filename: binderFile, code: fs.readFileSync(binderFile, "utf8"), options: [{ repoRoot }], }, ], invalid: [], }); }); });