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  }