import { describe, test, expect } from "vitest";
import { toMermaid } from "./graph-mermaid";
import type { GraphIR } from "./graph-ir";
const ir: GraphIR = {
nodes: [
{ id: "vpc", kind: "Vpc", lexicon: "gcp", attrs: {} },
{ id: "subnet", kind: "Subnet", lexicon: "gcp", attrs: {} },
{ id: "ns", kind: "Namespace", lexicon: "k8s", attrs: {} },
],
edges: [
{ from: "subnet", to: "vpc", kind: "ref", viaAttr: "network" },
{ from: "ns", to: "subnet", kind: "ref", viaAttr: "subnet", toAttr: "selfLink" },
],
groups: { byLexicon: { gcp: ["subnet", "vpc"], k8s: ["ns"] } },
};
describe("toMermaid", () => {
test("renders a flowchart with lexicon subgraphs, nodes, and labelled edges", () => {
const out = toMermaid(ir);
expect(out).toContain("flowchart TD");
expect(out).toContain('subgraph lex_gcp["gcp"]');
expect(out).toContain('subgraph lex_k8s["k8s"]');
// node label = name + kind on two lines
expect(out).toContain('vpc["vpc
Vpc"]');
// edge with consumer-property label
expect(out).toContain("subnet -->|\"network\"| vpc");
// T3 edge shows consumer → producer attribute
expect(out).toContain('ns -->|"subnet → selfLink"| subnet');
});
test("is deterministic", () => {
expect(toMermaid(ir)).toEqual(toMermaid(ir));
});
test("sanitizes ids that aren't Mermaid-safe but keeps the human label", () => {
const dirty: GraphIR = {
nodes: [
{ id: "east/db-0", kind: "StatefulSet", lexicon: "k8s", attrs: {} },
{ id: "east/db-1", kind: "StatefulSet", lexicon: "k8s", attrs: {} },
],
edges: [{ from: "east/db-1", to: "east/db-0", kind: "ref" }],
groups: {},
};
const out = toMermaid(dirty);
// ids are sanitized and unique
expect(out).toContain('east_db_0["east/db-0
StatefulSet"]');
expect(out).toContain('east_db_1["east/db-1
StatefulSet"]');
expect(out).toContain("east_db_1 --> east_db_0");
});
test("places ungrouped nodes at the top level", () => {
const out = toMermaid({
nodes: [{ id: "loner", kind: "Thing", lexicon: "x", attrs: {} }],
edges: [],
groups: {},
});
expect(out).toContain('loner["loner
Thing"]');
expect(out).not.toContain("subgraph");
});
});