✅ Add JSONBodyHandler and tests
- Implemented JSONBodyHandler for automatic JSON body parsing. - Added comprehensive unit tests for JSONBodyHandler covering successful decoding, invalid JSON, empty bodies, and error propagation. - Refactored handler_test.go to improve test structure and resolve linting issues (funlen, gocritic).
This commit is contained in:
parent
18d987caf7
commit
5a38283bb0
2 changed files with 118 additions and 0 deletions
20
handler.go
20
handler.go
|
|
@ -1,6 +1,7 @@
|
|||
package ezhandler
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
)
|
||||
|
|
@ -42,3 +43,22 @@ func (fn ResponseHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) erro
|
|||
_, err = io.Copy(w, body)
|
||||
return err
|
||||
}
|
||||
|
||||
// JSONBodyHandler can be used as a [Handler] which automatically parses the json body into a value, which is passed to the function.
|
||||
type JSONBodyHandler[V any] func(w http.ResponseWriter, r *http.Request, body V) error
|
||||
|
||||
var _ Handler = JSONBodyHandler[map[string]any](nil)
|
||||
|
||||
func (fn JSONBodyHandler[V]) ServeHTTP(w http.ResponseWriter, r *http.Request) error {
|
||||
reqBody := r.Body
|
||||
//nolint:errcheck // This is usually fine
|
||||
defer reqBody.Close()
|
||||
|
||||
dec := json.NewDecoder(reqBody)
|
||||
var body V
|
||||
if err := dec.Decode(&body); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return fn(w, r, body)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
package ezhandler_test
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"codeberg.org/danjones000/ezhandler"
|
||||
|
|
@ -46,3 +48,99 @@ func TestHandlerFunc_ServeHTTP(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
type testBody struct {
|
||||
Name string `json:"name"`
|
||||
Age int `json:"age"`
|
||||
}
|
||||
|
||||
type jsonBodyTest struct {
|
||||
name string
|
||||
requestBody string
|
||||
handler ezhandler.JSONBodyHandler[testBody]
|
||||
expectedStatus int
|
||||
expectedBody string
|
||||
expectedErr error
|
||||
expectedErrContains string
|
||||
additionAssert func(*assert.Assertions, testBody)
|
||||
}
|
||||
|
||||
var jsonBodyHandlerTests = []jsonBodyTest{
|
||||
{
|
||||
name: "successful decoding and handler execution",
|
||||
requestBody: `{"name":"John Doe","age":30}`,
|
||||
handler: ezhandler.JSONBodyHandler[testBody](func(w http.ResponseWriter, r *http.Request, body testBody) error {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, _ = w.Write([]byte("success"))
|
||||
return nil
|
||||
}),
|
||||
expectedStatus: http.StatusOK,
|
||||
expectedBody: "success",
|
||||
expectedErr: nil,
|
||||
additionAssert: func(as *assert.Assertions, body testBody) {
|
||||
as.Equal("John Doe", body.Name)
|
||||
as.Equal(30, body.Age)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "invalid JSON body",
|
||||
requestBody: `{"name":"John Doe","age":}`,
|
||||
handler: ezhandler.JSONBodyHandler[testBody](func(w http.ResponseWriter, r *http.Request, body testBody) error {
|
||||
return nil // Should not be called
|
||||
}),
|
||||
expectedErrContains: "invalid character",
|
||||
},
|
||||
{
|
||||
name: "empty body for struct type",
|
||||
requestBody: "",
|
||||
handler: ezhandler.JSONBodyHandler[testBody](func(w http.ResponseWriter, r *http.Request, body testBody) error {
|
||||
return nil // Should not be called
|
||||
}),
|
||||
expectedErr: io.EOF,
|
||||
},
|
||||
{
|
||||
name: "handler function returns error",
|
||||
requestBody: `{"name":"Jane Doe","age":25}`,
|
||||
handler: ezhandler.JSONBodyHandler[testBody](func(w http.ResponseWriter, r *http.Request, body testBody) error {
|
||||
return errTest
|
||||
}),
|
||||
expectedErr: errTest,
|
||||
},
|
||||
}
|
||||
|
||||
func TestJSONBodyHandler_ServeHTTP(t *testing.T) {
|
||||
for _, tc := range jsonBodyHandlerTests {
|
||||
runJSONBodyHandlerTest(t, tc)
|
||||
}
|
||||
}
|
||||
|
||||
func runJSONBodyHandlerTest(tt *testing.T, tc jsonBodyTest) {
|
||||
tt.Run(tc.name, func(t *testing.T) {
|
||||
handler := ezhandler.JSONBodyHandler[testBody](func(w http.ResponseWriter, r *http.Request, body testBody) error {
|
||||
if err := tc.handler(w, r, body); err != nil {
|
||||
return err
|
||||
}
|
||||
if tc.additionAssert != nil {
|
||||
as := assert.New(t)
|
||||
tc.additionAssert(as, body)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
req := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(tc.requestBody))
|
||||
rec := httptest.NewRecorder()
|
||||
|
||||
err := handler.ServeHTTP(rec, req)
|
||||
|
||||
switch {
|
||||
case tc.expectedErr != nil:
|
||||
assert.Equal(t, tc.expectedErr, err)
|
||||
case tc.expectedErrContains != "":
|
||||
assert.ErrorContains(t, err, tc.expectedErrContains)
|
||||
default:
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, tc.expectedStatus, rec.Code)
|
||||
assert.Equal(t, tc.expectedBody, rec.Body.String())
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue