github.com/kaleido-io/firefly@v0.0.0-20210622132723-8b4b6aacb971/internal/events/event_dispatcher_test.go (about) 1 // Copyright © 2021 Kaleido, Inc. 2 // 3 // SPDX-License-Identifier: Apache-2.0 4 // 5 // Licensed under the Apache License, Version 2.0 (the "License"); 6 // you may not use this file except in compliance with the License. 7 // You may obtain a copy of the License at 8 // 9 // http://www.apache.org/licenses/LICENSE-2.0 10 // 11 // Unless required by applicable law or agreed to in writing, software 12 // distributed under the License is distributed on an "AS IS" BASIS, 13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 // See the License for the specific language governing permissions and 15 // limitations under the License. 16 17 package events 18 19 import ( 20 "context" 21 "fmt" 22 "regexp" 23 "testing" 24 25 "github.com/kaleido-io/firefly/internal/config" 26 "github.com/kaleido-io/firefly/internal/log" 27 "github.com/kaleido-io/firefly/mocks/databasemocks" 28 "github.com/kaleido-io/firefly/mocks/eventsmocks" 29 "github.com/kaleido-io/firefly/pkg/database" 30 "github.com/kaleido-io/firefly/pkg/events" 31 "github.com/kaleido-io/firefly/pkg/fftypes" 32 "github.com/stretchr/testify/assert" 33 "github.com/stretchr/testify/mock" 34 ) 35 36 func newTestEventDispatcher(mdi database.Plugin, mei events.Plugin, sub *subscription) (*eventDispatcher, func()) { 37 ctx, cancel := context.WithCancel(context.Background()) 38 config.Reset() 39 return newEventDispatcher(ctx, mei, mdi, fftypes.NewUUID().String(), sub, newEventNotifier(ctx, "ut")), cancel 40 } 41 42 func TestEventDispatcherStartStop(t *testing.T) { 43 mdi := &databasemocks.Plugin{} 44 mei := &eventsmocks.Plugin{} 45 ten := uint16(10) 46 oldest := fftypes.SubOptsFirstEventOldest 47 ed, cancel := newTestEventDispatcher(mdi, mei, &subscription{ 48 dispatcherElection: make(chan bool, 1), 49 definition: &fftypes.Subscription{ 50 SubscriptionRef: fftypes.SubscriptionRef{Namespace: "ns1", Name: "sub1"}, 51 Ephemeral: true, 52 Options: fftypes.SubscriptionOptions{ 53 ReadAhead: &ten, 54 FirstEvent: &oldest, 55 }, 56 }, 57 }) 58 defer cancel() 59 ge := mdi.On("GetEvents", mock.Anything, mock.Anything, mock.Anything).Return([]*fftypes.Event{}, nil) 60 confirmedElected := make(chan bool) 61 ge.RunFn = func(a mock.Arguments) { 62 <-confirmedElected 63 } 64 65 assert.Equal(t, int(10), ed.readAhead) 66 ed.start() 67 confirmedElected <- true 68 close(confirmedElected) 69 ed.eventPoller.eventNotifier.newEvents <- 12345 70 ed.close() 71 } 72 73 func TestEventDispatcherLeaderElection(t *testing.T) { 74 log.SetLevel("debug") 75 76 sub := &subscription{ 77 dispatcherElection: make(chan bool, 1), 78 definition: &fftypes.Subscription{ 79 SubscriptionRef: fftypes.SubscriptionRef{Namespace: "ns1", Name: "sub1"}, 80 }, 81 } 82 83 gev1Wait := make(chan bool) 84 gev1Done := make(chan struct{}) 85 mei := &eventsmocks.Plugin{} 86 mdi1 := &databasemocks.Plugin{} 87 gev1 := mdi1.On("GetEvents", mock.Anything, mock.Anything, mock.Anything).Return([]*fftypes.Event{}, nil) 88 mdi1.On("GetOffset", mock.Anything, fftypes.OffsetTypeSubscription, "ns1", "sub1").Return(&fftypes.Offset{ 89 Type: fftypes.OffsetTypeSubscription, 90 Namespace: "ns1", 91 Name: "sub1", 92 Current: 12345, 93 }, nil) 94 gev1.RunFn = func(a mock.Arguments) { 95 gev1Wait <- true 96 <-gev1Done 97 } 98 ed1, cancel1 := newTestEventDispatcher(mdi1, mei, sub) 99 100 mdi2 := &databasemocks.Plugin{} // No mocks, so will bail if called 101 ed2, cancel2 := newTestEventDispatcher(mdi2, mei, sub /* same sub */) 102 103 ed1.start() 104 <-gev1Wait 105 ed2.start() 106 107 cancel2() 108 ed2.close() // while ed1 is active 109 close(gev1Done) 110 cancel1() 111 112 } 113 114 func TestEventDispatcherReadAheadOutOfOrderAcks(t *testing.T) { 115 log.SetLevel("debug") 116 var five = uint16(5) 117 sub := &subscription{ 118 dispatcherElection: make(chan bool, 1), 119 definition: &fftypes.Subscription{ 120 SubscriptionRef: fftypes.SubscriptionRef{ID: fftypes.NewUUID(), Namespace: "ns1", Name: "sub1"}, 121 Options: fftypes.SubscriptionOptions{ 122 ReadAhead: &five, 123 }, 124 }, 125 eventMatcher: regexp.MustCompile(fmt.Sprintf("^%s|%s$", fftypes.EventTypeMessageConfirmed, fftypes.EventTypeMessageConfirmed)), 126 } 127 128 mdi := &databasemocks.Plugin{} 129 130 eventDeliveries := make(chan *fftypes.EventDelivery) 131 mei := &eventsmocks.Plugin{} 132 deliveryRequestMock := mei.On("DeliveryRequest", mock.Anything, mock.Anything).Return(nil) 133 deliveryRequestMock.RunFn = func(a mock.Arguments) { 134 eventDeliveries <- a.Get(1).(*fftypes.EventDelivery) 135 } 136 137 ed, cancel := newTestEventDispatcher(mdi, mei, sub) 138 defer cancel() 139 go ed.deliverEvents() 140 141 // Setup the IDs 142 ref1 := fftypes.NewUUID() 143 ev1 := fftypes.NewUUID() 144 ref2 := fftypes.NewUUID() 145 ev2 := fftypes.NewUUID() 146 ref3 := fftypes.NewUUID() 147 ev3 := fftypes.NewUUID() 148 ref4 := fftypes.NewUUID() 149 ev4 := fftypes.NewUUID() 150 151 // Capture offset commits 152 offsetUpdates := make(chan int64) 153 uof := mdi.On("UpdateOffset", mock.Anything, mock.Anything, mock.Anything).Return(nil) 154 uof.RunFn = func(a mock.Arguments) { 155 f, err := a.Get(2).(database.Update).Finalize() 156 assert.NoError(t, err) 157 v, _ := f.SetOperations[0].Value.Value() 158 offsetUpdates <- v.(int64) 159 } 160 // Setup enrichment 161 mdi.On("GetMessages", mock.Anything, mock.MatchedBy(func(filter database.Filter) bool { 162 fi, err := filter.Finalize() 163 assert.NoError(t, err) 164 assert.Equal(t, fmt.Sprintf(`( id IN ['%s','%s','%s','%s'] ) && ( namespace == 'ns1' )`, ref1, ref2, ref3, ref4), fi.String()) 165 return true 166 })).Return([]*fftypes.Message{ 167 {Header: fftypes.MessageHeader{ID: ref1}}, 168 {Header: fftypes.MessageHeader{ID: ref2}}, 169 {Header: fftypes.MessageHeader{ID: ref4}}, 170 }, nil) 171 mdi.On("GetDataRefs", mock.Anything, mock.MatchedBy(func(filter database.Filter) bool { 172 fi, err := filter.Finalize() 173 assert.NoError(t, err) 174 assert.Equal(t, fmt.Sprintf(`( id IN ['%s','%s','%s','%s'] ) && ( namespace == 'ns1' )`, ref1, ref2, ref3, ref4), fi.String()) 175 return true 176 })).Return(fftypes.DataRefs{ 177 {ID: ref3}, 178 }, nil) 179 180 // Deliver a batch of messages 181 batch1Done := make(chan struct{}) 182 go func() { 183 repoll, err := ed.bufferedDelivery([]fftypes.LocallySequenced{ 184 &fftypes.Event{ID: ev1, Sequence: 10000001, Reference: ref1, Type: fftypes.EventTypeMessageConfirmed}, // match 185 &fftypes.Event{ID: ev2, Sequence: 10000002, Reference: ref2, Type: fftypes.EventTypeMessageInvalid}, 186 &fftypes.Event{ID: ev3, Sequence: 10000003, Reference: ref3, Type: fftypes.EventTypeMessageConfirmed}, // match 187 &fftypes.Event{ID: ev4, Sequence: 10000004, Reference: ref4, Type: fftypes.EventTypeMessageConfirmed}, // match 188 }) 189 assert.NoError(t, err) 190 assert.True(t, repoll) 191 close(batch1Done) 192 }() 193 194 // Wait for the two calls to deliver the matching messages to the client (read ahead allows this) 195 event1 := <-eventDeliveries 196 assert.Equal(t, *ev1, *event1.ID) 197 assert.Equal(t, *ref1, *event1.Message.Header.ID) 198 event3 := <-eventDeliveries 199 assert.Equal(t, *ev3, *event3.ID) 200 assert.Equal(t, *ref3, *event3.Data.ID) 201 event4 := <-eventDeliveries 202 assert.Equal(t, *ev4, *event4.ID) 203 assert.Equal(t, *ref4, *event4.Message.Header.ID) 204 205 // Send back the two acks - out of order to validate the read-ahead logic 206 go func() { 207 ed.deliveryResponse(&fftypes.EventDeliveryResponse{ID: event4.ID}) 208 ed.deliveryResponse(&fftypes.EventDeliveryResponse{ID: event1.ID}) 209 ed.deliveryResponse(&fftypes.EventDeliveryResponse{ID: event3.ID}) 210 }() 211 212 // Confirm we get the offset updates in the correct order, even though the confirmations 213 // came in a different order from the app. 214 assert.Equal(t, int64(10000001), <-offsetUpdates) 215 assert.Equal(t, int64(10000003), <-offsetUpdates) 216 assert.Equal(t, int64(10000004), <-offsetUpdates) 217 218 // This should complete the batch 219 <-batch1Done 220 221 mdi.AssertExpectations(t) 222 mei.AssertExpectations(t) 223 } 224 225 func TestEventDispatcherNoReadAheadInOrder(t *testing.T) { 226 log.SetLevel("debug") 227 sub := &subscription{ 228 dispatcherElection: make(chan bool, 1), 229 definition: &fftypes.Subscription{ 230 SubscriptionRef: fftypes.SubscriptionRef{ID: fftypes.NewUUID(), Namespace: "ns1", Name: "sub1"}, 231 Ephemeral: true, 232 Options: fftypes.SubscriptionOptions{}, 233 }, 234 } 235 236 mdi := &databasemocks.Plugin{} 237 238 eventDeliveries := make(chan *fftypes.EventDelivery) 239 mei := &eventsmocks.Plugin{} 240 deliveryRequestMock := mei.On("DeliveryRequest", mock.Anything, mock.Anything).Return(nil) 241 deliveryRequestMock.RunFn = func(a mock.Arguments) { 242 eventDeliveries <- a.Get(1).(*fftypes.EventDelivery) 243 } 244 245 ed, cancel := newTestEventDispatcher(mdi, mei, sub) 246 defer cancel() 247 go ed.deliverEvents() 248 249 // Setup the IDs 250 ref1 := fftypes.NewUUID() 251 ev1 := fftypes.NewUUID() 252 ref2 := fftypes.NewUUID() 253 ev2 := fftypes.NewUUID() 254 ref3 := fftypes.NewUUID() 255 ev3 := fftypes.NewUUID() 256 ref4 := fftypes.NewUUID() 257 ev4 := fftypes.NewUUID() 258 259 // Setup enrichment 260 mdi.On("GetMessages", mock.Anything, mock.Anything).Return([]*fftypes.Message{ 261 {Header: fftypes.MessageHeader{ID: ref1}}, 262 {Header: fftypes.MessageHeader{ID: ref2}}, 263 {Header: fftypes.MessageHeader{ID: ref3}}, 264 {Header: fftypes.MessageHeader{ID: ref4}}, 265 }, nil) 266 mdi.On("GetDataRefs", mock.Anything, mock.Anything).Return(fftypes.DataRefs{}, nil) 267 268 // Deliver a batch of messages 269 batch1Done := make(chan struct{}) 270 go func() { 271 repoll, err := ed.bufferedDelivery([]fftypes.LocallySequenced{ 272 &fftypes.Event{ID: ev1, Sequence: 10000001, Reference: ref1, Type: fftypes.EventTypeMessageConfirmed}, // match 273 &fftypes.Event{ID: ev2, Sequence: 10000002, Reference: ref2, Type: fftypes.EventTypeMessageConfirmed}, // match 274 &fftypes.Event{ID: ev3, Sequence: 10000003, Reference: ref3, Type: fftypes.EventTypeMessageConfirmed}, // match 275 &fftypes.Event{ID: ev4, Sequence: 10000004, Reference: ref4, Type: fftypes.EventTypeMessageConfirmed}, // match 276 }) 277 assert.NoError(t, err) 278 assert.True(t, repoll) 279 close(batch1Done) 280 }() 281 282 // Wait for the two calls to deliver the matching messages to the client (read ahead allows this) 283 event1 := <-eventDeliveries 284 assert.Equal(t, *ev1, *event1.ID) 285 assert.Equal(t, *ref1, *event1.Message.Header.ID) 286 select { 287 case <-eventDeliveries: 288 assert.Fail(t, "should not have read ahead") 289 default: 290 } 291 ed.deliveryResponse(&fftypes.EventDeliveryResponse{ID: event1.ID}) 292 293 event2 := <-eventDeliveries 294 ed.deliveryResponse(&fftypes.EventDeliveryResponse{ID: event2.ID}) 295 296 event3 := <-eventDeliveries 297 ed.deliveryResponse(&fftypes.EventDeliveryResponse{ID: event3.ID}) 298 299 event4 := <-eventDeliveries 300 ed.deliveryResponse(&fftypes.EventDeliveryResponse{ID: event4.ID}) 301 302 // This should complete the batch 303 <-batch1Done 304 305 mdi.AssertExpectations(t) 306 mei.AssertExpectations(t) 307 } 308 309 func TestEnrichEventsFailGetMessages(t *testing.T) { 310 311 sub := &subscription{ 312 definition: &fftypes.Subscription{}, 313 } 314 mei := &eventsmocks.Plugin{} 315 mdi := &databasemocks.Plugin{} 316 ed, cancel := newTestEventDispatcher(mdi, mei, sub) 317 defer cancel() 318 319 mdi.On("GetMessages", mock.Anything, mock.Anything).Return(nil, fmt.Errorf("pop")) 320 321 id1 := fftypes.NewUUID() 322 _, err := ed.enrichEvents([]fftypes.LocallySequenced{&fftypes.Event{ID: id1}}) 323 324 assert.EqualError(t, err, "pop") 325 } 326 327 func TestEnrichEventsFailGetData(t *testing.T) { 328 329 sub := &subscription{ 330 definition: &fftypes.Subscription{}, 331 } 332 mei := &eventsmocks.Plugin{} 333 mdi := &databasemocks.Plugin{} 334 ed, cancel := newTestEventDispatcher(mdi, mei, sub) 335 defer cancel() 336 337 mdi.On("GetMessages", mock.Anything, mock.Anything).Return([]*fftypes.Message{}, nil) 338 mdi.On("GetDataRefs", mock.Anything, mock.Anything).Return(nil, fmt.Errorf("pop")) 339 340 id1 := fftypes.NewUUID() 341 _, err := ed.enrichEvents([]fftypes.LocallySequenced{&fftypes.Event{ID: id1}}) 342 343 assert.EqualError(t, err, "pop") 344 } 345 346 func TestFilterEventsMatch(t *testing.T) { 347 348 sub := &subscription{ 349 definition: &fftypes.Subscription{}, 350 } 351 mei := &eventsmocks.Plugin{} 352 mdi := &databasemocks.Plugin{} 353 ed, cancel := newTestEventDispatcher(mdi, mei, sub) 354 defer cancel() 355 356 gid1 := fftypes.NewRandB32() 357 id1 := fftypes.NewUUID() 358 id2 := fftypes.NewUUID() 359 id3 := fftypes.NewUUID() 360 events := ed.filterEvents([]*fftypes.EventDelivery{ 361 { 362 Event: fftypes.Event{ 363 ID: id1, 364 Type: fftypes.EventTypeMessageConfirmed, 365 }, 366 Message: &fftypes.Message{ 367 Header: fftypes.MessageHeader{ 368 Topics: fftypes.FFNameArray{"topic1"}, 369 Tag: "tag1", 370 Group: nil, 371 }, 372 }, 373 }, 374 { 375 Event: fftypes.Event{ 376 ID: id2, 377 Type: fftypes.EventTypeMessageConfirmed, 378 }, 379 Message: &fftypes.Message{ 380 Header: fftypes.MessageHeader{ 381 Topics: fftypes.FFNameArray{"topic1"}, 382 Tag: "tag2", 383 Group: gid1, 384 }, 385 }, 386 }, 387 { 388 Event: fftypes.Event{ 389 ID: id3, 390 Type: fftypes.EventTypeMessageInvalid, 391 }, 392 Message: &fftypes.Message{ 393 Header: fftypes.MessageHeader{ 394 Topics: fftypes.FFNameArray{"topic2"}, 395 Tag: "tag1", 396 Group: nil, 397 }, 398 }, 399 }, 400 }) 401 402 ed.subscription.eventMatcher = regexp.MustCompile(fmt.Sprintf("^%s$", fftypes.EventTypeMessageConfirmed)) 403 ed.subscription.topicsFilter = regexp.MustCompile(".*") 404 ed.subscription.tagFilter = regexp.MustCompile(".*") 405 ed.subscription.groupFilter = regexp.MustCompile(".*") 406 matched := ed.filterEvents(events) 407 assert.Equal(t, 2, len(matched)) 408 assert.Equal(t, *id1, *matched[0].ID) 409 assert.Equal(t, *id2, *matched[1].ID) 410 // id three has the wrong event type 411 412 ed.subscription.eventMatcher = nil 413 ed.subscription.topicsFilter = nil 414 ed.subscription.tagFilter = nil 415 ed.subscription.groupFilter = nil 416 matched = ed.filterEvents(events) 417 assert.Equal(t, 3, len(matched)) 418 assert.Equal(t, *id1, *matched[0].ID) 419 assert.Equal(t, *id2, *matched[1].ID) 420 assert.Equal(t, *id3, *matched[2].ID) 421 422 ed.subscription.topicsFilter = regexp.MustCompile("topic1") 423 matched = ed.filterEvents(events) 424 assert.Equal(t, 2, len(matched)) 425 assert.Equal(t, *id1, *matched[0].ID) 426 assert.Equal(t, *id2, *matched[1].ID) 427 428 ed.subscription.topicsFilter = nil 429 ed.subscription.tagFilter = regexp.MustCompile("tag2") 430 matched = ed.filterEvents(events) 431 assert.Equal(t, 1, len(matched)) 432 assert.Equal(t, *id2, *matched[0].ID) 433 434 ed.subscription.topicsFilter = nil 435 ed.subscription.groupFilter = regexp.MustCompile(gid1.String()) 436 matched = ed.filterEvents(events) 437 assert.Equal(t, 1, len(matched)) 438 assert.Equal(t, *id2, *matched[0].ID) 439 440 ed.subscription.groupFilter = regexp.MustCompile("^$") 441 matched = ed.filterEvents(events) 442 assert.Equal(t, 0, len(matched)) 443 444 } 445 446 func TestBufferedDeliveryNoEvents(t *testing.T) { 447 448 sub := &subscription{ 449 definition: &fftypes.Subscription{}, 450 } 451 mei := &eventsmocks.Plugin{} 452 mdi := &databasemocks.Plugin{} 453 ed, cancel := newTestEventDispatcher(mdi, mei, sub) 454 defer cancel() 455 456 repoll, err := ed.bufferedDelivery([]fftypes.LocallySequenced{}) 457 assert.False(t, repoll) 458 assert.Nil(t, err) 459 460 } 461 462 func TestBufferedDeliveryEnrichFail(t *testing.T) { 463 464 sub := &subscription{ 465 definition: &fftypes.Subscription{}, 466 } 467 mei := &eventsmocks.Plugin{} 468 mdi := &databasemocks.Plugin{} 469 ed, cancel := newTestEventDispatcher(mdi, mei, sub) 470 defer cancel() 471 472 mdi.On("GetMessages", mock.Anything, mock.Anything).Return(nil, fmt.Errorf("pop")) 473 474 repoll, err := ed.bufferedDelivery([]fftypes.LocallySequenced{&fftypes.Event{ID: fftypes.NewUUID()}}) 475 assert.False(t, repoll) 476 assert.EqualError(t, err, "pop") 477 478 } 479 480 func TestBufferedDeliveryClosedContext(t *testing.T) { 481 482 sub := &subscription{ 483 definition: &fftypes.Subscription{}, 484 } 485 mei := &eventsmocks.Plugin{} 486 mdi := &databasemocks.Plugin{} 487 ed, cancel := newTestEventDispatcher(mdi, mei, sub) 488 go ed.deliverEvents() 489 cancel() 490 491 mdi.On("GetMessages", mock.Anything, mock.Anything).Return(nil, nil) 492 mdi.On("GetDataRefs", mock.Anything, mock.Anything).Return(nil, nil) 493 mei.On("DeliveryRequest", mock.Anything, mock.Anything).Return(nil) 494 495 repoll, err := ed.bufferedDelivery([]fftypes.LocallySequenced{&fftypes.Event{ID: fftypes.NewUUID()}}) 496 assert.False(t, repoll) 497 assert.Regexp(t, "FF10182", err) 498 499 } 500 501 func TestBufferedDeliveryNackRewind(t *testing.T) { 502 503 sub := &subscription{ 504 definition: &fftypes.Subscription{}, 505 } 506 mei := &eventsmocks.Plugin{} 507 mdi := &databasemocks.Plugin{} 508 ed, cancel := newTestEventDispatcher(mdi, mei, sub) 509 defer cancel() 510 go ed.deliverEvents() 511 512 mdi.On("GetMessages", mock.Anything, mock.Anything).Return(nil, nil) 513 mdi.On("GetDataRefs", mock.Anything, mock.Anything).Return(nil, nil) 514 mdi.On("UpdateOffset", mock.Anything, mock.Anything, mock.Anything).Return(nil) 515 516 delivered := make(chan struct{}) 517 deliver := mei.On("DeliveryRequest", mock.Anything, mock.Anything).Return(nil) 518 deliver.RunFn = func(a mock.Arguments) { 519 close(delivered) 520 } 521 522 bdDone := make(chan struct{}) 523 ev1 := fftypes.NewUUID() 524 ed.eventPoller.pollingOffset = 100050 // ahead of nack 525 go func() { 526 repoll, err := ed.bufferedDelivery([]fftypes.LocallySequenced{&fftypes.Event{ID: ev1, Sequence: 100001}}) 527 assert.NoError(t, err) 528 assert.True(t, repoll) 529 close(bdDone) 530 }() 531 532 <-delivered 533 ed.deliveryResponse(&fftypes.EventDeliveryResponse{ 534 ID: ev1, 535 Rejected: true, 536 }) 537 538 <-bdDone 539 assert.Equal(t, int64(100001), ed.eventPoller.pollingOffset) 540 } 541 542 func TestBufferedDeliveryAckFail(t *testing.T) { 543 544 sub := &subscription{ 545 definition: &fftypes.Subscription{}, 546 } 547 mei := &eventsmocks.Plugin{} 548 mdi := &databasemocks.Plugin{} 549 ed, cancel := newTestEventDispatcher(mdi, mei, sub) 550 defer cancel() 551 go ed.deliverEvents() 552 ed.readAhead = 50 553 554 mdi.On("GetMessages", mock.Anything, mock.Anything).Return(nil, nil) 555 mdi.On("GetDataRefs", mock.Anything, mock.Anything).Return(nil, nil) 556 mdi.On("UpdateOffset", mock.Anything, mock.Anything, mock.Anything).Return(fmt.Errorf("pop")) 557 558 delivered := make(chan bool) 559 deliver := mei.On("DeliveryRequest", mock.Anything, mock.Anything).Return(nil) 560 deliver.RunFn = func(a mock.Arguments) { 561 delivered <- true 562 } 563 564 bdDone := make(chan struct{}) 565 ev1 := fftypes.NewUUID() 566 ev2 := fftypes.NewUUID() 567 ed.eventPoller.pollingOffset = 100000 568 go func() { 569 repoll, err := ed.bufferedDelivery([]fftypes.LocallySequenced{ 570 &fftypes.Event{ID: ev1, Sequence: 100001}, 571 &fftypes.Event{ID: ev2, Sequence: 100002}, 572 }) 573 assert.EqualError(t, err, "pop") 574 assert.False(t, repoll) 575 close(bdDone) 576 }() 577 578 <-delivered 579 <-delivered 580 ed.deliveryResponse(&fftypes.EventDeliveryResponse{ 581 ID: ev1, 582 }) 583 584 <-bdDone 585 assert.Equal(t, int64(100001), ed.eventPoller.pollingOffset) 586 } 587 588 func TestBufferedDeliveryFailNack(t *testing.T) { 589 log.SetLevel("trace") 590 591 sub := &subscription{ 592 definition: &fftypes.Subscription{}, 593 } 594 mei := &eventsmocks.Plugin{} 595 mdi := &databasemocks.Plugin{} 596 ed, cancel := newTestEventDispatcher(mdi, mei, sub) 597 defer cancel() 598 go ed.deliverEvents() 599 ed.readAhead = 50 600 601 mdi.On("GetMessages", mock.Anything, mock.Anything).Return(nil, nil) 602 mdi.On("GetDataRefs", mock.Anything, mock.Anything).Return(nil, nil) 603 mdi.On("UpdateOffset", mock.Anything, mock.Anything, mock.Anything).Return(fmt.Errorf("pop")) 604 605 failNacked := make(chan bool) 606 deliver := mei.On("DeliveryRequest", mock.Anything, mock.Anything).Return(fmt.Errorf("pop")) 607 deliver.RunFn = func(a mock.Arguments) { 608 failNacked <- true 609 } 610 611 bdDone := make(chan struct{}) 612 ev1 := fftypes.NewUUID() 613 ev2 := fftypes.NewUUID() 614 ed.eventPoller.pollingOffset = 100000 615 go func() { 616 repoll, err := ed.bufferedDelivery([]fftypes.LocallySequenced{ 617 &fftypes.Event{ID: ev1, Sequence: 100001}, 618 &fftypes.Event{ID: ev2, Sequence: 100002}, 619 }) 620 assert.NoError(t, err) 621 assert.True(t, repoll) 622 close(bdDone) 623 }() 624 625 <-failNacked 626 627 <-bdDone 628 assert.Equal(t, int64(100000), ed.eventPoller.pollingOffset) 629 } 630 631 func TestBufferedFinalAckFail(t *testing.T) { 632 633 sub := &subscription{ 634 definition: &fftypes.Subscription{}, 635 topicsFilter: regexp.MustCompile("never matches"), 636 } 637 mei := &eventsmocks.Plugin{} 638 mdi := &databasemocks.Plugin{} 639 ed, cancel := newTestEventDispatcher(mdi, mei, sub) 640 defer cancel() 641 go ed.deliverEvents() 642 ed.readAhead = 50 643 644 mdi.On("GetMessages", mock.Anything, mock.Anything).Return(nil, nil) 645 mdi.On("GetDataRefs", mock.Anything, mock.Anything).Return(nil, nil) 646 mdi.On("UpdateOffset", mock.Anything, mock.Anything, mock.Anything).Return(fmt.Errorf("pop")) 647 648 ev1 := fftypes.NewUUID() 649 ev2 := fftypes.NewUUID() 650 ed.eventPoller.pollingOffset = 100000 651 repoll, err := ed.bufferedDelivery([]fftypes.LocallySequenced{ 652 &fftypes.Event{ID: ev1, Sequence: 100001}, 653 &fftypes.Event{ID: ev2, Sequence: 100002}, 654 }) 655 assert.EqualError(t, err, "pop") 656 assert.False(t, repoll) 657 658 assert.Equal(t, int64(100002), ed.eventPoller.pollingOffset) 659 } 660 661 func TestAckNotInFlightNoop(t *testing.T) { 662 663 sub := &subscription{ 664 definition: &fftypes.Subscription{}, 665 } 666 mei := &eventsmocks.Plugin{} 667 mdi := &databasemocks.Plugin{} 668 ed, cancel := newTestEventDispatcher(mdi, mei, sub) 669 defer cancel() 670 671 ed.deliveryResponse(&fftypes.EventDeliveryResponse{ID: fftypes.NewUUID()}) 672 } 673 674 func TestAckClosed(t *testing.T) { 675 676 sub := &subscription{ 677 definition: &fftypes.Subscription{}, 678 } 679 mei := &eventsmocks.Plugin{} 680 mdi := &databasemocks.Plugin{} 681 ed, cancel := newTestEventDispatcher(mdi, mei, sub) 682 cancel() 683 684 id1 := fftypes.NewUUID() 685 ed.inflight[*id1] = &fftypes.Event{ID: id1} 686 ed.deliveryResponse(&fftypes.EventDeliveryResponse{ID: id1}) 687 } 688 689 func TestGetEvents(t *testing.T) { 690 ag, cancel := newTestAggregator() 691 defer cancel() 692 693 sub := &subscription{ 694 definition: &fftypes.Subscription{}, 695 } 696 mei := &eventsmocks.Plugin{} 697 mdi := ag.database.(*databasemocks.Plugin) 698 mdi.On("GetEvents", ag.ctx, mock.Anything).Return([]*fftypes.Event{ 699 {Sequence: 12345}, 700 }, nil) 701 702 ed, cancel := newTestEventDispatcher(mdi, mei, sub) 703 cancel() 704 705 lc, err := ed.getEvents(ag.ctx, database.EventQueryFactory.NewFilter(ag.ctx).Gte("sequence", 12345)) 706 assert.NoError(t, err) 707 assert.Equal(t, int64(12345), lc[0].LocalSequence()) 708 }