diff --git a/context.go b/context.go index 3885301..4bf8144 100644 --- a/context.go +++ b/context.go @@ -78,32 +78,34 @@ func (c *Context) QueryParams() url.Values { return c.request.URL.Query() } +// mustWrite writes raw bytes to the response. +func (c *Context) mustWrite(blob []byte) { + if _, err := c.Response().Write(blob); err != nil { + panic(err) + } +} + // JSON sends JSON response with the given status code. -// -// Returns an error if an error happened during sending response otherwise returns nil. -func (c *Context) JSON(code int, obj any) error { +func (c *Context) JSON(code int, obj any) { c.writeContentType("application/json") c.response.WriteHeader(code) - return c.kid.jsonSerializer.Write(c.Response(), obj, "") + c.kid.jsonSerializer.Write(c.Response(), obj, "") } // JSONIndent sends JSON response with the given status code. // Sends response with the given indent. -// -// Returns an error if an error happened during sending response otherwise returns nil. -func (c *Context) JSONIndent(code int, obj any, indent string) error { +func (c *Context) JSONIndent(code int, obj any, indent string) { c.writeContentType("application/json") c.response.WriteHeader(code) - return c.kid.jsonSerializer.Write(c.Response(), obj, indent) + c.kid.jsonSerializer.Write(c.Response(), obj, indent) } // JSONByte sends JSON response with the given status code. // Writes JSON blob untouched to response. -func (c *Context) JSONByte(code int, blob []byte) error { +func (c *Context) JSONByte(code int, blob []byte) { c.writeContentType("application/json") c.response.WriteHeader(code) - _, err := c.Response().Write(blob) - return err + c.mustWrite(blob) } // ReadJSON reads request's body as JSON and stores it in the given object. @@ -115,29 +117,26 @@ func (c *Context) ReadJSON(out any) error { // XML sends XML response with the given status code. // // Returns an error if an error happened during sending response otherwise returns nil. -func (c *Context) XML(code int, obj any) error { +func (c *Context) XML(code int, obj any) { c.writeContentType("application/xml") c.response.WriteHeader(code) - return c.kid.xmlSerializer.Write(c.Response(), obj, "") + c.kid.xmlSerializer.Write(c.Response(), obj, "") } // XMLIndent sends XML response with the given status code. // Sends response with the given indent. -// -// Returns an error if an error happened during sending response otherwise returns nil. -func (c *Context) XMLIndent(code int, obj any, indent string) error { +func (c *Context) XMLIndent(code int, obj any, indent string) { c.writeContentType("application/xml") c.response.WriteHeader(code) - return c.kid.xmlSerializer.Write(c.Response(), obj, indent) + c.kid.xmlSerializer.Write(c.Response(), obj, indent) } // XMLByte sends XML response with the given status code. // Writes JSON blob untouched to response. -func (c *Context) XMLByte(code int, blob []byte) error { +func (c *Context) XMLByte(code int, blob []byte) { c.writeContentType("application/xml") c.response.WriteHeader(code) - _, err := c.Response().Write(blob) - return err + c.mustWrite(blob) } // ReadXML reads request's body as XML and stores it in the given object. @@ -150,22 +149,17 @@ func (c *Context) ReadXML(out any) error { // // tpl must be a relative path to templates root directory. // Defaults to "templates/". -// -// Returns an error if an error happened during sending response otherwise returns nil. -func (c *Context) HTML(code int, tpl string, data any) error { +func (c *Context) HTML(code int, tpl string, data any) { c.writeContentType("text/html") c.response.WriteHeader(code) - return c.kid.htmlRenderer.RenderHTML(c.Response(), tpl, data) + c.kid.htmlRenderer.RenderHTML(c.Response(), tpl, data) } // HTMLString sends bare string as HTML response with the given status code. -// -// Returns an error if an error happened during sending response otherwise returns nil. -func (c *Context) HTMLString(code int, tpl string) error { +func (c *Context) HTMLString(code int, tpl string) { c.writeContentType("text/html") c.response.WriteHeader(code) - _, err := c.Response().Write([]byte(tpl)) - return err + c.mustWrite([]byte(tpl)) } // NoContent returns an empty response with the given status code. diff --git a/context_test.go b/context_test.go index 4448b44..db20e5c 100644 --- a/context_test.go +++ b/context_test.go @@ -3,6 +3,7 @@ package kid import ( "encoding/json" "encoding/xml" + "errors" "fmt" "net/http" "net/http/httptest" @@ -11,11 +12,18 @@ import ( "strings" "testing" - "github.com/mojixcoder/kid/errors" htmlrenderer "github.com/mojixcoder/kid/html_renderer" "github.com/stretchr/testify/assert" ) +type errWriter struct { + *httptest.ResponseRecorder +} + +func (errWriter) Write(blob []byte) (int, error) { + return 0, errors.New("new err") +} + type person struct { Name string `json:"name" xml:"name"` Age int `json:"age" xml:"age"` @@ -238,11 +246,26 @@ func TestContext_ReadJSON(t *testing.T) { ctx.reset(req, res) var p2 person - httpErr := ctx.ReadJSON(&p2).(*errors.HTTPError) + err = ctx.ReadJSON(&p2) + + assert.Error(t, err) +} + +func TestContext_mustWrite(t *testing.T) { + ctx := newContext(New()) + + res := httptest.NewRecorder() + ctx.reset(nil, res) + + ctx.mustWrite([]byte("byte")) + + assert.Equal(t, "byte", res.Body.String()) + + ctx.reset(nil, errWriter{res}) - assert.Error(t, httpErr) - assert.Error(t, httpErr.Err) - assert.Equal(t, http.StatusBadRequest, httpErr.Code) + assert.Panics(t, func() { + ctx.mustWrite([]byte("byte")) + }) } func TestContext_JSON(t *testing.T) { @@ -253,9 +276,8 @@ func TestContext_JSON(t *testing.T) { ctx.reset(nil, res) p := person{Name: "foo", Age: 1999} - err := ctx.JSON(http.StatusCreated, &p) + ctx.JSON(http.StatusCreated, &p) - assert.NoError(t, err) assert.Equal(t, http.StatusCreated, res.Code) assert.Equal(t, "application/json", res.Header().Get("Content-Type")) assert.Equal(t, "{\"name\":\"foo\",\"age\":1999}\n", res.Body.String()) @@ -264,11 +286,9 @@ func TestContext_JSON(t *testing.T) { ctx.reset(nil, res) - httpErr := ctx.JSON(http.StatusCreated, make(chan bool)).(*errors.HTTPError) - - assert.Error(t, httpErr) - assert.Error(t, httpErr.Err) - assert.Equal(t, http.StatusInternalServerError, httpErr.Code) + assert.Panics(t, func() { + ctx.JSON(http.StatusCreated, make(chan bool)) + }) } func TestContext_JSONIndent(t *testing.T) { @@ -279,9 +299,8 @@ func TestContext_JSONIndent(t *testing.T) { ctx.reset(nil, res) p := person{Name: "foo", Age: 1999} - err := ctx.JSONIndent(http.StatusCreated, &p, " ") + ctx.JSONIndent(http.StatusCreated, &p, " ") - assert.NoError(t, err) assert.Equal(t, http.StatusCreated, res.Code) assert.Equal(t, "application/json", res.Header().Get("Content-Type")) assert.Equal(t, "{\n \"name\": \"foo\",\n \"age\": 1999\n}\n", res.Body.String()) @@ -290,11 +309,9 @@ func TestContext_JSONIndent(t *testing.T) { ctx.reset(nil, res) - httpErr := ctx.JSONIndent(http.StatusCreated, make(chan bool), " ").(*errors.HTTPError) - - assert.Error(t, httpErr) - assert.Error(t, httpErr.Err) - assert.Equal(t, http.StatusInternalServerError, httpErr.Code) + assert.Panics(t, func() { + ctx.JSONIndent(http.StatusCreated, make(chan bool), " ") + }) } func TestContext_JSONByte(t *testing.T) { @@ -309,9 +326,8 @@ func TestContext_JSONByte(t *testing.T) { blob, err := json.Marshal(p) assert.NoError(t, err) - err = ctx.JSONByte(http.StatusOK, blob) + ctx.JSONByte(http.StatusOK, blob) - assert.NoError(t, err) assert.Equal(t, http.StatusOK, res.Code) assert.Equal(t, "application/json", res.Header().Get("Content-Type")) assert.Equal(t, "{\"name\":\"foo\",\"age\":1999}", res.Body.String()) @@ -335,11 +351,9 @@ func TestContext_ReadXML(t *testing.T) { ctx.reset(req, nil) var p2 person - httpErr := ctx.ReadXML(&p2).(*errors.HTTPError) + err = ctx.ReadXML(&p2) - assert.Error(t, httpErr) - assert.Error(t, httpErr.Err) - assert.Equal(t, http.StatusBadRequest, httpErr.Code) + assert.Error(t, err) } func TestContext_XML(t *testing.T) { @@ -350,9 +364,8 @@ func TestContext_XML(t *testing.T) { ctx.reset(nil, res) p := person{Name: "foo", Age: 1999} - err := ctx.XML(http.StatusCreated, &p) + ctx.XML(http.StatusCreated, &p) - assert.NoError(t, err) assert.Equal(t, http.StatusCreated, res.Code) assert.Equal(t, "application/xml", res.Header().Get("Content-Type")) assert.Equal(t, "foo1999", res.Body.String()) @@ -361,11 +374,9 @@ func TestContext_XML(t *testing.T) { ctx.reset(nil, res) - httpErr := ctx.XML(http.StatusCreated, make(chan bool)).(*errors.HTTPError) - - assert.Error(t, httpErr) - assert.Error(t, httpErr.Err) - assert.Equal(t, http.StatusInternalServerError, httpErr.Code) + assert.Panics(t, func() { + ctx.XML(http.StatusCreated, make(chan bool)) + }) } func TestContext_XMLIndent(t *testing.T) { @@ -376,9 +387,8 @@ func TestContext_XMLIndent(t *testing.T) { ctx.reset(nil, res) p := person{Name: "foo", Age: 1999} - err := ctx.XMLIndent(http.StatusCreated, &p, " ") + ctx.XMLIndent(http.StatusCreated, &p, " ") - assert.NoError(t, err) assert.Equal(t, http.StatusCreated, res.Code) assert.Equal(t, "application/xml", res.Header().Get("Content-Type")) assert.Equal(t, "\n foo\n 1999\n", res.Body.String()) @@ -387,11 +397,9 @@ func TestContext_XMLIndent(t *testing.T) { ctx.reset(nil, res) - httpErr := ctx.XMLIndent(http.StatusCreated, make(chan bool), " ").(*errors.HTTPError) - - assert.Error(t, httpErr) - assert.Error(t, httpErr.Err) - assert.Equal(t, http.StatusInternalServerError, httpErr.Code) + assert.Panics(t, func() { + ctx.XMLIndent(http.StatusCreated, make(chan bool), " ") + }) } func TestContext_XMLByte(t *testing.T) { @@ -406,9 +414,8 @@ func TestContext_XMLByte(t *testing.T) { blob, err := xml.Marshal(p) assert.NoError(t, err) - err = ctx.XMLByte(http.StatusOK, blob) + ctx.XMLByte(http.StatusOK, blob) - assert.NoError(t, err) assert.Equal(t, http.StatusOK, res.Code) assert.Equal(t, "application/xml", res.Header().Get("Content-Type")) assert.Equal(t, "foo1999", res.Body.String()) @@ -425,7 +432,7 @@ func TestContext_HTML(t *testing.T) { res := httptest.NewRecorder() ctx.reset(nil, res) - err := ctx.HTML(http.StatusAccepted, "index.html", nil) + ctx.HTML(http.StatusAccepted, "index.html", nil) newLine := getNewLineStr() expectedRes := fmt.Sprintf( @@ -433,7 +440,6 @@ func TestContext_HTML(t *testing.T) { newLine, newLine, newLine, newLine, ) - assert.NoError(t, err) assert.Equal(t, http.StatusAccepted, res.Code) assert.Equal(t, expectedRes, res.Body.String()) assert.Equal(t, "text/html", res.Header().Get("Content-Type")) @@ -445,11 +451,9 @@ func TestContext_HTMLString(t *testing.T) { res := httptest.NewRecorder() ctx.reset(nil, res) - err := ctx.HTMLString(http.StatusAccepted, "

