go-micro.dev/v5@v5.12.0/server/rpc_events_test.go (about) 1 package server 2 3 import ( 4 "context" 5 "sync" 6 "sync/atomic" 7 "testing" 8 "time" 9 10 "go-micro.dev/v5/broker" 11 "go-micro.dev/v5/registry" 12 ) 13 14 // TestSubscriberNoDuplicates verifies that when multiple subscribers are registered 15 // for the same topic with different queues, each handler is called exactly once 16 // per published message (no duplicate deliveries). 17 func TestSubscriberNoDuplicates(t *testing.T) { 18 // Create a memory broker 19 memBroker := broker.NewMemoryBroker() 20 if err := memBroker.Connect(); err != nil { 21 t.Fatalf("Failed to connect broker: %v", err) 22 } 23 defer memBroker.Disconnect() 24 25 // Create a memory registry 26 memRegistry := registry.NewMemoryRegistry() 27 28 // Create server with memory broker and registry 29 srv := NewRPCServer( 30 Broker(memBroker), 31 Registry(memRegistry), 32 Name("test.service"), 33 Id("test-1"), 34 Address("127.0.0.1:0"), 35 ) 36 37 // Track handler invocations 38 var countA, countB, countC int32 39 40 // Handler functions 41 handlerA := func(ctx context.Context, msg *TestMessage) error { 42 atomic.AddInt32(&countA, 1) 43 return nil 44 } 45 46 handlerB := func(ctx context.Context, msg *TestMessage) error { 47 atomic.AddInt32(&countB, 1) 48 return nil 49 } 50 51 handlerC := func(ctx context.Context, msg *TestMessage) error { 52 atomic.AddInt32(&countC, 1) 53 return nil 54 } 55 56 // Register three subscribers with same topic but different queues 57 topic := "EVENT_1" 58 59 subA := srv.NewSubscriber(topic, handlerA, SubscriberQueue("A")) 60 if err := srv.Subscribe(subA); err != nil { 61 t.Fatalf("Failed to subscribe A: %v", err) 62 } 63 64 subB := srv.NewSubscriber(topic, handlerB, SubscriberQueue("B")) 65 if err := srv.Subscribe(subB); err != nil { 66 t.Fatalf("Failed to subscribe B: %v", err) 67 } 68 69 subC := srv.NewSubscriber(topic, handlerC, SubscriberQueue("C")) 70 if err := srv.Subscribe(subC); err != nil { 71 t.Fatalf("Failed to subscribe C: %v", err) 72 } 73 74 // Start the server (this will trigger reSubscribe) 75 if err := srv.Start(); err != nil { 76 t.Fatalf("Failed to start server: %v", err) 77 } 78 defer srv.Stop() 79 80 // Give server time to establish subscriptions 81 time.Sleep(100 * time.Millisecond) 82 83 // Publish a message to the topic 84 if err := memBroker.Publish(topic, &broker.Message{ 85 Header: map[string]string{ 86 "Micro-Topic": topic, 87 "Content-Type": "application/json", 88 }, 89 Body: []byte(`{"value":"test"}`), 90 }); err != nil { 91 t.Fatalf("Failed to publish message: %v", err) 92 } 93 94 // Give handlers time to process 95 time.Sleep(200 * time.Millisecond) 96 97 // Verify each handler was called exactly once 98 if got := atomic.LoadInt32(&countA); got != 1 { 99 t.Errorf("Handler A called %d times, expected 1", got) 100 } 101 if got := atomic.LoadInt32(&countB); got != 1 { 102 t.Errorf("Handler B called %d times, expected 1", got) 103 } 104 if got := atomic.LoadInt32(&countC); got != 1 { 105 t.Errorf("Handler C called %d times, expected 1", got) 106 } 107 } 108 109 // TestSubscriberMultipleTopics verifies that subscribers for different topics 110 // each receive their respective messages correctly. 111 func TestSubscriberMultipleTopics(t *testing.T) { 112 // Create a memory broker 113 memBroker := broker.NewMemoryBroker() 114 if err := memBroker.Connect(); err != nil { 115 t.Fatalf("Failed to connect broker: %v", err) 116 } 117 defer memBroker.Disconnect() 118 119 // Create a memory registry 120 memRegistry := registry.NewMemoryRegistry() 121 122 // Create server 123 srv := NewRPCServer( 124 Broker(memBroker), 125 Registry(memRegistry), 126 Name("test.service"), 127 Id("test-2"), 128 Address("127.0.0.1:0"), 129 ) 130 131 // Track handler invocations 132 var count1, count2 int32 133 var wg sync.WaitGroup 134 wg.Add(2) 135 136 // Handler functions 137 handler1 := func(ctx context.Context, msg *TestMessage) error { 138 atomic.AddInt32(&count1, 1) 139 wg.Done() 140 return nil 141 } 142 143 handler2 := func(ctx context.Context, msg *TestMessage) error { 144 atomic.AddInt32(&count2, 1) 145 wg.Done() 146 return nil 147 } 148 149 // Register subscribers for different topics 150 topic1 := "TOPIC_1" 151 topic2 := "TOPIC_2" 152 153 sub1 := srv.NewSubscriber(topic1, handler1) 154 if err := srv.Subscribe(sub1); err != nil { 155 t.Fatalf("Failed to subscribe to topic1: %v", err) 156 } 157 158 sub2 := srv.NewSubscriber(topic2, handler2) 159 if err := srv.Subscribe(sub2); err != nil { 160 t.Fatalf("Failed to subscribe to topic2: %v", err) 161 } 162 163 // Start the server 164 if err := srv.Start(); err != nil { 165 t.Fatalf("Failed to start server: %v", err) 166 } 167 defer srv.Stop() 168 169 // Give server time to establish subscriptions 170 time.Sleep(100 * time.Millisecond) 171 172 // Publish messages to different topics 173 if err := memBroker.Publish(topic1, &broker.Message{ 174 Header: map[string]string{ 175 "Micro-Topic": topic1, 176 "Content-Type": "application/json", 177 }, 178 Body: []byte(`{"value":"test1"}`), 179 }); err != nil { 180 t.Fatalf("Failed to publish to topic1: %v", err) 181 } 182 183 if err := memBroker.Publish(topic2, &broker.Message{ 184 Header: map[string]string{ 185 "Micro-Topic": topic2, 186 "Content-Type": "application/json", 187 }, 188 Body: []byte(`{"value":"test2"}`), 189 }); err != nil { 190 t.Fatalf("Failed to publish to topic2: %v", err) 191 } 192 193 // Wait for handlers to be called 194 done := make(chan struct{}) 195 go func() { 196 wg.Wait() 197 close(done) 198 }() 199 200 select { 201 case <-done: 202 // Success 203 case <-time.After(2 * time.Second): 204 t.Fatal("Timeout waiting for handlers to be called") 205 } 206 207 // Verify each handler was called exactly once 208 if got := atomic.LoadInt32(&count1); got != 1 { 209 t.Errorf("Handler 1 called %d times, expected 1", got) 210 } 211 if got := atomic.LoadInt32(&count2); got != 1 { 212 t.Errorf("Handler 2 called %d times, expected 1", got) 213 } 214 } 215 216 // TestMessage is a test message type 217 type TestMessage struct { 218 Value string `json:"value"` 219 }