# test.govlab/docs/core/analyzers/graph.analyzer.test.ts

> 65 lines of code and 0 definitions.

Tree: Codebase Testing
Language: typescript
Canonical: https://banes-lab.com/anatomy/testing#file-testing-test-govlab-docs-core-analyzers-graph-analyzer-test-ts
Source text: https://banes-lab.com/source/testing/test.govlab/docs/core/analyzers/graph.analyzer.test.ts.txt

Listed in [test.govlab/docs/core/analyzers](https://banes-lab.com/api/source/testing/test.govlab/docs/core/analyzers.md), after [test.govlab/docs/core/analyzers/drift.analyzer.test.ts](https://banes-lab.com/source/testing/test.govlab/docs/core/analyzers/drift.analyzer.test.ts.md) and before [test.govlab/docs/core/analyzers/invocation.analyzer.test.ts](https://banes-lab.com/source/testing/test.govlab/docs/core/analyzers/invocation.analyzer.test.ts.md).

## Contained in

- [test.govlab/docs/core/analyzers](https://banes-lab.com/anatomy/testing/folder-testing-test-govlab-docs-core-analyzers.md)

## Source

```typescript
import { describe, expect, it } from "vitest";
import { DEFAULT_ROOT_PREFIX } from "@govlab/docs/configuration/constants/document.constants.ts";
import type { DocNode } from "@govlab/docs/types/document.types.ts";
import { buildDocGraph } from "@govlab/docs/core/analyzers/graph.analyzer.ts";

const node = function node(name: string, over: Partial<DocNode> = {}): DocNode {
    return {
        concern: "ai",
        dependsOn: [],
        governs: [],
        links: [],
        name,
        relPath: `${DEFAULT_ROOT_PREFIX}notes/ai/${name}.md`,
        summary: "",
        supersedes: [],
        type: "note",
        ...over,
    };
};

describe("buildDocGraph", () => {
    it("resolves edges through the name registry", () => {
        const graph = buildDocGraph([node("a", { dependsOn: ["b"], links: ["c"] }), node("b"), node("c")]);
        expect(graph.deadEdges).toStrictEqual([]);
        expect(Object.keys(graph.byName).toSorted((left, right) => left.localeCompare(right))).toStrictEqual([
            "a",
            "b",
            "c",
        ]);
    });

    it("reports an edge to an unknown name as a dead edge with its field", () => {
        const graph = buildDocGraph([node("a", { dependsOn: ["ghost"], supersedes: ["missing"] }), node("b")]);
        const fields = graph.deadEdges
            .map((edge) => `${edge.field}:${edge.target}`)
            .toSorted((left, right) => left.localeCompare(right));
        expect(fields).toStrictEqual(["depends-on:ghost", "supersedes:missing"]);
    });

    it("marks a superseded target and leaves a governs edge unresolved by name", () => {
        const graph = buildDocGraph([node("new", { governs: ["src/thing.ts"], supersedes: ["old"] }), node("old")]);
        expect(graph.superseded).toStrictEqual(["old"]);
        expect(graph.deadEdges).toStrictEqual([]);
    });

    it("detects a depends-on cycle and passes an acyclic graph", () => {
        const cyclic = buildDocGraph([
            node("a", { dependsOn: ["b"] }),
            node("b", { dependsOn: ["c"] }),
            node("c", { dependsOn: ["a"] }),
        ]);
        expect(cyclic.cycles).toHaveLength(1);
        expect([...new Set(cyclic.cycles[0])].toSorted((left, right) => left.localeCompare(right))).toStrictEqual([
            "a",
            "b",
            "c",
        ]);
        const acyclic = buildDocGraph([
            node("a", { dependsOn: ["b", "c"] }),
            node("b", { dependsOn: ["c"] }),
            node("c"),
        ]);
        expect(acyclic.cycles).toStrictEqual([]);
    });

    it("reports a duplicate name and keeps the first", () => {
        const graph = buildDocGraph([node("dup", { summary: "first" }), node("dup", { summary: "second" })]);
        expect(graph.duplicateNames).toStrictEqual(["dup"]);
        expect(graph.byName["dup"]?.summary).toBe("first");
    });
});
```
