diff --git a/Makefile b/Makefile index abeca2077a..903b33fbb7 100644 --- a/Makefile +++ b/Makefile @@ -123,7 +123,7 @@ lint-rich: .PHONY: test test: fmt - $(GOTEST) -short -race ./... + $(GOTEST) -gcflags="all=-N -l" -short -race ./... .PHONY: test-rich test-rich: diff --git a/blockchain/blockdao/blockdao.go b/blockchain/blockdao/blockdao.go index 79eaedd366..5c6e538517 100644 --- a/blockchain/blockdao/blockdao.go +++ b/blockchain/blockdao/blockdao.go @@ -9,13 +9,12 @@ import ( "context" "sync/atomic" - "github.com/pkg/errors" - "github.com/prometheus/client_golang/prometheus" - "go.uber.org/zap" - "github.com/iotexproject/go-pkgs/cache" "github.com/iotexproject/go-pkgs/hash" "github.com/iotexproject/iotex-proto/golang/iotextypes" + "github.com/pkg/errors" + "github.com/prometheus/client_golang/prometheus" + "go.uber.org/zap" "github.com/iotexproject/iotex-core/action" "github.com/iotexproject/iotex-core/blockchain/block" @@ -137,7 +136,9 @@ func (dao *blockDAO) checkIndexers(ctx context.Context) error { return nil } -func (dao *blockDAO) Stop(ctx context.Context) error { return dao.lifecycle.OnStop(ctx) } +func (dao *blockDAO) Stop(ctx context.Context) error { + return dao.lifecycle.OnStop(ctx) +} func (dao *blockDAO) GetBlockHash(height uint64) (hash.Hash256, error) { timer := dao.timerFactory.NewTimer("get_block_hash") diff --git a/blockchain/blockdao/blockdao_test.go b/blockchain/blockdao/blockdao_test.go index a6c1c57072..8d136b446d 100644 --- a/blockchain/blockdao/blockdao_test.go +++ b/blockchain/blockdao/blockdao_test.go @@ -7,11 +7,14 @@ import ( "testing" "time" + "github.com/agiledragon/gomonkey/v2" + "github.com/golang/mock/gomock" + "github.com/iotexproject/go-pkgs/cache" + "github.com/iotexproject/go-pkgs/hash" + "github.com/iotexproject/iotex-proto/golang/iotextypes" "github.com/pkg/errors" "github.com/stretchr/testify/require" - "github.com/iotexproject/go-pkgs/hash" - "github.com/iotexproject/iotex-core/action" "github.com/iotexproject/iotex-core/action/protocol" "github.com/iotexproject/iotex-core/blockchain/block" @@ -19,11 +22,589 @@ import ( "github.com/iotexproject/iotex-core/blockchain/genesis" "github.com/iotexproject/iotex-core/db" "github.com/iotexproject/iotex-core/pkg/compress" + "github.com/iotexproject/iotex-core/pkg/lifecycle" + "github.com/iotexproject/iotex-core/pkg/prometheustimer" "github.com/iotexproject/iotex-core/pkg/unit" "github.com/iotexproject/iotex-core/test/identityset" + "github.com/iotexproject/iotex-core/test/mock/mock_blockdao" "github.com/iotexproject/iotex-core/testutil" ) +func TestNewBlockDAOWithIndexersAndCache(t *testing.T) { + r := require.New(t) + + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + var ( + blockdao = mock_blockdao.NewMockBlockDAO(ctrl) + indexers = []BlockIndexer{mock_blockdao.NewMockBlockIndexer(ctrl)} + ) + + t.Run("BlockStoreIsNil", func(t *testing.T) { + dao := NewBlockDAOWithIndexersAndCache(nil, []BlockIndexer{}, 100) + r.Nil(dao) + }) + t.Run("NeedNewCache", func(t *testing.T) { + t.Run("FailedToNewPrometheusTimer", func(t *testing.T) { + p := gomonkey.NewPatches() + defer p.Reset() + + p = p.ApplyFuncReturn(prometheustimer.New, nil, errors.New(t.Name())) + + dao := NewBlockDAOWithIndexersAndCache(blockdao, indexers, 1) + r.Nil(dao) + }) + }) + t.Run("Success", func(t *testing.T) { + p := gomonkey.NewPatches() + defer p.Reset() + + p = p.ApplyFuncReturn(prometheustimer.New, nil, nil) + + dao := NewBlockDAOWithIndexersAndCache(blockdao, indexers, 1) + r.NotNil(dao) + }) +} + +func Test_blockDAO_Start(t *testing.T) { + r := require.New(t) + + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockblockdao := mock_blockdao.NewMockBlockDAO(ctrl) + blockdao := &blockDAO{ + lifecycle: lifecycle.Lifecycle{}, + blockStore: mockblockdao, + } + + t.Run("FailedToStartLifeCycle", func(t *testing.T) { + p := gomonkey.NewPatches() + defer p.Reset() + + p.ApplyMethodReturn(&lifecycle.Lifecycle{}, "OnStart", errors.New(t.Name())) + + err := blockdao.Start(context.Background()) + r.ErrorContains(err, t.Name()) + }) + + t.Run("FailedToGetBlockStoreHeight", func(t *testing.T) { + p := gomonkey.NewPatches() + defer p.Reset() + + p.ApplyMethodReturn(&lifecycle.Lifecycle{}, "OnStart", nil) + mockblockdao.EXPECT().Height().Return(uint64(0), errors.New(t.Name())).Times(1) + + err := blockdao.Start(context.Background()) + r.ErrorContains(err, t.Name()) + }) + + t.Run("Success", func(t *testing.T) { + p := gomonkey.NewPatches() + defer p.Reset() + + expectedHeight := uint64(1) + + p.ApplyMethodReturn(&lifecycle.Lifecycle{}, "OnStart", nil) + mockblockdao.EXPECT().Height().Return(expectedHeight, nil).Times(1) + p.ApplyPrivateMethod(&blockDAO{}, "checkIndexers", func(*blockDAO, context.Context) error { return nil }) + + err := blockdao.Start(context.Background()) + r.NoError(err) + r.Equal(blockdao.tipHeight, expectedHeight) + }) +} + +func Test_blockDAO_checkIndexers(t *testing.T) { + r := require.New(t) + + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockblockdao := mock_blockdao.NewMockBlockDAO(ctrl) + mockblockindexer := mock_blockdao.NewMockBlockIndexer(ctrl) + + blockdao := &blockDAO{ + lifecycle: lifecycle.Lifecycle{}, + blockStore: mockblockdao, + indexers: []BlockIndexer{mockblockindexer}, + } + + t.Run("FailedToCheckIndexer", func(t *testing.T) { + p := gomonkey.NewPatches() + defer p.Reset() + + p.ApplyMethodReturn(&BlockIndexerChecker{}, "CheckIndexer", errors.New(t.Name())) + + err := blockdao.checkIndexers(context.Background()) + r.ErrorContains(err, t.Name()) + }) + + t.Run("Success", func(t *testing.T) { + var ( + tipHeight = uint64(0) + daoTip = uint64(0) + ) + + // // mock required context + ctx := context.Background() + ctx = protocol.WithBlockchainCtx(ctx, protocol.BlockchainCtx{}) + ctx = genesis.WithGenesisContext(ctx, genesis.Genesis{}) + + // mock tipHeight return + mockblockindexer.EXPECT().Height().Return(tipHeight, nil).Times(1) + // mock doaTip return + mockblockdao.EXPECT().Height().Return(daoTip, nil).Times(1) + mockblockdao.EXPECT().GetBlockByHeight(gomock.Any()).Return(&block.Block{}, nil).Times(1) + + err := blockdao.checkIndexers(ctx) + r.NoError(err) + }) +} + +func Test_blockDAO_Stop(t *testing.T) { + r := require.New(t) + + dao := &blockDAO{lifecycle: lifecycle.Lifecycle{}} + + t.Run("FailedToStopLifecycle", func(t *testing.T) { + p := gomonkey.NewPatches() + defer p.Reset() + + p = p.ApplyMethodReturn(&lifecycle.Lifecycle{}, "OnStop", errors.New(t.Name())) + + err := dao.Stop(context.Background()) + + r.ErrorContains(err, t.Name()) + }) + + t.Run("Success", func(t *testing.T) { + p := gomonkey.NewPatches() + defer p.Reset() + + p = p.ApplyMethodReturn(&lifecycle.Lifecycle{}, "OnStop", nil) + + err := dao.Stop(context.Background()) + + r.NoError(err) + }) +} + +func Test_blockDAO_GetBlockHash(t *testing.T) { + r := require.New(t) + + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + store := mock_blockdao.NewMockBlockDAO(ctrl) + + dao := &blockDAO{blockStore: store} + + t.Run("FailedToGetBlock", func(t *testing.T) { + store.EXPECT().GetBlockHash(gomock.Any()).Return(hash.Hash256{}, errors.New(t.Name())).Times(1) + + h, err := dao.GetBlockHash(100) + + r.Empty(h) + r.ErrorContains(err, t.Name()) + }) + + t.Run("Success", func(t *testing.T) { + store.EXPECT().GetBlockHash(gomock.Any()).Return(hash.Hash256{}, nil).Times(1) + + _, err := dao.GetBlockHash(100) + + r.NoError(err) + }) +} + +func Test_blockDAO_GetBlockHeight(t *testing.T) { + r := require.New(t) + + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + store := mock_blockdao.NewMockBlockDAO(ctrl) + + dao := &blockDAO{blockStore: store} + + t.Run("FailedToGetBlock", func(t *testing.T) { + store.EXPECT().GetBlockHeight(gomock.Any()).Return(uint64(0), errors.New(t.Name())).Times(1) + + _, err := dao.GetBlockHeight(hash.Hash256{}) + + r.ErrorContains(err, t.Name()) + }) + + t.Run("Success", func(t *testing.T) { + store.EXPECT().GetBlockHeight(gomock.Any()).Return(uint64(100), nil).Times(1) + + height, err := dao.GetBlockHeight(hash.Hash256{}) + + r.Equal(height, uint64(100)) + r.NoError(err) + }) +} + +func Test_blockDAO_GetBlock(t *testing.T) { + r := require.New(t) + + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + store := mock_blockdao.NewMockBlockDAO(ctrl) + + dao := &blockDAO{blockStore: store} + + t.Run("FailedToGetBlock", func(t *testing.T) { + store.EXPECT().GetBlock(gomock.Any()).Return(nil, errors.New(t.Name())).Times(1) + + blk, err := dao.GetBlock(hash.Hash256{}) + + r.Nil(blk) + r.ErrorContains(err, t.Name()) + }) + + t.Run("Success", func(t *testing.T) { + store.EXPECT().GetBlock(gomock.Any()).Return(&block.Block{}, nil).Times(1) + + blk, err := dao.GetBlock(hash.Hash256{}) + + r.NotNil(blk) + r.NoError(err) + }) +} + +func Test_blockDAO_GetBlockByHeight(t *testing.T) { + r := require.New(t) + + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + store := mock_blockdao.NewMockBlockDAO(ctrl) + + dao := &blockDAO{blockStore: store} + + t.Run("FailedToGetBlockByHash", func(t *testing.T) { + store.EXPECT().GetBlockByHeight(gomock.Any()).Return(nil, errors.New(t.Name())).Times(1) + + blk, err := dao.GetBlockByHeight(100) + + r.Nil(blk) + r.ErrorContains(err, t.Name()) + }) + + t.Run("Success", func(t *testing.T) { + store.EXPECT().GetBlockByHeight(gomock.Any()).Return(&block.Block{}, nil).Times(1) + + blk, err := dao.GetBlockByHeight(100) + + r.NotNil(blk) + r.NoError(err) + }) +} + +func Test_blockDAO_HeaderByHeight(t *testing.T) { + r := require.New(t) + + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + store := mock_blockdao.NewMockBlockDAO(ctrl) + + dao := &blockDAO{ + blockStore: store, + headerCache: cache.NewThreadSafeLruCache(100), + } + + t.Run("HitCache", func(t *testing.T) { + dao.headerCache.Add(uint64(100), &block.Header{}) + + blk, err := dao.HeaderByHeight(100) + + r.NotNil(blk) + r.NoError(err) + }) + + t.Run("FailedToGetHeaderFromBlockStore", func(t *testing.T) { + store.EXPECT().HeaderByHeight(gomock.Any()).Return(nil, errors.New(t.Name())).Times(1) + + header, err := dao.HeaderByHeight(101) + + r.Nil(header) + r.ErrorContains(err, t.Name()) + }) + + t.Run("Success", func(t *testing.T) { + store.EXPECT().HeaderByHeight(gomock.Any()).Return(&block.Header{}, nil).Times(1) + + header, err := dao.HeaderByHeight(102) + + r.NotNil(header) + r.NoError(err) + }) + +} + +func Test_blockDAO_FooterByHeight(t *testing.T) { + r := require.New(t) + + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + store := mock_blockdao.NewMockBlockDAO(ctrl) + + dao := &blockDAO{ + blockStore: store, + footerCache: cache.NewThreadSafeLruCache(3), + } + + t.Run("HitCache", func(t *testing.T) { + dao.footerCache.Add(uint64(100), &block.Footer{}) + + footer, err := dao.FooterByHeight(100) + + r.NotNil(footer) + r.NoError(err) + }) + + t.Run("FailedToGetFooterFromBlockStore", func(t *testing.T) { + store.EXPECT().FooterByHeight(gomock.Any()).Return(nil, errors.New(t.Name())).Times(1) + + footer, err := dao.FooterByHeight(101) + + r.Nil(footer) + r.ErrorContains(err, t.Name()) + }) + + t.Run("Success", func(t *testing.T) { + store.EXPECT().FooterByHeight(gomock.Any()).Return(&block.Footer{}, nil).Times(1) + + footer, err := dao.FooterByHeight(102) + + r.NotNil(footer) + r.NoError(err) + }) +} + +func Test_blockDAO_Height(t *testing.T) { + r := require.New(t) + + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + store := mock_blockdao.NewMockBlockDAO(ctrl) + + dao := &blockDAO{blockStore: store} + + t.Run("FailedToGetHeightFromBlockStore", func(t *testing.T) { + store.EXPECT().Height().Return(uint64(0), errors.New(t.Name())).Times(1) + + height, err := dao.Height() + + r.Zero(height) + r.ErrorContains(err, t.Name()) + }) + + t.Run("Success", func(t *testing.T) { + store.EXPECT().Height().Return(uint64(1000), nil).Times(1) + + height, err := dao.Height() + + r.Equal(height, uint64(1000)) + r.NoError(err) + }) +} + +func Test_blockDAO_Header(t *testing.T) { + r := require.New(t) + + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + store := mock_blockdao.NewMockBlockDAO(ctrl) + + dao := &blockDAO{ + blockStore: store, + headerCache: cache.NewThreadSafeLruCache(3), + } + + t.Run("HitCache", func(t *testing.T) { + h := hash.Hash256{0x76, 0x6d, 0x8b, 0x5b, 0x98, 0xa5, 0xb2, 0xdb, 0x8d, 0x99, 0x0, 0xd2, 0x9c, 0xd1, 0x31, 0xf1, 0x59, 0xb6, 0x2f, 0x7e, 0x74, 0x6b, 0x92, 0x1b, 0x42, 0x68, 0x97, 0x4a, 0x47, 0x3e, 0x8d, 0xc5} + dao.headerCache.Add(h, &block.Header{}) + + header, err := dao.Header(h) + + r.NotNil(header) + r.NoError(err) + }) + + t.Run("FailedToGetHeaderFromBlockStore", func(t *testing.T) { + store.EXPECT().Header(gomock.Any()).Return(nil, errors.New(t.Name())).Times(1) + + header, err := dao.Header(hash.Hash256{}) + + r.Nil(header) + r.ErrorContains(err, t.Name()) + }) + + t.Run("Success", func(t *testing.T) { + store.EXPECT().Header(gomock.Any()).Return(&block.Header{}, nil).Times(1) + + header, err := dao.Header(hash.Hash256{}) + + r.NotNil(header) + r.NoError(err) + }) +} + +func Test_blockDAO_GetReceipts(t *testing.T) { + r := require.New(t) + + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + store := mock_blockdao.NewMockBlockDAO(ctrl) + + dao := &blockDAO{blockStore: store} + + t.Run("FailedToGetReceipts", func(t *testing.T) { + store.EXPECT().GetReceipts(gomock.Any()).Return(nil, errors.New(t.Name())).Times(1) + + receipts, err := dao.GetReceipts(100) + + r.Nil(receipts) + r.ErrorContains(err, t.Name()) + }) + + t.Run("Success", func(t *testing.T) { + store.EXPECT().GetReceipts(gomock.Any()).Return([]*action.Receipt{{}, {}}, nil).Times(1) + + receipts, err := dao.GetReceipts(100) + + r.Len(receipts, 2) + r.NoError(err) + }) +} + +func Test_blockDAO_ContainsTransactionLog(t *testing.T) { + r := require.New(t) + + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + store := mock_blockdao.NewMockBlockDAO(ctrl) + + dao := &blockDAO{blockStore: store} + + store.EXPECT().ContainsTransactionLog().Return(true).Times(1) + r.True(dao.ContainsTransactionLog()) + store.EXPECT().ContainsTransactionLog().Return(false).Times(1) + r.False(dao.ContainsTransactionLog()) +} + +func Test_blockDAO_TransactionLogs(t *testing.T) { + r := require.New(t) + + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + store := mock_blockdao.NewMockBlockDAO(ctrl) + + dao := &blockDAO{blockStore: store} + + t.Run("FailedToTransactionLogs", func(t *testing.T) { + store.EXPECT().TransactionLogs(gomock.Any()).Return(nil, errors.New(t.Name())).Times(1) + + txlogs, err := dao.TransactionLogs(0) + + r.Nil(txlogs) + r.ErrorContains(err, t.Name()) + }) + + t.Run("Success", func(t *testing.T) { + store.EXPECT().TransactionLogs(gomock.Any()).Return(&iotextypes.TransactionLogs{}, nil).Times(1) + + txlogs, err := dao.TransactionLogs(0) + + r.NotNil(txlogs) + r.NoError(err) + }) +} + +func Test_blockDAO_PutBlock(t *testing.T) { + r := require.New(t) + + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + indexer := mock_blockdao.NewMockBlockIndexer(ctrl) + store := mock_blockdao.NewMockBlockDAO(ctrl) + + dao := &blockDAO{ + indexers: []BlockIndexer{indexer}, + blockStore: store, + } + + t.Run("FailedToPutBlockToBlockStore", func(t *testing.T) { + store.EXPECT().PutBlock(gomock.Any(), gomock.Any()).Return(errors.New(t.Name())).Times(1) + + err := dao.PutBlock(context.Background(), &block.Block{}) + + r.ErrorContains(err, t.Name()) + }) + + t.Run("FailedToPutBlockToIndexer", func(t *testing.T) { + store.EXPECT().PutBlock(gomock.Any(), gomock.Any()).Return(nil).Times(1) + indexer.EXPECT().PutBlock(gomock.Any(), gomock.Any()).Return(errors.New(t.Name())).Times(1) + + err := dao.PutBlock(context.Background(), &block.Block{}) + + r.ErrorContains(err, t.Name()) + }) + + t.Run("Success", func(t *testing.T) { + store.EXPECT().PutBlock(gomock.Any(), gomock.Any()).Return(nil).Times(1) + indexer.EXPECT().PutBlock(gomock.Any(), gomock.Any()).Return(nil).Times(1) + + err := dao.PutBlock(context.Background(), &block.Block{}) + + r.NoError(err) + }) +} + +func Test_lruCache(t *testing.T) { + r := require.New(t) + + _cache := cache.NewThreadSafeLruCache(5) + + t.Run("EmptyCache", func(t *testing.T) { + lruCachePut(nil, "any", "any") + v, ok := lruCacheGet(nil, "any") + r.Nil(v) + r.False(ok) + }) + + t.Run("ShouldEqualAndFalse", func(t *testing.T) { + v1, ok1 := lruCacheGet(_cache, "any") + v2, ok2 := _cache.Get("any") + r.Equal(v1, v2) + r.Equal(ok1, ok2) + r.False(ok1) + }) + + t.Run("AfterPutShouldEqualAndTrue", func(t *testing.T) { + lruCachePut(_cache, "any", "any") + + v1, ok1 := lruCacheGet(_cache, "any") + v2, ok2 := _cache.Get("any") + r.Equal(v1, v2) + r.Equal(ok1, ok2) + r.True(ok1) + }) +} + func getTestBlocks(t *testing.T) []*block.Block { amount := uint64(50 << 22) tsf1, err := action.SignedTransfer(identityset.Address(28).String(), identityset.PrivateKey(28), 1, big.NewInt(int64(amount)), nil, testutil.TestGasLimit, big.NewInt(0)) diff --git a/blockchain/blockdao/blockindexer.go b/blockchain/blockdao/blockindexer.go index f8cefba58e..6c698917e5 100644 --- a/blockchain/blockdao/blockindexer.go +++ b/blockchain/blockdao/blockindexer.go @@ -92,9 +92,13 @@ func (bic *BlockIndexerChecker) CheckIndexer(ctx context.Context, indexer BlockI return err } } - producer := blk.PublicKey().Address() + pk := blk.PublicKey() + if pk == nil { + return errors.New("failed to get pubkey") + } + producer := pk.Address() if producer == nil { - return errors.New("failed to get address") + return errors.New("failed to get producer address") } bcCtx.Tip.Height = tipBlk.Height() if bcCtx.Tip.Height > 0 { diff --git a/blockchain/blockdao/blockindexer_test.go b/blockchain/blockdao/blockindexer_test.go index d80ae9788e..3d1978f37b 100644 --- a/blockchain/blockdao/blockindexer_test.go +++ b/blockchain/blockdao/blockindexer_test.go @@ -7,15 +7,18 @@ package blockdao import ( "context" + "errors" "strconv" "testing" + "github.com/agiledragon/gomonkey/v2" "github.com/golang/mock/gomock" + "github.com/iotexproject/go-pkgs/crypto" + "github.com/iotexproject/iotex-proto/golang/iotextypes" "github.com/stretchr/testify/require" "google.golang.org/protobuf/types/known/timestamppb" - "github.com/iotexproject/iotex-proto/golang/iotextypes" - + "github.com/iotexproject/iotex-core/action" "github.com/iotexproject/iotex-core/action/protocol" "github.com/iotexproject/iotex-core/blockchain/block" "github.com/iotexproject/iotex-core/blockchain/genesis" @@ -142,3 +145,123 @@ func TestCheckIndexerWithStart(t *testing.T) { }) } } + +func TestBlockIndexerChecker_CheckIndexer(t *testing.T) { + r := require.New(t) + + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + ctx := context.Background() + store := mock_blockdao.NewMockBlockDAO(ctrl) + dao := &blockDAO{blockStore: store} + bic := NewBlockIndexerChecker(dao) + indexer := mock_blockdao.NewMockBlockIndexer(ctrl) + + t.Run("WithoutBlockchainContext", func(t *testing.T) { + err := bic.CheckIndexer(ctx, indexer, 0, nil) + r.ErrorContains(err, "failed to find blockchain ctx") + }) + + t.Run("WithoutGenesisContext", func(t *testing.T) { + ctx = protocol.WithBlockchainCtx(ctx, protocol.BlockchainCtx{}) + + err := bic.CheckIndexer(ctx, indexer, 0, nil) + r.ErrorContains(err, "failed to find genesis ctx") + }) + + t.Run("FailedToGetIndexerHeight", func(t *testing.T) { + ctx = genesis.WithGenesisContext(ctx, genesis.Genesis{}) + + indexer.EXPECT().Height().Return(uint64(0), errors.New(t.Name())).Times(1) + + err := bic.CheckIndexer(ctx, indexer, 0, nil) + r.ErrorContains(err, t.Name()) + }) + + t.Run("FailedToGetDaoTipHeight", func(t *testing.T) { + indexer.EXPECT().Height().Return(uint64(1), nil).Times(1) + store.EXPECT().Height().Return(uint64(0), errors.New(t.Name())).Times(1) + + err := bic.CheckIndexer(ctx, indexer, 0, nil) + r.ErrorContains(err, t.Name()) + }) + + t.Run("IndexerTipHeightHigherThanDaoTipHeight", func(t *testing.T) { + tipHeight := uint64(100) + daoTip := uint64(99) + + indexer.EXPECT().Height().Return(tipHeight, nil).Times(1) + store.EXPECT().Height().Return(daoTip, nil).Times(1) + + err := bic.CheckIndexer(ctx, indexer, 0, nil) + r.ErrorContains(err, "indexer tip height cannot by higher than dao tip height") + }) + + t.Run("FailedToGetBlockByHeight", func(t *testing.T) { + tipHeight := uint64(98) + daoTip := uint64(99) + + indexer.EXPECT().Height().Return(tipHeight, nil).Times(1) + store.EXPECT().Height().Return(daoTip, nil).Times(1) + store.EXPECT().GetBlockByHeight(gomock.Any()).Return(nil, errors.New(t.Name())).Times(1) + + err := bic.CheckIndexer(ctx, indexer, 0, nil) + r.ErrorContains(err, t.Name()) + }) + + t.Run("LoopFromStartHeightToTargetHeight", func(t *testing.T) { + tipHeight := uint64(98) + daoTip := uint64(99) + + t.Run("FailedToGetBlockByHeight", func(t *testing.T) { + indexer.EXPECT().Height().Return(tipHeight, nil).Times(1) + store.EXPECT().Height().Return(daoTip, nil).Times(1) + store.EXPECT().GetBlockByHeight(gomock.Any()).Return(&block.Block{}, nil).Times(1) + store.EXPECT().GetBlockByHeight(gomock.Any()).Return(nil, errors.New(t.Name())).Times(1) + + err := bic.CheckIndexer(ctx, indexer, 0, nil) + r.ErrorContains(err, t.Name()) + }) + + t.Run("FailedToGetReceipts", func(t *testing.T) { + indexer.EXPECT().Height().Return(tipHeight, nil).Times(1) + store.EXPECT().Height().Return(daoTip, nil).Times(1) + store.EXPECT().GetBlockByHeight(gomock.Any()).Return(&block.Block{}, nil).Times(2) + store.EXPECT().GetReceipts(gomock.Any()).Return(nil, errors.New(t.Name())).Times(1) + + err := bic.CheckIndexer(ctx, indexer, 0, nil) + r.ErrorContains(err, t.Name()) + }) + + t.Run("FailedToGetPubKey", func(t *testing.T) { + indexer.EXPECT().Height().Return(tipHeight, nil).Times(1) + store.EXPECT().Height().Return(daoTip, nil).Times(1) + store.EXPECT().GetBlockByHeight(gomock.Any()).Return(&block.Block{ + Header: block.Header{}, + }, nil).Times(2) + store.EXPECT().GetReceipts(gomock.Any()).Return([]*action.Receipt{}, nil).Times(1) + + err := bic.CheckIndexer(ctx, indexer, 0, nil) + r.ErrorContains(err, "failed to get pubkey") + }) + + pubkey, _ := crypto.HexStringToPublicKey("04806b217cb0b6a675974689fd99549e525d967287eee9a62dc4e598eea981b8158acfe026da7bf58397108abd0607672832c28ef3bc7b5855077f6e67ab5fc096") + + t.Run("FailedToGetAddress", func(t *testing.T) { + indexer.EXPECT().Height().Return(tipHeight, nil).Times(1) + store.EXPECT().Height().Return(daoTip, nil).Times(1) + store.EXPECT().GetBlockByHeight(gomock.Any()).Return(&block.Block{}, nil).Times(2) + store.EXPECT().GetReceipts(gomock.Any()).Return([]*action.Receipt{}, nil).Times(1) + + p := gomonkey.NewPatches() + defer p.Reset() + + p.ApplyMethodReturn(&block.Header{}, "PublicKey", pubkey) + p.ApplyMethodReturn(pubkey, "Address", nil) + + err := bic.CheckIndexer(ctx, indexer, 0, nil) + r.ErrorContains(err, "failed to get producer address") + }) + }) +} diff --git a/go.test.sh b/go.test.sh index f05412187f..f133ec380e 100755 --- a/go.test.sh +++ b/go.test.sh @@ -6,7 +6,7 @@ cat /dev/null > coverage.txt for dir in $(go list -f '{{.ImportPath}}={{join .TestGoFiles ","}}' ./...);do array=(${dir//=/ }) if [ -n "${array[1]}" ]; then - go test -gcflags=all=-l -short -v -coverprofile=coverage.out -covermode=count "${array[0]}" + go test -gcflags="all=-N -l" -short -v -coverprofile=coverage.out -covermode=count "${array[0]}" courtney -l=coverage.out -o=profile.out if [ -f profile.out ]; then cat profile.out >> coverage.txt