From db2ee619be451512f6e9ed0a3426e3c4cbae141c Mon Sep 17 00:00:00 2001 From: codex Date: Tue, 21 Apr 2026 05:13:02 -0300 Subject: [PATCH] test(metis): cover harbor error and prune paths --- pkg/service/coverage_more_test.go | 200 ++++++++++++++++++++++++++++++ 1 file changed, 200 insertions(+) diff --git a/pkg/service/coverage_more_test.go b/pkg/service/coverage_more_test.go index 4fcae4f..92a8120 100644 --- a/pkg/service/coverage_more_test.go +++ b/pkg/service/coverage_more_test.go @@ -165,6 +165,206 @@ func TestServiceHarborErrorBranches(t *testing.T) { } } +func TestServiceHarborAdditionalErrorBranches(t *testing.T) { + base := Settings{ + HarborUsername: "admin", + HarborPassword: "pw", + HarborProject: "metis", + HarborRegistry: "registry.example", + } + + app := &App{settings: base} + app.settings.HarborAPIBase = "://bad-url" + if err := app.ensureHarborProject(); err == nil { + t.Fatal("expected invalid Harbor lookup URL to fail") + } + if err := app.pruneHarborArtifacts("node", 1); err == nil { + t.Fatal("expected invalid Harbor prune URL to fail") + } + + app.settings.HarborAPIBase = "http://127.0.0.1:1/api/v2.0" + if err := app.ensureHarborProject(); err == nil { + t.Fatal("expected Harbor lookup connection failure") + } + if err := app.pruneHarborArtifacts("node", 1); err == nil { + t.Fatal("expected Harbor artifact connection failure") + } + + badProjectJSON := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + _, _ = w.Write([]byte(`{bad-json`)) + })) + defer badProjectJSON.Close() + app.settings.HarborAPIBase = badProjectJSON.URL + if err := app.ensureHarborProject(); err == nil { + t.Fatal("expected malformed Harbor project response to fail") + } + + createFailed := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + switch { + case r.Method == http.MethodGet && r.URL.Path == "/projects": + _, _ = w.Write([]byte(`[]`)) + case r.Method == http.MethodPost && r.URL.Path == "/projects": + http.Error(w, "create failed", http.StatusInternalServerError) + default: + http.NotFound(w, r) + } + })) + defer createFailed.Close() + app.settings.HarborAPIBase = createFailed.URL + if err := app.ensureHarborProject(); err == nil { + t.Fatal("expected Harbor project create failure") + } + + createConflict := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + switch { + case r.Method == http.MethodGet && r.URL.Path == "/projects": + _, _ = w.Write([]byte(`[]`)) + case r.Method == http.MethodPost && r.URL.Path == "/projects": + w.WriteHeader(http.StatusConflict) + default: + http.NotFound(w, r) + } + })) + defer createConflict.Close() + app.settings.HarborAPIBase = createConflict.URL + if err := app.ensureHarborProject(); err != nil { + t.Fatalf("expected Harbor project conflict to be accepted: %v", err) + } + + postDropped := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + switch { + case r.Method == http.MethodGet && r.URL.Path == "/projects": + _, _ = w.Write([]byte(`[]`)) + case r.Method == http.MethodPost && r.URL.Path == "/projects": + conn, _, err := w.(http.Hijacker).Hijack() + if err != nil { + t.Fatalf("hijack Harbor POST: %v", err) + } + _ = conn.Close() + default: + http.NotFound(w, r) + } + })) + defer postDropped.Close() + app.settings.HarborAPIBase = postDropped.URL + if err := app.ensureHarborProject(); err == nil { + t.Fatal("expected dropped Harbor project create connection") + } +} + +func TestServiceHarborPruneAdditionalBranches(t *testing.T) { + base := Settings{ + HarborAPIBase: "http://unused", + HarborUsername: "admin", + HarborPassword: "pw", + HarborProject: "metis", + HarborRegistry: "registry.example", + } + + for name, status := range map[string]int{ + "missing": http.StatusNotFound, + "broken": http.StatusServiceUnavailable, + } { + t.Run(name, func(t *testing.T) { + harbor := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method == http.MethodGet && strings.Contains(r.URL.Path, "/artifacts") { + http.Error(w, name, status) + return + } + http.NotFound(w, r) + })) + defer harbor.Close() + app := &App{settings: base} + app.settings.HarborAPIBase = harbor.URL + err := app.pruneHarborArtifacts("node", 1) + if status == http.StatusNotFound && err != nil { + t.Fatalf("expected missing repository to be ignored: %v", err) + } + if status != http.StatusNotFound && err == nil { + t.Fatal("expected artifact list failure") + } + }) + } + + badArtifacts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if r.Method == http.MethodGet && strings.Contains(r.URL.Path, "/artifacts") { + _, _ = w.Write([]byte(`{bad-json`)) + return + } + http.NotFound(w, r) + })) + defer badArtifacts.Close() + app := &App{settings: base} + app.settings.HarborAPIBase = badArtifacts.URL + if err := app.pruneHarborArtifacts("node", 1); err == nil { + t.Fatal("expected malformed artifact list to fail") + } + + deletes := 0 + pruneOK := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + switch { + case r.Method == http.MethodGet && strings.Contains(r.URL.Path, "/artifacts"): + _ = json.NewEncoder(w).Encode([]map[string]any{ + {"digest": "sha256:oldest", "push_time": "2026-04-01T08:00:00Z"}, + {"digest": "sha256:newest", "push_time": "2026-04-01T10:00:00Z"}, + }) + case r.Method == http.MethodDelete && strings.Contains(r.URL.Path, "/artifacts/"): + deletes++ + w.WriteHeader(http.StatusOK) + default: + http.NotFound(w, r) + } + })) + defer pruneOK.Close() + app.settings.HarborAPIBase = pruneOK.URL + if err := app.pruneHarborArtifacts("node", 1); err != nil { + t.Fatalf("expected old artifact prune to succeed: %v", err) + } + if deletes != 1 { + t.Fatalf("expected one old artifact delete, got %d", deletes) + } + + pruneDeleteFailed := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + switch { + case r.Method == http.MethodGet && strings.Contains(r.URL.Path, "/artifacts"): + _ = json.NewEncoder(w).Encode([]map[string]any{ + {"digest": "sha256:stale", "push_time": "2026-04-01T08:00:00Z"}, + }) + case r.Method == http.MethodDelete && strings.Contains(r.URL.Path, "/artifacts/"): + http.Error(w, "delete failed", http.StatusInternalServerError) + default: + http.NotFound(w, r) + } + })) + defer pruneDeleteFailed.Close() + app.settings.HarborAPIBase = pruneDeleteFailed.URL + if err := app.pruneHarborArtifacts("node", 0); err == nil { + t.Fatal("expected artifact delete failure") + } + + deleteDropped := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + switch { + case r.Method == http.MethodGet && strings.Contains(r.URL.Path, "/artifacts"): + _ = json.NewEncoder(w).Encode([]map[string]any{ + {"digest": "sha256:stale", "push_time": "2026-04-01T08:00:00Z"}, + }) + case r.Method == http.MethodDelete && strings.Contains(r.URL.Path, "/artifacts/"): + conn, _, err := w.(http.Hijacker).Hijack() + if err != nil { + t.Fatalf("hijack Harbor DELETE: %v", err) + } + _ = conn.Close() + default: + http.NotFound(w, r) + } + })) + defer deleteDropped.Close() + app.settings.HarborAPIBase = deleteDropped.URL + if err := app.pruneHarborArtifacts("node", 0); err == nil { + t.Fatal("expected dropped Harbor artifact delete connection") + } +} + func TestServiceClusterAndRemotePodBranches(t *testing.T) { origTokenPath := kubeServiceAccountTokenPath origCAPath := kubeServiceAccountCAPath