Skip to content

Commit a5912bd

Browse files
authored
feat: Support entire 2xx HTTP status range for webhooks (#15)
* feat: Support entire 2xx HTTP status range for webhook to be considered successful * refactor: use constants for http status check * chore: Group tests * chore: Remove unused variable to make golangcilint happy
1 parent e4af176 commit a5912bd

File tree

2 files changed

+83
-48
lines changed

2 files changed

+83
-48
lines changed

webhook.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ func (wh *Webhook) Send(ctx context.Context, destination, text string) error {
6969
}
7070
defer resp.Body.Close()
7171

72-
if resp.StatusCode != http.StatusOK {
72+
if resp.StatusCode < http.StatusOK || resp.StatusCode >= http.StatusMultipleChoices {
7373
errMsg := fmt.Sprintf("webhook request failed with non-OK status code: %d", resp.StatusCode)
7474
respBody, e := io.ReadAll(resp.Body)
7575
if e != nil {

webhook_test.go

Lines changed: 82 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"bytes"
55
"context"
66
"errors"
7+
"fmt"
78
"io"
89
"net/http"
910
"testing"
@@ -18,70 +19,104 @@ func (c funcWebhookClient) Do(r *http.Request) (*http.Response, error) {
1819
return c(r)
1920
}
2021

21-
var okWebhookClient = funcWebhookClient(func(*http.Request) (*http.Response, error) {
22-
return &http.Response{
23-
StatusCode: http.StatusOK,
24-
Body: io.NopCloser(bytes.NewBufferString("ok")),
25-
}, nil
26-
})
27-
2822
type errReader struct {
2923
}
3024

3125
func (errReader) Read(_ []byte) (n int, err error) {
3226
return 0, errors.New("test error")
3327
}
3428

29+
func assertNoErrorWithStatus(t *testing.T, wh *Webhook, status int) {
30+
t.Run(fmt.Sprintf("HTTP-Status %d", status), func(t *testing.T) {
31+
wh.webhookClient = funcWebhookClient(func(*http.Request) (*http.Response, error) {
32+
return &http.Response{
33+
StatusCode: status,
34+
Body: io.NopCloser(errReader{}),
35+
}, nil
36+
})
37+
err := wh.Send(context.Background(), "http:/example.org/url", "")
38+
assert.NoError(t, err)
39+
})
40+
}
41+
42+
func assertErrorWithStatus(t *testing.T, wh *Webhook, status int) {
43+
t.Run(fmt.Sprintf("HTTP-Status %d", status), func(t *testing.T) {
44+
wh.webhookClient = funcWebhookClient(func(*http.Request) (*http.Response, error) {
45+
return &http.Response{
46+
StatusCode: status,
47+
Body: io.NopCloser(errReader{}),
48+
}, nil
49+
})
50+
err := wh.Send(context.Background(), "http:/example.org/url", "")
51+
assert.Error(t, err)
52+
})
53+
}
54+
3555
func TestWebhook_Send(t *testing.T) {
3656
// empty header to check wrong header handling case
3757
wh := NewWebhook(WebhookParams{Headers: []string{"Content-Type:application/json,text/plain", ""}})
38-
wh.webhookClient = funcWebhookClient(func(r *http.Request) (*http.Response, error) {
39-
assert.Len(t, r.Header, 1)
40-
assert.Equal(t, r.Header.Get("Content-Type"), "application/json,text/plain")
41-
42-
return &http.Response{
43-
StatusCode: http.StatusOK,
44-
Body: io.NopCloser(bytes.NewBufferString("")),
45-
}, nil
46-
})
4758
assert.NotNil(t, wh)
4859

49-
err := wh.Send(context.Background(), "https://example.org/webhook", "some_text")
50-
assert.NoError(t, err)
60+
t.Run("OK with JSON response", func(t *testing.T) {
61+
wh.webhookClient = funcWebhookClient(func(r *http.Request) (*http.Response, error) {
62+
assert.Len(t, r.Header, 1)
63+
assert.Equal(t, r.Header.Get("Content-Type"), "application/json,text/plain")
64+
65+
return &http.Response{
66+
StatusCode: http.StatusOK,
67+
Body: io.NopCloser(bytes.NewBufferString("")),
68+
}, nil
69+
})
70+
err := wh.Send(context.Background(), "https://example.org/webhook", "some_text")
71+
assert.NoError(t, err)
72+
})
5173

52-
wh.webhookClient = okWebhookClient
53-
assert.NoError(t, err)
54-
err = wh.Send(nil, "https://example.org/webhook", "some_text") //nolint
55-
require.Error(t, err)
56-
assert.Contains(t, err.Error(), "unable to create webhook request")
74+
t.Run("No context", func(t *testing.T) {
75+
err := wh.Send(nil, "https://example.org/webhook", "some_text") //nolint
76+
require.Error(t, err)
77+
assert.Contains(t, err.Error(), "unable to create webhook request")
78+
})
5779

58-
wh.webhookClient = funcWebhookClient(func(*http.Request) (*http.Response, error) {
59-
return nil, errors.New("request failed")
80+
t.Run("Failed request", func(t *testing.T) {
81+
wh.webhookClient = funcWebhookClient(func(*http.Request) (*http.Response, error) {
82+
return nil, errors.New("request failed")
83+
})
84+
err := wh.Send(context.Background(), "https://not-existing-url.net", "some_text")
85+
require.Error(t, err)
86+
assert.Contains(t, err.Error(), "webhook request failed")
6087
})
61-
err = wh.Send(context.Background(), "https://not-existing-url.net", "some_text")
62-
require.Error(t, err)
63-
assert.Contains(t, err.Error(), "webhook request failed")
64-
65-
wh.webhookClient = funcWebhookClient(func(*http.Request) (*http.Response, error) {
66-
return &http.Response{
67-
StatusCode: http.StatusNotFound,
68-
Body: io.NopCloser(bytes.NewBufferString("not found")),
69-
}, nil
88+
89+
t.Run("Not found with json response", func(t *testing.T) {
90+
wh.webhookClient = funcWebhookClient(func(*http.Request) (*http.Response, error) {
91+
return &http.Response{
92+
StatusCode: http.StatusNotFound,
93+
Body: io.NopCloser(bytes.NewBufferString("not found")),
94+
}, nil
95+
})
96+
err := wh.Send(context.Background(), "http:/example.org/invalid-url", "some_text")
97+
require.Error(t, err)
98+
assert.Contains(t, err.Error(), "non-OK status code: 404, body: not found")
7099
})
71-
err = wh.Send(context.Background(), "http:/example.org/invalid-url", "some_text")
72-
require.Error(t, err)
73-
assert.Contains(t, err.Error(), "non-OK status code: 404, body: not found")
74-
75-
wh.webhookClient = funcWebhookClient(func(*http.Request) (*http.Response, error) {
76-
return &http.Response{
77-
StatusCode: http.StatusNotFound,
78-
Body: io.NopCloser(errReader{}),
79-
}, nil
100+
101+
t.Run("Not found with no response", func(t *testing.T) {
102+
wh.webhookClient = funcWebhookClient(func(*http.Request) (*http.Response, error) {
103+
return &http.Response{
104+
StatusCode: http.StatusNotFound,
105+
Body: io.NopCloser(errReader{}),
106+
}, nil
107+
})
108+
err := wh.Send(context.Background(), "http:/example.org/invalid-url", "some_text")
109+
require.Error(t, err)
110+
assert.Contains(t, err.Error(), "non-OK status code: 404")
111+
assert.NotContains(t, err.Error(), "body")
80112
})
81-
err = wh.Send(context.Background(), "http:/example.org/invalid-url", "some_text")
82-
require.Error(t, err)
83-
assert.Contains(t, err.Error(), "non-OK status code: 404")
84-
assert.NotContains(t, err.Error(), "body")
113+
114+
assertErrorWithStatus(t, wh, http.StatusOK-1)
115+
assertNoErrorWithStatus(t, wh, http.StatusOK)
116+
assertNoErrorWithStatus(t, wh, http.StatusNoContent)
117+
assertNoErrorWithStatus(t, wh, http.StatusMultipleChoices-1)
118+
assertErrorWithStatus(t, wh, http.StatusMultipleChoices)
119+
assertErrorWithStatus(t, wh, http.StatusMultipleChoices+1)
85120
}
86121

87122
func TestWebhook_String(t *testing.T) {

0 commit comments

Comments
 (0)