Files
cv-site/internal/httputil/response_test.go
T

218 lines
5.0 KiB
Go
Raw Normal View History

package httputil
import (
"encoding/json"
"net/http"
"net/http/httptest"
"strings"
"testing"
c "github.com/juanatsap/cv-site/internal/constants"
)
func TestJSON(t *testing.T) {
tests := []struct {
name string
status int
data interface{}
wantStatus int
}{
{
name: "200 OK with map",
status: http.StatusOK,
data: map[string]string{"message": "success"},
wantStatus: http.StatusOK,
},
{
name: "201 Created with struct",
status: http.StatusCreated,
data: struct{ ID int }{ID: 123},
wantStatus: http.StatusCreated,
},
{
name: "400 Bad Request with error",
status: http.StatusBadRequest,
data: map[string]string{"error": "invalid request"},
wantStatus: http.StatusBadRequest,
},
{
name: "500 Internal Server Error",
status: http.StatusInternalServerError,
data: map[string]string{"error": "server error"},
wantStatus: http.StatusInternalServerError,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
rec := httptest.NewRecorder()
err := JSON(rec, tt.status, tt.data)
if err != nil {
t.Errorf("JSON() error = %v", err)
}
if rec.Code != tt.wantStatus {
t.Errorf("Status = %d, want %d", rec.Code, tt.wantStatus)
}
contentType := rec.Header().Get(c.HeaderContentType)
if contentType != c.ContentTypeJSON {
t.Errorf("Content-Type = %q, want %q", contentType, c.ContentTypeJSON)
}
// Verify JSON is valid
var result interface{}
if err := json.Unmarshal(rec.Body.Bytes(), &result); err != nil {
t.Errorf("Response is not valid JSON: %v", err)
}
})
}
}
func TestJSON_Array(t *testing.T) {
rec := httptest.NewRecorder()
data := []int{1, 2, 3, 4, 5}
err := JSON(rec, http.StatusOK, data)
if err != nil {
t.Errorf("JSON() error = %v", err)
}
var result []int
if err := json.Unmarshal(rec.Body.Bytes(), &result); err != nil {
t.Errorf("Failed to parse JSON array: %v", err)
}
if len(result) != 5 {
t.Errorf("Array length = %d, want 5", len(result))
}
}
func TestJSONOk(t *testing.T) {
rec := httptest.NewRecorder()
data := map[string]string{"status": "ok"}
err := JSONOk(rec, data)
if err != nil {
t.Errorf("JSONOk() error = %v", err)
}
if rec.Code != http.StatusOK {
t.Errorf("Status = %d, want %d", rec.Code, http.StatusOK)
}
contentType := rec.Header().Get(c.HeaderContentType)
if contentType != c.ContentTypeJSON {
t.Errorf("Content-Type = %q, want %q", contentType, c.ContentTypeJSON)
}
}
func TestJSONCached(t *testing.T) {
tests := []struct {
name string
maxAge int
}{
{"30 seconds", 30},
{"1 minute", 60},
{"1 hour", 3600},
{"1 day", 86400},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
rec := httptest.NewRecorder()
data := map[string]string{"data": "cached"}
err := JSONCached(rec, data, tt.maxAge)
if err != nil {
t.Errorf("JSONCached() error = %v", err)
}
if rec.Code != http.StatusOK {
t.Errorf("Status = %d, want %d", rec.Code, http.StatusOK)
}
cacheControl := rec.Header().Get(c.HeaderCacheControl)
expectedCache := "public, max-age="
if !strings.HasPrefix(cacheControl, expectedCache) {
t.Errorf("Cache-Control = %q, want prefix %q", cacheControl, expectedCache)
}
// Verify it contains the correct max-age value
expectedValue := "max-age=" + string(rune(tt.maxAge+'0'))
if tt.maxAge > 9 {
// For multi-digit numbers, just check it starts correctly
if !strings.Contains(cacheControl, "max-age=") {
t.Errorf("Cache-Control doesn't contain max-age")
}
}
_ = expectedValue
})
}
}
func TestHTML(t *testing.T) {
rec := httptest.NewRecorder()
HTML(rec)
contentType := rec.Header().Get(c.HeaderContentType)
if contentType != c.ContentTypeHTML {
t.Errorf("Content-Type = %q, want %q", contentType, c.ContentTypeHTML)
}
}
func TestNoContent(t *testing.T) {
rec := httptest.NewRecorder()
NoContent(rec)
if rec.Code != http.StatusNoContent {
t.Errorf("Status = %d, want %d", rec.Code, http.StatusNoContent)
}
// 204 No Content should have empty body
if rec.Body.Len() != 0 {
t.Errorf("Body should be empty for 204 No Content, got %q", rec.Body.String())
}
}
func TestJSON_NestedStruct(t *testing.T) {
type Inner struct {
Value string `json:"value"`
}
type Outer struct {
Name string `json:"name"`
Inner Inner `json:"inner"`
Values []int `json:"values"`
}
rec := httptest.NewRecorder()
data := Outer{
Name: "test",
Inner: Inner{Value: "nested"},
Values: []int{1, 2, 3},
}
err := JSON(rec, http.StatusOK, data)
if err != nil {
t.Errorf("JSON() error = %v", err)
}
var result Outer
if err := json.Unmarshal(rec.Body.Bytes(), &result); err != nil {
t.Errorf("Failed to parse nested JSON: %v", err)
}
if result.Name != "test" {
t.Errorf("Name = %q, want %q", result.Name, "test")
}
if result.Inner.Value != "nested" {
t.Errorf("Inner.Value = %q, want %q", result.Inner.Value, "nested")
}
if len(result.Values) != 3 {
t.Errorf("Values length = %d, want 3", len(result.Values))
}
}