|
|
@@ -0,0 +1,145 @@
|
|
|
+package pubsub
|
|
|
+
|
|
|
+import (
|
|
|
+ "context"
|
|
|
+ "sync"
|
|
|
+ "testing"
|
|
|
+ "time"
|
|
|
+
|
|
|
+ "github.com/stretchr/testify/assert"
|
|
|
+)
|
|
|
+
|
|
|
+func TestBrokerSubscribe(t *testing.T) {
|
|
|
+ t.Parallel()
|
|
|
+
|
|
|
+ t.Run("with cancellable context", func(t *testing.T) {
|
|
|
+ t.Parallel()
|
|
|
+ broker := NewBroker[string]()
|
|
|
+ ctx, cancel := context.WithCancel(context.Background())
|
|
|
+ defer cancel()
|
|
|
+
|
|
|
+ ch := broker.Subscribe(ctx)
|
|
|
+ assert.NotNil(t, ch)
|
|
|
+ assert.Equal(t, 1, broker.GetSubscriberCount())
|
|
|
+
|
|
|
+ // Cancel the context should remove the subscription
|
|
|
+ cancel()
|
|
|
+ time.Sleep(10 * time.Millisecond) // Give time for goroutine to process
|
|
|
+ assert.Equal(t, 0, broker.GetSubscriberCount())
|
|
|
+ })
|
|
|
+
|
|
|
+ t.Run("with background context", func(t *testing.T) {
|
|
|
+ t.Parallel()
|
|
|
+ broker := NewBroker[string]()
|
|
|
+
|
|
|
+ // Using context.Background() should not leak goroutines
|
|
|
+ ch := broker.Subscribe(context.Background())
|
|
|
+ assert.NotNil(t, ch)
|
|
|
+ assert.Equal(t, 1, broker.GetSubscriberCount())
|
|
|
+
|
|
|
+ // Shutdown should clean up all subscriptions
|
|
|
+ broker.Shutdown()
|
|
|
+ assert.Equal(t, 0, broker.GetSubscriberCount())
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+func TestBrokerPublish(t *testing.T) {
|
|
|
+ t.Parallel()
|
|
|
+ broker := NewBroker[string]()
|
|
|
+ ctx := t.Context()
|
|
|
+
|
|
|
+ ch := broker.Subscribe(ctx)
|
|
|
+
|
|
|
+ // Publish a message
|
|
|
+ broker.Publish(CreatedEvent, "test message")
|
|
|
+
|
|
|
+ // Verify message is received
|
|
|
+ select {
|
|
|
+ case event := <-ch:
|
|
|
+ assert.Equal(t, CreatedEvent, event.Type)
|
|
|
+ assert.Equal(t, "test message", event.Payload)
|
|
|
+ case <-time.After(100 * time.Millisecond):
|
|
|
+ t.Fatal("timeout waiting for message")
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func TestBrokerShutdown(t *testing.T) {
|
|
|
+ t.Parallel()
|
|
|
+ broker := NewBroker[string]()
|
|
|
+
|
|
|
+ // Create multiple subscribers
|
|
|
+ ch1 := broker.Subscribe(context.Background())
|
|
|
+ ch2 := broker.Subscribe(context.Background())
|
|
|
+
|
|
|
+ assert.Equal(t, 2, broker.GetSubscriberCount())
|
|
|
+
|
|
|
+ // Shutdown should close all channels and clean up
|
|
|
+ broker.Shutdown()
|
|
|
+
|
|
|
+ // Verify channels are closed
|
|
|
+ _, ok1 := <-ch1
|
|
|
+ _, ok2 := <-ch2
|
|
|
+ assert.False(t, ok1, "channel 1 should be closed")
|
|
|
+ assert.False(t, ok2, "channel 2 should be closed")
|
|
|
+
|
|
|
+ // Verify subscriber count is reset
|
|
|
+ assert.Equal(t, 0, broker.GetSubscriberCount())
|
|
|
+}
|
|
|
+
|
|
|
+func TestBrokerConcurrency(t *testing.T) {
|
|
|
+ t.Parallel()
|
|
|
+ broker := NewBroker[int]()
|
|
|
+
|
|
|
+ // Create a large number of subscribers
|
|
|
+ const numSubscribers = 100
|
|
|
+ var wg sync.WaitGroup
|
|
|
+ wg.Add(numSubscribers)
|
|
|
+
|
|
|
+ // Create a channel to collect received events
|
|
|
+ receivedEvents := make(chan int, numSubscribers)
|
|
|
+
|
|
|
+ for i := range numSubscribers {
|
|
|
+ go func(id int) {
|
|
|
+ defer wg.Done()
|
|
|
+ ctx, cancel := context.WithCancel(context.Background())
|
|
|
+ defer cancel()
|
|
|
+
|
|
|
+ ch := broker.Subscribe(ctx)
|
|
|
+
|
|
|
+ // Receive one message then cancel
|
|
|
+ select {
|
|
|
+ case event := <-ch:
|
|
|
+ receivedEvents <- event.Payload
|
|
|
+ case <-time.After(1 * time.Second):
|
|
|
+ t.Errorf("timeout waiting for message %d", id)
|
|
|
+ }
|
|
|
+ cancel()
|
|
|
+ }(i)
|
|
|
+ }
|
|
|
+
|
|
|
+ // Give subscribers time to set up
|
|
|
+ time.Sleep(10 * time.Millisecond)
|
|
|
+
|
|
|
+ // Publish messages to all subscribers
|
|
|
+ for i := range numSubscribers {
|
|
|
+ broker.Publish(CreatedEvent, i)
|
|
|
+ }
|
|
|
+
|
|
|
+ // Wait for all subscribers to finish
|
|
|
+ wg.Wait()
|
|
|
+ close(receivedEvents)
|
|
|
+
|
|
|
+ // Give time for cleanup goroutines to run
|
|
|
+ time.Sleep(10 * time.Millisecond)
|
|
|
+
|
|
|
+ // Verify all subscribers are cleaned up
|
|
|
+ assert.Equal(t, 0, broker.GetSubscriberCount())
|
|
|
+
|
|
|
+ // Verify we received the expected number of events
|
|
|
+ count := 0
|
|
|
+ for range receivedEvents {
|
|
|
+ count++
|
|
|
+ }
|
|
|
+ assert.Equal(t, numSubscribers, count)
|
|
|
+}
|
|
|
+
|