diff --git a/errors.go b/errors.go index b910d50..a9e3ad0 100644 --- a/errors.go +++ b/errors.go @@ -2,6 +2,7 @@ package main import ( "encoding/json" + "errors" "fmt" "time" ) @@ -121,7 +122,8 @@ func IsRetryable(err error) bool { return false } - if proxyErr, ok := err.(*ProxyError); ok { + var proxyErr *ProxyError + if errors.As(err, &proxyErr) { return proxyErr.Retryable } @@ -134,7 +136,8 @@ func GetStatusCode(err error) int { return 200 } - if proxyErr, ok := err.(*ProxyError); ok { + var proxyErr *ProxyError + if errors.As(err, &proxyErr) { return proxyErr.StatusCode } diff --git a/errors_test.go b/errors_test.go index 58731ab..6b4ba43 100644 --- a/errors_test.go +++ b/errors_test.go @@ -2,6 +2,7 @@ package main import ( "errors" + "fmt" "net/http" "testing" @@ -241,3 +242,21 @@ func TestProxyError_Retryable(t *testing.T) { }) } } + +// Test_IsRetryable_GetStatusCode_Wrapped verifies that retryability and status +// are correctly recovered from a ProxyError that has been wrapped (e.g. via +// %w), using errors.As rather than a direct type assertion. +func Test_IsRetryable_GetStatusCode_Wrapped(t *testing.T) { + base := NewProxyError(ErrCodeBackendError, "boom", 503, true) + wrapped := fmt.Errorf("request failed: %w", base) + assert.True(t, IsRetryable(wrapped), "retryable flag must survive wrapping") + assert.Equal(t, 503, GetStatusCode(wrapped), "status code must survive wrapping") + + nonRetryable := NewProxyError(ErrCodeInvalidRequest, "bad", 400, false) + wrapped2 := fmt.Errorf("wrapped: %w", nonRetryable) + assert.False(t, IsRetryable(wrapped2)) + assert.Equal(t, 400, GetStatusCode(wrapped2)) + + assert.False(t, IsRetryable(nil)) + assert.Equal(t, 200, GetStatusCode(nil)) +}