From 29ffa5d8b523229acf3c103947afd13510312e19 Mon Sep 17 00:00:00 2001 From: zanarelli Date: Sun, 2 Aug 2026 07:40:06 -0300 Subject: [PATCH] Document and test 2xx webhook response acceptance Clarify that the default HTTP response validator accepts any 2xx status, including 204 No Content, and add regression coverage so providers returning 204 cannot regress to a hard failure. Signed-off-by: zanarelli --- internal/notifier/client.go | 2 +- internal/notifier/client_test.go | 33 ++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/internal/notifier/client.go b/internal/notifier/client.go index a66bae7e7..ac1867cc0 100644 --- a/internal/notifier/client.go +++ b/internal/notifier/client.go @@ -43,7 +43,7 @@ type postOption func(*postOptions) func postMessage(ctx context.Context, address string, payload any, opts ...postOption) error { options := &postOptions{ - // Default validateResponse function verifies that the response status code is 200, 202 or 201. + // Default validateResponse accepts any 2xx status (including 204 No Content). responseValidator: func(resp *http.Response) error { s := resp.StatusCode if 200 <= s && s < 300 { diff --git a/internal/notifier/client_test.go b/internal/notifier/client_test.go index 864533e55..7e9cf5557 100644 --- a/internal/notifier/client_test.go +++ b/internal/notifier/client_test.go @@ -54,6 +54,39 @@ func Test_postMessage(t *testing.T) { g.Expect(err).ToNot(HaveOccurred()) } +func Test_postMessage_accepts2xxStatusCodes(t *testing.T) { + for _, code := range []int{ + http.StatusOK, + http.StatusCreated, + http.StatusAccepted, + http.StatusNoContent, + } { + t.Run(http.StatusText(code), func(t *testing.T) { + g := NewWithT(t) + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(code) + })) + defer ts.Close() + + err := postMessage(context.Background(), ts.URL, map[string]string{"status": "success"}) + g.Expect(err).ToNot(HaveOccurred()) + }) + } + + t.Run(http.StatusText(http.StatusBadRequest), func(t *testing.T) { + g := NewWithT(t) + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + w.WriteHeader(http.StatusBadRequest) + _, _ = w.Write([]byte("nope")) + })) + defer ts.Close() + + err := postMessage(context.Background(), ts.URL, map[string]string{"status": "success"}) + g.Expect(err).To(HaveOccurred()) + g.Expect(err.Error()).To(ContainSubstring("request failed with status code 400")) + }) +} + func Test_postMessage_timeout(t *testing.T) { g := NewWithT(t) ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {