Skip to content

Commit

Permalink
fix: HTTP response error handling #42 (#43)
Browse files Browse the repository at this point in the history
  • Loading branch information
System-Glitch authored Sep 30, 2024
1 parent ec24664 commit e0b192b
Show file tree
Hide file tree
Showing 2 changed files with 122 additions and 6 deletions.
22 changes: 16 additions & 6 deletions resend.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,18 +144,28 @@ func handleError(resp *http.Response) error {
// Handles errors most likely caused by the client
case http.StatusUnprocessableEntity, http.StatusBadRequest:
r := &InvalidRequestError{}
err := json.NewDecoder(resp.Body).Decode(r)
if err != nil {
return err
if strings.HasPrefix(resp.Header.Get("Content-Type"), "application/json") {
err := json.NewDecoder(resp.Body).Decode(r)
if err != nil {
r.Message = resp.Status
}
} else {
r.Message = resp.Status
}
return errors.New("[ERROR]: " + r.Message)
default:
// Tries to parse `message` attr from error
r := &DefaultError{}
err := json.NewDecoder(resp.Body).Decode(r)
if err != nil {
return err

if strings.HasPrefix(resp.Header.Get("Content-Type"), "application/json") {
err := json.NewDecoder(resp.Body).Decode(r)
if err != nil {
r.Message = resp.Status
}
} else {
r.Message = resp.Status
}

if r.Message != "" {
return errors.New("[ERROR]: " + r.Message)
}
Expand Down
106 changes: 106 additions & 0 deletions resend_test.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package resend

import (
"bytes"
"context"
"errors"
"fmt"
"io"
"net/http"
"testing"

Expand Down Expand Up @@ -44,3 +47,106 @@ func TestResendRequestShouldReturnErrorIfContextIsCancelled(t *testing.T) {
assert.True(t, errors.Unwrap(err) == context.Canceled)
assert.Nil(t, res)
}

func TestHandleError(t *testing.T) {
cases := []struct {
desc string
resp *http.Response
want error
}{
{
desc: "validation_error",
resp: &http.Response{
StatusCode: http.StatusUnprocessableEntity,
Status: fmt.Sprintf("%d %s", http.StatusUnprocessableEntity, http.StatusText(http.StatusUnprocessableEntity)),
Header: http.Header{"Content-Type": {"application/json; charset=utf-8"}},
Body: io.NopCloser(bytes.NewBufferString(`{"message":"Validation error"}`)),
},
want: errors.New("[ERROR]: Validation error"),
},
{
desc: "validation_error_no_json",
resp: &http.Response{
StatusCode: http.StatusUnprocessableEntity,
Status: fmt.Sprintf("%d %s", http.StatusUnprocessableEntity, http.StatusText(http.StatusUnprocessableEntity)),
Body: io.NopCloser(bytes.NewBufferString(`Validation error`)),
},
want: errors.New("[ERROR]: 422 Unprocessable Entity"),
},
{
desc: "bad_request",
resp: &http.Response{
StatusCode: http.StatusBadRequest,
Status: fmt.Sprintf("%d %s", http.StatusBadRequest, http.StatusText(http.StatusBadRequest)),
Header: http.Header{"Content-Type": {"application/json; charset=utf-8"}},
Body: io.NopCloser(bytes.NewBufferString(`{"message":"Validation error"}`)),
},
want: errors.New("[ERROR]: Validation error"),
},
{
desc: "bad_request_no_json",
resp: &http.Response{
StatusCode: http.StatusBadRequest,
Status: fmt.Sprintf("%d %s", http.StatusBadRequest, http.StatusText(http.StatusBadRequest)),
Body: io.NopCloser(bytes.NewBufferString(`Validation error`)),
},
want: errors.New("[ERROR]: 400 Bad Request"),
},
{
desc: "bad_request_invalid_json",
resp: &http.Response{
StatusCode: http.StatusBadRequest,
Status: fmt.Sprintf("%d %s", http.StatusBadRequest, http.StatusText(http.StatusBadRequest)),
Header: http.Header{"Content-Type": {"application/json; charset=utf-8"}},
Body: io.NopCloser(bytes.NewBufferString(`{`)),
},
want: errors.New("[ERROR]: 400 Bad Request"),
},
{
desc: "server_error",
resp: &http.Response{
StatusCode: http.StatusInternalServerError,
Status: fmt.Sprintf("%d %s", http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)),
Header: http.Header{"Content-Type": {"application/json; charset=utf-8"}},
Body: io.NopCloser(bytes.NewBufferString(`{"message":"Server error"}`)),
},
want: errors.New("[ERROR]: Server error"),
},
{
desc: "server_error_no_json",
resp: &http.Response{
StatusCode: http.StatusInternalServerError,
Status: fmt.Sprintf("%d %s", http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)),
Body: io.NopCloser(bytes.NewBufferString(`Server error`)),
},
want: errors.New("[ERROR]: 500 Internal Server Error"),
},
{
desc: "server_error_invalid_json",
resp: &http.Response{
StatusCode: http.StatusInternalServerError,
Status: fmt.Sprintf("%d %s", http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)),
Header: http.Header{"Content-Type": {"application/json; charset=utf-8"}},
Body: io.NopCloser(bytes.NewBufferString(`{`)),
},
want: errors.New("[ERROR]: 500 Internal Server Error"),
},
{
desc: "server_error_no_message",
resp: &http.Response{
StatusCode: http.StatusInternalServerError,
Status: fmt.Sprintf("%d %s", http.StatusInternalServerError, http.StatusText(http.StatusInternalServerError)),
Header: http.Header{"Content-Type": {"application/json; charset=utf-8"}},
Body: io.NopCloser(bytes.NewBufferString(`{}`)),
},
want: errors.New("[ERROR]: Unknown Error"),
},
}

for _, c := range cases {
t.Run(c.desc, func(t *testing.T) {
err := handleError(c.resp)
assert.Equal(t, c.want, err)
})
}
}

0 comments on commit e0b192b

Please sign in to comment.