fix: address review feedback
- Remove redundant newline from fmt.Println at line 325 - Add 9 comprehensive VersionConflictError tests to event_test.go: * Error message formatting with context fields * Field accessibility (ActorID, AttemptedVersion, CurrentVersion) * Unwrap() method and error wrapping * Sentinel error checking with errors.Is() * Type assertion support with errors.As() * Retry logic extraction of CurrentVersion * Special character handling in ActorID Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
160
event_test.go
160
event_test.go
@@ -2,6 +2,7 @@ package aether
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
@@ -1335,3 +1336,162 @@ func TestReplayError_WithLargeRawData(t *testing.T) {
|
|||||||
// Error() should still work
|
// Error() should still work
|
||||||
_ = err.Error()
|
_ = err.Error()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tests for VersionConflictError
|
||||||
|
|
||||||
|
func TestVersionConflictError_ErrorMessage(t *testing.T) {
|
||||||
|
err := &VersionConflictError{
|
||||||
|
ActorID: "order-123",
|
||||||
|
CurrentVersion: 5,
|
||||||
|
AttemptedVersion: 5,
|
||||||
|
}
|
||||||
|
|
||||||
|
msg := err.Error()
|
||||||
|
if !strings.Contains(msg, "order-123") {
|
||||||
|
t.Errorf("expected ActorID in message, got: %s", msg)
|
||||||
|
}
|
||||||
|
if !strings.Contains(msg, "5") {
|
||||||
|
t.Errorf("expected versions in message, got: %s", msg)
|
||||||
|
}
|
||||||
|
if !strings.Contains(msg, "version conflict") {
|
||||||
|
t.Errorf("expected 'version conflict' in message, got: %s", msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestVersionConflictError_ActorIDField(t *testing.T) {
|
||||||
|
err := &VersionConflictError{
|
||||||
|
ActorID: "test-actor-456",
|
||||||
|
CurrentVersion: 10,
|
||||||
|
AttemptedVersion: 11,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err.ActorID != "test-actor-456" {
|
||||||
|
t.Errorf("ActorID field access failed: got %q, want %q", err.ActorID, "test-actor-456")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestVersionConflictError_AttemptedVersionField(t *testing.T) {
|
||||||
|
err := &VersionConflictError{
|
||||||
|
ActorID: "actor-123",
|
||||||
|
CurrentVersion: 5,
|
||||||
|
AttemptedVersion: 99,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err.AttemptedVersion != 99 {
|
||||||
|
t.Errorf("AttemptedVersion field access failed: got %d, want %d", err.AttemptedVersion, 99)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestVersionConflictError_CurrentVersionField(t *testing.T) {
|
||||||
|
err := &VersionConflictError{
|
||||||
|
ActorID: "actor-123",
|
||||||
|
CurrentVersion: 42,
|
||||||
|
AttemptedVersion: 43,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err.CurrentVersion != 42 {
|
||||||
|
t.Errorf("CurrentVersion field access failed: got %d, want %d", err.CurrentVersion, 42)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestVersionConflictError_Unwrap(t *testing.T) {
|
||||||
|
err := &VersionConflictError{
|
||||||
|
ActorID: "order-456",
|
||||||
|
CurrentVersion: 3,
|
||||||
|
AttemptedVersion: 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
unwrapped := err.Unwrap()
|
||||||
|
if !errors.Is(unwrapped, ErrVersionConflict) {
|
||||||
|
t.Errorf("expected Unwrap to return ErrVersionConflict sentinel")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestVersionConflictError_ErrorsIs(t *testing.T) {
|
||||||
|
err := &VersionConflictError{
|
||||||
|
ActorID: "actor-789",
|
||||||
|
CurrentVersion: 7,
|
||||||
|
AttemptedVersion: 8,
|
||||||
|
}
|
||||||
|
|
||||||
|
if !errors.Is(err, ErrVersionConflict) {
|
||||||
|
t.Error("expected errors.Is to recognize VersionConflictError as ErrVersionConflict")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestVersionConflictError_ErrorsAs(t *testing.T) {
|
||||||
|
originalErr := &VersionConflictError{
|
||||||
|
ActorID: "user-123",
|
||||||
|
CurrentVersion: 20,
|
||||||
|
AttemptedVersion: 21,
|
||||||
|
}
|
||||||
|
|
||||||
|
var versionErr *VersionConflictError
|
||||||
|
if !errors.As(originalErr, &versionErr) {
|
||||||
|
t.Fatal("expected errors.As to extract VersionConflictError")
|
||||||
|
}
|
||||||
|
|
||||||
|
if versionErr.ActorID != "user-123" {
|
||||||
|
t.Errorf("extracted ActorID mismatch: got %q, want %q", versionErr.ActorID, "user-123")
|
||||||
|
}
|
||||||
|
if versionErr.CurrentVersion != 20 {
|
||||||
|
t.Errorf("extracted CurrentVersion mismatch: got %d, want %d", versionErr.CurrentVersion, 20)
|
||||||
|
}
|
||||||
|
if versionErr.AttemptedVersion != 21 {
|
||||||
|
t.Errorf("extracted AttemptedVersion mismatch: got %d, want %d", versionErr.AttemptedVersion, 21)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestVersionConflictError_RetryLogicWithCurrentVersion(t *testing.T) {
|
||||||
|
// Demonstrates how applications extract CurrentVersion for retry logic
|
||||||
|
err := &VersionConflictError{
|
||||||
|
ActorID: "order-555",
|
||||||
|
CurrentVersion: 15,
|
||||||
|
AttemptedVersion: 16,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Application can check for conflict and use CurrentVersion
|
||||||
|
if errors.Is(err, ErrVersionConflict) {
|
||||||
|
var versionErr *VersionConflictError
|
||||||
|
if errors.As(err, &versionErr) {
|
||||||
|
// Application uses CurrentVersion to determine next attempt
|
||||||
|
nextVersion := versionErr.CurrentVersion + 1
|
||||||
|
if nextVersion != 16 {
|
||||||
|
t.Errorf("retry version calculation failed: got %d, want %d", nextVersion, 16)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestVersionConflictError_SpecialCharactersInActorID(t *testing.T) {
|
||||||
|
specialChars := []string{
|
||||||
|
"actor-with-dashes",
|
||||||
|
"actor_with_underscores",
|
||||||
|
"actor.with.dots",
|
||||||
|
"actor@special",
|
||||||
|
"actor with spaces",
|
||||||
|
"actor:with:colons",
|
||||||
|
"actor/with/slashes",
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, actorID := range specialChars {
|
||||||
|
t.Run(actorID, func(t *testing.T) {
|
||||||
|
err := &VersionConflictError{
|
||||||
|
ActorID: actorID,
|
||||||
|
CurrentVersion: 1,
|
||||||
|
AttemptedVersion: 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify fields are preserved
|
||||||
|
if err.ActorID != actorID {
|
||||||
|
t.Errorf("ActorID not preserved: got %q, want %q", err.ActorID, actorID)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify message includes the ActorID
|
||||||
|
msg := err.Error()
|
||||||
|
if !strings.Contains(msg, actorID) {
|
||||||
|
t.Errorf("ActorID not in error message for %q: %s", actorID, msg)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -322,7 +322,7 @@ func main() {
|
|||||||
eventStore := store.NewInMemoryEventStore()
|
eventStore := store.NewInMemoryEventStore()
|
||||||
actorID := "order-123"
|
actorID := "order-123"
|
||||||
|
|
||||||
fmt.Println("=== Version Conflict Retry Patterns ===\n")
|
fmt.Println("=== Version Conflict Retry Patterns ===")
|
||||||
|
|
||||||
// Demonstrate patterns (using simplified versions)
|
// Demonstrate patterns (using simplified versions)
|
||||||
log.Println("Pattern 1: Simple Exponential Backoff")
|
log.Println("Pattern 1: Simple Exponential Backoff")
|
||||||
|
|||||||
Reference in New Issue
Block a user