package aether import ( "encoding/json" "strings" "testing" "time" ) func TestEvent_JSONSerialization(t *testing.T) { ts := time.Date(2026, 1, 9, 12, 0, 0, 0, time.UTC) event := &Event{ ID: "evt-123", EventType: "OrderPlaced", ActorID: "order-456", CommandID: "cmd-789", Version: 1, Data: map[string]interface{}{ "total": 100.50, "currency": "USD", }, Timestamp: ts, } data, err := json.Marshal(event) if err != nil { t.Fatalf("failed to marshal Event: %v", err) } var decoded Event if err := json.Unmarshal(data, &decoded); err != nil { t.Fatalf("failed to unmarshal Event: %v", err) } if decoded.ID != event.ID { t.Errorf("ID mismatch: got %q, want %q", decoded.ID, event.ID) } if decoded.EventType != event.EventType { t.Errorf("EventType mismatch: got %q, want %q", decoded.EventType, event.EventType) } if decoded.ActorID != event.ActorID { t.Errorf("ActorID mismatch: got %q, want %q", decoded.ActorID, event.ActorID) } if decoded.CommandID != event.CommandID { t.Errorf("CommandID mismatch: got %q, want %q", decoded.CommandID, event.CommandID) } if decoded.Version != event.Version { t.Errorf("Version mismatch: got %d, want %d", decoded.Version, event.Version) } if !decoded.Timestamp.Equal(event.Timestamp) { t.Errorf("Timestamp mismatch: got %v, want %v", decoded.Timestamp, event.Timestamp) } if decoded.Data["total"] != event.Data["total"] { t.Errorf("Data.total mismatch: got %v, want %v", decoded.Data["total"], event.Data["total"]) } if decoded.Data["currency"] != event.Data["currency"] { t.Errorf("Data.currency mismatch: got %v, want %v", decoded.Data["currency"], event.Data["currency"]) } } func TestEvent_JSONFieldNames(t *testing.T) { event := &Event{ ID: "evt-123", EventType: "TestEvent", ActorID: "actor-456", CommandID: "cmd-789", Version: 1, Data: map[string]interface{}{}, Timestamp: time.Now(), } data, err := json.Marshal(event) if err != nil { t.Fatalf("failed to marshal Event: %v", err) } jsonStr := string(data) expectedFields := []string{ `"id"`, `"eventType"`, `"actorId"`, `"commandId"`, `"version"`, `"data"`, `"timestamp"`, } for _, field := range expectedFields { if !strings.Contains(jsonStr, field) { t.Errorf("expected JSON field %s not found in: %s", field, jsonStr) } } } func TestEvent_OmitEmptyCommandID(t *testing.T) { event := &Event{ ID: "evt-123", EventType: "TestEvent", ActorID: "actor-456", Version: 1, Data: map[string]interface{}{}, Timestamp: time.Now(), } data, err := json.Marshal(event) if err != nil { t.Fatalf("failed to marshal Event: %v", err) } if strings.Contains(string(data), `"commandId"`) { t.Error("expected commandId to be omitted when empty") } } func TestActorSnapshot_JSONSerialization(t *testing.T) { ts := time.Date(2026, 1, 9, 12, 0, 0, 0, time.UTC) snapshot := &ActorSnapshot{ ActorID: "actor-123", Version: 10, State: map[string]interface{}{ "balance": 500.00, "status": "active", "metadata": map[string]interface{}{"region": "us-west"}, }, Timestamp: ts, } data, err := json.Marshal(snapshot) if err != nil { t.Fatalf("failed to marshal ActorSnapshot: %v", err) } var decoded ActorSnapshot if err := json.Unmarshal(data, &decoded); err != nil { t.Fatalf("failed to unmarshal ActorSnapshot: %v", err) } if decoded.ActorID != snapshot.ActorID { t.Errorf("ActorID mismatch: got %q, want %q", decoded.ActorID, snapshot.ActorID) } if decoded.Version != snapshot.Version { t.Errorf("Version mismatch: got %d, want %d", decoded.Version, snapshot.Version) } if !decoded.Timestamp.Equal(snapshot.Timestamp) { t.Errorf("Timestamp mismatch: got %v, want %v", decoded.Timestamp, snapshot.Timestamp) } if decoded.State["balance"] != snapshot.State["balance"] { t.Errorf("State.balance mismatch: got %v, want %v", decoded.State["balance"], snapshot.State["balance"]) } if decoded.State["status"] != snapshot.State["status"] { t.Errorf("State.status mismatch: got %v, want %v", decoded.State["status"], snapshot.State["status"]) } } func TestActorSnapshot_JSONFieldNames(t *testing.T) { snapshot := &ActorSnapshot{ ActorID: "actor-123", Version: 1, State: map[string]interface{}{}, Timestamp: time.Now(), } data, err := json.Marshal(snapshot) if err != nil { t.Fatalf("failed to marshal ActorSnapshot: %v", err) } jsonStr := string(data) expectedFields := []string{ `"actorId"`, `"version"`, `"state"`, `"timestamp"`, } for _, field := range expectedFields { if !strings.Contains(jsonStr, field) { t.Errorf("expected JSON field %s not found in: %s", field, jsonStr) } } } func TestEvent_EmptyData(t *testing.T) { event := &Event{ ID: "evt-empty", EventType: "EmptyEvent", ActorID: "actor-123", Version: 1, Data: map[string]interface{}{}, Timestamp: time.Now(), } data, err := json.Marshal(event) if err != nil { t.Fatalf("failed to marshal Event with empty data: %v", err) } var decoded Event if err := json.Unmarshal(data, &decoded); err != nil { t.Fatalf("failed to unmarshal Event with empty data: %v", err) } if len(decoded.Data) != 0 { t.Errorf("expected empty Data map, got %v", decoded.Data) } } func TestEvent_NilData(t *testing.T) { event := &Event{ ID: "evt-nil", EventType: "NilDataEvent", ActorID: "actor-123", Version: 1, Data: nil, Timestamp: time.Now(), } data, err := json.Marshal(event) if err != nil { t.Fatalf("failed to marshal Event with nil data: %v", err) } var decoded Event if err := json.Unmarshal(data, &decoded); err != nil { t.Fatalf("failed to unmarshal Event with nil data: %v", err) } if decoded.Data != nil { t.Errorf("expected nil Data, got %v", decoded.Data) } } func TestActorSnapshot_EmptyState(t *testing.T) { snapshot := &ActorSnapshot{ ActorID: "actor-empty", Version: 1, State: map[string]interface{}{}, Timestamp: time.Now(), } data, err := json.Marshal(snapshot) if err != nil { t.Fatalf("failed to marshal ActorSnapshot with empty state: %v", err) } var decoded ActorSnapshot if err := json.Unmarshal(data, &decoded); err != nil { t.Fatalf("failed to unmarshal ActorSnapshot with empty state: %v", err) } if len(decoded.State) != 0 { t.Errorf("expected empty State map, got %v", decoded.State) } } func TestActorSnapshot_NilState(t *testing.T) { snapshot := &ActorSnapshot{ ActorID: "actor-nil", Version: 1, State: nil, Timestamp: time.Now(), } data, err := json.Marshal(snapshot) if err != nil { t.Fatalf("failed to marshal ActorSnapshot with nil state: %v", err) } var decoded ActorSnapshot if err := json.Unmarshal(data, &decoded); err != nil { t.Fatalf("failed to unmarshal ActorSnapshot with nil state: %v", err) } if decoded.State != nil { t.Errorf("expected nil State, got %v", decoded.State) } } func TestEvent_LargePayload(t *testing.T) { largeString := strings.Repeat("x", 1024*1024) // 1MB string event := &Event{ ID: "evt-large", EventType: "LargePayloadEvent", ActorID: "actor-123", Version: 1, Data: map[string]interface{}{ "largeField": largeString, }, Timestamp: time.Now(), } data, err := json.Marshal(event) if err != nil { t.Fatalf("failed to marshal Event with large payload: %v", err) } var decoded Event if err := json.Unmarshal(data, &decoded); err != nil { t.Fatalf("failed to unmarshal Event with large payload: %v", err) } if decoded.Data["largeField"] != largeString { t.Error("large payload was not preserved correctly") } } func TestActorSnapshot_LargePayload(t *testing.T) { largeString := strings.Repeat("y", 1024*1024) // 1MB string snapshot := &ActorSnapshot{ ActorID: "actor-large", Version: 1, State: map[string]interface{}{ "largeField": largeString, }, Timestamp: time.Now(), } data, err := json.Marshal(snapshot) if err != nil { t.Fatalf("failed to marshal ActorSnapshot with large payload: %v", err) } var decoded ActorSnapshot if err := json.Unmarshal(data, &decoded); err != nil { t.Fatalf("failed to unmarshal ActorSnapshot with large payload: %v", err) } if decoded.State["largeField"] != largeString { t.Error("large payload was not preserved correctly") } } func TestEvent_SpecialCharacters(t *testing.T) { specialStrings := map[string]interface{}{ "unicode": "Hello, \u4e16\u754c! \U0001F600", "newlines": "line1\nline2\r\nline3", "tabs": "col1\tcol2\tcol3", "quotes": `"quoted" and 'single'`, "backslash": `path\to\file`, "nullchar": "before\x00after", "html": "", "json_chars": `{"nested": "value"}`, } event := &Event{ ID: "evt-special", EventType: "Special\nChars", ActorID: "actor-\t123", Version: 1, Data: specialStrings, Timestamp: time.Now(), } data, err := json.Marshal(event) if err != nil { t.Fatalf("failed to marshal Event with special characters: %v", err) } var decoded Event if err := json.Unmarshal(data, &decoded); err != nil { t.Fatalf("failed to unmarshal Event with special characters: %v", err) } for key, expected := range specialStrings { if decoded.Data[key] != expected { t.Errorf("Data[%q] mismatch: got %q, want %q", key, decoded.Data[key], expected) } } if decoded.EventType != event.EventType { t.Errorf("EventType with special chars mismatch: got %q, want %q", decoded.EventType, event.EventType) } if decoded.ActorID != event.ActorID { t.Errorf("ActorID with special chars mismatch: got %q, want %q", decoded.ActorID, event.ActorID) } } func TestActorSnapshot_SpecialCharacters(t *testing.T) { specialState := map[string]interface{}{ "unicode": "\u00e9\u00e8\u00ea", "emoji": "\U0001F4BB\U0001F680", "brackets": "[]{}()", } snapshot := &ActorSnapshot{ ActorID: "actor-\u00e9", Version: 1, State: specialState, Timestamp: time.Now(), } data, err := json.Marshal(snapshot) if err != nil { t.Fatalf("failed to marshal ActorSnapshot with special characters: %v", err) } var decoded ActorSnapshot if err := json.Unmarshal(data, &decoded); err != nil { t.Fatalf("failed to unmarshal ActorSnapshot with special characters: %v", err) } for key, expected := range specialState { if decoded.State[key] != expected { t.Errorf("State[%q] mismatch: got %q, want %q", key, decoded.State[key], expected) } } } func TestEvent_TimestampTimezones(t *testing.T) { timezones := []struct { name string loc *time.Location }{ {"UTC", time.UTC}, {"FixedEast", time.FixedZone("EST", -5*3600)}, {"FixedWest", time.FixedZone("PST", -8*3600)}, {"FixedPositive", time.FixedZone("JST", 9*3600)}, } for _, tz := range timezones { t.Run(tz.name, func(t *testing.T) { ts := time.Date(2026, 6, 15, 14, 30, 45, 123456789, tz.loc) event := &Event{ ID: "evt-tz", EventType: "TimezoneTest", ActorID: "actor-123", Version: 1, Data: map[string]interface{}{}, Timestamp: ts, } data, err := json.Marshal(event) if err != nil { t.Fatalf("failed to marshal Event: %v", err) } var decoded Event if err := json.Unmarshal(data, &decoded); err != nil { t.Fatalf("failed to unmarshal Event: %v", err) } // JSON timestamp preserves the instant in time (Unix time) // but may normalize to UTC on decode if !decoded.Timestamp.Equal(ts) { t.Errorf("Timestamp instant mismatch: got %v, want %v", decoded.Timestamp.Unix(), ts.Unix()) } }) } } func TestActorSnapshot_TimestampTimezones(t *testing.T) { timezones := []struct { name string loc *time.Location }{ {"UTC", time.UTC}, {"FixedEast", time.FixedZone("CET", 1*3600)}, {"FixedWest", time.FixedZone("HST", -10*3600)}, } for _, tz := range timezones { t.Run(tz.name, func(t *testing.T) { ts := time.Date(2026, 12, 31, 23, 59, 59, 999999999, tz.loc) snapshot := &ActorSnapshot{ ActorID: "actor-tz", Version: 100, State: map[string]interface{}{}, Timestamp: ts, } data, err := json.Marshal(snapshot) if err != nil { t.Fatalf("failed to marshal ActorSnapshot: %v", err) } var decoded ActorSnapshot if err := json.Unmarshal(data, &decoded); err != nil { t.Fatalf("failed to unmarshal ActorSnapshot: %v", err) } if !decoded.Timestamp.Equal(ts) { t.Errorf("Timestamp instant mismatch: got %v, want %v", decoded.Timestamp.Unix(), ts.Unix()) } }) } } func TestEvent_TimestampNanosecondPrecision(t *testing.T) { ts := time.Date(2026, 1, 9, 12, 0, 0, 123456789, time.UTC) event := &Event{ ID: "evt-nano", EventType: "NanoTest", ActorID: "actor-123", Version: 1, Data: map[string]interface{}{}, Timestamp: ts, } data, err := json.Marshal(event) if err != nil { t.Fatalf("failed to marshal Event: %v", err) } var decoded Event if err := json.Unmarshal(data, &decoded); err != nil { t.Fatalf("failed to unmarshal Event: %v", err) } // Go's time.Time JSON marshaling uses RFC3339Nano which preserves nanoseconds if decoded.Timestamp.Nanosecond() != ts.Nanosecond() { t.Errorf("Nanosecond precision lost: got %d, want %d", decoded.Timestamp.Nanosecond(), ts.Nanosecond()) } } func TestEvent_ZeroTimestamp(t *testing.T) { event := &Event{ ID: "evt-zero", EventType: "ZeroTimeTest", ActorID: "actor-123", Version: 1, Data: map[string]interface{}{}, Timestamp: time.Time{}, } data, err := json.Marshal(event) if err != nil { t.Fatalf("failed to marshal Event with zero timestamp: %v", err) } var decoded Event if err := json.Unmarshal(data, &decoded); err != nil { t.Fatalf("failed to unmarshal Event with zero timestamp: %v", err) } if !decoded.Timestamp.IsZero() { t.Errorf("expected zero timestamp, got %v", decoded.Timestamp) } } func TestEvent_NestedData(t *testing.T) { nestedData := map[string]interface{}{ "level1": map[string]interface{}{ "level2": map[string]interface{}{ "level3": map[string]interface{}{ "value": "deeply nested", }, }, }, "array": []interface{}{"a", "b", "c"}, "mixed": []interface{}{ map[string]interface{}{"key": "value"}, 123.0, true, nil, }, } event := &Event{ ID: "evt-nested", EventType: "NestedDataEvent", ActorID: "actor-123", Version: 1, Data: nestedData, Timestamp: time.Now(), } data, err := json.Marshal(event) if err != nil { t.Fatalf("failed to marshal Event with nested data: %v", err) } var decoded Event if err := json.Unmarshal(data, &decoded); err != nil { t.Fatalf("failed to unmarshal Event with nested data: %v", err) } // Verify nested structure is preserved level1, ok := decoded.Data["level1"].(map[string]interface{}) if !ok { t.Fatal("level1 is not a map") } level2, ok := level1["level2"].(map[string]interface{}) if !ok { t.Fatal("level2 is not a map") } level3, ok := level2["level3"].(map[string]interface{}) if !ok { t.Fatal("level3 is not a map") } if level3["value"] != "deeply nested" { t.Errorf("deeply nested value mismatch: got %v", level3["value"]) } } func TestEvent_NumericTypes(t *testing.T) { event := &Event{ ID: "evt-numeric", EventType: "NumericTest", ActorID: "actor-123", Version: 1, Data: map[string]interface{}{ "integer": 42, "float": 3.14159, "negative": -100, "zero": 0, "large": 1e15, }, Timestamp: time.Now(), } data, err := json.Marshal(event) if err != nil { t.Fatalf("failed to marshal Event with numeric data: %v", err) } var decoded Event if err := json.Unmarshal(data, &decoded); err != nil { t.Fatalf("failed to unmarshal Event with numeric data: %v", err) } // JSON numbers decode as float64 if decoded.Data["integer"].(float64) != 42 { t.Errorf("integer mismatch: got %v", decoded.Data["integer"]) } if decoded.Data["float"].(float64) != 3.14159 { t.Errorf("float mismatch: got %v", decoded.Data["float"]) } } func TestEvent_BooleanAndNull(t *testing.T) { event := &Event{ ID: "evt-bool-null", EventType: "BoolNullTest", ActorID: "actor-123", Version: 1, Data: map[string]interface{}{ "trueVal": true, "falseVal": false, "nullVal": nil, }, Timestamp: time.Now(), } data, err := json.Marshal(event) if err != nil { t.Fatalf("failed to marshal Event: %v", err) } var decoded Event if err := json.Unmarshal(data, &decoded); err != nil { t.Fatalf("failed to unmarshal Event: %v", err) } if decoded.Data["trueVal"] != true { t.Errorf("trueVal mismatch: got %v", decoded.Data["trueVal"]) } if decoded.Data["falseVal"] != false { t.Errorf("falseVal mismatch: got %v", decoded.Data["falseVal"]) } if decoded.Data["nullVal"] != nil { t.Errorf("nullVal mismatch: got %v", decoded.Data["nullVal"]) } } func TestEvent_VersionEdgeCases(t *testing.T) { testCases := []struct { name string version int64 }{ {"zero", 0}, {"one", 1}, {"large", 9223372036854775807}, // MaxInt64 {"negative", -1}, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { event := &Event{ ID: "evt-version", EventType: "VersionTest", ActorID: "actor-123", Version: tc.version, Data: map[string]interface{}{}, Timestamp: time.Now(), } data, err := json.Marshal(event) if err != nil { t.Fatalf("failed to marshal Event: %v", err) } var decoded Event if err := json.Unmarshal(data, &decoded); err != nil { t.Fatalf("failed to unmarshal Event: %v", err) } if decoded.Version != tc.version { t.Errorf("Version mismatch: got %d, want %d", decoded.Version, tc.version) } }) } } func TestActorSnapshot_VersionEdgeCases(t *testing.T) { testCases := []struct { name string version int64 }{ {"zero", 0}, {"one", 1}, {"large", 9223372036854775807}, } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { snapshot := &ActorSnapshot{ ActorID: "actor-version", Version: tc.version, State: map[string]interface{}{}, Timestamp: time.Now(), } data, err := json.Marshal(snapshot) if err != nil { t.Fatalf("failed to marshal ActorSnapshot: %v", err) } var decoded ActorSnapshot if err := json.Unmarshal(data, &decoded); err != nil { t.Fatalf("failed to unmarshal ActorSnapshot: %v", err) } if decoded.Version != tc.version { t.Errorf("Version mismatch: got %d, want %d", decoded.Version, tc.version) } }) } }