package web
import (
"context"
"strings"
"testing"
"time"
"gitea.mynodes.xyz/ilker/llmgw/internal/control/adminapi"
"gitea.mynodes.xyz/ilker/llmgw/internal/control/inventory"
"gitea.mynodes.xyz/ilker/llmgw/internal/promq"
)
// mapFleet is a node of four cards: chat holds two and is idle, code holds
// one and is busy, one is free.
type mapFleet struct{ fakeSource }
func (mapFleet) Nodes() []adminapi.NodeView {
busy := func(i int, util, temp float64) inventory.GPUSensor {
return inventory.GPUSensor{Index: i, UtilPercent: util, TempC: temp, MemoryUsedMB: 40000, MemoryTotalMB: 81559, PowerW: 300}
}
return []adminapi.NodeView{{ID: "gpu07", State: "ready", GPUTotal: 4, GPUFree: 1, Classes: []string{"h100-80g"},
Load: &adminapi.LoadView{GPUs: []inventory.GPUSensor{busy(0, 1, 35), busy(1, 0, 34), busy(2, 93, 81), worn(busy(3, 0, 30))}}}}
}
func worn(s inventory.GPUSensor) inventory.GPUSensor {
s.Wear = &inventory.GPUWear{RemapPending: true, XIDs: 1, LastXID: 79, LastXIDText: "GPU has fallen off the bus."}
return s
}
func (mapFleet) Replicas(string) []adminapi.ReplicaView {
return []adminapi.ReplicaView{
{ID: "chat-1", Model: "chat", State: "ready", IdleSec: 1800, Shards: []adminapi.ShardView{{Node: "gpu07", GPUs: []int{0, 1}}}},
{ID: "code-1", Model: "code", State: "ready", IdleSec: 2, Shards: []adminapi.ShardView{{Node: "gpu07", GPUs: []int{2}}}},
}
}
func TestTheClusterPageMapsEveryCardAndNamesTheIdleOnes(t *testing.T) {
_, body := get(t, Dashboard(mapFleet{}, nil, "v1", time.Second, true, NavExtras{}, nil), "http://127.0.0.1:9090/", nil)
if n := strings.Count(body, `class="gpu-cell"`); n != 4 {
t.Errorf("%d cards on the map, want 4", n)
}
for _, want := range []string{
"gpu07 GPU 2 · code (code-1) · 93% busy",
"gpu07 GPU 3 · free",
"memory rows remapped, waiting for a reset · XID 79 (GPU has fallen off the bus.)",
"border:2px solid #c0392b",
"border-style:dashed",
"Cards held and doing nothing",
"chat-1
gpu07", "30m0s ago",
} {
if !strings.Contains(body, want) {
t.Errorf("the page lacks %q", want)
}
}
if strings.Contains(body, "code-1gpu07") {
t.Error("a busy replica was listed as idle")
}
_, temp := get(t, Dashboard(mapFleet{}, nil, "v1", time.Second, true, NavExtras{}, nil), "http://127.0.0.1:9090/?gpumap=temp", nil)
if !strings.Contains(temp, "temperature") || !strings.Contains(temp, ">81") {
t.Error("the map coloured by temperature does not say so, or show it")
}
_, cpu := get(t, Dashboard(cpuFleet{}, nil, "v1", time.Second, true, NavExtras{}, nil), "http://127.0.0.1:9090/", nil)
if strings.Contains(cpu, "gpu-map") {
t.Error("a fleet without GPUs has a GPU map")
}
}
func TestEachModelSaysWhatItGetsFromItsCards(t *testing.T) {
h := &fakeHistory{byExpr: map[string][]promq.Series{}}
for i, vals := range [][2]float64{{120, 30}, {2, 2}, {1200, 400}, {80, 40}} {
h.byExpr[efficiencyQueries[i].expr] = []promq.Series{ts("chat", vals[0], vals[0]), ts("code", vals[1], vals[1])}
}
got := loadEfficiency(context.Background(), h)
if len(got) != 2 || got[0].Model != "chat" {
t.Fatalf("%+v", got)
}
c := got[0]
if c.PerGPU != 60 || c.PerWh != 120*3600/1200.0 || c.Util != 80 {
t.Errorf("chat: %+v", c)
}
if got[1].PerGPU != 15 {
t.Errorf("code: %+v", got[1])
}
}