StreamRecorder/pkg/httpClient/error_test.go
2025-08-11 00:06:34 -05:00

319 lines
7.0 KiB
Go

package httpClient
import (
"fmt"
"net/http"
"strings"
"testing"
)
func TestHTTPError_Error(t *testing.T) {
tests := []struct {
name string
statusCode int
message string
want string
}{
{
name: "basic http error",
statusCode: 404,
message: "Not Found",
want: "HTTP 404: Not Found",
},
{
name: "server error",
statusCode: 500,
message: "Internal Server Error",
want: "HTTP 500: Internal Server Error",
},
{
name: "unauthorized error",
statusCode: 401,
message: "Unauthorized",
want: "HTTP 401: Unauthorized",
},
{
name: "empty message",
statusCode: 400,
message: "",
want: "HTTP 400: ",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := &HTTPError{
StatusCode: tt.statusCode,
Message: tt.message,
}
got := err.Error()
if got != tt.want {
t.Errorf("HTTPError.Error() = %q, want %q", got, tt.want)
}
})
}
}
func TestHTTPError_Is(t *testing.T) {
err404 := &HTTPError{StatusCode: 404, Message: "Not Found"}
err500 := &HTTPError{StatusCode: 500, Message: "Server Error"}
otherErr404 := &HTTPError{StatusCode: 404, Message: "Different message"}
regularError := fmt.Errorf("regular error")
tests := []struct {
name string
err error
target error
want bool
}{
{
name: "same error instance",
err: err404,
target: err404,
want: true,
},
{
name: "different HTTP errors with same status",
err: err404,
target: otherErr404,
want: true,
},
{
name: "different HTTP errors with different status",
err: err404,
target: err500,
want: false,
},
{
name: "HTTP error vs regular error",
err: err404,
target: regularError,
want: false,
},
{
name: "regular error vs HTTP error",
err: regularError,
target: err404,
want: false,
},
{
name: "nil target",
err: err404,
target: nil,
want: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var got bool
if httpErr, ok := tt.err.(*HTTPError); ok {
got = httpErr.Is(tt.target)
} else {
got = false // Non-HTTP errors return false
}
if got != tt.want {
t.Errorf("HTTPError.Is() = %v, want %v", got, tt.want)
}
})
}
}
func TestIsHTTPError(t *testing.T) {
httpErr := &HTTPError{StatusCode: 404, Message: "Not Found"}
regularErr := fmt.Errorf("regular error")
tests := []struct {
name string
err error
want bool
}{
{
name: "http error",
err: httpErr,
want: true,
},
{
name: "regular error",
err: regularErr,
want: false,
},
{
name: "nil error",
err: nil,
want: false,
},
{
name: "wrapped http error",
err: fmt.Errorf("wrapped: %w", httpErr),
want: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := IsHTTPError(tt.err)
if got != tt.want {
t.Errorf("IsHTTPError() = %v, want %v", got, tt.want)
}
})
}
}
func TestGetHTTPStatusCode(t *testing.T) {
tests := []struct {
name string
err error
want int
}{
{
name: "http error 404",
err: &HTTPError{StatusCode: 404, Message: "Not Found"},
want: 404,
},
{
name: "http error 500",
err: &HTTPError{StatusCode: 500, Message: "Server Error"},
want: 500,
},
{
name: "wrapped http error",
err: fmt.Errorf("wrapped: %w", &HTTPError{StatusCode: 403, Message: "Forbidden"}),
want: 403,
},
{
name: "regular error",
err: fmt.Errorf("regular error"),
want: 0,
},
{
name: "nil error",
err: nil,
want: 0,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := GetHTTPStatusCode(tt.err)
if got != tt.want {
t.Errorf("GetHTTPStatusCode() = %v, want %v", got, tt.want)
}
})
}
}
func TestNewHTTPError(t *testing.T) {
statusCode := 404
message := "Page not found"
err := NewHTTPError(statusCode, message)
// Check type
httpErr, ok := err.(*HTTPError)
if !ok {
t.Fatalf("NewHTTPError should return *HTTPError, got %T", err)
}
// Check fields
if httpErr.StatusCode != statusCode {
t.Errorf("Expected StatusCode=%d, got %d", statusCode, httpErr.StatusCode)
}
if httpErr.Message != message {
t.Errorf("Expected Message=%q, got %q", message, httpErr.Message)
}
// Check error string
expectedErrorString := fmt.Sprintf("HTTP %d: %s", statusCode, message)
if httpErr.Error() != expectedErrorString {
t.Errorf("Expected error string=%q, got %q", expectedErrorString, httpErr.Error())
}
}
func TestHTTPError_StatusCodeChecks(t *testing.T) {
tests := []struct {
name string
statusCode int
isClient bool
isServer bool
}{
{"200 OK", 200, false, false},
{"400 Bad Request", 400, true, false},
{"401 Unauthorized", 401, true, false},
{"404 Not Found", 404, true, false},
{"499 Client Error", 499, true, false},
{"500 Server Error", 500, false, true},
{"502 Bad Gateway", 502, false, true},
{"599 Server Error", 599, false, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := &HTTPError{StatusCode: tt.statusCode, Message: "test"}
isClient := err.StatusCode >= 400 && err.StatusCode < 500
isServer := err.StatusCode >= 500 && err.StatusCode < 600
if isClient != tt.isClient {
t.Errorf("Status %d: expected isClient=%v, got %v", tt.statusCode, tt.isClient, isClient)
}
if isServer != tt.isServer {
t.Errorf("Status %d: expected isServer=%v, got %v", tt.statusCode, tt.isServer, isServer)
}
})
}
}
func TestHTTPError_Integration(t *testing.T) {
// Test that HTTPError integrates well with standard error handling
err := NewHTTPError(http.StatusNotFound, "Resource not found")
// Should be able to use with errors.Is
target := &HTTPError{StatusCode: http.StatusNotFound}
if !err.(*HTTPError).Is(target) {
t.Error("HTTPError should match target with same status code")
}
// Should be detectable as HTTPError
if !IsHTTPError(err) {
t.Error("Should be detectable as HTTPError")
}
// Should return correct status code
if GetHTTPStatusCode(err) != http.StatusNotFound {
t.Error("Should return correct status code")
}
// Should have meaningful string representation
errorString := err.Error()
if !strings.Contains(errorString, "404") {
t.Error("Error string should contain status code")
}
if !strings.Contains(errorString, "Resource not found") {
t.Error("Error string should contain message")
}
}
func TestHTTPError_EdgeCases(t *testing.T) {
// Test with zero status code
err := NewHTTPError(0, "Zero status")
if err.Error() != "HTTP 0: Zero status" {
t.Errorf("Unexpected error string for zero status: %s", err.Error())
}
// Test with very long message
longMessage := strings.Repeat("a", 1000)
err = NewHTTPError(500, longMessage)
if !strings.Contains(err.Error(), longMessage) {
t.Error("Long message should be preserved")
}
// Test status code boundaries
for _, code := range []int{399, 400, 499, 500, 599, 600} {
err := &HTTPError{StatusCode: code}
// Should not panic
_ = err.Error()
}
}