github.com/evdatsion/aphelion-dpos-bft@v0.32.1/types/event_bus_test.go (about)

     1  package types
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"testing"
     7  	"time"
     8  
     9  	"github.com/stretchr/testify/assert"
    10  	"github.com/stretchr/testify/require"
    11  
    12  	abci "github.com/evdatsion/aphelion-dpos-bft/abci/types"
    13  	cmn "github.com/evdatsion/aphelion-dpos-bft/libs/common"
    14  	tmpubsub "github.com/evdatsion/aphelion-dpos-bft/libs/pubsub"
    15  	tmquery "github.com/evdatsion/aphelion-dpos-bft/libs/pubsub/query"
    16  )
    17  
    18  func TestEventBusPublishEventTx(t *testing.T) {
    19  	eventBus := NewEventBus()
    20  	err := eventBus.Start()
    21  	require.NoError(t, err)
    22  	defer eventBus.Stop()
    23  
    24  	tx := Tx("foo")
    25  	result := abci.ResponseDeliverTx{
    26  		Data: []byte("bar"),
    27  		Events: []abci.Event{
    28  			{Type: "testType", Attributes: []cmn.KVPair{{Key: []byte("baz"), Value: []byte("1")}}},
    29  		},
    30  	}
    31  
    32  	// PublishEventTx adds all these 3 tags, so the query below should work
    33  	query := fmt.Sprintf("tm.event='Tx' AND tx.height=1 AND tx.hash='%X' AND testType.baz=1", tx.Hash())
    34  	txsSub, err := eventBus.Subscribe(context.Background(), "test", tmquery.MustParse(query))
    35  	require.NoError(t, err)
    36  
    37  	done := make(chan struct{})
    38  	go func() {
    39  		msg := <-txsSub.Out()
    40  		edt := msg.Data().(EventDataTx)
    41  		assert.Equal(t, int64(1), edt.Height)
    42  		assert.Equal(t, uint32(0), edt.Index)
    43  		assert.Equal(t, tx, edt.Tx)
    44  		assert.Equal(t, result, edt.Result)
    45  		close(done)
    46  	}()
    47  
    48  	err = eventBus.PublishEventTx(EventDataTx{TxResult{
    49  		Height: 1,
    50  		Index:  0,
    51  		Tx:     tx,
    52  		Result: result,
    53  	}})
    54  	assert.NoError(t, err)
    55  
    56  	select {
    57  	case <-done:
    58  	case <-time.After(1 * time.Second):
    59  		t.Fatal("did not receive a transaction after 1 sec.")
    60  	}
    61  }
    62  
    63  func TestEventBusPublishEventNewBlock(t *testing.T) {
    64  	eventBus := NewEventBus()
    65  	err := eventBus.Start()
    66  	require.NoError(t, err)
    67  	defer eventBus.Stop()
    68  
    69  	block := MakeBlock(0, []Tx{}, nil, []Evidence{})
    70  	resultBeginBlock := abci.ResponseBeginBlock{
    71  		Events: []abci.Event{
    72  			{Type: "testType", Attributes: []cmn.KVPair{{Key: []byte("baz"), Value: []byte("1")}}},
    73  		},
    74  	}
    75  	resultEndBlock := abci.ResponseEndBlock{
    76  		Events: []abci.Event{
    77  			{Type: "testType", Attributes: []cmn.KVPair{{Key: []byte("foz"), Value: []byte("2")}}},
    78  		},
    79  	}
    80  
    81  	// PublishEventNewBlock adds the tm.event tag, so the query below should work
    82  	query := "tm.event='NewBlock' AND testType.baz=1 AND testType.foz=2"
    83  	blocksSub, err := eventBus.Subscribe(context.Background(), "test", tmquery.MustParse(query))
    84  	require.NoError(t, err)
    85  
    86  	done := make(chan struct{})
    87  	go func() {
    88  		msg := <-blocksSub.Out()
    89  		edt := msg.Data().(EventDataNewBlock)
    90  		assert.Equal(t, block, edt.Block)
    91  		assert.Equal(t, resultBeginBlock, edt.ResultBeginBlock)
    92  		assert.Equal(t, resultEndBlock, edt.ResultEndBlock)
    93  		close(done)
    94  	}()
    95  
    96  	err = eventBus.PublishEventNewBlock(EventDataNewBlock{
    97  		Block:            block,
    98  		ResultBeginBlock: resultBeginBlock,
    99  		ResultEndBlock:   resultEndBlock,
   100  	})
   101  	assert.NoError(t, err)
   102  
   103  	select {
   104  	case <-done:
   105  	case <-time.After(1 * time.Second):
   106  		t.Fatal("did not receive a block after 1 sec.")
   107  	}
   108  }
   109  
   110  func TestEventBusPublishEventTxDuplicateKeys(t *testing.T) {
   111  	eventBus := NewEventBus()
   112  	err := eventBus.Start()
   113  	require.NoError(t, err)
   114  	defer eventBus.Stop()
   115  
   116  	tx := Tx("foo")
   117  	result := abci.ResponseDeliverTx{
   118  		Data: []byte("bar"),
   119  		Events: []abci.Event{
   120  			{
   121  				Type: "transfer",
   122  				Attributes: []cmn.KVPair{
   123  					{Key: []byte("sender"), Value: []byte("foo")},
   124  					{Key: []byte("recipient"), Value: []byte("bar")},
   125  					{Key: []byte("amount"), Value: []byte("5")},
   126  				},
   127  			},
   128  			{
   129  				Type: "transfer",
   130  				Attributes: []cmn.KVPair{
   131  					{Key: []byte("sender"), Value: []byte("baz")},
   132  					{Key: []byte("recipient"), Value: []byte("cat")},
   133  					{Key: []byte("amount"), Value: []byte("13")},
   134  				},
   135  			},
   136  			{
   137  				Type: "withdraw.rewards",
   138  				Attributes: []cmn.KVPair{
   139  					{Key: []byte("address"), Value: []byte("bar")},
   140  					{Key: []byte("source"), Value: []byte("iceman")},
   141  					{Key: []byte("amount"), Value: []byte("33")},
   142  				},
   143  			},
   144  		},
   145  	}
   146  
   147  	testCases := []struct {
   148  		query         string
   149  		expectResults bool
   150  	}{
   151  		{
   152  			"tm.event='Tx' AND tx.height=1 AND transfer.sender='DoesNotExist'",
   153  			false,
   154  		},
   155  		{
   156  			"tm.event='Tx' AND tx.height=1 AND transfer.sender='foo'",
   157  			true,
   158  		},
   159  		{
   160  			"tm.event='Tx' AND tx.height=1 AND transfer.sender='baz'",
   161  			true,
   162  		},
   163  		{
   164  			"tm.event='Tx' AND tx.height=1 AND transfer.sender='foo' AND transfer.sender='baz'",
   165  			true,
   166  		},
   167  		{
   168  			"tm.event='Tx' AND tx.height=1 AND transfer.sender='foo' AND transfer.sender='DoesNotExist'",
   169  			false,
   170  		},
   171  	}
   172  
   173  	for i, tc := range testCases {
   174  		sub, err := eventBus.Subscribe(context.Background(), fmt.Sprintf("client-%d", i), tmquery.MustParse(tc.query))
   175  		require.NoError(t, err)
   176  
   177  		done := make(chan struct{})
   178  
   179  		go func() {
   180  			msg := <-sub.Out()
   181  			data := msg.Data().(EventDataTx)
   182  			assert.Equal(t, int64(1), data.Height)
   183  			assert.Equal(t, uint32(0), data.Index)
   184  			assert.Equal(t, tx, data.Tx)
   185  			assert.Equal(t, result, data.Result)
   186  			close(done)
   187  		}()
   188  
   189  		err = eventBus.PublishEventTx(EventDataTx{TxResult{
   190  			Height: 1,
   191  			Index:  0,
   192  			Tx:     tx,
   193  			Result: result,
   194  		}})
   195  		assert.NoError(t, err)
   196  
   197  		select {
   198  		case <-done:
   199  			if !tc.expectResults {
   200  				require.Fail(t, "unexpected transaction result(s) from subscription")
   201  			}
   202  		case <-time.After(1 * time.Second):
   203  			if tc.expectResults {
   204  				require.Fail(t, "failed to receive a transaction after 1 second")
   205  			}
   206  		}
   207  	}
   208  }
   209  
   210  func TestEventBusPublishEventNewBlockHeader(t *testing.T) {
   211  	eventBus := NewEventBus()
   212  	err := eventBus.Start()
   213  	require.NoError(t, err)
   214  	defer eventBus.Stop()
   215  
   216  	block := MakeBlock(0, []Tx{}, nil, []Evidence{})
   217  	resultBeginBlock := abci.ResponseBeginBlock{
   218  		Events: []abci.Event{
   219  			{Type: "testType", Attributes: []cmn.KVPair{{Key: []byte("baz"), Value: []byte("1")}}},
   220  		},
   221  	}
   222  	resultEndBlock := abci.ResponseEndBlock{
   223  		Events: []abci.Event{
   224  			{Type: "testType", Attributes: []cmn.KVPair{{Key: []byte("foz"), Value: []byte("2")}}},
   225  		},
   226  	}
   227  
   228  	// PublishEventNewBlockHeader adds the tm.event tag, so the query below should work
   229  	query := "tm.event='NewBlockHeader' AND testType.baz=1 AND testType.foz=2"
   230  	headersSub, err := eventBus.Subscribe(context.Background(), "test", tmquery.MustParse(query))
   231  	require.NoError(t, err)
   232  
   233  	done := make(chan struct{})
   234  	go func() {
   235  		msg := <-headersSub.Out()
   236  		edt := msg.Data().(EventDataNewBlockHeader)
   237  		assert.Equal(t, block.Header, edt.Header)
   238  		assert.Equal(t, resultBeginBlock, edt.ResultBeginBlock)
   239  		assert.Equal(t, resultEndBlock, edt.ResultEndBlock)
   240  		close(done)
   241  	}()
   242  
   243  	err = eventBus.PublishEventNewBlockHeader(EventDataNewBlockHeader{
   244  		Header:           block.Header,
   245  		ResultBeginBlock: resultBeginBlock,
   246  		ResultEndBlock:   resultEndBlock,
   247  	})
   248  	assert.NoError(t, err)
   249  
   250  	select {
   251  	case <-done:
   252  	case <-time.After(1 * time.Second):
   253  		t.Fatal("did not receive a block header after 1 sec.")
   254  	}
   255  }
   256  
   257  func TestEventBusPublish(t *testing.T) {
   258  	eventBus := NewEventBus()
   259  	err := eventBus.Start()
   260  	require.NoError(t, err)
   261  	defer eventBus.Stop()
   262  
   263  	const numEventsExpected = 14
   264  
   265  	sub, err := eventBus.Subscribe(context.Background(), "test", tmquery.Empty{}, numEventsExpected)
   266  	require.NoError(t, err)
   267  
   268  	done := make(chan struct{})
   269  	go func() {
   270  		numEvents := 0
   271  		for range sub.Out() {
   272  			numEvents++
   273  			if numEvents >= numEventsExpected {
   274  				close(done)
   275  				return
   276  			}
   277  		}
   278  	}()
   279  
   280  	err = eventBus.Publish(EventNewBlockHeader, EventDataNewBlockHeader{})
   281  	require.NoError(t, err)
   282  	err = eventBus.PublishEventNewBlock(EventDataNewBlock{})
   283  	require.NoError(t, err)
   284  	err = eventBus.PublishEventNewBlockHeader(EventDataNewBlockHeader{})
   285  	require.NoError(t, err)
   286  	err = eventBus.PublishEventVote(EventDataVote{})
   287  	require.NoError(t, err)
   288  	err = eventBus.PublishEventNewRoundStep(EventDataRoundState{})
   289  	require.NoError(t, err)
   290  	err = eventBus.PublishEventTimeoutPropose(EventDataRoundState{})
   291  	require.NoError(t, err)
   292  	err = eventBus.PublishEventTimeoutWait(EventDataRoundState{})
   293  	require.NoError(t, err)
   294  	err = eventBus.PublishEventNewRound(EventDataNewRound{})
   295  	require.NoError(t, err)
   296  	err = eventBus.PublishEventCompleteProposal(EventDataCompleteProposal{})
   297  	require.NoError(t, err)
   298  	err = eventBus.PublishEventPolka(EventDataRoundState{})
   299  	require.NoError(t, err)
   300  	err = eventBus.PublishEventUnlock(EventDataRoundState{})
   301  	require.NoError(t, err)
   302  	err = eventBus.PublishEventRelock(EventDataRoundState{})
   303  	require.NoError(t, err)
   304  	err = eventBus.PublishEventLock(EventDataRoundState{})
   305  	require.NoError(t, err)
   306  	err = eventBus.PublishEventValidatorSetUpdates(EventDataValidatorSetUpdates{})
   307  	require.NoError(t, err)
   308  
   309  	select {
   310  	case <-done:
   311  	case <-time.After(1 * time.Second):
   312  		t.Fatalf("expected to receive %d events after 1 sec.", numEventsExpected)
   313  	}
   314  }
   315  
   316  func BenchmarkEventBus(b *testing.B) {
   317  	benchmarks := []struct {
   318  		name        string
   319  		numClients  int
   320  		randQueries bool
   321  		randEvents  bool
   322  	}{
   323  		{"10Clients1Query1Event", 10, false, false},
   324  		{"100Clients", 100, false, false},
   325  		{"1000Clients", 1000, false, false},
   326  
   327  		{"10ClientsRandQueries1Event", 10, true, false},
   328  		{"100Clients", 100, true, false},
   329  		{"1000Clients", 1000, true, false},
   330  
   331  		{"10ClientsRandQueriesRandEvents", 10, true, true},
   332  		{"100Clients", 100, true, true},
   333  		{"1000Clients", 1000, true, true},
   334  
   335  		{"10Clients1QueryRandEvents", 10, false, true},
   336  		{"100Clients", 100, false, true},
   337  		{"1000Clients", 1000, false, true},
   338  	}
   339  
   340  	for _, bm := range benchmarks {
   341  		b.Run(bm.name, func(b *testing.B) {
   342  			benchmarkEventBus(bm.numClients, bm.randQueries, bm.randEvents, b)
   343  		})
   344  	}
   345  }
   346  
   347  func benchmarkEventBus(numClients int, randQueries bool, randEvents bool, b *testing.B) {
   348  	// for random* functions
   349  	cmn.Seed(time.Now().Unix())
   350  
   351  	eventBus := NewEventBusWithBufferCapacity(0) // set buffer capacity to 0 so we are not testing cache
   352  	eventBus.Start()
   353  	defer eventBus.Stop()
   354  
   355  	ctx := context.Background()
   356  	q := EventQueryNewBlock
   357  
   358  	for i := 0; i < numClients; i++ {
   359  		if randQueries {
   360  			q = randQuery()
   361  		}
   362  		sub, err := eventBus.Subscribe(ctx, fmt.Sprintf("client-%d", i), q)
   363  		if err != nil {
   364  			b.Fatal(err)
   365  		}
   366  		go func() {
   367  			for {
   368  				select {
   369  				case <-sub.Out():
   370  				case <-sub.Cancelled():
   371  					return
   372  				}
   373  			}
   374  		}()
   375  	}
   376  
   377  	eventType := EventNewBlock
   378  
   379  	b.ReportAllocs()
   380  	b.ResetTimer()
   381  	for i := 0; i < b.N; i++ {
   382  		if randEvents {
   383  			eventType = randEvent()
   384  		}
   385  
   386  		eventBus.Publish(eventType, EventDataString("Gamora"))
   387  	}
   388  }
   389  
   390  var events = []string{
   391  	EventNewBlock,
   392  	EventNewBlockHeader,
   393  	EventNewRound,
   394  	EventNewRoundStep,
   395  	EventTimeoutPropose,
   396  	EventCompleteProposal,
   397  	EventPolka,
   398  	EventUnlock,
   399  	EventLock,
   400  	EventRelock,
   401  	EventTimeoutWait,
   402  	EventVote}
   403  
   404  func randEvent() string {
   405  	return events[cmn.RandIntn(len(events))]
   406  }
   407  
   408  var queries = []tmpubsub.Query{
   409  	EventQueryNewBlock,
   410  	EventQueryNewBlockHeader,
   411  	EventQueryNewRound,
   412  	EventQueryNewRoundStep,
   413  	EventQueryTimeoutPropose,
   414  	EventQueryCompleteProposal,
   415  	EventQueryPolka,
   416  	EventQueryUnlock,
   417  	EventQueryLock,
   418  	EventQueryRelock,
   419  	EventQueryTimeoutWait,
   420  	EventQueryVote}
   421  
   422  func randQuery() tmpubsub.Query {
   423  	return queries[cmn.RandIntn(len(queries))]
   424  }