From 3e3e9ad8275d798913f4258b0bdcd24085da4666 Mon Sep 17 00:00:00 2001 From: Landon Cox Date: Sun, 29 Mar 2026 11:17:13 -0700 Subject: [PATCH 1/3] test: add unit tests for internal/httputil package Adds comprehensive tests for WriteJSONResponse covering: - Content-Type header setting - Status code propagation (200, 201, 400, 404, 500) - JSON encoding of structs, maps, slices, nested types - Nil body, empty struct, special characters, unicode Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- internal/httputil/httputil_test.go | 140 +++++++++++++++++++++++++++++ 1 file changed, 140 insertions(+) create mode 100644 internal/httputil/httputil_test.go diff --git a/internal/httputil/httputil_test.go b/internal/httputil/httputil_test.go new file mode 100644 index 000000000..c011e006b --- /dev/null +++ b/internal/httputil/httputil_test.go @@ -0,0 +1,140 @@ +package httputil + +import ( + "encoding/json" + "net/http" + "net/http/httptest" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestWriteJSONResponse(t *testing.T) { + t.Run("sets content-type to application/json", func(t *testing.T) { + rec := httptest.NewRecorder() + WriteJSONResponse(rec, http.StatusOK, map[string]string{"key": "value"}) + + assert.Equal(t, "application/json", rec.Header().Get("Content-Type")) + }) + + t.Run("writes the provided status code", func(t *testing.T) { + tests := []struct { + name string + statusCode int + }{ + {"200 OK", http.StatusOK}, + {"201 Created", http.StatusCreated}, + {"400 Bad Request", http.StatusBadRequest}, + {"404 Not Found", http.StatusNotFound}, + {"500 Internal Server Error", http.StatusInternalServerError}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + rec := httptest.NewRecorder() + WriteJSONResponse(rec, tt.statusCode, nil) + + assert.Equal(t, tt.statusCode, rec.Code) + }) + } + }) + + t.Run("encodes body as JSON", func(t *testing.T) { + type payload struct { + Name string `json:"name"` + Count int `json:"count"` + } + rec := httptest.NewRecorder() + WriteJSONResponse(rec, http.StatusOK, payload{Name: "test", Count: 42}) + + var got payload + err := json.NewDecoder(rec.Body).Decode(&got) + require.NoError(t, err) + assert.Equal(t, "test", got.Name) + assert.Equal(t, 42, got.Count) + }) + + t.Run("encodes map body as JSON", func(t *testing.T) { + rec := httptest.NewRecorder() + WriteJSONResponse(rec, http.StatusOK, map[string]interface{}{ + "error": "not found", + "code": 404, + "details": []string{"a", "b"}, + }) + + var got map[string]interface{} + err := json.NewDecoder(rec.Body).Decode(&got) + require.NoError(t, err) + assert.Equal(t, "not found", got["error"]) + assert.Equal(t, float64(404), got["code"]) + assert.Len(t, got["details"], 2) + }) + + t.Run("encodes nil body as JSON null", func(t *testing.T) { + rec := httptest.NewRecorder() + WriteJSONResponse(rec, http.StatusNoContent, nil) + + assert.Equal(t, http.StatusNoContent, rec.Code) + assert.JSONEq(t, "null", rec.Body.String()) + }) + + t.Run("encodes empty struct as empty JSON object", func(t *testing.T) { + rec := httptest.NewRecorder() + WriteJSONResponse(rec, http.StatusOK, struct{}{}) + + assert.JSONEq(t, "{}", rec.Body.String()) + }) + + t.Run("encodes slice body as JSON array", func(t *testing.T) { + rec := httptest.NewRecorder() + WriteJSONResponse(rec, http.StatusOK, []string{"alpha", "beta"}) + + var got []string + err := json.NewDecoder(rec.Body).Decode(&got) + require.NoError(t, err) + assert.ElementsMatch(t, []string{"alpha", "beta"}, got) + }) + + t.Run("encodes nested structs", func(t *testing.T) { + type inner struct { + ID int `json:"id"` + } + type outer struct { + Items []inner `json:"items"` + } + rec := httptest.NewRecorder() + WriteJSONResponse(rec, http.StatusOK, outer{Items: []inner{{ID: 1}, {ID: 2}}}) + + var got outer + err := json.NewDecoder(rec.Body).Decode(&got) + require.NoError(t, err) + require.Len(t, got.Items, 2) + assert.Equal(t, 1, got.Items[0].ID) + assert.Equal(t, 2, got.Items[1].ID) + }) + + t.Run("body with special characters", func(t *testing.T) { + rec := httptest.NewRecorder() + WriteJSONResponse(rec, http.StatusOK, map[string]string{ + "msg": `hello "world" & `, + }) + + var got map[string]string + err := json.NewDecoder(rec.Body).Decode(&got) + require.NoError(t, err) + assert.Equal(t, `hello "world" & `, got["msg"]) + }) + + t.Run("body with unicode", func(t *testing.T) { + rec := httptest.NewRecorder() + WriteJSONResponse(rec, http.StatusOK, map[string]string{ + "greeting": "こんにちは 🌍", + }) + + var got map[string]string + err := json.NewDecoder(rec.Body).Decode(&got) + require.NoError(t, err) + assert.Equal(t, "こんにちは 🌍", got["greeting"]) + }) +} From 47230768141c9d104654fb4a17fbf8b4d3d31f71 Mon Sep 17 00:00:00 2001 From: Landon Cox Date: Sun, 29 Mar 2026 11:25:23 -0700 Subject: [PATCH 2/3] Update internal/httputil/httputil_test.go Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- internal/httputil/httputil_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/httputil/httputil_test.go b/internal/httputil/httputil_test.go index c011e006b..5d0477a33 100644 --- a/internal/httputil/httputil_test.go +++ b/internal/httputil/httputil_test.go @@ -93,7 +93,7 @@ func TestWriteJSONResponse(t *testing.T) { var got []string err := json.NewDecoder(rec.Body).Decode(&got) require.NoError(t, err) - assert.ElementsMatch(t, []string{"alpha", "beta"}, got) + assert.Equal(t, []string{"alpha", "beta"}, got) }) t.Run("encodes nested structs", func(t *testing.T) { From a0ac9702136bb1a9b637dfcec1e0cb37a672e1c3 Mon Sep 17 00:00:00 2001 From: Landon Cox Date: Sun, 29 Mar 2026 11:25:32 -0700 Subject: [PATCH 3/3] Update internal/httputil/httputil_test.go Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- internal/httputil/httputil_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/httputil/httputil_test.go b/internal/httputil/httputil_test.go index 5d0477a33..d24a309ff 100644 --- a/internal/httputil/httputil_test.go +++ b/internal/httputil/httputil_test.go @@ -73,9 +73,9 @@ func TestWriteJSONResponse(t *testing.T) { t.Run("encodes nil body as JSON null", func(t *testing.T) { rec := httptest.NewRecorder() - WriteJSONResponse(rec, http.StatusNoContent, nil) + WriteJSONResponse(rec, http.StatusOK, nil) - assert.Equal(t, http.StatusNoContent, rec.Code) + assert.Equal(t, http.StatusOK, rec.Code) assert.JSONEq(t, "null", rec.Body.String()) })