package eventbus import ( "sync" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestEventBus_PublishSubscribe(t *testing.T) { bus := New() sub, cleanup := bus.Subscribe("test-topic") defer cleanup() event := &Event{Seq: 1, EventType: "status", Message: "hello"} bus.Publish("test-topic", event) got, ok := sub.Ring.Pop() require.True(t, ok) assert.Equal(t, event, got) } func TestEventBus_MultipleSubscribers(t *testing.T) { bus := New() sub1, cleanup1 := bus.Subscribe("topic") defer cleanup1() sub2, cleanup2 := bus.Subscribe("topic") defer cleanup2() sub3, cleanup3 := bus.Subscribe("topic") defer cleanup3() event := &Event{Seq: 1, EventType: "status"} bus.Publish("topic", event) got1, ok := sub1.Ring.Pop() require.True(t, ok) assert.Equal(t, event, got1) got2, ok := sub2.Ring.Pop() require.True(t, ok) assert.Equal(t, event, got2) got3, ok := sub3.Ring.Pop() require.True(t, ok) assert.Equal(t, event, got3) } func TestEventBus_GlobalSubscriber(t *testing.T) { bus := New() sub, cleanup := bus.SubscribeGlobal() defer cleanup() bus.Publish("topic-a", &Event{Seq: 1}) bus.Publish("topic-b", &Event{Seq: 2}) bus.Publish("topic-c", &Event{Seq: 3}) got, ok := sub.Ring.Pop() require.True(t, ok) assert.Equal(t, int64(1), got.Seq) got, ok = sub.Ring.Pop() require.True(t, ok) assert.Equal(t, int64(2), got.Seq) got, ok = sub.Ring.Pop() require.True(t, ok) assert.Equal(t, int64(3), got.Seq) } func TestEventBus_TopicIsolation(t *testing.T) { bus := New() subA, cleanupA := bus.Subscribe("topic-a") defer cleanupA() bus.Publish("topic-b", &Event{Seq: 1}) _, ok := subA.Ring.Pop() assert.False(t, ok) } func TestEventBus_Notification(t *testing.T) { bus := New() sub, cleanup := bus.Subscribe("topic") defer cleanup() bus.Publish("topic", &Event{Seq: 1}) select { case <-sub.C: case <-time.After(100 * time.Millisecond): t.Fatal("expected notification on channel") } } func TestEventBus_Unsubscribe(t *testing.T) { bus := New() sub, cleanup := bus.Subscribe("topic") bus.Publish("topic", &Event{Seq: 1}) _, ok := sub.Ring.Pop() require.True(t, ok) cleanup() bus.Publish("topic", &Event{Seq: 2}) _, ok = sub.Ring.Pop() assert.False(t, ok) } func TestEventBus_SlowSubscriber(t *testing.T) { bus := New() sub, cleanup := bus.Subscribe("topic") defer cleanup() for i := 0; i < 500; i++ { bus.Publish("topic", &Event{Seq: int64(i)}) } assert.Equal(t, 256, sub.Ring.Len()) first, ok := sub.Ring.Pop() require.True(t, ok) assert.Equal(t, int64(244), first.Seq) } func TestEventBus_HasTopic(t *testing.T) { bus := New() assert.False(t, bus.HasTopic("topic")) sub, cleanup := bus.Subscribe("topic") _ = sub assert.True(t, bus.HasTopic("topic")) cleanup() assert.False(t, bus.HasTopic("topic")) } func TestEventBus_ConcurrentPublishSubscribe(t *testing.T) { bus := New() var wg sync.WaitGroup for i := 0; i < 5; i++ { wg.Add(1) go func(id int) { defer wg.Done() sub, cleanup := bus.Subscribe("topic") defer cleanup() for j := 0; j < 100; j++ { sub.Ring.Pop() } }(i) } for i := 0; i < 5; i++ { wg.Add(1) go func(id int) { defer wg.Done() for j := 0; j < 100; j++ { bus.Publish("topic", &Event{Seq: int64(id*100 + j)}) } }(i) } wg.Wait() }