test.rules/shared/manifests/writing.canon.manifest.test.ts

test.rules/shared/manifests/writing.canon.manifest.test.ts is a file in Codebase Testing. 125 lines of code and 0 definitions.

import {
    AGENT_MARKER,
    COORDINATORS,
    IMPERATIVE_OPENERS,
    IRREGULAR_PARTICIPLES,
    JOIN_CAP,
    NON_IMPERATIVE_OPENERS,
    NOT_PARTICIPLES,
    PARTICIPLE_MIN_LENGTH,
    PARTICIPLE_SUFFIX,
    PASSIVE_ADVERBS,
    PASSIVE_ADVERB_SUFFIX,
    PASSIVE_AUXILIARIES,
    SENTENCE_CAP,
    SENTENCE_TERMINALS,
    SENTENCE_WORD_STOPS,
} from "@ssot/govlab/shared/manifests/sentence.manifest.ts";
import { CANONICAL_TERMS, canonicalFor, synonymsOf } from "@ssot/govlab/shared/manifests/vocabulary.manifest.ts";
import type { CanonRule, FieldMood, TermRecord } from "@ssot/govlab/types/writing.types.ts";
import { LESSON_FIELD_MOODS, PROSE_LAYER } from "@ssot/govlab/shared/manifests/writing.prose.manifest.ts";
import {
    STRINGS_CHANNEL,
    WRITING_CANON,
    isCheckEnforced,
} from "@ssot/govlab/shared/manifests/writing.canon.manifest.ts";
import { describe, expect, it } from "vitest";
import { CARD_RULES } from "@ssot/govlab/shared/manifests/writing.card.manifest.ts";
import { COMPOSITION_RULES } from "@ssot/govlab/shared/manifests/writing.composition.manifest.ts";
import { DOCUMENT_LAYER } from "@ssot/govlab/shared/manifests/writing.document.manifest.ts";
import { IDENTIFIER_LAYER } from "@ssot/govlab/shared/manifests/writing.identifier.manifest.ts";
import { NOTE_LAYER } from "@ssot/govlab/shared/manifests/writing.note.manifest.ts";
import { OUTPUT_LAYER } from "@ssot/govlab/shared/manifests/writing.output.manifest.ts";
import { PUNCTUATION_POLICY } from "@ssot/govlab/shared/manifests/punctuation.manifest.ts";
import { ROOT_LAYER } from "@ssot/govlab/shared/manifests/writing.root.manifest.ts";
import { SENTENCE_RULES } from "@ssot/govlab/shared/manifests/writing.sentence.manifest.ts";
import { SHORT_LAYER } from "@ssot/govlab/shared/manifests/writing.short.manifest.ts";
import { SOURCE_RULES } from "@ssot/govlab/shared/manifests/writing.source.manifest.ts";
import { STRUCTURE_RULES } from "@ssot/govlab/shared/manifests/writing.structure.manifest.ts";
import { WORD_RULES } from "@ssot/govlab/shared/manifests/writing.word.manifest.ts";
import { firstTermIn } from "@ssot/govlab/shared/matchers/vocabulary.matcher.ts";

const CANON_RULES: readonly CanonRule[] = WRITING_CANON.flatMap((layer) => layer.rules);

