Skip to content

Commit

Permalink
Add evm listener tests
Browse files Browse the repository at this point in the history
  • Loading branch information
mpetrun5 committed Oct 16, 2023
1 parent 2f9f117 commit 8ccaab8
Show file tree
Hide file tree
Showing 4 changed files with 330 additions and 5 deletions.
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,4 @@ genmocks:
mockgen -source=chains/evm/transactor/signAndSend/signAndSend.go -destination=./mock/signAndSend.go -package mock
mockgen -source=./store/store.go -destination=./mock/store.go -package mock
mockgen -source=./relayer/message/handler.go -destination=./mock/message.go -package mock
mockgen -source=./chains/evm/listener/listener.go -destination=./mock/listener.go -package mock
13 changes: 8 additions & 5 deletions chains/evm/listener/listener.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ import (
"math/big"
"time"

"github.com/ChainSafe/sygma-core/store"

"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
)
Expand All @@ -26,13 +24,17 @@ type BlockDeltaMeter interface {
TrackBlockDelta(domainID uint8, head *big.Int, current *big.Int)
}

type BlockStorer interface {
StoreBlock(block *big.Int, domainID uint8) error
}

type EVMListener struct {
client ChainClient
eventHandlers []EventHandler
metrics BlockDeltaMeter
blockstore BlockStorer

domainID uint8
blockstore *store.BlockStore
blockRetryInterval time.Duration
blockConfirmations *big.Int
blockInterval *big.Int
Expand All @@ -45,7 +47,7 @@ type EVMListener struct {
func NewEVMListener(
client ChainClient,
eventHandlers []EventHandler,
blockstore *store.BlockStore,
blockstore BlockStorer,
metrics BlockDeltaMeter,
domainID uint8,
blockRetryInterval time.Duration,
Expand All @@ -69,6 +71,7 @@ func NewEVMListener(
// configured for the listener.
func (l *EVMListener) ListenToEvents(ctx context.Context, startBlock *big.Int) {
endBlock := big.NewInt(0)
loop:
for {
select {
case <-ctx.Done():
Expand Down Expand Up @@ -98,7 +101,7 @@ func (l *EVMListener) ListenToEvents(ctx context.Context, startBlock *big.Int) {
err := handler.HandleEvents(startBlock, new(big.Int).Sub(endBlock, big.NewInt(1)))
if err != nil {
l.log.Error().Err(err).Msgf("Unable to handle events")
continue
continue loop
}
}

Expand Down
158 changes: 158 additions & 0 deletions chains/evm/listener/listener_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
package listener_test

import (
"context"
"fmt"
"math/big"
"testing"
"time"

"github.com/ChainSafe/sygma-core/chains/evm/listener"
"github.com/ChainSafe/sygma-core/mock"
"github.com/stretchr/testify/suite"
"go.uber.org/mock/gomock"
)

type ListenerTestSuite struct {
suite.Suite
listener *listener.EVMListener
mockClient *mock.MockChainClient
mockEventHandler *mock.MockEventHandler
mockBlockStorer *mock.MockBlockStorer
mockBlockDeltaMeter *mock.MockBlockDeltaMeter
domainID uint8
}

func TestRunTestSuite(t *testing.T) {
suite.Run(t, new(ListenerTestSuite))
}

func (s *ListenerTestSuite) SetupTest() {
ctrl := gomock.NewController(s.T())
s.domainID = 1
s.mockClient = mock.NewMockChainClient(ctrl)
s.mockEventHandler = mock.NewMockEventHandler(ctrl)
s.mockBlockStorer = mock.NewMockBlockStorer(ctrl)
s.mockBlockDeltaMeter = mock.NewMockBlockDeltaMeter(ctrl)
s.listener = listener.NewEVMListener(
s.mockClient,
[]listener.EventHandler{s.mockEventHandler, s.mockEventHandler},
s.mockBlockStorer,
s.mockBlockDeltaMeter,
s.domainID,
time.Millisecond*75,
big.NewInt(5),
big.NewInt(5))
}

func (s *ListenerTestSuite) Test_ListenToEvents_RetriesIfBlockUnavailable() {
s.mockClient.EXPECT().LatestBlock().Return(big.NewInt(0), fmt.Errorf("error"))

ctx, cancel := context.WithCancel(context.Background())
go s.listener.ListenToEvents(ctx, big.NewInt(100))

time.Sleep(time.Millisecond * 50)
cancel()
}

func (s *ListenerTestSuite) Test_ListenToEvents_SleepsIfBlockTooNew() {
s.mockClient.EXPECT().LatestBlock().Return(big.NewInt(109), nil)

ctx, cancel := context.WithCancel(context.Background())
go s.listener.ListenToEvents(ctx, big.NewInt(100))

time.Sleep(time.Millisecond * 50)
cancel()
}

func (s *ListenerTestSuite) Test_ListenToEvents_RetriesInCaseOfHandlerFailure() {
startBlock := big.NewInt(100)
endBlock := big.NewInt(105)
head := big.NewInt(110)

// First pass
s.mockClient.EXPECT().LatestBlock().Return(head, nil)
s.mockBlockDeltaMeter.EXPECT().TrackBlockDelta(uint8(1), head, endBlock)
s.mockEventHandler.EXPECT().HandleEvents(startBlock, new(big.Int).Sub(endBlock, big.NewInt(1))).Return(fmt.Errorf("error"))
// Second pass
s.mockClient.EXPECT().LatestBlock().Return(head, nil)
s.mockBlockDeltaMeter.EXPECT().TrackBlockDelta(uint8(1), head, endBlock)
s.mockEventHandler.EXPECT().HandleEvents(startBlock, new(big.Int).Sub(endBlock, big.NewInt(1))).Return(nil)
s.mockEventHandler.EXPECT().HandleEvents(startBlock, new(big.Int).Sub(endBlock, big.NewInt(1))).Return(nil)
s.mockBlockStorer.EXPECT().StoreBlock(endBlock, s.domainID).Return(nil)
// third pass
s.mockClient.EXPECT().LatestBlock().Return(head, nil)

ctx, cancel := context.WithCancel(context.Background())

go s.listener.ListenToEvents(ctx, big.NewInt(100))

time.Sleep(time.Millisecond * 50)
cancel()
}

func (s *ListenerTestSuite) Test_ListenToEvents_StoresBlockIfEventHandlingSuccessful() {
startBlock := big.NewInt(100)
endBlock := big.NewInt(105)
head := big.NewInt(110)

s.mockClient.EXPECT().LatestBlock().Return(head, nil)
// prevent infinite runs
s.mockClient.EXPECT().LatestBlock().Return(big.NewInt(95), nil)
s.mockBlockDeltaMeter.EXPECT().TrackBlockDelta(uint8(1), head, endBlock)
s.mockEventHandler.EXPECT().HandleEvents(startBlock, new(big.Int).Sub(endBlock, big.NewInt(1))).Return(nil)
s.mockEventHandler.EXPECT().HandleEvents(startBlock, new(big.Int).Sub(endBlock, big.NewInt(1))).Return(nil)
s.mockBlockStorer.EXPECT().StoreBlock(endBlock, s.domainID).Return(nil)

ctx, cancel := context.WithCancel(context.Background())

go s.listener.ListenToEvents(ctx, big.NewInt(100))

time.Sleep(time.Millisecond * 50)
cancel()
}

func (s *ListenerTestSuite) Test_ListenToEvents_IgnoresBlocStorerError() {
startBlock := big.NewInt(100)
endBlock := big.NewInt(105)
head := big.NewInt(110)

s.mockClient.EXPECT().LatestBlock().Return(head, nil)
// prevent infinite runs
s.mockClient.EXPECT().LatestBlock().Return(big.NewInt(95), nil)
s.mockBlockDeltaMeter.EXPECT().TrackBlockDelta(uint8(1), head, endBlock)
s.mockEventHandler.EXPECT().HandleEvents(startBlock, new(big.Int).Sub(endBlock, big.NewInt(1))).Return(nil)
s.mockEventHandler.EXPECT().HandleEvents(startBlock, new(big.Int).Sub(endBlock, big.NewInt(1))).Return(nil)
s.mockBlockStorer.EXPECT().StoreBlock(endBlock, s.domainID).Return(fmt.Errorf("error"))

ctx, cancel := context.WithCancel(context.Background())

go s.listener.ListenToEvents(ctx, big.NewInt(100))

time.Sleep(time.Millisecond * 50)
cancel()
}

func (s *ListenerTestSuite) Test_ListenToEvents_UsesHeadAsStartBlockIfNilPassed() {
startBlock := big.NewInt(110)
endBlock := big.NewInt(115)
oldHead := big.NewInt(110)
newHead := big.NewInt(120)

s.mockClient.EXPECT().LatestBlock().Return(oldHead, nil)
s.mockClient.EXPECT().LatestBlock().Return(newHead, nil)
s.mockClient.EXPECT().LatestBlock().Return(big.NewInt(65), nil)

s.mockBlockDeltaMeter.EXPECT().TrackBlockDelta(uint8(1), big.NewInt(120), endBlock)

s.mockEventHandler.EXPECT().HandleEvents(startBlock, new(big.Int).Sub(endBlock, big.NewInt(1))).Return(nil)
s.mockEventHandler.EXPECT().HandleEvents(startBlock, new(big.Int).Sub(endBlock, big.NewInt(1))).Return(nil)
s.mockBlockStorer.EXPECT().StoreBlock(endBlock, s.domainID).Return(nil)

ctx, cancel := context.WithCancel(context.Background())

go s.listener.ListenToEvents(ctx, nil)

time.Sleep(time.Millisecond * 100)
cancel()
}
163 changes: 163 additions & 0 deletions mock/listener.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 8ccaab8

Please sign in to comment.