import { describe, expect, test } from "bun:test"; import type { KubernetesObject } from "@kubernetes/client-node"; import { buildNamespaceGraphs, deriveNodeStatus, renderNamespaceGraphs, } from "../../lib/graph"; type TestObject = KubernetesObject & { spec?: Record; status?: Record; }; const object = ( kind: string, name: string, extra: Partial = {}, ): TestObject => { const { metadata, ...rest } = extra; return { apiVersion: "v1", kind, ...rest, metadata: { name, namespace: "demo", ...metadata }, }; }; describe("ANSI resource graph", () => { test("builds a deterministic owner tree and prefers owner edges", () => { const deployment = object("Deployment", "web", { status: { replicas: 1, readyReplicas: 1 }, }); const replicaSet = object("ReplicaSet", "web-123", { metadata: { ownerReferences: [ { apiVersion: "apps/v1", kind: "Deployment", name: "web", uid: "1" }, ], }, status: { replicas: 1, readyReplicas: 1 }, }); const pod = object("Pod", "web-123-abc", { metadata: { labels: { app: "web" }, ownerReferences: [ { apiVersion: "apps/v1", kind: "ReplicaSet", name: "web-123", uid: "2", }, ], }, status: { phase: "Running", conditions: [{ type: "Ready", status: "True" }], }, }); const service = object("Service", "web", { spec: { selector: { app: "web" } }, }); const [graph] = buildNamespaceGraphs([ pod, service, replicaSet, deployment, ]); expect(graph?.roots.map((node) => node.id)).toEqual([ "Deployment/web", "Service/web", ]); expect(graph?.roots[0]?.children[0]?.id).toBe("ReplicaSet/web-123"); expect(graph?.roots[0]?.children[0]?.children[0]?.id).toBe( "Pod/web-123-abc", ); expect(graph?.roots[1]?.status).toEqual({ label: "1 ep", level: "good" }); }); test("hides stopped Deployment subtrees unless includeIdle is set", () => { const deployment = object("Deployment", "stopped", { status: { replicas: 0, readyReplicas: 0 }, }); const replicaSet = object("ReplicaSet", "stopped-old", { metadata: { ownerReferences: [ { apiVersion: "apps/v1", kind: "Deployment", name: "stopped", uid: "1", }, ], }, status: { replicas: 0, readyReplicas: 0 }, }); const pod = object("Pod", "stopped-old-pod", { metadata: { ownerReferences: [ { apiVersion: "apps/v1", kind: "ReplicaSet", name: "stopped-old", uid: "2", }, ], }, status: { phase: "Failed" }, }); expect( buildNamespaceGraphs([deployment, replicaSet, pod])[0]?.roots, ).toEqual([]); const roots = buildNamespaceGraphs([deployment, replicaSet, pod], { includeIdle: true, })[0]?.roots; expect(roots?.[0]?.id).toBe("Deployment/stopped"); expect(roots?.[0]?.children[0]?.children[0]?.id).toBe( "Pod/stopped-old-pod", ); }); test("derives good, warning, and bad statuses", () => { expect( deriveNodeStatus( object("Pod", "ready", { status: { phase: "Running", conditions: [{ type: "Ready", status: "True" }], }, }), ), ).toEqual({ label: "ready", level: "good" }); expect( deriveNodeStatus( object("Deployment", "progressing", { status: { replicas: 3, readyReplicas: 1 }, }), ), ).toEqual({ label: "1/3", level: "warn" }); expect( deriveNodeStatus( object("Pod", "failed", { status: { phase: "Failed" } }), ), ).toEqual({ label: "failed", level: "bad", }); }); test("renders tree connectors, kind colors, and ANSI status badges", () => { const output = renderNamespaceGraphs([ { namespace: "demo", roots: [ { id: "Deployment/web", status: { label: "1/1", level: "good" }, children: [ { id: "Pod/web-abc", status: { label: "pending", level: "warn" }, children: [], }, ], }, ], }, ]); expect(output).toContain("demo\n└─ \u001b[34mDeployment/web\u001b[0m"); expect(output).toContain("\u001b[42m\u001b[30m 1/1 \u001b[0m"); expect(output).toContain(" └─ \u001b[32mPod/web-abc\u001b[0m"); expect(output).toContain("\u001b[43m\u001b[30m pending \u001b[0m"); }); });