# test.content/core/validators/evidence.validator.test.ts

> 112 lines of code and 0 definitions.

Tree: Codebase Testing
Language: typescript
Canonical: https://banes-lab.com/anatomy/testing#file-testing-test-content-core-validators-evidence-validator-test-ts
Source text: https://banes-lab.com/source/testing/test.content/core/validators/evidence.validator.test.ts.txt

Listed in [test.content/core/validators](https://banes-lab.com/api/source/testing/test.content/core/validators.md), after [test.content/core/validators/coverage.validator.test.ts](https://banes-lab.com/source/testing/test.content/core/validators/coverage.validator.test.ts.md) and before [test.content/core/validators/leak.validator.test.ts](https://banes-lab.com/source/testing/test.content/core/validators/leak.validator.test.ts.md).

## Contained in

- [test.content/core/validators](https://banes-lab.com/anatomy/testing/folder-testing-test-content-core-validators.md)

## Source

```typescript
import type { ContentGraph, DeclaredAbsence, Evidence } from "@banes-lab/content/types/coverage.types.ts";
import { citeFindings, groundingOf } from "@banes-lab/content/core/validators/evidence.validator.ts";
import { describe, expect, it } from "vitest";
import { EVIDENCE } from "@banes-lab/web/configuration/constants/evidence.source.constants.ts";
import { EVIDENCE_ABSENT } from "@banes-lab/content/configuration/constants/evidence.constants.ts";
import { citeUnresolved } from "@banes-lab/content/configuration/strings/coverage.strings.ts";
import { treeLabelOf } from "@banes-lab/web/domain/converters/source.converter.ts";

const GRAPHS: readonly ContentGraph[] = [
    {
        page: "page",
        sections: {
            grounded: { requires: [], teaches: ["a"], traces: [] },
            open: { requires: [], teaches: ["b"], traces: [] },
            practice: { requires: [], teaches: ["c"], traces: [] },
            story: { narrative: true, requires: [], teaches: [], traces: [] },
        },
    },
];

const chapter = function chapter(section: string): Evidence["subject"] & { readonly kind: "chapter" } {
    return { kind: "chapter", page: "page", section, tab: "tab" };
};

const keyOf = function keyOf(node: Evidence["nodes"][number]): string | null {
    if (node.kind === "folder") {
        return null;
    }
    return node.kind === "definition" ? (node.file ?? node.name) : node.name;
};

const EVIDENCE_FIXTURE: readonly Evidence[] = [{ nodes: [], subject: chapter("grounded") }];
const ABSENCE_FIXTURE: readonly DeclaredAbsence[] = [
    { reason: "a-practice-not-a-construct", subject: chapter("practice") },
];
const EVERY_OTHER_ABSENT: readonly DeclaredAbsence[] = [
    ...ABSENCE_FIXTURE,
    { reason: "named-not-built", subject: chapter("open") },
];

describe("groundingOf", () => {
    it("sorts every teaching section into grounded, declared absent or not yet assessed, and skips narrative", () => {
        const report = groundingOf(GRAPHS, EVIDENCE_FIXTURE, ABSENCE_FIXTURE);
        expect(report.grounded).toStrictEqual(["page#grounded"]);
        expect(report.absent).toStrictEqual(["page#practice"]);
        expect(report.unassessed).toStrictEqual(["page#open"]);
    });

    it("fails a teaching section that is neither grounded nor declared absent", () => {
        const report = groundingOf(GRAPHS, EVIDENCE_FIXTURE, ABSENCE_FIXTURE);
        expect(report.findings.map((held) => held.section)).toStrictEqual(["open"]);
    });

    it("fails a section that is both grounded and declared absent", () => {
        const absences: readonly DeclaredAbsence[] = [
            ...EVERY_OTHER_ABSENT,
            { reason: "outside-the-web-member", subject: chapter("grounded") },
        ];
        const report = groundingOf(GRAPHS, EVIDENCE_FIXTURE, absences);
        expect(report.findings.map((held) => held.section)).toStrictEqual(["grounded"]);
    });

    it("fails a declaration that names a section no graph declares", () => {
        const evidence = [...EVIDENCE_FIXTURE, { nodes: [], subject: chapter("ghost") }];
        const report = groundingOf(GRAPHS, evidence, EVERY_OTHER_ABSENT);
        expect(report.findings.map((held) => held.section)).toStrictEqual(["ghost"]);
    });

    it("holds the site's own registry free of contradictions between grounding and absence", () => {
        const grounded = new Set(
            EVIDENCE.flatMap((entry) => (entry.subject.kind === "chapter" ? [entry.subject.section] : [])),
        );
        expect(
            EVIDENCE_ABSENT.filter((entry) => entry.subject.kind === "chapter" && grounded.has(entry.subject.section)),
        ).toStrictEqual([]);
    });
});

describe("citeFindings", () => {
    const located: Readonly<Record<string, string>> = {
        "coordination~tools/a.ts": "coordination~tools/a.ts",
        "site.ts": "core/site.ts",
        "stray.ts": "content~core/stray.ts",
    };
    const locate = (node: Evidence["nodes"][number]): string | null => {
        const key = keyOf(node);
        return key === null ? null : (located[key] ?? null);
    };

    it("accepts a node in the Site tree and a qualified node in another tree", () => {
        const entry: Evidence = {
            nodes: [
                { kind: "file", name: "site.ts" },
                { kind: "file", name: "coordination~tools/a.ts" },
                { file: "coordination~tools/a.ts", kind: "definition", name: "run" },
            ],
            subject: chapter("grounded"),
        };
        expect(citeFindings([entry], locate)).toStrictEqual([]);
    });

    it("reports a node that resolves nowhere and one that leaves the Site tree unqualified", () => {
        const entry: Evidence = {
            nodes: [
                { kind: "file", name: "gone.ts" },
                { kind: "file", name: "stray.ts" },
            ],
            subject: { kind: "record", ref: "arch:x" },
        };
        const messages = citeFindings([entry], locate).map((held) => `${held.page} ${held.message}`);
        expect(messages).toHaveLength(2);
        expect(messages[0]).toContain("arch:x The EVIDENCE entry names gone.ts, and no file");
        expect(messages[1]).toContain(`which exists only in the ${treeLabelOf("content")} tree.`);
        expect(messages[1]).toContain("write the entry as content~core/stray.ts.");
    });
});

describe("citeUnresolved", () => {
    it("names every tree it is given, the last one after or", () => {
        expect(citeUnresolved(["Site tree", "Build tree", "Governance tree"])).toContain(
            "in the Site tree, Build tree or Governance tree has that name",
        );
        expect(citeUnresolved(["Site tree"])).toContain("in the Site tree has that name");
    });
});
```
