# test.rules/rules/eslint/closure-rule-shape.eslint.rule.test.ts

> 102 lines of code and 0 definitions.

Tree: Codebase Testing
Language: typescript
Canonical: https://banes-lab.com/anatomy/testing#file-testing-test-rules-rules-eslint-closure-rule-shape-eslint-rule-test-ts
Source text: https://banes-lab.com/source/testing/test.rules/rules/eslint/closure-rule-shape.eslint.rule.test.ts.txt

Listed in [test.rules/rules/eslint](https://banes-lab.com/api/source/testing/test.rules/rules/eslint.md), after [test.rules/rules/eslint/closure-register-verb-reserved.eslint.rule.test.ts](https://banes-lab.com/source/testing/test.rules/rules/eslint/closure-register-verb-reserved.eslint.rule.test.ts.md) and before [test.rules/rules/eslint/closure-show-flag-polarity.eslint.rule.test.ts](https://banes-lab.com/source/testing/test.rules/rules/eslint/closure-show-flag-polarity.eslint.rule.test.ts.md).

## Contained in

- [test.rules/rules/eslint](https://banes-lab.com/anatomy/testing/folder-testing-test-rules-rules-eslint.md)

## Source

```typescript
import { describe, expect, it } from "vitest";
import { Linter } from "eslint";
import { absolutePath } from "@ssot/paths";
import rule from "@ssot/govlab/rules/eslint/closure-rule-shape.eslint.rule.ts";
import { sep } from "node:path";

const linter = new Linter();
const config = [
    {
        files: ["**/*.ts"],
        languageOptions: {
            ecmaVersion: 2025 as const,
            parser: await import("@typescript-eslint/parser"),
            sourceType: "module" as const,
        },
        plugins: { t: { rules: { r: rule } } },
        rules: { "t/r": "error" as const },
    },
];

const RULE_FILE = `${absolutePath("govlabHost.rules").split(sep).join("/")}/eslint/probe.eslint.rule.ts`;
const OTHER_FILE = `${absolutePath("govlabHost.rules").split(sep).join("/")}/eslint/notes.ts`;

const idsFor = function idsFor(code: string, file = RULE_FILE): string[] {
    return linter
        .verify(code, config, file)
        .map((message) => message.messageId)
        .filter((id): id is string => typeof id === "string");
};

const WELL_FORMED = `export default {
    create(context) {
        return { Program(node) { context.report({ messageId: "bad", node }); } };
    },
    meta: {
        docs: {
            checks: defineCheck({ detects: [], enforces: [] }),
            description: "Describes the shape this rule enforces.",
        },
        messages: { bad: "A finding." },
        schema: [],
        type: "problem",
    },
};`;

const DECLARATION = "            checks: defineCheck({ detects: [], enforces: [] }),\n";

describe("closure-rule-shape", () => {
    it("accepts a rule declaring every required part", () => {
        expect(idsFor(WELL_FORMED)).toStrictEqual([]);
    });

    it("ignores a file that is not a rule file", () => {
        expect(idsFor("export const notes: any = 1;", OTHER_FILE)).toStrictEqual([]);
    });

    it("reports an explicit any anywhere in a rule file", () => {
        expect(idsFor(WELL_FORMED.replace("create(context)", "create(context: any)"))).toContain("explicitAny");
    });

    it("reports a file whose object declares create without meta", () => {
        expect(idsFor("export default { create() { return {}; } };")).toContain("missingRuleObject");
    });

    it("reports a file whose object declares meta without create", () => {
        const code = `export default { meta: { docs: { description: "x" }, messages: { a: "b" }, schema: [], type: "problem" } };`;
        expect(idsFor(code)).toContain("missingRuleObject");
    });

    it("pairs messages across every rule object a plugin wrapper carries", () => {
        const code = `defineCheck({ detects: [], enforces: [] });
const a = {
    create(context) { return { Program(node) { context.report({ messageId: "one", node }); } }; },
    meta: { docs: { description: "First." }, messages: { one: "A." }, schema: [], type: "problem" },
};
const b = {
    create(context) { return { Program(node) { context.report({ messageId: "two", node }); } }; },
    meta: { docs: { description: "Second." }, messages: { two: "B." }, schema: [], type: "problem" },
};
export default { plugins: { ns: { rules: { a, b } } }, tool: "eslint" };`;
        expect(idsFor(code)).toStrictEqual([]);
    });

    it("reports a missing meta key", () => {
        expect(idsFor(WELL_FORMED.replace("        schema: [],\n", ""))).toContain("missingMetaKey");
    });

    it("reports an empty or absent description", () => {
        expect(idsFor(WELL_FORMED.replace('"Describes the shape this rule enforces."', '"  "'))).toContain(
            "missingDescription",
        );
    });

    it("reports a rule that declares no check, and accepts a module-level declaration in its place", () => {
        const undeclared = WELL_FORMED.replace(DECLARATION, "");
        expect(idsFor(undeclared)).toContain("missingChecks");
        expect(idsFor(`defineCheck({ detects: [], enforces: [] });\n${undeclared}`)).not.toContain("missingChecks");
    });

    it("reports a self-declared severity", () => {
        expect(idsFor(WELL_FORMED.replace("schema: [],", 'schema: [], severity: "error",'))).toContain(
            "severityDeclared",
        );
    });

    it("reports a messageId reported but not declared", () => {
        expect(idsFor(WELL_FORMED.replace('bad: "A finding."', 'other: "A finding."'))).toContain("unknownMessageId");
    });

    it("reports a message declared but never reported", () => {
        expect(
            idsFor(WELL_FORMED.replace('messages: { bad: "A finding." }', 'messages: { bad: "A.", spare: "B." }')),
        ).toContain("unusedMessage");
    });

    it("reports a rule that re-applies the member guard", () => {
        const code = `import { isGovernedFile } from "../../shared/resolvers/anchor.resolver.ts";\n${WELL_FORMED}`;
        expect(idsFor(code)).toContain("projectGuard");
    });
});
```
