1
0
mirror of https://github.com/redis/go-redis.git synced 2025-11-26 06:23:09 +03:00

feat(errors): Introduce typed errors (#3602)

* typed errors

* add error documentation

* backwards compatibility

* update readme, remove Is methods

* Update internal/proto/redis_errors.go

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update internal/proto/redis_errors.go

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* support error wrapping for io and context errors

* use unwrapping of errors in push for consistency

* add common error types

* fix test

* fix flaky test

* add comments in the example

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Nedyalko Dyakov
2025-11-19 17:31:54 +02:00
committed by GitHub
parent 817e62455b
commit 6c24f600de
11 changed files with 2069 additions and 68 deletions

View File

@@ -2,6 +2,7 @@ package push
import (
"context"
"fmt"
"testing"
)
@@ -313,3 +314,85 @@ func (h *UnitTestHandler) Reset() {
h.lastNotification = nil
h.errorToReturn = nil
}
// TestErrorWrapping tests that error checking functions work with wrapped errors
func TestErrorWrapping(t *testing.T) {
t.Run("IsHandlerExistsError with wrapped error", func(t *testing.T) {
// Create a HandlerError
handlerErr := ErrHandlerExists("test-notification")
// Wrap it
wrappedErr := fmt.Errorf("operation failed: %w", handlerErr)
doubleWrappedErr := fmt.Errorf("context: %w", wrappedErr)
// Should still be detected through wrapping
if !IsHandlerExistsError(doubleWrappedErr) {
t.Errorf("IsHandlerExistsError should detect wrapped error")
}
// Verify it doesn't match other error types
if IsProtectedHandlerError(doubleWrappedErr) {
t.Errorf("IsProtectedHandlerError should not match handler exists error")
}
})
t.Run("IsProtectedHandlerError with wrapped error", func(t *testing.T) {
// Create a protected handler error
protectedErr := ErrProtectedHandler("protected-notification")
// Wrap it
wrappedErr := fmt.Errorf("unregister failed: %w", protectedErr)
// Should still be detected through wrapping
if !IsProtectedHandlerError(wrappedErr) {
t.Errorf("IsProtectedHandlerError should detect wrapped error")
}
// Verify it doesn't match other error types
if IsHandlerExistsError(wrappedErr) {
t.Errorf("IsHandlerExistsError should not match protected handler error")
}
})
t.Run("IsVoidProcessorError with wrapped error", func(t *testing.T) {
// Create a void processor error
voidErr := ErrVoidProcessorRegister("test-notification")
// Wrap it multiple times
wrappedErr := fmt.Errorf("register failed: %w", voidErr)
doubleWrappedErr := fmt.Errorf("processor error: %w", wrappedErr)
// Should still be detected through wrapping
if !IsVoidProcessorError(doubleWrappedErr) {
t.Errorf("IsVoidProcessorError should detect wrapped error")
}
})
t.Run("IsHandlerNilError with wrapped error", func(t *testing.T) {
// Wrap the nil handler error
wrappedErr := fmt.Errorf("validation failed: %w", ErrHandlerNil)
// Should still be detected through wrapping
if !IsHandlerNilError(wrappedErr) {
t.Errorf("IsHandlerNilError should detect wrapped error")
}
})
t.Run("Error functions return false for non-matching errors", func(t *testing.T) {
// Create a different error
otherErr := fmt.Errorf("some other error")
if IsHandlerExistsError(otherErr) {
t.Errorf("IsHandlerExistsError should return false for non-matching error")
}
if IsProtectedHandlerError(otherErr) {
t.Errorf("IsProtectedHandlerError should return false for non-matching error")
}
if IsVoidProcessorError(otherErr) {
t.Errorf("IsVoidProcessorError should return false for non-matching error")
}
if IsHandlerNilError(otherErr) {
t.Errorf("IsHandlerNilError should return false for non-matching error")
}
})
}