package errors import ( "errors" "testing" ) func TestNew(t *testing.T) { e := New(CategoryInternal, "something went wrong") if e.Category != CategoryInternal { t.Errorf("Expected category Internal, got %v", e.Category) } if e.Message != "something went wrong" { t.Errorf("Expected message 'something went wrong', got '%s'", e.Message) } if e.StatusCode != 500 { t.Errorf("Expected status code 500, got %d", e.StatusCode) } if e.Retryable { t.Error("Internal errors should not be retryable by default") } } func TestNewWithCause(t *testing.T) { cause := errors.New("underlying error") e := NewWithCause(CategoryUpstream, "upstream failed", cause) if e.Cause != cause { t.Error("Cause not set correctly") } expectedMsg := "[upstream] upstream failed: underlying error" if e.Error() != expectedMsg { t.Errorf("Expected error message '%s', got '%s'", expectedMsg, e.Error()) } } func TestWrap(t *testing.T) { original := errors.New("original error") wrapped := Wrap(original, CategoryNetwork, "network issue") if wrapped == nil { t.Fatal("Wrap returned nil for non-nil error") } if wrapped.Cause != original { t.Error("Wrapped error should preserve original cause") } pe := New(CategoryRateLimit, "rate limited") wrapped2 := Wrap(pe, CategoryUnknown, "wrapped") if wrapped2 != pe { t.Error("Wrapping PipelineError should return the same error") } } func TestWrapNil(t *testing.T) { wrapped := Wrap(nil, CategoryInternal, "test") if wrapped != nil { t.Error("Wrapping nil should return nil") } } func TestErrorHelpers(t *testing.T) { tests := []struct { name string err *PipelineError expectedCat ErrorCategory expectedStatus int expectedRetry bool }{ {"Auth", AuthError("unauthorized"), CategoryAuth, 401, false}, {"RateLimit", RateLimitError("too many requests"), CategoryRateLimit, 429, true}, {"Validation", ValidationError("invalid input"), CategoryValidation, 400, false}, {"Upstream", UpstreamError("provider down"), CategoryUpstream, 502, true}, {"Internal", InternalError("server error"), CategoryInternal, 500, false}, {"Network", NetworkError("connection lost"), CategoryNetwork, 503, true}, {"Timeout", TimeoutError("request timeout"), CategoryTimeout, 504, true}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if tt.err.Category != tt.expectedCat { t.Errorf("Expected category %v, got %v", tt.expectedCat, tt.err.Category) } if tt.err.StatusCode != tt.expectedStatus { t.Errorf("Expected status %d, got %d", tt.expectedStatus, tt.err.StatusCode) } if tt.err.Retryable != tt.expectedRetry { t.Errorf("Expected retryable=%v, got %v", tt.expectedRetry, tt.err.Retryable) } }) } } func TestWithContext(t *testing.T) { e := New(CategoryValidation, "invalid field"). WithContext("field", "email"). WithContext("value", "invalid-email") if e.Context == nil { t.Fatal("Context should not be nil") } if e.Context["field"] != "email" { t.Errorf("Expected field='email', got %v", e.Context["field"]) } } func TestIsPipelineError(t *testing.T) { pe := New(CategoryInternal, "test") result, ok := IsPipelineError(pe) if !ok { t.Error("IsPipelineError should return true for PipelineError") } if result != pe { t.Error("IsPipelineError should return the same error") } stdErr := errors.New("standard error") _, ok = IsPipelineError(stdErr) if ok { t.Error("IsPipelineError should return false for standard error") } } func TestIsRetryableError(t *testing.T) { rateLimit := RateLimitError("test") if !IsRetryableError(rateLimit) { t.Error("Rate limit error should be retryable") } auth := AuthError("test") if IsRetryableError(auth) { t.Error("Auth error should not be retryable") } }