Hello

") + ctx.HTMLString(http.StatusAccepted, "

Hello

") - assert.NoError(t, err) assert.Equal(t, http.StatusAccepted, res.Code) assert.Equal(t, "

Hello

", res.Body.String()) assert.Equal(t, "text/html", res.Header().Get("Content-Type")) - } diff --git a/errors/errors.go b/errors/errors.go deleted file mode 100644 index 0cb9dd9..0000000 --- a/errors/errors.go +++ /dev/null @@ -1,54 +0,0 @@ -// Package errors implements error interface. -// -// HTTPError has first class support in Kid and is used for returning proper responses when an error happens. -package errors - -import ( - "fmt" - "net/http" -) - -// HTTPError is the struct for returning HTTP errors. -// -// Can be used by Kid's default error handler. -type HTTPError struct { - Code int `json:"-"` - Message any `json:"message"` - Err error `json:"-"` -} - -// Verifying interface compliance. -var _ error = (*HTTPError)(nil) - -// Error implements the error interface and returns error as string. -func (e *HTTPError) Error() string { - if e.Err == nil { - return fmt.Sprintf(`{"code": %d, "message": %q}`, e.Code, e.Message) - } - return fmt.Sprintf(`{"code": %d, "message": %q, "error": %q}`, e.Code, e.Message, e.Err) -} - -// Unwrap implements the errors.Unwrap interface. -func (e *HTTPError) Unwrap() error { - return e.Err -} - -// WithError sets the internal error of HTTP error. -func (e *HTTPError) WithError(err error) *HTTPError { - e.Err = err - return e -} - -// WithMessage sets the error message. -// -// This message will be sent in response in Kid's default error handler. So it should of a type which can be converted to JSON. -func (e *HTTPError) WithMessage(message any) *HTTPError { - e.Message = message - return e -} - -// NewHTTPError returns a new HTTP error. -func NewHTTPError(code int) *HTTPError { - err := HTTPError{Code: code, Message: http.StatusText(code)} - return &err -} diff --git a/errors/errors_test.go b/errors/errors_test.go deleted file mode 100644 index 51aaf95..0000000 --- a/errors/errors_test.go +++ /dev/null @@ -1,62 +0,0 @@ -package errors - -import ( - "errors" - "fmt" - "net/http" - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestNewHTTPError(t *testing.T) { - err := NewHTTPError(http.StatusCreated) - - assert.Error(t, err) - assert.Equal(t, http.StatusCreated, err.Code) - assert.Equal(t, http.StatusText(http.StatusCreated), err.Message) - assert.Nil(t, err.Err) -} - -func TestHTTPError_WithMessage(t *testing.T) { - err := NewHTTPError(http.StatusOK).WithMessage("new message") - - assert.Equal(t, http.StatusOK, err.Code) - assert.Equal(t, "new message", err.Message) - assert.Nil(t, err.Err) -} - -func TestHTTPError_WithError(t *testing.T) { - someErr := errors.New("some error") - err := NewHTTPError(http.StatusOK).WithError(someErr) - - assert.Equal(t, http.StatusOK, err.Code) - assert.Equal(t, http.StatusText(http.StatusOK), err.Message) - assert.ErrorIs(t, err.Err, someErr) -} - -func TestHTTPError_Error(t *testing.T) { - err := NewHTTPError(http.StatusOK) - - assert.Equal(t, `{"code": 200, "message": "OK"}`, err.Error()) - - err.WithMessage("something went wrong").WithError(errors.New("some error")) - - assert.Equal(t, `{"code": 200, "message": "something went wrong", "error": "some error"}`, err.Error()) -} - -func TestHTTPError_Unwrap(t *testing.T) { - someErr := errors.New("some error") - err := NewHTTPError(http.StatusForbidden).WithError(someErr) - - newErr := fmt.Errorf("some new error: %w", err) - - unwrapedErr := errors.Unwrap(newErr) - assert.ErrorIs(t, unwrapedErr, err) - - unwrapedErr = errors.Unwrap(unwrapedErr) - assert.ErrorIs(t, unwrapedErr, someErr) - - err = NewHTTPError(http.StatusForbidden) - assert.NoError(t, errors.Unwrap(err)) -} diff --git a/html_renderer/html.go b/html_renderer/html.go index d99c79d..0017bba 100644 --- a/html_renderer/html.go +++ b/html_renderer/html.go @@ -3,14 +3,11 @@ package htmlrenderer import ( "errors" - "fmt" "html/template" "io/fs" "net/http" "path/filepath" "strings" - - kiderrors "github.com/mojixcoder/kid/errors" ) var ( @@ -73,18 +70,17 @@ func (r *defaultHTMLRenderer) AddFunc(name string, f any) { } // RenderHTML implements Kid's HTML renderer. -func (r *defaultHTMLRenderer) RenderHTML(res http.ResponseWriter, path string, data any) error { +func (r *defaultHTMLRenderer) RenderHTML(res http.ResponseWriter, path string, data any) { if err := r.loadTemplates(); err != nil { - return newInternalServerHTTPError(err, err.Error()) + panic(err) } if tpl, ok := r.templates[path]; !ok { - return newInternalServerHTTPError(ErrTemplateNotFound, fmt.Sprintf("template %s not found", path)) + panic(ErrTemplateNotFound) } else { if err := tpl.Execute(res, data); err != nil { - return newInternalServerHTTPError(err, err.Error()) + panic(err) } - return nil } } @@ -126,7 +122,7 @@ func (r *defaultHTMLRenderer) loadTemplates() error { templateFiles, layoutFiles, err := r.getTemplateAndLayoutFiles() if err != nil { - return newInternalServerHTTPError(err, err.Error()) + return err } for _, templateFile := range templateFiles { @@ -168,8 +164,3 @@ func getFilesToParse(templatePath string, layouts []string) []string { files = append(files, layouts...) return files } - -// newInternalServerHTTPError returns a new HTTP error. -func newInternalServerHTTPError(err error, msg any) error { - return kiderrors.NewHTTPError(http.StatusInternalServerError).WithError(err).WithMessage(msg) -} diff --git a/html_renderer/html_test.go b/html_renderer/html_test.go index 580a5f6..a50bfdc 100644 --- a/html_renderer/html_test.go +++ b/html_renderer/html_test.go @@ -3,12 +3,10 @@ package htmlrenderer import ( "fmt" "html/template" - "net/http" "net/http/httptest" "path/filepath" "testing" - "github.com/mojixcoder/kid/errors" "github.com/stretchr/testify/assert" ) @@ -90,12 +88,9 @@ func TestDefaultHTMLRenderer_loadTemplates(t *testing.T) { htmlRenderer := newTestHTMLRenderer() htmlRenderer.rootDir = "invalid_path" - httpErr := htmlRenderer.loadTemplates().(*errors.HTTPError) + err := htmlRenderer.loadTemplates() - assert.Error(t, httpErr) - assert.Error(t, httpErr.Err) - assert.Equal(t, httpErr.Err.Error(), httpErr.Message) - assert.Equal(t, http.StatusInternalServerError, httpErr.Code) + assert.Error(t, err) assert.False(t, htmlRenderer.isInitialized) htmlRenderer = newTestHTMLRenderer() @@ -103,7 +98,7 @@ func TestDefaultHTMLRenderer_loadTemplates(t *testing.T) { return "Hello " + name }) - err := htmlRenderer.loadTemplates() + err = htmlRenderer.loadTemplates() assert.NoError(t, err) assert.True(t, htmlRenderer.isInitialized) @@ -162,26 +157,23 @@ func TestDefaultHTMLRenderer_RenderHTML(t *testing.T) { res := httptest.NewRecorder() - err := htmlRenderer.RenderHTML(res, "index.html", nil) - assert.Error(t, err) + assert.Panics(t, func() { + htmlRenderer.RenderHTML(res, "index.html", nil) + }) htmlRenderer = newTestHTMLRenderer() htmlRenderer.AddFunc("greet", func(name string) string { return "Hello " + name }) - httpErr := htmlRenderer.RenderHTML(res, "doesn't_exists.html", nil).(*errors.HTTPError) - - assert.Error(t, httpErr) - assert.ErrorIs(t, ErrTemplateNotFound, httpErr.Err) - assert.Equal(t, "template doesn't_exists.html not found", httpErr.Message) - assert.Equal(t, http.StatusInternalServerError, httpErr.Code) + assert.PanicsWithError(t, ErrTemplateNotFound.Error(), func() { + htmlRenderer.RenderHTML(res, "doesn't_exists.html", nil) + }) newline := getNewLineStr() res = httptest.NewRecorder() - err = htmlRenderer.RenderHTML(res, "index.html", nil) - assert.NoError(t, err) + htmlRenderer.RenderHTML(res, "index.html", nil) assert.Equal( t, fmt.Sprintf("%s%s