describe("the writing canon", () => {
    it("records every rule once with a statement, a reason, and examples whose repair differs from the rejection", () => {
        const ids = CANON_RULES.map((rule) => rule.id);
        expect(new Set(ids).size).toBe(ids.length);
        for (const rule of CANON_RULES) {
            expect(rule.rule.length).toBeGreaterThan(0);
            expect(rule.why.length).toBeGreaterThan(0);
            for (const example of rule.examples) {
                expect(example.repaired).not.toBe(example.rejected);
            }
        }
    });

    it("composes one layer per file and folds each facet into the layer whose ids it carries", () => {
        const layers = [
            ROOT_LAYER,
            PROSE_LAYER,
            SHORT_LAYER,
            OUTPUT_LAYER,
            IDENTIFIER_LAYER,
            DOCUMENT_LAYER,
            NOTE_LAYER,
        ];
        expect(WRITING_CANON).toStrictEqual(layers);
        const facets: [typeof ROOT_LAYER, readonly CanonRule[]][] = [
            [ROOT_LAYER, COMPOSITION_RULES],
            [ROOT_LAYER, SENTENCE_RULES],
            [ROOT_LAYER, WORD_RULES],
            [SHORT_LAYER, CARD_RULES],
            [PROSE_LAYER, STRUCTURE_RULES],
            [PROSE_LAYER, SOURCE_RULES],
        ];
        for (const [layer, rules] of facets) {
            expect(rules.length).toBeGreaterThan(0);
            for (const rule of rules) {
                expect(layer.rules).toContain(rule);
                expect(rule.id.startsWith(`${layer.id}.`)).toBe(true);
            }
        }
    });

    it("derives the punctuation policy from the enforced checks rather than a second boolean", () => {
        expect(PUNCTUATION_POLICY.longDash).toBe(isCheckEnforced("no-long-dash", STRINGS_CHANNEL));
        expect(PUNCTUATION_POLICY.semicolon).toBe(isCheckEnforced("no-semicolon", STRINGS_CHANNEL));
        expect(PUNCTUATION_POLICY.digitMetric).toBe(isCheckEnforced("no-unmeasured-numbers", STRINGS_CHANNEL));
    });

    it("declares a mood for every lesson field it governs", () => {
        const moods: FieldMood[] = Object.values(LESSON_FIELD_MOODS);
        expect(moods.length).toBeGreaterThan(0);
        expect(LESSON_FIELD_MOODS["application"]).toBe("imperative");
        expect(LESSON_FIELD_MOODS["decision"]).toBe("declarative");
    });
});

describe("the sentence registry", () => {
    it("holds the caps, the terminals, the stops and the word classes as data", () => {
        expect(SENTENCE_CAP).toBeGreaterThan(JOIN_CAP);
        expect(SENTENCE_TERMINALS.has(".")).toBe(true);
        expect(SENTENCE_WORD_STOPS.has(" ")).toBe(true);
        expect(SENTENCE_WORD_STOPS.has("-")).toBe(false);
        expect(COORDINATORS.has("and")).toBe(true);
        expect(PASSIVE_AUXILIARIES.has("is")).toBe(true);
        expect(PASSIVE_ADVERBS.has("not")).toBe(true);
        expect(PASSIVE_ADVERB_SUFFIX).toBe("ly");
        expect(PARTICIPLE_SUFFIX).toBe("ed");
        expect(PARTICIPLE_MIN_LENGTH).toBeGreaterThan(PARTICIPLE_SUFFIX.length);
        expect(NOT_PARTICIPLES.has("indeed")).toBe(true);
        expect(IRREGULAR_PARTICIPLES.has("written")).toBe(true);
        expect(AGENT_MARKER).toBe("by");
        expect(IMPERATIVE_OPENERS.has("use")).toBe(true);
        expect(NON_IMPERATIVE_OPENERS.has("the")).toBe(true);
    });
});

describe("the term registry and its matcher", () => {
    const terms: TermRecord[] = [{ canonical: "gate", concept: "gate", synonyms: ["checker"] }];

    it("ships empty, flattens synonyms and resolves a synonym to its record", () => {
        expect(CANONICAL_TERMS).toHaveLength(0);
        expect(synonymsOf(terms)).toStrictEqual(["checker"]);
        expect(canonicalFor(terms, "checker")?.canonical).toBe("gate");
        expect(canonicalFor(terms, "gate")).toBeNull();
    });

    it("matches a term at word boundaries only", () => {
        expect(firstTermIn("The checker reports.", ["checker"])).toBe("checker");
        expect(firstTermIn("The checkerboard.", ["checker"])).toBeNull();
        expect(firstTermIn("A Checker, again", ["checker"])).toBe("checker");
    });
});