Add event metadata support for distributed tracing and auditing
All checks were successful
CI / build (push) Successful in 15s
All checks were successful
CI / build (push) Successful in 15s
- Add Metadata field (map[string]string) to Event struct with omitempty - Add helper methods for common metadata: SetCorrelationID/GetCorrelationID, SetCausationID/GetCausationID, SetUserID/GetUserID, SetTraceID/GetTraceID, SetSpanID/GetSpanID - Add WithMetadataFrom helper for copying metadata between events - Add metadata key constants for standard fields - Add comprehensive unit tests for metadata serialization and helpers - Add store tests verifying metadata persistence Closes #7 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit was merged in pull request #33.
This commit is contained in:
@@ -1686,3 +1686,222 @@ func TestConcurrentSaveAndGetSnapshot(t *testing.T) {
|
||||
t.Errorf("expected latest version %d, got %d", expectedMaxVersion, snapshot.Version)
|
||||
}
|
||||
}
|
||||
|
||||
// === Event Metadata Persistence Tests ===
|
||||
|
||||
func TestSaveEvent_WithMetadata(t *testing.T) {
|
||||
store := NewInMemoryEventStore()
|
||||
|
||||
event := &aether.Event{
|
||||
ID: "evt-meta",
|
||||
EventType: "OrderPlaced",
|
||||
ActorID: "order-456",
|
||||
Version: 1,
|
||||
Data: map[string]interface{}{
|
||||
"total": 100.50,
|
||||
},
|
||||
Metadata: map[string]string{
|
||||
"correlationId": "corr-123",
|
||||
"causationId": "cause-456",
|
||||
"userId": "user-789",
|
||||
"traceId": "trace-abc",
|
||||
"spanId": "span-def",
|
||||
},
|
||||
Timestamp: time.Now(),
|
||||
}
|
||||
|
||||
err := store.SaveEvent(event)
|
||||
if err != nil {
|
||||
t.Fatalf("SaveEvent failed: %v", err)
|
||||
}
|
||||
|
||||
// Retrieve and verify metadata is persisted
|
||||
events, err := store.GetEvents("order-456", 0)
|
||||
if err != nil {
|
||||
t.Fatalf("GetEvents failed: %v", err)
|
||||
}
|
||||
if len(events) != 1 {
|
||||
t.Fatalf("expected 1 event, got %d", len(events))
|
||||
}
|
||||
|
||||
retrieved := events[0]
|
||||
if retrieved.Metadata == nil {
|
||||
t.Fatal("expected Metadata to be persisted")
|
||||
}
|
||||
if retrieved.Metadata["correlationId"] != "corr-123" {
|
||||
t.Errorf("correlationId mismatch: got %q, want %q", retrieved.Metadata["correlationId"], "corr-123")
|
||||
}
|
||||
if retrieved.Metadata["causationId"] != "cause-456" {
|
||||
t.Errorf("causationId mismatch: got %q, want %q", retrieved.Metadata["causationId"], "cause-456")
|
||||
}
|
||||
if retrieved.Metadata["userId"] != "user-789" {
|
||||
t.Errorf("userId mismatch: got %q, want %q", retrieved.Metadata["userId"], "user-789")
|
||||
}
|
||||
if retrieved.Metadata["traceId"] != "trace-abc" {
|
||||
t.Errorf("traceId mismatch: got %q, want %q", retrieved.Metadata["traceId"], "trace-abc")
|
||||
}
|
||||
if retrieved.Metadata["spanId"] != "span-def" {
|
||||
t.Errorf("spanId mismatch: got %q, want %q", retrieved.Metadata["spanId"], "span-def")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSaveEvent_WithNilMetadata(t *testing.T) {
|
||||
store := NewInMemoryEventStore()
|
||||
|
||||
event := &aether.Event{
|
||||
ID: "evt-nil-meta",
|
||||
EventType: "OrderPlaced",
|
||||
ActorID: "order-456",
|
||||
Version: 1,
|
||||
Data: map[string]interface{}{},
|
||||
Metadata: nil,
|
||||
Timestamp: time.Now(),
|
||||
}
|
||||
|
||||
err := store.SaveEvent(event)
|
||||
if err != nil {
|
||||
t.Fatalf("SaveEvent failed: %v", err)
|
||||
}
|
||||
|
||||
events, err := store.GetEvents("order-456", 0)
|
||||
if err != nil {
|
||||
t.Fatalf("GetEvents failed: %v", err)
|
||||
}
|
||||
if len(events) != 1 {
|
||||
t.Fatalf("expected 1 event, got %d", len(events))
|
||||
}
|
||||
|
||||
// Nil metadata should remain nil
|
||||
if events[0].Metadata != nil {
|
||||
t.Errorf("expected nil Metadata, got %v", events[0].Metadata)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSaveEvent_WithEmptyMetadata(t *testing.T) {
|
||||
store := NewInMemoryEventStore()
|
||||
|
||||
event := &aether.Event{
|
||||
ID: "evt-empty-meta",
|
||||
EventType: "OrderPlaced",
|
||||
ActorID: "order-456",
|
||||
Version: 1,
|
||||
Data: map[string]interface{}{},
|
||||
Metadata: map[string]string{},
|
||||
Timestamp: time.Now(),
|
||||
}
|
||||
|
||||
err := store.SaveEvent(event)
|
||||
if err != nil {
|
||||
t.Fatalf("SaveEvent failed: %v", err)
|
||||
}
|
||||
|
||||
events, err := store.GetEvents("order-456", 0)
|
||||
if err != nil {
|
||||
t.Fatalf("GetEvents failed: %v", err)
|
||||
}
|
||||
if len(events) != 1 {
|
||||
t.Fatalf("expected 1 event, got %d", len(events))
|
||||
}
|
||||
|
||||
// Empty metadata should be preserved (as empty map)
|
||||
if events[0].Metadata == nil {
|
||||
t.Error("expected empty Metadata map to be preserved, got nil")
|
||||
}
|
||||
if len(events[0].Metadata) != 0 {
|
||||
t.Errorf("expected empty Metadata, got %d entries", len(events[0].Metadata))
|
||||
}
|
||||
}
|
||||
|
||||
func TestSaveEvent_MetadataWithHelpers(t *testing.T) {
|
||||
store := NewInMemoryEventStore()
|
||||
|
||||
event := &aether.Event{
|
||||
ID: "evt-helpers",
|
||||
EventType: "OrderPlaced",
|
||||
ActorID: "order-456",
|
||||
Version: 1,
|
||||
Data: map[string]interface{}{},
|
||||
Timestamp: time.Now(),
|
||||
}
|
||||
|
||||
// Use helper methods to set metadata
|
||||
event.SetCorrelationID("corr-helper")
|
||||
event.SetCausationID("cause-helper")
|
||||
event.SetUserID("user-helper")
|
||||
event.SetTraceID("trace-helper")
|
||||
event.SetSpanID("span-helper")
|
||||
|
||||
err := store.SaveEvent(event)
|
||||
if err != nil {
|
||||
t.Fatalf("SaveEvent failed: %v", err)
|
||||
}
|
||||
|
||||
events, err := store.GetEvents("order-456", 0)
|
||||
if err != nil {
|
||||
t.Fatalf("GetEvents failed: %v", err)
|
||||
}
|
||||
if len(events) != 1 {
|
||||
t.Fatalf("expected 1 event, got %d", len(events))
|
||||
}
|
||||
|
||||
retrieved := events[0]
|
||||
if retrieved.GetCorrelationID() != "corr-helper" {
|
||||
t.Errorf("GetCorrelationID mismatch: got %q", retrieved.GetCorrelationID())
|
||||
}
|
||||
if retrieved.GetCausationID() != "cause-helper" {
|
||||
t.Errorf("GetCausationID mismatch: got %q", retrieved.GetCausationID())
|
||||
}
|
||||
if retrieved.GetUserID() != "user-helper" {
|
||||
t.Errorf("GetUserID mismatch: got %q", retrieved.GetUserID())
|
||||
}
|
||||
if retrieved.GetTraceID() != "trace-helper" {
|
||||
t.Errorf("GetTraceID mismatch: got %q", retrieved.GetTraceID())
|
||||
}
|
||||
if retrieved.GetSpanID() != "span-helper" {
|
||||
t.Errorf("GetSpanID mismatch: got %q", retrieved.GetSpanID())
|
||||
}
|
||||
}
|
||||
|
||||
func TestSaveEvent_MetadataPreservedAcrossMultipleEvents(t *testing.T) {
|
||||
store := NewInMemoryEventStore()
|
||||
|
||||
// Save multiple events with different metadata
|
||||
for i := 1; i <= 3; i++ {
|
||||
event := &aether.Event{
|
||||
ID: fmt.Sprintf("evt-%d", i),
|
||||
EventType: "OrderUpdated",
|
||||
ActorID: "order-456",
|
||||
Version: int64(i),
|
||||
Data: map[string]interface{}{},
|
||||
Metadata: map[string]string{
|
||||
"correlationId": fmt.Sprintf("corr-%d", i),
|
||||
"eventIndex": fmt.Sprintf("%d", i),
|
||||
},
|
||||
Timestamp: time.Now(),
|
||||
}
|
||||
if err := store.SaveEvent(event); err != nil {
|
||||
t.Fatalf("SaveEvent failed for event %d: %v", i, err)
|
||||
}
|
||||
}
|
||||
|
||||
events, err := store.GetEvents("order-456", 0)
|
||||
if err != nil {
|
||||
t.Fatalf("GetEvents failed: %v", err)
|
||||
}
|
||||
if len(events) != 3 {
|
||||
t.Fatalf("expected 3 events, got %d", len(events))
|
||||
}
|
||||
|
||||
// Verify each event has its own metadata
|
||||
for i, event := range events {
|
||||
expectedCorr := fmt.Sprintf("corr-%d", i+1)
|
||||
expectedIdx := fmt.Sprintf("%d", i+1)
|
||||
|
||||
if event.Metadata["correlationId"] != expectedCorr {
|
||||
t.Errorf("event %d correlationId mismatch: got %q, want %q", i+1, event.Metadata["correlationId"], expectedCorr)
|
||||
}
|
||||
if event.Metadata["eventIndex"] != expectedIdx {
|
||||
t.Errorf("event %d eventIndex mismatch: got %q, want %q", i+1, event.Metadata["eventIndex"], expectedIdx)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user