fix: address review feedback

- Removed duplicate blank line in event.go
- Use original event timestamp instead of time.Now() for EventStored
- Fixed MockEventBroadcaster.Subscribe to return nil instead of closed channel
- Added integration tests for EventStored with JetStreamEventStore

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
Claude Code
2026-01-13 22:25:10 +01:00
parent 0f89b07c0b
commit 5223cf136a
5 changed files with 213 additions and 9 deletions

View File

@@ -1536,3 +1536,209 @@ func BenchmarkJetStreamEventStore_GetEvents(b *testing.B) {
}
}
}
// === JetStreamEventStore EventStored Integration Tests ===
// TestJetStreamEventStored_PublishedAfterSaveSuccess tests that EventStored is published after successful SaveEvent
func TestJetStreamEventStored_PublishedAfterSaveSuccess(t *testing.T) {
nc := getTestNATSConnection(t)
if nc == nil {
return
}
defer nc.Close()
streamName := fmt.Sprintf("test-eventstored-%d", time.Now().UnixNano())
defer cleanupStream(nc, streamName)
store, err := NewJetStreamEventStore(nc, streamName)
if err != nil {
t.Fatalf("failed to create store: %v", err)
}
mockBus := NewMockEventBroadcaster()
store.WithEventBus(mockBus)
event := &aether.Event{
ID: "evt-123",
EventType: "OrderPlaced",
ActorID: "order-456",
Version: 1,
Data: map[string]interface{}{"total": 100.50},
Timestamp: time.Now(),
}
err = store.SaveEvent(event)
if err != nil {
t.Fatalf("SaveEvent failed: %v", err)
}
// Verify EventStored was published
published := mockBus.GetPublishedEvents()
if len(published) != 1 {
t.Fatalf("expected 1 published event, got %d", len(published))
}
storedEvent := published[0]
if storedEvent.EventType != "EventStored" {
t.Errorf("expected EventType 'EventStored', got %q", storedEvent.EventType)
}
if storedEvent.ActorID != "order-456" {
t.Errorf("expected ActorID 'order-456', got %q", storedEvent.ActorID)
}
if storedEvent.Data["eventId"] != "evt-123" {
t.Errorf("expected eventId 'evt-123', got %v", storedEvent.Data["eventId"])
}
if storedEvent.Data["version"] != int64(1) {
t.Errorf("expected version 1, got %v", storedEvent.Data["version"])
}
}
// TestJetStreamEventStored_PreservesOriginalTimestamp tests that EventStored preserves the original event's timestamp
func TestJetStreamEventStored_PreservesOriginalTimestamp(t *testing.T) {
nc := getTestNATSConnection(t)
if nc == nil {
return
}
defer nc.Close()
streamName := fmt.Sprintf("test-timestamp-%d", time.Now().UnixNano())
defer cleanupStream(nc, streamName)
store, err := NewJetStreamEventStore(nc, streamName)
if err != nil {
t.Fatalf("failed to create store: %v", err)
}
mockBus := NewMockEventBroadcaster()
store.WithEventBus(mockBus)
// Use a fixed timestamp in the past
pastTime := time.Now().Add(-1 * time.Hour)
event := &aether.Event{
ID: "evt-123",
EventType: "TestEvent",
ActorID: "actor-1",
Version: 1,
Data: map[string]interface{}{},
Timestamp: pastTime,
}
err = store.SaveEvent(event)
if err != nil {
t.Fatalf("SaveEvent failed: %v", err)
}
published := mockBus.GetPublishedEvents()
if len(published) != 1 {
t.Fatalf("expected 1 published event, got %d", len(published))
}
storedEvent := published[0]
publishedTimestamp := storedEvent.Data["timestamp"].(time.Time)
// Check timestamp is preserved exactly
if !publishedTimestamp.Equal(pastTime) {
t.Errorf("expected timestamp %v, got %v", pastTime, publishedTimestamp)
}
}
// TestJetStreamEventStored_MultipleEventsPublished tests that multiple EventStored events are published in order
func TestJetStreamEventStored_MultipleEventsPublished(t *testing.T) {
nc := getTestNATSConnection(t)
if nc == nil {
return
}
defer nc.Close()
streamName := fmt.Sprintf("test-multi-%d", time.Now().UnixNano())
defer cleanupStream(nc, streamName)
store, err := NewJetStreamEventStore(nc, streamName)
if err != nil {
t.Fatalf("failed to create store: %v", err)
}
mockBus := NewMockEventBroadcaster()
store.WithEventBus(mockBus)
// Save 5 events
for i := 1; i <= 5; i++ {
event := &aether.Event{
ID: fmt.Sprintf("evt-%d", i),
EventType: "TestEvent",
ActorID: "actor-1",
Version: int64(i),
Data: map[string]interface{}{},
Timestamp: time.Now(),
}
if err := store.SaveEvent(event); err != nil {
t.Fatalf("SaveEvent %d failed: %v", i, err)
}
}
// Verify 5 EventStored events were published
published := mockBus.GetPublishedEvents()
if len(published) != 5 {
t.Fatalf("expected 5 published events, got %d", len(published))
}
// Verify each has correct data
for i := 0; i < 5; i++ {
if published[i].Data["version"] != int64(i+1) {
t.Errorf("event %d: expected version %d, got %v", i, i+1, published[i].Data["version"])
}
}
}
// TestJetStreamEventStored_NotPublishedOnVersionConflict tests that EventStored is not published on version conflict
func TestJetStreamEventStored_NotPublishedOnVersionConflict(t *testing.T) {
nc := getTestNATSConnection(t)
if nc == nil {
return
}
defer nc.Close()
streamName := fmt.Sprintf("test-conflict-%d", time.Now().UnixNano())
defer cleanupStream(nc, streamName)
store, err := NewJetStreamEventStore(nc, streamName)
if err != nil {
t.Fatalf("failed to create store: %v", err)
}
mockBus := NewMockEventBroadcaster()
store.WithEventBus(mockBus)
// Save first event
event1 := &aether.Event{
ID: "evt-1",
EventType: "TestEvent",
ActorID: "actor-1",
Version: 1,
Data: map[string]interface{}{},
Timestamp: time.Now(),
}
if err := store.SaveEvent(event1); err != nil {
t.Fatalf("First SaveEvent failed: %v", err)
}
// Try to save event with same version (conflict)
event2 := &aether.Event{
ID: "evt-2",
EventType: "TestEvent",
ActorID: "actor-1",
Version: 1, // Same version - should conflict
Data: map[string]interface{}{},
Timestamp: time.Now(),
}
err = store.SaveEvent(event2)
if err == nil {
t.Fatal("expected VersionConflictError, got nil")
}
// Verify only 1 EventStored was published (from first event)
published := mockBus.GetPublishedEvents()
if len(published) != 1 {
t.Fatalf("expected 1 published event after conflict, got %d", len(published))
}
}