package ezhandler_test import ( "encoding/json" "io" "net/http" "net/http/httptest" "strings" "testing" "codeberg.org/danjones000/ezhandler" "github.com/stretchr/testify/assert" ) func TestHandlerFunc_ServeHTTP(t *testing.T) { tests := []struct { name string handlerFunc ezhandler.HandlerFunc expectedErr error }{ { name: "no error", handlerFunc: ezhandler.HandlerFunc(func(w http.ResponseWriter, r *http.Request) error { w.WriteHeader(http.StatusOK) return nil }), expectedErr: nil, }, { name: "with error", handlerFunc: ezhandler.HandlerFunc(func(w http.ResponseWriter, r *http.Request) error { return errTest }), expectedErr: errTest, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { req := httptest.NewRequest(http.MethodGet, "/", http.NoBody) rec := httptest.NewRecorder() err := tt.handlerFunc.ServeHTTP(rec, req) if tt.expectedErr != nil { assert.Equal(t, tt.expectedErr, err) } else { assert.NoError(t, err) } }) } } 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()) } }) } type jsonBodyResponderTest struct { name string requestBody string handler ezhandler.JSONResponder[testBody] expectedStatus int expectedBody testBody expectedErr error expectedErrContains string } var jsonBodyResponderTests = []jsonBodyResponderTest{ { name: "successful decoding and response", requestBody: `{"name":"John Doe","age":30}`, handler: ezhandler.JSONResponder[testBody](func(r *http.Request, body testBody) (ezhandler.ResponseHelper, error) { return ezhandler.JSONResponse(body), nil }), expectedStatus: http.StatusOK, expectedBody: testBody{Name: "John Doe", Age: 30}, expectedErr: nil, }, { name: "invalid JSON body", requestBody: `{"name":"John Doe","age":}`, handler: ezhandler.JSONResponder[testBody](func(r *http.Request, body testBody) (ezhandler.ResponseHelper, error) { return nil, nil // Should not be called }), expectedErrContains: "invalid character", }, { name: "empty body for struct type", requestBody: "", handler: ezhandler.JSONResponder[testBody](func(r *http.Request, body testBody) (ezhandler.ResponseHelper, error) { return nil, nil // Should not be called }), expectedErr: io.EOF, }, { name: "handler function returns error", requestBody: `{"name":"Jane Doe","age":25}`, handler: ezhandler.JSONResponder[testBody](func(r *http.Request, body testBody) (ezhandler.ResponseHelper, error) { return nil, errTest }), expectedErr: errTest, }, } func TestJSONResponder_ServeHTTP(t *testing.T) { for _, tc := range jsonBodyResponderTests { runJSONResponderTest(t, tc) } } func runJSONResponderTest(tt *testing.T, tc jsonBodyResponderTest) { tt.Run(tc.name, func(t *testing.T) { req := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(tc.requestBody)) rec := httptest.NewRecorder() err := tc.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) // Marshal expectedBody to JSON string for comparison expectedBodyBytes, marshalErr := json.Marshal(tc.expectedBody) assert.NoError(t, marshalErr) assert.JSONEq(t, string(expectedBodyBytes), rec.Body.String()) assert.Equal(t, "application/json", rec.Header().Get("Content-Type")) } }) }