Files
llmgw/internal/web/admin_nodeengine_test.go
Ilker ManapandClaude Opus 5.5 dc483d8c68
Release / build (push) Successful in 1m12s
Release / notes (push) Successful in 3s
CI / test (push) Successful in 3m52s
Choose each engine's build from the node's hardware, for every GPU generation
Each engine has a list of builds, best first, from what its project
publishes and how each was compiled: llama.cpp CUDA 13.3, CUDA 12.8
(driver 570 for the GPUs it ships as PTX), then Vulkan, never the CPU
build on an NVIDIA node; vLLM CUDA 13, then CUDA 12.9, and for GPUs
older than every build of the version asked for, the newest built for
them (V100: 0.19.1). The node takes the first its hardware meets and
that installs and computes on the GPU, moving on when one does not, and
reports the version it runs. GPU processes are read from nvidia-smi's
full report, so Vulkan engines count as on their GPU.

Refs #2.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-01 14:35:58 +02:00

76 lines
2.9 KiB
Go

package web
import (
"context"
"io"
"log/slog"
"net/http"
"net/url"
"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/store"
)
// buildFleet is one GPU node whose vLLM build was chosen, and one CPU node.
type buildFleet struct{ fakeFleet }
func (buildFleet) Nodes() []adminapi.NodeView {
return []adminapi.NodeView{
{ID: "gpu07", State: "ready", GPUTotal: 8, GPUFree: 8,
EngineBuilds: map[string]string{"vllm": "cu129"},
Provision: []inventory.EngineInstall{{Engine: "vllm", Version: "0.30.0", Variant: "cu129", State: "installing"}}},
{ID: "cpu01", State: "ready"},
}
}
type recordedChoice struct{ node, engine, build, by string }
type fakeEngineAdmin struct{ got []recordedChoice }
func (f *fakeEngineAdmin) SetNodeEngine(_ context.Context, node, engine, build, by string) error {
f.got = append(f.got, recordedChoice{node, engine, build, by})
return nil
}
func TestTheVLLMBuildIsChosenOnTheClusterPage(t *testing.T) {
a := fakeAccounts{user: store.User{ID: "u1", Email: "a@example.com", IsAdmin: true}}
p := NewAccountPages(a, nil, nil, "v0", slog.New(slog.NewTextHandler(io.Discard, nil)))
mux := http.NewServeMux()
mux.HandleFunc("GET /admin", p.AdminPage(Dashboard(buildFleet{}, nil, "v1", time.Second, false, NavExtras{}, nil)))
fe := &fakeEngineAdmin{}
p.RegisterNodeEngines(mux, fe)
body := mirrorGet(t, mux, "/admin").Body.String()
if !strings.Contains(body, `action="/admin/nodes/gpu07/engines"`) ||
!strings.Contains(body, `<option value="cu129" selected>CUDA 12.9</option>`) {
t.Fatalf("no choice of build for the GPU node, with its current one selected: %s", body)
}
if strings.Contains(body, `action="/admin/nodes/cpu01/engines"`) {
t.Error("a node with no GPU was offered a vLLM build")
}
if !strings.Contains(body, "0.30.0 CUDA 12.9 installing") {
t.Error("what is installed is not shown beside the choice")
}
// The read-only page on the admin listener has no session, so no form.
_, ro := get(t, Dashboard(buildFleet{}, nil, "v1", time.Second, true, NavExtras{}, nil), "http://127.0.0.1:9090/", nil)
if strings.Contains(ro, "/engines\"") {
t.Error("the admin listener's page offers a change it cannot sign")
}
w := mirrorPost(t, mux, "/admin/nodes/gpu07/engines", url.Values{"csrf": {"tok"}, "engine": {"vllm"}, "build": {"none"}})
if w.Code != http.StatusSeeOther || len(fe.got) != 1 || fe.got[0] != (recordedChoice{"gpu07", "vllm", "none", "a@example.com"}) {
t.Fatalf("status %d, chose %+v", w.Code, fe.got)
}
if loc := w.Header().Get("Location"); !strings.Contains(loc, "notice=") {
t.Errorf("redirected to %s, saying nothing", loc)
}
if w := mirrorPost(t, mux, "/admin/nodes/gpu07/engines", url.Values{"csrf": {"bad"}, "engine": {"vllm"}, "build": {"none"}}); w.Code != http.StatusBadRequest || len(fe.got) != 1 {
t.Errorf("a forged form was accepted: %d", w.Code)
}
}