Skip to content

Commit

Permalink
feat: implement errors interfaces & improve tests (#91)
Browse files Browse the repository at this point in the history
  • Loading branch information
abemedia committed May 6, 2023
1 parent 060d18a commit 0a282f0
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 90 deletions.
1 change: 1 addition & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ issues:
- path: (.+)_test.go
linters:
- errcheck
- errorlint
- funlen
- goerr113
- gosec
Expand Down
11 changes: 9 additions & 2 deletions errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"encoding"
"encoding/xml"
"errors"
"net/http"
"strconv"

"github.com/abemedia/go-don/internal/byteconv"
Expand All @@ -33,6 +32,14 @@ func (e *HTTPError) Error() string {
return e.err.Error()
}

func (e *HTTPError) Is(err error) bool {
return errors.Is(e.err, err) || errors.Is(StatusError(e.code), err)
}

func (e *HTTPError) Unwrap() error {
return e.err
}

func (e *HTTPError) StatusCode() int {
if e.code != 0 {
return e.code
Expand All @@ -43,7 +50,7 @@ func (e *HTTPError) StatusCode() int {
return sc.StatusCode()
}

return http.StatusInternalServerError
return fasthttp.StatusInternalServerError
}

func (e *HTTPError) MarshalText() ([]byte, error) {
Expand Down
153 changes: 65 additions & 88 deletions errors_test.go
Original file line number Diff line number Diff line change
@@ -1,123 +1,100 @@
package don_test

import (
"context"
"encoding/xml"
"errors"
"fmt"
"net/http"
"testing"

"github.com/abemedia/go-don"
_ "github.com/abemedia/go-don/encoding/json"
_ "github.com/abemedia/go-don/encoding/text"
_ "github.com/abemedia/go-don/encoding/xml"
_ "github.com/abemedia/go-don/encoding/yaml"
"github.com/abemedia/go-don/internal/byteconv"
"github.com/abemedia/go-don/pkg/httptest"
"github.com/goccy/go-json"
"github.com/google/go-cmp/cmp"
"github.com/valyala/fasthttp"
"gopkg.in/yaml.v3"
)

func TestError(t *testing.T) {
tests := []struct {
err error
mime string
body string
code int
}{
{
err: don.ErrBadRequest,
mime: "text/plain; charset=utf-8",
body: "Bad Request\n",
code: http.StatusBadRequest,
},
{
err: don.Error(errors.New("test"), http.StatusBadRequest),
mime: "text/plain; charset=utf-8",
body: "test\n",
code: http.StatusBadRequest,
},
{
err: errors.New("test"),
mime: "text/plain; charset=utf-8",
body: "test\n",
code: http.StatusInternalServerError,
},
{
err: errors.New("test"),
mime: "application/json; charset=utf-8",
body: `{"message":"test"}` + "\n",
code: http.StatusInternalServerError,
},
{
err: errors.New("test"),
mime: "application/x-yaml; charset=utf-8",
body: "message: test\n",
code: http.StatusInternalServerError,
},
{
err: errors.New("test"),
mime: "application/xml; charset=utf-8",
body: "<message>test</message>",
code: http.StatusInternalServerError,
},
{
err: &testError{},
mime: "text/plain; charset=utf-8",
body: "test\n",
code: http.StatusInternalServerError,
},
{
err: &testError{},
mime: "application/json; charset=utf-8",
body: `{"custom":"test"}` + "\n",
code: http.StatusInternalServerError,
},
{
err: &testError{},
mime: "application/x-yaml; charset=utf-8",
body: "custom: test\n",
code: http.StatusInternalServerError,
},
{
err: &testError{},
mime: "application/xml; charset=utf-8",
body: "<custom>test</custom>",
code: http.StatusInternalServerError,
},
func TestError_Is(t *testing.T) {
if !errors.Is(don.Error(errTest, 0), errTest) {
t.Error("should match wrapped error")
}
if !errors.Is(don.Error(errTest, fasthttp.StatusBadRequest), don.ErrBadRequest) {
t.Error("should match status error")
}
}

for _, tc := range tests {
ctx := httptest.NewRequest("", "/", "", map[string]string{"Accept": tc.mime})
func TestError_Unwrap(t *testing.T) {
if errors.Unwrap(don.Error(errTest, 0)) != errTest {
t.Error("should unwrap wrapped error")
}
}

api := don.New(nil)
api.Get("/", don.H(func(ctx context.Context, req don.Empty) (any, error) { return nil, tc.err }))
api.RequestHandler()(ctx)
func TestError_StatusCode(t *testing.T) {
if don.Error(don.ErrBadRequest, 0).StatusCode() != fasthttp.StatusBadRequest {
t.Error("should respect wrapped error's status code")
}
if don.Error(don.ErrBadRequest, fasthttp.StatusConflict).StatusCode() != fasthttp.StatusConflict {
t.Error("should ignore wrapped error's status code if explicitly set")
}
}

type response struct {
Code int
Body string
Header map[string]string
}
func TestError_MarshalText(t *testing.T) {
b, _ := don.Error(errTest, 0).MarshalText()
if diff := cmp.Diff("test", string(b)); diff != "" {
t.Error(diff)
}
b, _ = don.Error(&testError{}, 0).MarshalText()
if diff := cmp.Diff("custom", string(b)); diff != "" {
t.Error(diff)
}
}

res := response{ctx.Response.StatusCode(), string(ctx.Response.Body()), map[string]string{}}
ctx.Response.Header.VisitAll(func(key, value []byte) { res.Header[string(key)] = string(value) })
func TestError_MarshalJSON(t *testing.T) {
b, _ := json.Marshal(don.Error(errTest, 0))
if diff := cmp.Diff(`{"message":"test"}`, string(b)); diff != "" {
t.Error(diff)
}
b, _ = json.Marshal(don.Error(&testError{}, 0))
if diff := cmp.Diff(`{"custom":"test"}`, string(b)); diff != "" {
t.Error(diff)
}
}

want := response{tc.code, tc.body, map[string]string{"Content-Type": tc.mime}}
if diff := cmp.Diff(want, res); diff != "" {
t.Error(diff)
}
func TestError_MarshalXML(t *testing.T) {
b, _ := xml.Marshal(don.Error(errTest, 0))
if diff := cmp.Diff("<message>test</message>", string(b)); diff != "" {
t.Error(diff)
}
b, _ = xml.Marshal(don.Error(&testError{}, 0))
if diff := cmp.Diff("<custom>test</custom>", string(b)); diff != "" {
t.Error(diff)
}
}

func TestError_MarshalYAML(t *testing.T) {
b, _ := yaml.Marshal(don.Error(errTest, 0))
if diff := cmp.Diff("message: test\n", string(b)); diff != "" {
t.Error(diff)
}
b, _ = yaml.Marshal(don.Error(&testError{}, 0))
if diff := cmp.Diff("custom: test\n", string(b)); diff != "" {
t.Error(diff)
}
}

var errTest = errors.New("test")

type testError struct{}

func (e *testError) Error() string {
return "test"
}

func (e *testError) MarshalText() ([]byte, error) {
return byteconv.Atob(e.Error()), nil
return []byte("custom"), nil
}

func (e *testError) MarshalJSON() ([]byte, error) {
Expand Down

0 comments on commit 0a282f0

Please sign in to comment.