content

%s%s", newline, newline, newline, newline), @@ -189,28 +181,16 @@ func TestDefaultHTMLRenderer_RenderHTML(t *testing.T) { ) res = httptest.NewRecorder() - err = htmlRenderer.RenderHTML(res, "pages/page.html", map[string]string{"key": "page contents"}) - assert.NoError(t, err) + htmlRenderer.RenderHTML(res, "pages/page.html", map[string]string{"key": "page contents"}) assert.Equal(t, fmt.Sprintf("%s%s

page contents

%s%s", newline, newline, newline, newline), res.Body.String(), ) res = httptest.NewRecorder() - err = htmlRenderer.RenderHTML(res, "pages/page2.html", nil) - assert.NoError(t, err) + htmlRenderer.RenderHTML(res, "pages/page2.html", nil) assert.Equal(t, fmt.Sprintf("%s%s

Hello Tom

%s%s", newline, newline, newline, newline), res.Body.String(), ) } - -func TestNewInternalServerHTTPError(t *testing.T) { - err := errors.NewHTTPError(http.StatusBadRequest) - httpErr := newInternalServerHTTPError(err, err.Error()).(*errors.HTTPError) - - assert.Error(t, httpErr) - assert.ErrorIs(t, httpErr.Err, err) - assert.Equal(t, http.StatusInternalServerError, httpErr.Code) - assert.Equal(t, err.Error(), httpErr.Message) -} diff --git a/html_renderer/renderer.go b/html_renderer/renderer.go index 66aa78b..caf8d76 100644 --- a/html_renderer/renderer.go +++ b/html_renderer/renderer.go @@ -5,5 +5,5 @@ import "net/http" // HTMLRenderer is the interface for rendering type HTMLRenderer interface { // RenderHTML renders html template - RenderHTML(res http.ResponseWriter, path string, data any) error + RenderHTML(res http.ResponseWriter, path string, data any) } diff --git a/options_test.go b/options_test.go index 5462330..d3d4469 100644 --- a/options_test.go +++ b/options_test.go @@ -9,13 +9,9 @@ import ( type mockEverything struct{} -func (*mockEverything) RenderHTML(res http.ResponseWriter, path string, data any) error { - return nil -} +func (*mockEverything) RenderHTML(res http.ResponseWriter, path string, data any) {} -func (*mockEverything) Write(w http.ResponseWriter, in any, indent string) error { - return nil -} +func (*mockEverything) Write(w http.ResponseWriter, in any, indent string) {} func (*mockEverything) Read(req *http.Request, out any) error { return nil diff --git a/serializer/json.go b/serializer/json.go index d8c27dc..07e332e 100644 --- a/serializer/json.go +++ b/serializer/json.go @@ -18,24 +18,22 @@ func NewJSONSerializer() Serializer { } // Marshal writes the given object as JSON to response. -func (s defaultJSONSerializer) Write(w http.ResponseWriter, in any, indent string) error { +func (s defaultJSONSerializer) Write(w http.ResponseWriter, in any, indent string) { encoder := json.NewEncoder(w) encoder.SetIndent("", indent) if err := encoder.Encode(in); err != nil { - return newHTTPErrorFromError(http.StatusInternalServerError, err) + panic(err) } - - return nil } // Unmarshal reads request's body as JSON and puts it in the given obj. func (s defaultJSONSerializer) Read(req *http.Request, out any) error { if err := json.NewDecoder(req.Body).Decode(out); err != nil { if _, ok := err.(*json.InvalidUnmarshalError); ok { - return newHTTPErrorFromError(http.StatusInternalServerError, err) + panic(err) } - return newHTTPErrorFromError(http.StatusBadRequest, err) + return err } return nil } diff --git a/serializer/json_test.go b/serializer/json_test.go index 0427a50..c6c8f3d 100644 --- a/serializer/json_test.go +++ b/serializer/json_test.go @@ -1,13 +1,11 @@ package serializer import ( - "encoding/json" "net/http" "net/http/httptest" "strings" "testing" - "github.com/mojixcoder/kid/errors" "github.com/stretchr/testify/assert" ) @@ -29,29 +27,20 @@ func TestDefaultJSONSerializer_Write(t *testing.T) { res := httptest.NewRecorder() p := person{Name: "Mojix", Age: 22} - err := serializer.Write(res, p, "") - assert.NoError(t, err) - + serializer.Write(res, p, "") assert.Equal(t, "{\"name\":\"Mojix\",\"age\":22}\n", res.Body.String()) res = httptest.NewRecorder() - err = serializer.Write(res, p, " ") - assert.NoError(t, err) - + serializer.Write(res, p, " ") assert.Equal(t, "{\n \"name\": \"Mojix\",\n \"age\": 22\n}\n", res.Body.String()) res = httptest.NewRecorder() // Channel type cannot be converted to JSON. - err = serializer.Write(res, make(chan bool), "") - assert.Error(t, err) - - httpErr := err.(*errors.HTTPError) - - assert.Equal(t, http.StatusInternalServerError, httpErr.Code) - assert.Equal(t, httpErr.Err.Error(), httpErr.Message) - assert.Error(t, httpErr.Err) + assert.Panics(t, func() { + serializer.Write(res, make(chan bool), "") + }) } func TestDefaultJSONSerializer_Read(t *testing.T) { @@ -68,24 +57,15 @@ func TestDefaultJSONSerializer_Read(t *testing.T) { req = httptest.NewRequest(http.MethodGet, "/", strings.NewReader("{\"name\":\"Mojix\",\"age\":22}")) - // Invalid argument passed to unmarshal. var p2 person - err = serializer.Read(req, p2) - assert.Error(t, err) - httpErr := err.(*errors.HTTPError) - _, ok := httpErr.Err.(*json.InvalidUnmarshalError) - - assert.Equal(t, http.StatusInternalServerError, httpErr.Code) - assert.Equal(t, httpErr.Err.Error(), httpErr.Message) - assert.Error(t, httpErr.Err) - assert.True(t, ok) + // Invalid argument passed to unmarshal. + assert.Panics(t, func() { + serializer.Read(req, p2) + }) req = httptest.NewRequest(http.MethodGet, "/", strings.NewReader("{\"name\":\"Mojix\",\"age\":22.5}")) err = serializer.Read(req, &p2) - assert.Error(t, err) - assert.Error(t, err.(*errors.HTTPError).Err) - assert.Equal(t, http.StatusBadRequest, err.(*errors.HTTPError).Code) } diff --git a/serializer/serializer.go b/serializer/serializer.go index 94a1906..106b124 100644 --- a/serializer/serializer.go +++ b/serializer/serializer.go @@ -13,7 +13,7 @@ import ( // It can be implemented to read/write custom JSON/XML serializers. type Serializer interface { // Write writes object with the given indent to response body. - Write(w http.ResponseWriter, in any, indent string) error + Write(w http.ResponseWriter, in any, indent string) // Read reads request body and store it in the given object. Read(req *http.Request, out any) error diff --git a/serializer/utils.go b/serializer/utils.go deleted file mode 100644 index 4efb3ea..0000000 --- a/serializer/utils.go +++ /dev/null @@ -1,8 +0,0 @@ -package serializer - -import "github.com/mojixcoder/kid/errors" - -// newHTTPErrorFromError returns a new HTTP error from an error. -func newHTTPErrorFromError(code int, err error) *errors.HTTPError { - return errors.NewHTTPError(code).WithMessage(err.Error()).WithError(err) -} diff --git a/serializer/utils_test.go b/serializer/utils_test.go deleted file mode 100644 index 3e1865c..0000000 --- a/serializer/utils_test.go +++ /dev/null @@ -1,21 +0,0 @@ -package serializer - -import ( - "errors" - "net/http" - "testing" - - "github.com/stretchr/testify/assert" -) - -func TestNewHTTPErrorFromError(t *testing.T) { - err := errors.New("test error") - - httpErr := newHTTPErrorFromError(http.StatusBadRequest, err) - - assert.Error(t, httpErr) - assert.Error(t, httpErr.Err) - assert.ErrorIs(t, httpErr.Err, err) - assert.Equal(t, err.Error(), httpErr.Message) - assert.Equal(t, http.StatusBadRequest, httpErr.Code) -} diff --git a/serializer/xml.go b/serializer/xml.go index c234cc5..b1ef0a3 100644 --- a/serializer/xml.go +++ b/serializer/xml.go @@ -18,24 +18,22 @@ func NewXMLSerializer() Serializer { } // Write writes the given object as XML to response. -func (s defaultXMLSerializer) Write(w http.ResponseWriter, in any, indent string) error { +func (s defaultXMLSerializer) Write(w http.ResponseWriter, in any, indent string) { encoder := xml.NewEncoder(w) encoder.Indent("", indent) if err := encoder.Encode(in); err != nil { - return newHTTPErrorFromError(http.StatusInternalServerError, err) + panic(err) } - - return nil } // Read reads request's body as XML and puts it in the given obj. func (s defaultXMLSerializer) Read(req *http.Request, out any) error { if err := xml.NewDecoder(req.Body).Decode(out); err != nil { if err.Error() == "non-pointer passed to Unmarshal" { - return newHTTPErrorFromError(http.StatusInternalServerError, err) + panic(err) } - return newHTTPErrorFromError(http.StatusBadRequest, err) + return err } return nil } diff --git a/serializer/xml_test.go b/serializer/xml_test.go index e6e043f..a341112 100644 --- a/serializer/xml_test.go +++ b/serializer/xml_test.go @@ -6,7 +6,6 @@ import ( "strings" "testing" - "github.com/mojixcoder/kid/errors" "github.com/stretchr/testify/assert" ) @@ -24,27 +23,18 @@ func TestDefaultXMLSerializer_Write(t *testing.T) { p := person{Name: "Mojix", Age: 22} - err := serializer.Write(res, p, "") - - assert.NoError(t, err) + serializer.Write(res, p, "") assert.Equal(t, "Mojix22", res.Body.String()) res = httptest.NewRecorder() - err = serializer.Write(res, p, " ") - - assert.NoError(t, err) + serializer.Write(res, p, " ") assert.Equal(t, "\n Mojix\n 22\n", res.Body.String()) // Unsupported type. - err = serializer.Write(res, make(chan bool), "") - assert.Error(t, err) - - httpErr := err.(*errors.HTTPError) - - assert.Equal(t, http.StatusInternalServerError, httpErr.Code) - assert.Equal(t, httpErr.Err.Error(), httpErr.Message) - assert.Error(t, httpErr.Err) + assert.Panics(t, func() { + serializer.Write(res, make(chan bool), "") + }) } func TestDefaultXMLSerializer_Read(t *testing.T) { @@ -61,25 +51,15 @@ func TestDefaultXMLSerializer_Read(t *testing.T) { req = httptest.NewRequest(http.MethodGet, "/", strings.NewReader("Mojix22")) - // Invalid argument passed to unmarshal. var p2 person - err = serializer.Read(req, p2) - assert.Error(t, err) - - httpErr := err.(*errors.HTTPError) - assert.Equal(t, http.StatusInternalServerError, httpErr.Code) - assert.Equal(t, httpErr.Err.Error(), httpErr.Message) - assert.Error(t, httpErr.Err) + // Invalid argument passed to unmarshal. + assert.Panics(t, func() { + serializer.Read(req, p2) + }) req = httptest.NewRequest(http.MethodGet, "/", strings.NewReader("Mojix22.5")) err = serializer.Read(req, &p2) assert.Error(t, err) - - httpErr = err.(*errors.HTTPError) - - assert.Equal(t, http.StatusBadRequest, httpErr.Code) - assert.Equal(t, httpErr.Err.Error(), httpErr.Message) - assert.Error(t, httpErr.Err) }