Add comprehensive unit tests for Event and ActorSnapshot types
Test JSON serialization/deserialization, field names, omitempty behavior, edge cases (empty/nil data, large payloads, special characters including unicode and control chars), timestamp handling across timezones, nanosecond precision, version edge cases, and nested data structures. Closes #1 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit was merged in pull request #29.
This commit is contained in:
742
event_test.go
Normal file
742
event_test.go
Normal file
@@ -0,0 +1,742 @@
|
||||
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": "<script>alert('xss')</script>",
|
||||
"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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user