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 "./no-undeclared-event-publish.js"; function makeFixture({ manifestUseCases, useCaseBody }) { const repoRoot = fs.mkdtempSync(path.join(os.tmpdir(), "nuep-")); const featureDir = path.join(repoRoot, "packages", "demo"); fs.mkdirSync(path.join(featureDir, "src", "application", "use-cases"), { recursive: true }); const useCasesObj = Object.entries(manifestUseCases) .map(([name, uc]) => ` ${name}: { mutates: ${uc.mutates}, audits: [${uc.audits.map((a) => `"${a}"`).join(", ")}], publishes: [${uc.publishes.map((p) => `"${p}"`).join(", ")}], 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 useCaseFile = path.join(featureDir, "src", "application", "use-cases", "sign-up.use-case.ts"); fs.writeFileSync(useCaseFile, useCaseBody); return { repoRoot, useCaseFile }; } const tester = new RuleTester({ languageOptions: { parser: await import("@typescript-eslint/parser"), ecmaVersion: "latest", sourceType: "module", }, }); describe("no-undeclared-event-publish", () => { it("passes when bus.publish event name matches manifest publishes[]", () => { const { repoRoot, useCaseFile } = makeFixture({ manifestUseCases: { signUp: { mutates: true, audits: [], publishes: ["demo.signed-up"] } }, useCaseBody: `export const signUpUseCase = (bus) => async () => { bus.publish("demo.signed-up", {}); };`, }); tester.run("no-undeclared-event-publish", rule, { valid: [{ filename: useCaseFile, code: fs.readFileSync(useCaseFile, "utf8"), options: [{ repoRoot }] }], invalid: [], }); }); it("fires when bus.publish event name is not in manifest", () => { const { repoRoot, useCaseFile } = makeFixture({ manifestUseCases: { signUp: { mutates: true, audits: [], publishes: [] } }, useCaseBody: `export const signUpUseCase = (bus) => async () => { bus.publish("demo.signed-up", {}); };`, }); tester.run("no-undeclared-event-publish", rule, { valid: [], invalid: [{ filename: useCaseFile, code: fs.readFileSync(useCaseFile, "utf8"), options: [{ repoRoot }], errors: [{ messageId: "undeclared", data: { event: "demo.signed-up", useCase: "signUp" } }], }], }); }); it("is a no-op when bus.publish is called with a non-literal argument", () => { const { repoRoot, useCaseFile } = makeFixture({ manifestUseCases: { signUp: { mutates: true, audits: [], publishes: [] } }, useCaseBody: `export const signUpUseCase = (bus, name) => async () => { bus.publish(name, {}); };`, }); tester.run("no-undeclared-event-publish", rule, { valid: [{ filename: useCaseFile, code: fs.readFileSync(useCaseFile, "utf8"), options: [{ repoRoot }] }], invalid: [], }); }); });