ezhandler/response_test.go
Dan Jones 1c16a893d6 Refactor ResponseHelper and add ResponderHandler
- Modified ResponseHelper.Body signature to return an io.Reader and an error.
- Updated ResponseHandler.ServeHTTP to handle errors from ResponseHelper.Body.
- Implemented JSONResponse and JSONResponseWithStatus functions for easier JSON responses.
- Added comprehensive unit tests for JSON response handling, including error scenarios.
- Extended Helper.ResponderHandler with a new test case to ensure proper error handling and response generation.
- Resolved linting issues related to dynamic error definition and function length in tests.
2025-07-08 09:12:53 -05:00

163 lines
4.3 KiB
Go

package ezhandler_test
import (
"codeberg.org/danjones000/ezhandler"
"encoding/json"
"errors"
"github.com/stretchr/testify/assert"
"net/http"
"net/http/httptest"
"testing"
)
var errCircularMarshal = errors.New("json: unsupported value: encountered a cycle")
func TestJSONResponse(t *testing.T) {
type testData struct {
Message string `json:"message"`
Code int `json:"code"`
}
data := testData{Message: "Hello", Code: 200}
respHelper := ezhandler.JSONResponse(data)
assert.Equal(t, http.StatusOK, respHelper.Status())
headers := respHelper.Headers()
assert.Equal(t, "application/json", headers.Get("Content-Type"))
body, err := respHelper.Body()
assert.NoError(t, err)
assert.NotNil(t, body)
var decodedData testData
err = json.NewDecoder(body).Decode(&decodedData)
assert.NoError(t, err)
assert.Equal(t, data, decodedData)
}
func TestJSONResponseWithStatus(t *testing.T) {
type testData struct {
Key string `json:"key"`
}
data := testData{Key: "value"}
customStatus := http.StatusCreated
respHelper := ezhandler.JSONResponseWithStatus(data, customStatus)
assert.Equal(t, customStatus, respHelper.Status())
headers := respHelper.Headers()
assert.Equal(t, "application/json", headers.Get("Content-Type"))
body, err := respHelper.Body()
assert.NoError(t, err)
assert.NotNil(t, body)
var decodedData testData
err = json.NewDecoder(body).Decode(&decodedData)
assert.NoError(t, err)
assert.Equal(t, data, decodedData)
}
func TestJSONResponse_BodyError(t *testing.T) {
// This type cannot be marshaled to JSON due to a circular reference
type Circular struct {
Self *Circular
}
data := Circular{}
data.Self = &data
respHelper := ezhandler.JSONResponse(data)
body, err := respHelper.Body()
assert.Error(t, err)
assert.Nil(t, body)
}
func TestResponseHandler_ServeHTTP_JSONResponse(t *testing.T) {
type testData struct {
Message string `json:"message"`
}
tests := []struct {
name string
handler ezhandler.ResponseHandler
expectedStatus int
expectedBody string
expectedError error
}{
{
name: "successful JSON response",
handler: ezhandler.ResponseHandler(func(r *http.Request) (ezhandler.ResponseHelper, error) {
return ezhandler.JSONResponse(testData{Message: "success"}), nil
}),
expectedStatus: http.StatusOK,
expectedBody: "{\"message\":\"success\"}",
expectedError: nil,
},
{
name: "JSON response with custom status",
handler: ezhandler.ResponseHandler(func(r *http.Request) (ezhandler.ResponseHelper, error) {
return ezhandler.JSONResponseWithStatus(testData{Message: "created"}, http.StatusCreated), nil
}),
expectedStatus: http.StatusCreated,
expectedBody: "{\"message\":\"created\"}",
expectedError: nil,
},
{
name: "error from ResponseHandler",
handler: ezhandler.ResponseHandler(func(r *http.Request) (ezhandler.ResponseHelper, error) {
return nil, errTest // Using errTest from helper_test.go
}),
expectedStatus: http.StatusOK, // Status won't be set if handler returns error before writing header
expectedBody: "",
expectedError: errTest,
},
{
name: "error from Body() method",
handler: ezhandler.ResponseHandler(func(r *http.Request) (ezhandler.ResponseHelper, error) {
type Circular struct {
Self *Circular
}
data := Circular{}
data.Self = &data
return ezhandler.JSONResponse(data), nil
}),
expectedStatus: http.StatusOK, // Status won't be set if Body() returns error
expectedBody: "",
expectedError: errCircularMarshal, // The error is returned by ServeHTTP, not the handler func
},
}
for _, tt := range tests {
runResponseHandlerTest(t, tt)
}
}
func runResponseHandlerTest(t *testing.T, tt struct {
name string
handler ezhandler.ResponseHandler
expectedStatus int
expectedBody string
expectedError error
}) {
t.Run(tt.name, func(t *testing.T) {
req := httptest.NewRequest(http.MethodGet, "/", http.NoBody)
rec := httptest.NewRecorder()
err := tt.handler.ServeHTTP(rec, req)
if tt.expectedError != nil {
assert.ErrorContains(t, err, tt.expectedError.Error())
} else {
assert.NoError(t, err)
assert.Equal(t, tt.expectedStatus, rec.Code)
assert.Equal(t, tt.expectedBody, rec.Body.String())
if tt.expectedBody != "" {
assert.Equal(t, "application/json", rec.Header().Get("Content-Type"))
}
}
})
}