319 lines
7.0 KiB
Go
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()
|
|
}
|
|
}
|