core/quality/eslint/require-test-coverage.eslint.rule.ts

core/quality/eslint/require-test-coverage.eslint.rule.ts is a file in GovLab Quality. 122 lines of code and 23 definitions.

import { asConstructNode, collectLocalConstructs, constructsOf } from "#core/selectors/construct.selector";
import { govlabSettings, normalizedFilename } from "#core/selectors/eslint.selector";
import type { ConstructNode } from "#types/construct.types";
import type { Rule } from "eslint";
import type { Scope } from "#types/coverage.types";
import { govlabMeta } from "#core/factories/eslint.factory";
import { isDefaultConstruct } from "#core/predicates/construct.predicate";
import { isSkippable } from "#core/predicates/coverage.predicate";
import { resolveScope } from "#core/resolvers/coverage.resolver";

type Reporter = (name: string, target: ConstructNode) => void;

interface CoverageEnv {
    context: Rule.RuleContext;
    localConstructs: Set<string>;
    report: Reporter;
    scope: Scope;
}

const isRuleNode = function isRuleNode(value: unknown): value is Rule.Node {
    return typeof value === "object" && value !== null && "type" in value;
};

const resolvedScopeOf = function resolvedScopeOf(context: Rule.RuleContext): Scope | null {
    const file = normalizedFilename(context);
    if (isSkippable(file, govlabSettings(context).exclude ?? [])) {
        return null;
    }
    return resolveScope(file);
};

const reportSpecifiers = function reportSpecifiers(specifiers: ConstructNode[], env: CoverageEnv): void {
    for (const spec of specifiers) {
        const localName = spec.local?.name;
        const exportedName = spec.exported?.name;
        if (typeof localName === "string" && typeof exportedName === "string" && env.localConstructs.has(localName)) {
            env.report(exportedName, spec);
        }
    }
};

const exportNamed = function exportNamed(node: ConstructNode | null, env: CoverageEnv): void {
    if (node === null) {
        return;
    }
    if (node.declaration) {
        for (const construct of constructsOf(node.declaration)) {
            env.report(construct.name, construct.node);
        }
        return;
    }
    if (!node.source) {
        reportSpecifiers(node.specifiers ?? [], env);
    }
};

const isLocalReference = function isLocalReference(
    declaration: ConstructNode | null | undefined,
    localConstructs: Set<string>,
): boolean {
    return (
        declaration?.type === "Identifier" &&
        typeof declaration.name === "string" &&
        localConstructs.has(declaration.name)
    );
};

const defaultExport = function defaultExport(node: Rule.Node, env: CoverageEnv): void {
    const declaration = asConstructNode(node)?.declaration;
    if (!isDefaultConstruct(declaration) && !isLocalReference(declaration, env.localConstructs)) {
        return;
    }
    if (!env.scope.coversDefault(env.scope.specifier)) {
        env.context.report({
            data: { specifier: env.scope.specifier ?? env.scope.label },
            messageId: "uncoveredDefault",
            node,
        });
    }
};

const onProgram = function onProgram(node: Rule.Node, env: CoverageEnv): void {
    for (const name of collectLocalConstructs(asConstructNode(node)?.body ?? [])) {
        env.localConstructs.add(name);
    }
};

const coverageListeners = function coverageListeners(context: Rule.RuleContext, scope: Scope): Rule.RuleListener {
    const localConstructs = new Set<string>();
    const report: Reporter = (name, target) => {
        if (!scope.coversName(name) && isRuleNode(target)) {
            context.report({ data: { module: scope.label, symbol: name }, messageId: "uncovered", node: target });
        }
    };
    const env: CoverageEnv = { context, localConstructs, report, scope };
    const onDefault = (node: Rule.Node): void => {
        defaultExport(node, env);
    };
    const onNamed = (node: Rule.Node): void => {
        exportNamed(asConstructNode(node), env);
    };
    const onProgramNode = (node: Rule.Node): void => {
        onProgram(node, env);
    };
    const handlers: [string, (node: Rule.Node) => void][] = [
        ["ExportDefaultDeclaration", onDefault],
        ["ExportNamedDeclaration", onNamed],
        ["Program", onProgramNode],
    ];
    return Object.fromEntries(handlers);
};

export default {
    create(context: Rule.RuleContext): Rule.RuleListener {
        const scope = resolvedScopeOf(context);
        if (scope === null) {
            return {};
        }
        return coverageListeners(context, scope);
    },
    meta: govlabMeta({
        canonical: ["test-quality"],
        description:
            "Every exported construct — a function/class declaration, a const bound to a function, an object literal with methods, an instance or factory result, a grouped `export { … }` specifier, or a default export — must have a provisioned test. Host source is covered by a testbase test importing it via a #host/* specifier; workspace source is covered by a co-located test inside its own package. The gate output is the live coverage ledger.",
        messages: {
            uncovered:
                "Construct '{{symbol}}' in module '{{module}}' has no provisioned test — add a test that references '{{symbol}}' in the module's test scope.",
            uncoveredDefault: "Default export of '{{specifier}}' has no provisioned test — add a test that imports it.",
        },
        ruleId: "require_test_coverage",
        schema: [],
    }),
} satisfies Rule.RuleModule;