From cfd0bae515b48b28e5b565dcca526c17c6982cd7 Mon Sep 17 00:00:00 2001 From: Ben Vincent Date: Fri, 3 Jul 2026 13:38:49 +1000 Subject: [PATCH] test: server Run, upstream 500/401 branches, virtual dead members, local DB accessor --- internal/api/v2/errorpaths_test.go | 7 +++++++ internal/proxy/engine_test.go | 24 ++++++++++++++++++++++++ internal/server/server_test.go | 27 +++++++++++++++++++++++++++ 3 files changed, 58 insertions(+) diff --git a/internal/api/v2/errorpaths_test.go b/internal/api/v2/errorpaths_test.go index 56bd343..abedb70 100644 --- a/internal/api/v2/errorpaths_test.go +++ b/internal/api/v2/errorpaths_test.go @@ -121,3 +121,10 @@ func TestLocalErrorPaths(t *testing.T) { t.Errorf("remove = %d, want 500", c) } } + +func TestLocalHandlerDBAccessor(t *testing.T) { + db := closedDB(t) + if NewLocalHandler(db, nil).DB() != db { + t.Error("DB() should return the handler's database") + } +} diff --git a/internal/proxy/engine_test.go b/internal/proxy/engine_test.go index 34f541c..6e42fb7 100644 --- a/internal/proxy/engine_test.go +++ b/internal/proxy/engine_test.go @@ -108,6 +108,11 @@ func mockUpstream(w http.ResponseWriter, r *http.Request) { w.Write([]byte("protected payload 2")) case "/token": w.Write([]byte(`{"token":"minted-token","expires_in":300}`)) + case "/err500": + w.WriteHeader(http.StatusInternalServerError) + case "/noauth": // 401 with an unusable challenge (no realm) + w.Header().Set("Www-Authenticate", `Bearer service="reg"`) + w.WriteHeader(http.StatusUnauthorized) default: http.NotFound(w, r) } @@ -324,6 +329,25 @@ func TestFetchUpstreamError(t *testing.T) { } } +func TestFetchUpstreamStatusErrors(t *testing.T) { + requireStack(t) + ctx := context.Background() + p := prov(t, models.PackageGeneric) + + r := seed(t, genericRemote("eng-500")) + _, err := testEngine.Fetch(ctx, r, "err500", p) + var pe *ProxyError + if err == nil || !asProxyError(err, &pe) || pe.Status != http.StatusInternalServerError { + t.Errorf("expected 500 ProxyError, got %v", err) + } + + r = seed(t, genericRemote("eng-noauth")) + _, err = testEngine.Fetch(ctx, r, "noauth", p) + if err == nil || !asProxyError(err, &pe) || pe.Status != http.StatusUnauthorized { + t.Errorf("expected 401 ProxyError, got %v", err) + } +} + func TestBearerTokenParsing(t *testing.T) { // Non-Bearer challenges and missing realms are rejected. if _, _, err := fetchBearerToken(context.Background(), "Basic realm=x", models.Remote{}); err == nil { diff --git a/internal/server/server_test.go b/internal/server/server_test.go index 09ae339..141539e 100644 --- a/internal/server/server_test.go +++ b/internal/server/server_test.go @@ -484,6 +484,33 @@ func TestRunOnListener(t *testing.T) { } } +func TestRun(t *testing.T) { + requireStack(t) + ctx, cancel := context.WithCancel(context.Background()) + errc := make(chan error, 1) + go func() { errc <- testSrv.Run(ctx) }() + time.Sleep(300 * time.Millisecond) // let it bind and start serving + cancel() + select { + case err := <-errc: + if err != nil { + t.Errorf("Run returned error: %v", err) + } + case <-time.After(12 * time.Second): + t.Fatal("Run did not shut down") + } +} + +func TestServerVirtualUnreachableMembers(t *testing.T) { + requireStack(t) + // A virtual whose only member does not exist -> no members reachable. + req(t, "POST", "/api/v2/virtuals", `{"name":"srv-vbad","package_type":"helm","members":["nonexistent-member"]}`) + defer req(t, "DELETE", "/api/v2/virtuals/srv-vbad", "") + if resp, _ := req(t, "GET", "/api/v1/virtual/srv-vbad/index.yaml", ""); resp.StatusCode != 502 { + t.Errorf("virtual with dead members = %d, want 502", resp.StatusCode) + } +} + func TestServerVirtualLocalPyPIMerge(t *testing.T) { requireStack(t) for _, n := range []string{"a", "b"} {