From 445bcde2265aab6e986e08c2a3562249da66b897 Mon Sep 17 00:00:00 2001 From: Fede Barcelona Date: Mon, 27 Apr 2026 10:58:05 +0200 Subject: [PATCH] ci: improve sysdig client test coverage --- internal/infra/sysdig/client_test.go | 157 ++++++++++++++++++++++++--- 1 file changed, 140 insertions(+), 17 deletions(-) diff --git a/internal/infra/sysdig/client_test.go b/internal/infra/sysdig/client_test.go index d0c1848..f40124e 100644 --- a/internal/infra/sysdig/client_test.go +++ b/internal/infra/sysdig/client_test.go @@ -12,21 +12,26 @@ import ( "github.com/sysdiglabs/sysdig-mcp-server/internal/infra/sysdig" ) +func permissionsHandler(captureHeaders func(http.Header)) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if captureHeaders != nil { + captureHeaders(r.Header) + } + if r.URL.Path == "/api/users/me/permissions" { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + _, _ = w.Write([]byte(`{"permissions":[]}`)) + return + } + w.WriteHeader(http.StatusNotFound) + }) +} + var _ = Describe("Client TLS", func() { var ts *httptest.Server BeforeEach(func() { - // Start a TLS server with a self-signed certificate - ts = httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - if r.URL.Path == "/api/users/me/permissions" { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - _, _ = w.Write([]byte(`{"permissions":[]}`)) - return - } - w.WriteHeader(http.StatusNotFound) - })) - // Redirect server logs to GinkgoWriter to avoid noise in test output + ts = httptest.NewTLSServer(permissionsHandler(nil)) ts.Config.ErrorLog = log.New(GinkgoWriter, "", 0) }) @@ -35,35 +40,153 @@ var _ = Describe("Client TLS", func() { }) It("should fail request with self-signed cert by default", func() { - // Create client pointing to the TLS server without custom transport client, err := sysdig.NewSysdigClient( sysdig.WithFixedHostAndToken(ts.URL, "dummy-token"), ) Expect(err).NotTo(HaveOccurred()) - // Attempt request _, err = client.GetMyPermissionsWithResponse(context.Background()) Expect(err).To(HaveOccurred()) - // Verification that it failed due to certificate issues Expect(err.Error()).To(Or(ContainSubstring("certificate"), ContainSubstring("unknown authority"))) }) It("should succeed request when using custom HTTP client with InsecureSkipVerify", func() { - // Create custom HTTP client that skips verification transport := http.DefaultTransport.(*http.Transport).Clone() transport.TLSClientConfig = &tls.Config{InsecureSkipVerify: true} httpClient := &http.Client{Transport: transport} - // Create client using the custom HTTP client client, err := sysdig.NewSysdigClient( sysdig.WithFixedHostAndToken(ts.URL, "dummy-token"), sysdig.WithHTTPClient(httpClient), ) Expect(err).NotTo(HaveOccurred()) - // Attempt request resp, err := client.GetMyPermissionsWithResponse(context.Background()) Expect(err).NotTo(HaveOccurred()) Expect(resp.HTTPResponse.StatusCode).To(Equal(http.StatusOK)) }) }) + +var _ = Describe("Context helpers", func() { + It("roundtrips token through context", func() { + ctx := sysdig.WrapContextWithToken(context.Background(), "my-token") + Expect(sysdig.GetTokenFromContext(ctx)).To(Equal("my-token")) + }) + + It("roundtrips host through context", func() { + ctx := sysdig.WrapContextWithHost(context.Background(), "https://example.com") + Expect(sysdig.GetHostFromContext(ctx)).To(Equal("https://example.com")) + }) + + It("panics when token is missing from context", func() { + Expect(func() { + sysdig.GetTokenFromContext(context.Background()) + }).To(Panic()) + }) + + It("panics when host is missing from context", func() { + Expect(func() { + sysdig.GetHostFromContext(context.Background()) + }).To(Panic()) + }) +}) + +var _ = Describe("Client authentication", func() { + var ts *httptest.Server + var lastHeaders http.Header + + BeforeEach(func() { + ts = httptest.NewServer(permissionsHandler(func(h http.Header) { + lastHeaders = h.Clone() + })) + }) + + AfterEach(func() { + ts.Close() + }) + + Describe("WithHostAndTokenFromContext", func() { + It("authenticates using context values", func() { + client, err := sysdig.NewSysdigClient(sysdig.WithHostAndTokenFromContext()) + Expect(err).NotTo(HaveOccurred()) + + ctx := sysdig.WrapContextWithHost(context.Background(), ts.URL) + ctx = sysdig.WrapContextWithToken(ctx, "ctx-token") + + resp, err := client.GetMyPermissionsWithResponse(ctx) + Expect(err).NotTo(HaveOccurred()) + Expect(resp.HTTPResponse.StatusCode).To(Equal(http.StatusOK)) + Expect(lastHeaders.Get("Authorization")).To(Equal("Bearer ctx-token")) + }) + + It("fails when token is missing from context", func() { + client, err := sysdig.NewSysdigClient(sysdig.WithHostAndTokenFromContext()) + Expect(err).NotTo(HaveOccurred()) + + ctx := sysdig.WrapContextWithHost(context.Background(), ts.URL) + + _, err = client.GetMyPermissionsWithResponse(ctx) + Expect(err).To(MatchError(ContainSubstring("authorization token not present"))) + }) + }) + + Describe("WithFallbackAuthentication", func() { + It("uses first auth when it succeeds", func() { + client, err := sysdig.NewSysdigClient( + sysdig.WithFallbackAuthentication( + sysdig.WithFixedHostAndToken(ts.URL, "primary-token"), + sysdig.WithFixedHostAndToken(ts.URL, "fallback-token"), + ), + ) + Expect(err).NotTo(HaveOccurred()) + + resp, err := client.GetMyPermissionsWithResponse(context.Background()) + Expect(err).NotTo(HaveOccurred()) + Expect(resp.HTTPResponse.StatusCode).To(Equal(http.StatusOK)) + Expect(lastHeaders.Get("Authorization")).To(Equal("Bearer primary-token")) + }) + + It("falls back to second auth when first fails", func() { + client, err := sysdig.NewSysdigClient( + sysdig.WithFallbackAuthentication( + sysdig.WithHostAndTokenFromContext(), + sysdig.WithFixedHostAndToken(ts.URL, "fallback-token"), + ), + ) + Expect(err).NotTo(HaveOccurred()) + + resp, err := client.GetMyPermissionsWithResponse(context.Background()) + Expect(err).NotTo(HaveOccurred()) + Expect(resp.HTTPResponse.StatusCode).To(Equal(http.StatusOK)) + Expect(lastHeaders.Get("Authorization")).To(Equal("Bearer fallback-token")) + }) + + It("fails when all auth methods fail", func() { + client, err := sysdig.NewSysdigClient( + sysdig.WithFallbackAuthentication( + sysdig.WithHostAndTokenFromContext(), + sysdig.WithHostAndTokenFromContext(), + ), + ) + Expect(err).NotTo(HaveOccurred()) + + _, err = client.GetMyPermissionsWithResponse(context.Background()) + Expect(err).To(MatchError(ContainSubstring("unable to authenticate"))) + }) + }) + + Describe("WithVersion", func() { + It("sends User-Agent header with version", func() { + client, err := sysdig.NewSysdigClient( + sysdig.WithFixedHostAndToken(ts.URL, "tok"), + sysdig.WithVersion("2.0.0"), + ) + Expect(err).NotTo(HaveOccurred()) + + resp, err := client.GetMyPermissionsWithResponse(context.Background()) + Expect(err).NotTo(HaveOccurred()) + Expect(resp.HTTPResponse.StatusCode).To(Equal(http.StatusOK)) + Expect(lastHeaders.Get("User-Agent")).To(Equal("sysdig-mcp-server/2.0.0")) + }) + }) +})