package response import ( "encoding/json" "net/http" "net/http/httptest" "testing" "github.com/gofiber/fiber/v2" ) func parseBody(t *testing.T, resp *http.Response) map[string]any { t.Helper() var body map[string]any if err := json.NewDecoder(resp.Body).Decode(&body); err != nil { t.Fatalf("failed to parse response body: %v", err) } return body } func TestErrorWithDetailsResponseShape(t *testing.T) { app := fiber.New() app.Get("/test", func(c *fiber.Ctx) error { return ErrorWithDetails( c, fiber.StatusConflict, "conflict", "resource already exists", map[string]any{"field": "email"}, ) }) req := httptest.NewRequest(http.MethodGet, "/test", nil) resp, err := app.Test(req) if err != nil { t.Fatalf("request failed: %v", err) } if resp.StatusCode != http.StatusConflict { t.Fatalf("unexpected status code: %d", resp.StatusCode) } body := parseBody(t, resp) if body["error"] != "resource already exists" { t.Fatalf("unexpected error value: %v", body["error"]) } if body["code"] != "conflict" { t.Fatalf("unexpected code value: %v", body["code"]) } details, ok := body["details"].(map[string]any) if !ok { t.Fatalf("details should be map, got: %T", body["details"]) } if details["field"] != "email" { t.Fatalf("unexpected details value: %v", details["field"]) } } func TestErrorResponseShape(t *testing.T) { app := fiber.New() app.Get("/test", func(c *fiber.Ctx) error { return Error(c, fiber.StatusUnauthorized, "invalid_session", "login required") }) req := httptest.NewRequest(http.MethodGet, "/test", nil) resp, err := app.Test(req) if err != nil { t.Fatalf("request failed: %v", err) } if resp.StatusCode != http.StatusUnauthorized { t.Fatalf("unexpected status code: %d", resp.StatusCode) } body := parseBody(t, resp) if body["error"] != "login required" { t.Fatalf("unexpected error value: %v", body["error"]) } if body["code"] != "invalid_session" { t.Fatalf("unexpected code value: %v", body["code"]) } if _, exists := body["details"]; exists { t.Fatalf("details should be omitted when nil: %#v", body) } } func TestStatusCodeMapping(t *testing.T) { tests := []struct { name string status int expected string }{ {name: "bad request", status: fiber.StatusBadRequest, expected: "bad_request"}, {name: "unauthorized", status: fiber.StatusUnauthorized, expected: "invalid_session"}, {name: "forbidden", status: fiber.StatusForbidden, expected: "forbidden"}, {name: "not found", status: fiber.StatusNotFound, expected: "not_found"}, {name: "conflict", status: fiber.StatusConflict, expected: "conflict"}, {name: "too many requests", status: fiber.StatusTooManyRequests, expected: "rate_limited"}, {name: "service unavailable", status: fiber.StatusServiceUnavailable, expected: "service_unavailable"}, {name: "fallback", status: fiber.StatusInternalServerError, expected: "internal_error"}, } for _, tc := range tests { t.Run(tc.name, func(t *testing.T) { got := StatusCode(tc.status) if got != tc.expected { t.Fatalf("unexpected code: got=%s expected=%s", got, tc.expected) } }) } }