From d929729d7969269891396a62160f9fa4872c304f Mon Sep 17 00:00:00 2001 From: Claude Code Date: Tue, 13 Jan 2026 22:24:37 +0100 Subject: [PATCH] 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 --- event_test.go | 160 +++++++++++++++++++++++++++++ examples/version_conflict_retry.go | 2 +- 2 files changed, 161 insertions(+), 1 deletion(-) diff --git a/event_test.go b/event_test.go index 6590daf..511ecac 100644 --- a/event_test.go +++ b/event_test.go @@ -2,6 +2,7 @@ package aether import ( "encoding/json" + "errors" "strings" "testing" "time" @@ -1335,3 +1336,162 @@ func TestReplayError_WithLargeRawData(t *testing.T) { // Error() should still work _ = 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) + } + }) + } +} diff --git a/examples/version_conflict_retry.go b/examples/version_conflict_retry.go index ddd11a2..d2795b9 100644 --- a/examples/version_conflict_retry.go +++ b/examples/version_conflict_retry.go @@ -322,7 +322,7 @@ func main() { eventStore := store.NewInMemoryEventStore() actorID := "order-123" - fmt.Println("=== Version Conflict Retry Patterns ===\n") + fmt.Println("=== Version Conflict Retry Patterns ===") // Demonstrate patterns (using simplified versions) log.Println("Pattern 1: Simple Exponential Backoff")