package service import ( "encoding/json" "net/http" "net/http/httptest" "strings" "testing" "time" "metis/pkg/inventory" ) func TestRemoteHelperBranches(t *testing.T) { if got := prettyDeviceTarget(""); got != "the selected target" { t.Fatalf("prettyDeviceTarget empty = %q", got) } if got := prettyDeviceTarget("hosttmp:///tmp"); got != "/tmp" { t.Fatalf("prettyDeviceTarget hosttmp = %q", got) } if got := ramp(0, 10, 20, 1, 2); got != 1 { t.Fatalf("ramp before start = %v", got) } if got := mountedHostTmpDir("/tmp/metis-flash-test"); got != "/host-tmp/metis-flash-test" { t.Fatalf("mountedHostTmpDir = %q", got) } if got := shellQuote("a'b"); got != `'a'"'"'b'` { t.Fatalf("shellQuote = %q", got) } if got, msg := buildStageHeartbeat("n1", "b1", 5*time.Second); got < 8 || !strings.Contains(msg, "Scheduling") { t.Fatalf("buildStageHeartbeat early = %v %q", got, msg) } if got, msg := flashStageHeartbeat("h1", "artifact", 15*time.Second); got < 88 || !strings.Contains(msg, "Writing") { t.Fatalf("flashStageHeartbeat = %v %q", got, msg) } app := newTestApp(t) app.artifactStore["n1"] = ArtifactSummary{Node: "n1", Ref: "registry.example/metis/n1:latest"} if got := app.remoteArtifactNote("n1"); got != "registry.example/metis/n1:latest" { t.Fatalf("remoteArtifactNote = %q", got) } if got := inventoryNodeArch(&inventory.NodeSpec{}, &inventory.NodeClass{Arch: "amd64"}); got != "amd64" { t.Fatalf("inventoryNodeArch = %q", got) } worker := remoteWorkerEntrypoint(true, "--node", "n1") if !strings.Contains(worker, "metis-runtime-env.sh") || !strings.Contains(worker, "metis-ssh-env.sh") { t.Fatalf("remoteWorkerEntrypoint missing expected sources: %s", worker) } } func TestSelectBuilderHostPrefersWorkerAndArch(t *testing.T) { kube := fakeKubeServer(t) installKubeFactory(t, kube) app := newTestApp(t) node, err := app.selectBuilderHost("arm64", "titan-22") if err != nil { t.Fatalf("selectBuilderHost: %v", err) } if node.Name != "titan-22" { t.Fatalf("expected titan-22 builder, got %s", node.Name) } } func TestSelectBuilderHostErrorBranch(t *testing.T) { kube := fakeKubeServer(t) installKubeFactory(t, kube) app := newTestApp(t) if _, err := app.selectBuilderHost("s390x", "titan-22"); err == nil { t.Fatal("expected selectBuilderHost error") } } func TestSelectBuilderHostAvoidsBusyBuilderWhenPeersAreFree(t *testing.T) { kube := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { switch { case r.Method == http.MethodGet && r.URL.Path == "/api/v1/nodes": _ = json.NewEncoder(w).Encode(map[string]any{ "items": []any{ map[string]any{ "metadata": map[string]any{ "name": "titan-04", "labels": map[string]string{ "kubernetes.io/arch": "arm64", "hardware": "rpi5", "node-role.kubernetes.io/worker": "true", }, }, "spec": map[string]any{"unschedulable": false}, }, map[string]any{ "metadata": map[string]any{ "name": "titan-05", "labels": map[string]string{ "kubernetes.io/arch": "arm64", "hardware": "rpi5", "node-role.kubernetes.io/worker": "true", }, }, "spec": map[string]any{"unschedulable": false}, }, }, }) case r.Method == http.MethodGet && r.URL.Path == "/api/v1/namespaces/maintenance/pods": _ = json.NewEncoder(w).Encode(map[string]any{ "items": []any{ map[string]any{ "metadata": map[string]any{"labels": map[string]string{"app": "metis-remote", "metis-run": "build"}}, "spec": map[string]any{"nodeName": "titan-04"}, "status": map[string]any{"phase": "Running"}, }, }, }) default: http.NotFound(w, r) } })) defer kube.Close() installKubeFactory(t, kube) app := newTestApp(t) app.settings.Namespace = "maintenance" node, err := app.selectBuilderHost("arm64", "") if err != nil { t.Fatalf("selectBuilderHost: %v", err) } if node.Name != "titan-05" { t.Fatalf("expected titan-05 builder, got %s", node.Name) } }