caddyhttp: error handling

Signed-off-by: Mohammed Al Sahaf <msaa1990@gmail.com>
This commit is contained in:
Mohammed Al Sahaf 2026-03-20 20:46:32 +03:00
parent 719d879f3d
commit 476d75219c
No known key found for this signature in database

View File

@ -0,0 +1,168 @@
package caddyhttp
import (
"errors"
"fmt"
"strings"
"testing"
)
func TestHandlerErrorError(t *testing.T) {
tests := []struct {
name string
err HandlerError
contains []string
}{
{
name: "full error",
err: HandlerError{
ID: "abc123",
StatusCode: 404,
Err: fmt.Errorf("not found"),
Trace: "pkg.Func (file.go:10)",
},
contains: []string{"abc123", "404", "not found", "pkg.Func"},
},
{
name: "empty error",
err: HandlerError{},
contains: []string{},
},
{
name: "error with only status code",
err: HandlerError{
StatusCode: 500,
},
contains: []string{"500"},
},
{
name: "error with only message",
err: HandlerError{
Err: fmt.Errorf("something broke"),
},
contains: []string{"something broke"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := tt.err.Error()
for _, needle := range tt.contains {
if !strings.Contains(result, needle) {
t.Errorf("Error() = %q, should contain %q", result, needle)
}
}
})
}
}
func TestHandlerErrorUnwrap(t *testing.T) {
originalErr := fmt.Errorf("original error")
he := HandlerError{Err: originalErr}
unwrapped := he.Unwrap()
if unwrapped != originalErr {
t.Errorf("Unwrap() = %v, want %v", unwrapped, originalErr)
}
}
func TestError(t *testing.T) {
t.Run("creates error with ID and trace", func(t *testing.T) {
err := fmt.Errorf("test error")
he := Error(500, err)
if he.StatusCode != 500 {
t.Errorf("StatusCode = %d, want 500", he.StatusCode)
}
if he.ID == "" {
t.Error("ID should not be empty")
}
if len(he.ID) != 9 {
t.Errorf("ID length = %d, want 9", len(he.ID))
}
if he.Trace == "" {
t.Error("Trace should not be empty")
}
if he.Err != err {
t.Error("Err should be the original error")
}
})
t.Run("unwraps existing HandlerError", func(t *testing.T) {
inner := HandlerError{
ID: "existing_id",
StatusCode: 404,
Err: fmt.Errorf("not found"),
Trace: "existing trace",
}
he := Error(500, inner)
// Should keep existing ID
if he.ID != "existing_id" {
t.Errorf("ID = %q, want 'existing_id'", he.ID)
}
// Should keep existing StatusCode
if he.StatusCode != 404 {
t.Errorf("StatusCode = %d, want 404 (existing)", he.StatusCode)
}
// Should keep existing Trace
if he.Trace != "existing trace" {
t.Errorf("Trace = %q, want 'existing trace'", he.Trace)
}
})
t.Run("fills missing fields in existing HandlerError", func(t *testing.T) {
inner := HandlerError{
Err: fmt.Errorf("inner error"),
// ID, StatusCode, and Trace are all empty
}
he := Error(503, inner)
if he.ID == "" {
t.Error("should fill missing ID")
}
if he.StatusCode != 503 {
t.Errorf("should fill missing StatusCode with %d, got %d", 503, he.StatusCode)
}
if he.Trace == "" {
t.Error("should fill missing Trace")
}
})
t.Run("generates unique IDs", func(t *testing.T) {
ids := make(map[string]struct{})
for i := 0; i < 100; i++ {
he := Error(500, fmt.Errorf("error %d", i))
if _, exists := ids[he.ID]; exists {
t.Errorf("duplicate ID generated: %s", he.ID)
}
ids[he.ID] = struct{}{}
}
})
}
func TestErrorAsHandlerError(t *testing.T) {
he := Error(404, fmt.Errorf("not found"))
var target HandlerError
if !errors.As(he, &target) {
t.Error("Error() result should be assertable as HandlerError via errors.As")
}
}
func TestHandlerErrorWithWrappedError(t *testing.T) {
// Test that errors.As can unwrap a wrapped HandlerError
inner := HandlerError{
ID: "inner",
StatusCode: 404,
Err: fmt.Errorf("inner error"),
}
wrapped := fmt.Errorf("wrapped: %w", inner)
he := Error(500, wrapped)
// Since wrapped contains a HandlerError, it should be unwrapped
if he.ID != "inner" {
t.Errorf("should unwrap to inner ID 'inner', got %q", he.ID)
}
}