github.com/evdatsion/aphelion-dpos-bft@v0.32.1/libs/pubsub/pubsub_test.go (about) 1 package pubsub_test 2 3 import ( 4 "context" 5 "fmt" 6 "runtime/debug" 7 "testing" 8 "time" 9 10 "github.com/stretchr/testify/assert" 11 "github.com/stretchr/testify/require" 12 13 "github.com/evdatsion/aphelion-dpos-bft/libs/log" 14 15 "github.com/evdatsion/aphelion-dpos-bft/libs/pubsub" 16 "github.com/evdatsion/aphelion-dpos-bft/libs/pubsub/query" 17 ) 18 19 const ( 20 clientID = "test-client" 21 ) 22 23 func TestSubscribe(t *testing.T) { 24 s := pubsub.NewServer() 25 s.SetLogger(log.TestingLogger()) 26 s.Start() 27 defer s.Stop() 28 29 ctx := context.Background() 30 subscription, err := s.Subscribe(ctx, clientID, query.Empty{}) 31 require.NoError(t, err) 32 33 assert.Equal(t, 1, s.NumClients()) 34 assert.Equal(t, 1, s.NumClientSubscriptions(clientID)) 35 36 err = s.Publish(ctx, "Ka-Zar") 37 require.NoError(t, err) 38 assertReceive(t, "Ka-Zar", subscription.Out()) 39 40 published := make(chan struct{}) 41 go func() { 42 defer close(published) 43 44 err := s.Publish(ctx, "Quicksilver") 45 assert.NoError(t, err) 46 47 err = s.Publish(ctx, "Asylum") 48 assert.NoError(t, err) 49 50 err = s.Publish(ctx, "Ivan") 51 assert.NoError(t, err) 52 }() 53 54 select { 55 case <-published: 56 assertReceive(t, "Quicksilver", subscription.Out()) 57 assertCancelled(t, subscription, pubsub.ErrOutOfCapacity) 58 case <-time.After(3 * time.Second): 59 t.Fatal("Expected Publish(Asylum) not to block") 60 } 61 } 62 63 func TestSubscribeWithCapacity(t *testing.T) { 64 s := pubsub.NewServer() 65 s.SetLogger(log.TestingLogger()) 66 s.Start() 67 defer s.Stop() 68 69 ctx := context.Background() 70 assert.Panics(t, func() { 71 s.Subscribe(ctx, clientID, query.Empty{}, -1) 72 }) 73 assert.Panics(t, func() { 74 s.Subscribe(ctx, clientID, query.Empty{}, 0) 75 }) 76 subscription, err := s.Subscribe(ctx, clientID, query.Empty{}, 1) 77 require.NoError(t, err) 78 err = s.Publish(ctx, "Aggamon") 79 require.NoError(t, err) 80 assertReceive(t, "Aggamon", subscription.Out()) 81 } 82 83 func TestSubscribeUnbuffered(t *testing.T) { 84 s := pubsub.NewServer() 85 s.SetLogger(log.TestingLogger()) 86 s.Start() 87 defer s.Stop() 88 89 ctx := context.Background() 90 subscription, err := s.SubscribeUnbuffered(ctx, clientID, query.Empty{}) 91 require.NoError(t, err) 92 93 published := make(chan struct{}) 94 go func() { 95 defer close(published) 96 97 err := s.Publish(ctx, "Ultron") 98 assert.NoError(t, err) 99 100 err = s.Publish(ctx, "Darkhawk") 101 assert.NoError(t, err) 102 }() 103 104 select { 105 case <-published: 106 t.Fatal("Expected Publish(Darkhawk) to block") 107 case <-time.After(3 * time.Second): 108 assertReceive(t, "Ultron", subscription.Out()) 109 assertReceive(t, "Darkhawk", subscription.Out()) 110 } 111 } 112 113 func TestSlowClientIsRemovedWithErrOutOfCapacity(t *testing.T) { 114 s := pubsub.NewServer() 115 s.SetLogger(log.TestingLogger()) 116 s.Start() 117 defer s.Stop() 118 119 ctx := context.Background() 120 subscription, err := s.Subscribe(ctx, clientID, query.Empty{}) 121 require.NoError(t, err) 122 err = s.Publish(ctx, "Fat Cobra") 123 require.NoError(t, err) 124 err = s.Publish(ctx, "Viper") 125 require.NoError(t, err) 126 127 assertCancelled(t, subscription, pubsub.ErrOutOfCapacity) 128 } 129 130 func TestDifferentClients(t *testing.T) { 131 s := pubsub.NewServer() 132 s.SetLogger(log.TestingLogger()) 133 s.Start() 134 defer s.Stop() 135 136 ctx := context.Background() 137 subscription1, err := s.Subscribe(ctx, "client-1", query.MustParse("tm.events.type='NewBlock'")) 138 require.NoError(t, err) 139 err = s.PublishWithEvents(ctx, "Iceman", map[string][]string{"tm.events.type": {"NewBlock"}}) 140 require.NoError(t, err) 141 assertReceive(t, "Iceman", subscription1.Out()) 142 143 subscription2, err := s.Subscribe(ctx, "client-2", query.MustParse("tm.events.type='NewBlock' AND abci.account.name='Igor'")) 144 require.NoError(t, err) 145 err = s.PublishWithEvents(ctx, "Ultimo", map[string][]string{"tm.events.type": {"NewBlock"}, "abci.account.name": {"Igor"}}) 146 require.NoError(t, err) 147 assertReceive(t, "Ultimo", subscription1.Out()) 148 assertReceive(t, "Ultimo", subscription2.Out()) 149 150 subscription3, err := s.Subscribe(ctx, "client-3", query.MustParse("tm.events.type='NewRoundStep' AND abci.account.name='Igor' AND abci.invoice.number = 10")) 151 require.NoError(t, err) 152 err = s.PublishWithEvents(ctx, "Valeria Richards", map[string][]string{"tm.events.type": {"NewRoundStep"}}) 153 require.NoError(t, err) 154 assert.Zero(t, len(subscription3.Out())) 155 } 156 157 func TestSubscribeDuplicateKeys(t *testing.T) { 158 ctx := context.Background() 159 s := pubsub.NewServer() 160 s.SetLogger(log.TestingLogger()) 161 require.NoError(t, s.Start()) 162 defer s.Stop() 163 164 testCases := []struct { 165 query string 166 expected interface{} 167 }{ 168 { 169 "withdraw.rewards='17'", 170 "Iceman", 171 }, 172 { 173 "withdraw.rewards='22'", 174 "Iceman", 175 }, 176 { 177 "withdraw.rewards='1' AND withdraw.rewards='22'", 178 "Iceman", 179 }, 180 { 181 "withdraw.rewards='100'", 182 nil, 183 }, 184 } 185 186 for i, tc := range testCases { 187 sub, err := s.Subscribe(ctx, fmt.Sprintf("client-%d", i), query.MustParse(tc.query)) 188 require.NoError(t, err) 189 190 err = s.PublishWithEvents( 191 ctx, 192 "Iceman", 193 map[string][]string{ 194 "transfer.sender": {"foo", "bar", "baz"}, 195 "withdraw.rewards": {"1", "17", "22"}, 196 }, 197 ) 198 require.NoError(t, err) 199 200 if tc.expected != nil { 201 assertReceive(t, tc.expected, sub.Out()) 202 } else { 203 require.Zero(t, len(sub.Out())) 204 } 205 } 206 } 207 208 func TestClientSubscribesTwice(t *testing.T) { 209 s := pubsub.NewServer() 210 s.SetLogger(log.TestingLogger()) 211 s.Start() 212 defer s.Stop() 213 214 ctx := context.Background() 215 q := query.MustParse("tm.events.type='NewBlock'") 216 217 subscription1, err := s.Subscribe(ctx, clientID, q) 218 require.NoError(t, err) 219 err = s.PublishWithEvents(ctx, "Goblin Queen", map[string][]string{"tm.events.type": {"NewBlock"}}) 220 require.NoError(t, err) 221 assertReceive(t, "Goblin Queen", subscription1.Out()) 222 223 subscription2, err := s.Subscribe(ctx, clientID, q) 224 require.Error(t, err) 225 require.Nil(t, subscription2) 226 227 err = s.PublishWithEvents(ctx, "Spider-Man", map[string][]string{"tm.events.type": {"NewBlock"}}) 228 require.NoError(t, err) 229 assertReceive(t, "Spider-Man", subscription1.Out()) 230 } 231 232 func TestUnsubscribe(t *testing.T) { 233 s := pubsub.NewServer() 234 s.SetLogger(log.TestingLogger()) 235 s.Start() 236 defer s.Stop() 237 238 ctx := context.Background() 239 subscription, err := s.Subscribe(ctx, clientID, query.MustParse("tm.events.type='NewBlock'")) 240 require.NoError(t, err) 241 err = s.Unsubscribe(ctx, clientID, query.MustParse("tm.events.type='NewBlock'")) 242 require.NoError(t, err) 243 244 err = s.Publish(ctx, "Nick Fury") 245 require.NoError(t, err) 246 assert.Zero(t, len(subscription.Out()), "Should not receive anything after Unsubscribe") 247 248 assertCancelled(t, subscription, pubsub.ErrUnsubscribed) 249 } 250 251 func TestClientUnsubscribesTwice(t *testing.T) { 252 s := pubsub.NewServer() 253 s.SetLogger(log.TestingLogger()) 254 s.Start() 255 defer s.Stop() 256 257 ctx := context.Background() 258 _, err := s.Subscribe(ctx, clientID, query.MustParse("tm.events.type='NewBlock'")) 259 require.NoError(t, err) 260 err = s.Unsubscribe(ctx, clientID, query.MustParse("tm.events.type='NewBlock'")) 261 require.NoError(t, err) 262 263 err = s.Unsubscribe(ctx, clientID, query.MustParse("tm.events.type='NewBlock'")) 264 assert.Equal(t, pubsub.ErrSubscriptionNotFound, err) 265 err = s.UnsubscribeAll(ctx, clientID) 266 assert.Equal(t, pubsub.ErrSubscriptionNotFound, err) 267 } 268 269 func TestResubscribe(t *testing.T) { 270 s := pubsub.NewServer() 271 s.SetLogger(log.TestingLogger()) 272 s.Start() 273 defer s.Stop() 274 275 ctx := context.Background() 276 subscription, err := s.Subscribe(ctx, clientID, query.Empty{}) 277 require.NoError(t, err) 278 err = s.Unsubscribe(ctx, clientID, query.Empty{}) 279 require.NoError(t, err) 280 subscription, err = s.Subscribe(ctx, clientID, query.Empty{}) 281 require.NoError(t, err) 282 283 err = s.Publish(ctx, "Cable") 284 require.NoError(t, err) 285 assertReceive(t, "Cable", subscription.Out()) 286 } 287 288 func TestUnsubscribeAll(t *testing.T) { 289 s := pubsub.NewServer() 290 s.SetLogger(log.TestingLogger()) 291 s.Start() 292 defer s.Stop() 293 294 ctx := context.Background() 295 subscription1, err := s.Subscribe(ctx, clientID, query.MustParse("tm.events.type='NewBlock'")) 296 require.NoError(t, err) 297 subscription2, err := s.Subscribe(ctx, clientID, query.MustParse("tm.events.type='NewBlockHeader'")) 298 require.NoError(t, err) 299 300 err = s.UnsubscribeAll(ctx, clientID) 301 require.NoError(t, err) 302 303 err = s.Publish(ctx, "Nick Fury") 304 require.NoError(t, err) 305 assert.Zero(t, len(subscription1.Out()), "Should not receive anything after UnsubscribeAll") 306 assert.Zero(t, len(subscription2.Out()), "Should not receive anything after UnsubscribeAll") 307 308 assertCancelled(t, subscription1, pubsub.ErrUnsubscribed) 309 assertCancelled(t, subscription2, pubsub.ErrUnsubscribed) 310 } 311 312 func TestBufferCapacity(t *testing.T) { 313 s := pubsub.NewServer(pubsub.BufferCapacity(2)) 314 s.SetLogger(log.TestingLogger()) 315 316 assert.Equal(t, 2, s.BufferCapacity()) 317 318 ctx := context.Background() 319 err := s.Publish(ctx, "Nighthawk") 320 require.NoError(t, err) 321 err = s.Publish(ctx, "Sage") 322 require.NoError(t, err) 323 324 ctx, cancel := context.WithTimeout(ctx, 10*time.Millisecond) 325 defer cancel() 326 err = s.Publish(ctx, "Ironclad") 327 if assert.Error(t, err) { 328 assert.Equal(t, context.DeadlineExceeded, err) 329 } 330 } 331 332 func Benchmark10Clients(b *testing.B) { benchmarkNClients(10, b) } 333 func Benchmark100Clients(b *testing.B) { benchmarkNClients(100, b) } 334 func Benchmark1000Clients(b *testing.B) { benchmarkNClients(1000, b) } 335 336 func Benchmark10ClientsOneQuery(b *testing.B) { benchmarkNClientsOneQuery(10, b) } 337 func Benchmark100ClientsOneQuery(b *testing.B) { benchmarkNClientsOneQuery(100, b) } 338 func Benchmark1000ClientsOneQuery(b *testing.B) { benchmarkNClientsOneQuery(1000, b) } 339 340 func benchmarkNClients(n int, b *testing.B) { 341 s := pubsub.NewServer() 342 s.Start() 343 defer s.Stop() 344 345 ctx := context.Background() 346 for i := 0; i < n; i++ { 347 subscription, err := s.Subscribe(ctx, clientID, query.MustParse(fmt.Sprintf("abci.Account.Owner = 'Ivan' AND abci.Invoices.Number = %d", i))) 348 if err != nil { 349 b.Fatal(err) 350 } 351 go func() { 352 for { 353 select { 354 case <-subscription.Out(): 355 continue 356 case <-subscription.Cancelled(): 357 return 358 } 359 } 360 }() 361 } 362 363 b.ReportAllocs() 364 b.ResetTimer() 365 for i := 0; i < b.N; i++ { 366 s.PublishWithEvents(ctx, "Gamora", map[string][]string{"abci.Account.Owner": {"Ivan"}, "abci.Invoices.Number": {string(i)}}) 367 } 368 } 369 370 func benchmarkNClientsOneQuery(n int, b *testing.B) { 371 s := pubsub.NewServer() 372 s.Start() 373 defer s.Stop() 374 375 ctx := context.Background() 376 q := query.MustParse("abci.Account.Owner = 'Ivan' AND abci.Invoices.Number = 1") 377 for i := 0; i < n; i++ { 378 subscription, err := s.Subscribe(ctx, clientID, q) 379 if err != nil { 380 b.Fatal(err) 381 } 382 go func() { 383 for { 384 select { 385 case <-subscription.Out(): 386 continue 387 case <-subscription.Cancelled(): 388 return 389 } 390 } 391 }() 392 } 393 394 b.ReportAllocs() 395 b.ResetTimer() 396 for i := 0; i < b.N; i++ { 397 s.PublishWithEvents(ctx, "Gamora", map[string][]string{"abci.Account.Owner": {"Ivan"}, "abci.Invoices.Number": {"1"}}) 398 } 399 } 400 401 /////////////////////////////////////////////////////////////////////////////// 402 /// HELPERS 403 /////////////////////////////////////////////////////////////////////////////// 404 405 func assertReceive(t *testing.T, expected interface{}, ch <-chan pubsub.Message, msgAndArgs ...interface{}) { 406 select { 407 case actual := <-ch: 408 assert.Equal(t, expected, actual.Data(), msgAndArgs...) 409 case <-time.After(1 * time.Second): 410 t.Errorf("Expected to receive %v from the channel, got nothing after 1s", expected) 411 debug.PrintStack() 412 } 413 } 414 415 func assertCancelled(t *testing.T, subscription *pubsub.Subscription, err error) { 416 _, ok := <-subscription.Cancelled() 417 assert.False(t, ok) 418 assert.Equal(t, err, subscription.Err()) 419 }