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-consent-check.js"; function makeFixture({ requiresConsent, useCaseBody }) { const repoRoot = fs.mkdtempSync(path.join(os.tmpdir(), "nucc-")); const featureDir = path.join(repoRoot, "packages", "demo"); fs.mkdirSync(path.join(featureDir, "src", "application", "use-cases"), { recursive: true, }); const consentField = requiresConsent.length > 0 ? ` requiresConsent: [${requiresConsent.map((c) => `"${c}"`).join(", ")}],\n` : ""; fs.writeFileSync( path.join(featureDir, "src", "feature.manifest.ts"), `export const demoManifest = defineFeature({ name: "demo", requiredCores: [], ${consentField} useCases: { checkData: { mutates: false, audits: [], publishes: [], consumes: [] }, }, realtimeChannels: [], jobs: [], } as const);`, ); const useCaseFile = path.join( featureDir, "src", "application", "use-cases", "check-data.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-consent-check", () => { it("passes when consent.isGranted category matches manifest requiresConsent", () => { const { repoRoot, useCaseFile } = makeFixture({ requiresConsent: ["analytics"], useCaseBody: `export const checkDataUseCase = (consent) => async () => { if (!consent.isGranted("analytics")) throw new Error(); };`, }); tester.run("no-undeclared-consent-check", rule, { valid: [ { filename: useCaseFile, code: fs.readFileSync(useCaseFile, "utf8"), options: [{ repoRoot }], }, ], invalid: [], }); }); it("fires when category is undeclared or manifest declaration is unused", () => { const { repoRoot: rootA, useCaseFile: fileA } = makeFixture({ requiresConsent: ["analytics"], useCaseBody: `export const checkDataUseCase = (consent) => async () => { if (!consent.isGranted("marketing")) throw new Error(); };`, }); const { repoRoot: rootB, useCaseFile: fileB } = makeFixture({ requiresConsent: ["analytics"], useCaseBody: `export const checkDataUseCase = () => async () => { return { ok: true }; };`, }); tester.run("no-undeclared-consent-check", rule, { valid: [], invalid: [ { filename: fileA, code: fs.readFileSync(fileA, "utf8"), options: [{ repoRoot: rootA }], errors: [ { messageId: "undeclared", data: { category: "marketing", useCase: "checkData" }, }, ], }, { filename: fileB, code: fs.readFileSync(fileB, "utf8"), options: [{ repoRoot: rootB }], errors: [ { messageId: "unusedDeclaration", data: { useCase: "checkData" } }, ], }, ], }); }); it("is a no-op for non-use-case files", () => { const { repoRoot } = makeFixture({ requiresConsent: ["analytics"], useCaseBody: `export const checkDataUseCase = (consent) => async () => { consent.isGranted("unknown"); };`, }); const serviceFile = path.join( repoRoot, "packages", "demo", "src", "application", "services", "data.service.ts", ); fs.mkdirSync(path.dirname(serviceFile), { recursive: true }); fs.writeFileSync( serviceFile, `export function doWork(consent) { consent.isGranted("unknown"); }`, ); tester.run("no-undeclared-consent-check", rule, { valid: [ { filename: serviceFile, code: fs.readFileSync(serviceFile, "utf8"), options: [{ repoRoot }], }, ], invalid: [], }); }); });