package atlas_test import ( "reflect" "strings" "testing" "git.d-ma.be/mathias/cad-atlas/internal/atlas" ) func TestHostsFromNodes_DerivesSpecFromNodeStatus(t *testing.T) { nodes := []byte(`{"items":[ {"metadata":{"name":"koala"}, "status":{"capacity":{"cpu":"16","memory":"67108864Ki","nvidia.com/gpu":"1"}, "nodeInfo":{"architecture":"amd64","kubeletVersion":"v1.31.4+k3s1"}}}, {"metadata":{"name":"worker2"}, "status":{"capacity":{"cpu":"8","memory":"33554432Ki"}, "nodeInfo":{"architecture":"arm64","kubeletVersion":"v1.30.0+k3s1"}}} ]}`) hosts, err := atlas.HostsFromNodes(nodes) if err != nil { t.Fatalf("HostsFromNodes: %v", err) } want := []atlas.Host{ {Name: "koala", Spec: "amd64 · 16 cpu · 64Gi · 1× GPU · k3s v1.31.4"}, {Name: "worker2", Spec: "arm64 · 8 cpu · 32Gi · k3s v1.30.0"}, } if !reflect.DeepEqual(hosts, want) { t.Fatalf("hosts = %+v\nwant %+v", hosts, want) } } func TestMergeSubstrate_LiveOverridesAuthoredKeepsRestAppendsNew(t *testing.T) { authored := []atlas.Host{ {Name: "koala", Spec: "RTX 5070 · authored"}, {Name: "iguana", Spec: "M2 Ultra · Ollama / mlx"}, {Name: "piguard", Spec: "NGINX reverse-proxy · ntfy"}, } live := []atlas.Host{ {Name: "koala", Spec: "amd64 · 16 cpu · 60Gi · 1× GPU · k3s v1.34.5"}, {Name: "worker9", Spec: "arm64 · 4 cpu"}, } got := atlas.MergeSubstrate(authored, live) want := []atlas.Host{ {Name: "koala", Spec: "amd64 · 16 cpu · 60Gi · 1× GPU · k3s v1.34.5"}, // live wins {Name: "iguana", Spec: "M2 Ultra · Ollama / mlx"}, // authored kept {Name: "piguard", Spec: "NGINX reverse-proxy · ntfy"}, // authored kept {Name: "worker9", Spec: "arm64 · 4 cpu"}, // live-only appended } if !reflect.DeepEqual(got, want) { t.Fatalf("merge = %+v\nwant %+v", got, want) } } func TestMergeSubstrate_NoLiveReturnsAuthored(t *testing.T) { authored := []atlas.Host{{Name: "koala", Spec: "authored"}} got := atlas.MergeSubstrate(authored, nil) if !reflect.DeepEqual(got, authored) { t.Fatalf("merge with no live = %+v, want %+v", got, authored) } } func TestNamespaceSummary_FiltersSystemSortsAndJoins(t *testing.T) { ns := []byte(`{"items":[ {"metadata":{"name":"gitea"}}, {"metadata":{"name":"kube-system"}}, {"metadata":{"name":"ai-stack"}}, {"metadata":{"name":"default"}}, {"metadata":{"name":"flux-system"}}, {"metadata":{"name":"brain"}} ]}`) got, err := atlas.NamespaceSummary(ns) if err != nil { t.Fatalf("NamespaceSummary: %v", err) } if want := "ns: ai-stack · brain · gitea"; got != want { t.Fatalf("summary = %q, want %q", got, want) } } func TestNamespaceSummary_CapsWithMore(t *testing.T) { var items []string for i := 0; i < 15; i++ { items = append(items, `{"metadata":{"name":"app`+string(rune('a'+i))+`"}}`) } ns := []byte(`{"items":[` + strings.Join(items, ",") + `]}`) got, err := atlas.NamespaceSummary(ns) if err != nil { t.Fatalf("NamespaceSummary: %v", err) } if !strings.HasSuffix(got, "· +3 more") { t.Fatalf("expected cap suffix, got %q", got) } } func TestDeployState_ParsesImageAndReplicas(t *testing.T) { dep := []byte(`{"spec":{"replicas":2,"template":{"spec":{"containers":[ {"name":"cad-atlas","image":"localhost:5000/cad-atlas:3ff922a"}]}}}, "status":{"readyReplicas":1,"replicas":2}}`) d, err := atlas.DeployState(dep) if err != nil { t.Fatalf("DeployState: %v", err) } if d.Image != "localhost:5000/cad-atlas:3ff922a" || d.Ready != 1 || d.Desired != 2 { t.Fatalf("deploy = %+v", d) } if d.Tag() != "3ff922a" { t.Fatalf("tag = %q, want 3ff922a", d.Tag()) } } func TestDeployNode_GreenOnlyWhenFullyReady(t *testing.T) { ready := atlas.DeployNode(atlas.Deploy{Image: "localhost:5000/cad-atlas:abc", Ready: 1, Desired: 1}) if ready.Pill != "var(--green)" { t.Fatalf("ready pill = %q, want green", ready.Pill) } if ready.Title != "◆ deployed · cad-atlas:abc · 1/1 ready" { t.Fatalf("title = %q", ready.Title) } rolling := atlas.DeployNode(atlas.Deploy{Image: "x/cad-atlas:def", Ready: 0, Desired: 1}) if rolling.Pill != "var(--amber)" { t.Fatalf("rolling pill = %q, want amber", rolling.Pill) } } func TestHostsFromNodes_ErrorsOnBadJSON(t *testing.T) { if _, err := atlas.HostsFromNodes([]byte("{not json")); err == nil { t.Fatal("expected error on bad JSON, got nil") } }