Skip to content

Commit 99989d6

Browse files
pandemicsynvlymar
andauthored
If content-type explicitly something other than json don't try to parse body as json (#37)
* If content-type explicitly something other than json don't try to parse body as json * new tests * chore: prepare for 0.1.18 release --------- Co-authored-by: Victor Lymar <[email protected]>
1 parent fe17f22 commit 99989d6

File tree

3 files changed

+144
-1
lines changed

3 files changed

+144
-1
lines changed

knock/client.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,18 @@ func (c *Client) handleResponse(ctx context.Context, res *http.Response, v inter
151151
Message string `json:"message"`
152152
}
153153

154+
// in some scenarios we don't fully control the response, e.g. an ELB 502.
155+
if res.Header.Get("Content-Type") != jsonMediaType {
156+
return nil, &Error{
157+
msg: "malformed non-json error response body received with status code: " + http.StatusText(res.StatusCode),
158+
Code: ErrResponseMalformed,
159+
Meta: map[string]string{
160+
"body": string(out),
161+
"http_status": http.StatusText(res.StatusCode),
162+
},
163+
}
164+
}
165+
154166
errorRes := &errorResponse{}
155167
err = json.Unmarshal(out, errorRes)
156168
if err != nil {

knock/client_test.go

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
package knock
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"net/http"
7+
"net/http/httptest"
8+
"strings"
9+
"testing"
10+
)
11+
12+
func TestNewClient(t *testing.T) {
13+
t.Run("default client", func(t *testing.T) {
14+
client, err := NewClient()
15+
if err != nil {
16+
t.Fatalf("Expected no error, got %v", err)
17+
}
18+
if client == nil {
19+
t.Fatal("Expected non-nil client")
20+
}
21+
if client.baseURL.String() != DefaultBaseUrl {
22+
t.Errorf("Expected base URL %s, got %s", DefaultBaseUrl, client.baseURL.String())
23+
}
24+
})
25+
26+
t.Run("with custom base URL", func(t *testing.T) {
27+
customURL := "https://custom.knock.app/"
28+
client, err := NewClient(WithBaseURL(customURL))
29+
if err != nil {
30+
t.Fatalf("Expected no error, got %v", err)
31+
}
32+
if client == nil {
33+
t.Fatal("Expected non-nil client")
34+
}
35+
if client.baseURL.String() != customURL {
36+
t.Errorf("Expected base URL %s, got %s", customURL, client.baseURL.String())
37+
}
38+
})
39+
}
40+
41+
func TestClientDo(t *testing.T) {
42+
t.Run("successful request", func(t *testing.T) {
43+
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
44+
if !strings.Contains(r.Header.Get("User-Agent"), "Knock/Go v") {
45+
t.Errorf("Expected User-Agent to contain 'Knock/Go v', got %s", r.Header.Get("User-Agent"))
46+
}
47+
48+
w.Header().Set("Content-Type", "application/json")
49+
w.WriteHeader(http.StatusOK)
50+
err := json.NewEncoder(w).Encode(map[string]string{"message": "success"})
51+
if err != nil {
52+
t.Fatalf("Error encoding httptest response: %v", err)
53+
}
54+
}))
55+
defer server.Close()
56+
57+
client, _ := NewClient(WithBaseURL(server.URL))
58+
req, _ := client.newRequest("GET", "/test", nil, nil)
59+
60+
var response map[string]string
61+
_, err := client.do(context.Background(), req, &response)
62+
63+
if err != nil {
64+
t.Fatalf("Expected no error, got %v", err)
65+
}
66+
if response["message"] != "success" {
67+
t.Errorf("Expected message 'success', got %s", response["message"])
68+
}
69+
})
70+
71+
t.Run("error response", func(t *testing.T) {
72+
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
73+
w.Header().Set("Content-Type", "application/json")
74+
w.WriteHeader(http.StatusNotFound)
75+
err := json.NewEncoder(w).Encode(map[string]string{"code": "not_found", "message": "Resource not found"})
76+
if err != nil {
77+
t.Fatalf("Error encoding httptest response: %v", err)
78+
}
79+
}))
80+
defer server.Close()
81+
82+
client, _ := NewClient(WithBaseURL(server.URL))
83+
req, _ := client.newRequest("GET", "/test", nil, nil)
84+
85+
_, err := client.do(context.Background(), req, nil)
86+
87+
if err == nil {
88+
t.Fatal("Expected error, got nil")
89+
}
90+
knockErr, ok := err.(*Error)
91+
if !ok {
92+
t.Fatalf("Expected error of type *Error, got %T", err)
93+
}
94+
if knockErr.Code != ErrNotFound {
95+
t.Errorf("Expected error code %v, got %v", ErrNotFound, knockErr.Code)
96+
}
97+
if knockErr.Error() != "Resource not found" {
98+
t.Errorf("Expected error message 'Resource not found', got %s", knockErr.Error())
99+
}
100+
})
101+
102+
t.Run("unexpected non-json response", func(t *testing.T) {
103+
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
104+
w.WriteHeader(http.StatusBadGateway)
105+
_, err := w.Write([]byte("502 Bad Gateway"))
106+
if err != nil {
107+
t.Fatalf("Error writing httptest response: %v", err)
108+
}
109+
}))
110+
defer server.Close()
111+
112+
client, _ := NewClient(WithBaseURL(server.URL))
113+
req, _ := client.newRequest("GET", "/test", nil, nil)
114+
115+
_, err := client.do(context.Background(), req, nil)
116+
117+
if err == nil {
118+
t.Fatal("Expected error, got nil")
119+
}
120+
knockErr, ok := err.(*Error)
121+
if !ok {
122+
t.Fatalf("Expected error of type *Error, got %T", err)
123+
}
124+
if knockErr.Code != ErrResponseMalformed {
125+
t.Errorf("Expected error code %v, got %v", ErrNotFound, knockErr.Code)
126+
}
127+
if knockErr.Error() != "malformed non-json error response body received with status code: Bad Gateway" {
128+
t.Errorf("Expected error message 'Resource not found', got %s", knockErr.Error())
129+
}
130+
})
131+
}

knock/internal/sdk_version.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@ package internal
22

33
// SDKVersion is used to populate the sdk's user agent. It should be updated before
44
// releasing new versions of the SDK. Eventually we can automate this process.
5-
const SDKVersion = "0.1.17"
5+
const SDKVersion = "0.1.18"

0 commit comments

Comments
 (0)