github.com/iotexproject/iotex-core@v1.14.1-rc1/blockindex/sync_indexers_test.go (about)

     1  // Copyright (c) 2023 IoTeX Foundation
     2  // This source code is provided 'as is' and no warranties are given as to title or non-infringement, merchantability
     3  // or fitness for purpose and, to the extent permitted by law, all liability for your use of the code is disclaimed.
     4  // This source code is governed by Apache License 2.0 that can be found in the LICENSE file.
     5  
     6  package blockindex
     7  
     8  import (
     9  	"context"
    10  	"strconv"
    11  	"testing"
    12  
    13  	"github.com/golang/mock/gomock"
    14  	"github.com/stretchr/testify/require"
    15  
    16  	"github.com/iotexproject/iotex-core/blockchain/block"
    17  	"github.com/iotexproject/iotex-core/blockchain/blockdao"
    18  	"github.com/iotexproject/iotex-core/test/identityset"
    19  	"github.com/iotexproject/iotex-core/test/mock/mock_blockdao"
    20  )
    21  
    22  func TestSyncIndexers_StartHeight(t *testing.T) {
    23  	require := require.New(t)
    24  	ctrl := gomock.NewController(t)
    25  
    26  	cases := []struct {
    27  		name     string
    28  		indexers [][2]uint64 // [startHeight, height]
    29  		expect   uint64
    30  	}{
    31  		{"no indexers", nil, 0},
    32  		{"one indexer without start height", [][2]uint64{{0, 100}}, 101},
    33  		{"one indexer with start height I", [][2]uint64{{100, 200}}, 201},
    34  		{"one indexer with start height II", [][2]uint64{{300, 200}}, 300},
    35  		{"two indexers with start height I", [][2]uint64{{100, 200}, {200, 300}}, 201},
    36  		{"two indexers with start height II", [][2]uint64{{100, 200}, {400, 300}}, 201},
    37  		{"two indexers with start height III", [][2]uint64{{100, 350}, {400, 300}}, 351},
    38  		{"two indexers one with start height I", [][2]uint64{{0, 1}, {150, 1}}, 2},
    39  		{"two indexers one with start height II", [][2]uint64{{0, 1}, {150, 200}}, 2},
    40  		{"two indexers one with start height III", [][2]uint64{{0, 200}, {250, 1}}, 201},
    41  		{"two indexers one with start height IV", [][2]uint64{{0, 200}, {150, 1}}, 150},
    42  		{"two indexers I", [][2]uint64{{0, 5}, {0, 1}}, 2},
    43  		{"two indexers II", [][2]uint64{{0, 5}, {0, 5}}, 6},
    44  		{"two indexers III", [][2]uint64{{0, 5}, {0, 6}}, 6},
    45  		{"multiple indexers I", [][2]uint64{{0, 5}, {0, 6}, {0, 7}}, 6},
    46  		{"multiple indexers II", [][2]uint64{{0, 5}, {10, 6}, {0, 7}}, 6},
    47  		{"multiple indexers III", [][2]uint64{{10, 5}, {0, 6}, {0, 7}}, 7},
    48  	}
    49  
    50  	for _, c := range cases {
    51  		t.Run(c.name, func(t *testing.T) {
    52  			var indexers []blockdao.BlockIndexer
    53  			for _, indexer := range c.indexers {
    54  				if indexer[0] > 0 {
    55  					mockIndexerWithStart := mock_blockdao.NewMockBlockIndexerWithStart(ctrl)
    56  					mockIndexerWithStart.EXPECT().Start(gomock.Any()).Return(nil).Times(1)
    57  					mockIndexerWithStart.EXPECT().StartHeight().Return(indexer[0]).Times(1)
    58  					mockIndexerWithStart.EXPECT().Height().Return(indexer[1], nil).Times(1)
    59  					indexers = append(indexers, mockIndexerWithStart)
    60  				} else {
    61  					mockIndexer := mock_blockdao.NewMockBlockIndexer(ctrl)
    62  					mockIndexer.EXPECT().Start(gomock.Any()).Return(nil).Times(1)
    63  					mockIndexer.EXPECT().Height().Return(indexer[1], nil).Times(1)
    64  					indexers = append(indexers, mockIndexer)
    65  				}
    66  			}
    67  			ig := NewSyncIndexers(indexers...)
    68  			err := ig.Start(context.Background())
    69  			require.NoError(err)
    70  			height := ig.StartHeight()
    71  			require.Equal(c.expect, height)
    72  		})
    73  	}
    74  
    75  }
    76  
    77  func TestSyncIndexers_Height(t *testing.T) {
    78  	require := require.New(t)
    79  	ctrl := gomock.NewController(t)
    80  	defer ctrl.Finish()
    81  
    82  	cases := []struct {
    83  		heights []uint64
    84  		expect  uint64
    85  	}{
    86  		{[]uint64{}, 0},
    87  		{[]uint64{100}, 100},
    88  		{[]uint64{100, 100}, 100},
    89  		{[]uint64{90, 100}, 90},
    90  		{[]uint64{100, 90}, 90},
    91  		{[]uint64{100, 100, 100}, 100},
    92  		{[]uint64{90, 100, 100}, 90},
    93  		{[]uint64{90, 80, 100}, 80},
    94  		{[]uint64{90, 80, 70}, 70},
    95  	}
    96  
    97  	for i := range cases {
    98  		name := strconv.FormatUint(uint64(i), 10)
    99  		t.Run(name, func(t *testing.T) {
   100  			var indexers []blockdao.BlockIndexer
   101  			for _, height := range cases[i].heights {
   102  				mockIndexer := mock_blockdao.NewMockBlockIndexer(ctrl)
   103  				mockIndexer.EXPECT().Height().Return(height, nil).Times(1)
   104  				indexers = append(indexers, mockIndexer)
   105  			}
   106  			ig := NewSyncIndexers(indexers...)
   107  			height, err := ig.Height()
   108  			require.NoError(err)
   109  			require.Equal(cases[i].expect, height)
   110  		})
   111  	}
   112  }
   113  
   114  func TestSyncIndexers_PutBlock(t *testing.T) {
   115  	require := require.New(t)
   116  	ctrl := gomock.NewController(t)
   117  	defer ctrl.Finish()
   118  
   119  	cases := []struct {
   120  		indexers     [][2]uint64 // [startHeight, height]
   121  		blocks       []uint64    // blocks to put
   122  		expectBlocks [][]uint64  // expect blocks to put on every indexer
   123  	}{
   124  		{
   125  			[][2]uint64{},
   126  			[]uint64{100},
   127  			[][]uint64{},
   128  		},
   129  		{
   130  			[][2]uint64{{100, 10}},
   131  			[]uint64{10, 20, 90, 100, 101},
   132  			[][]uint64{{100, 101}},
   133  		},
   134  		{
   135  			[][2]uint64{{100, 210}},
   136  			[]uint64{10, 20, 90, 100, 101, 210, 211},
   137  			[][]uint64{{211}},
   138  		},
   139  		{
   140  			[][2]uint64{{0, 200}, {250, 1}},
   141  			[]uint64{1, 2, 201, 249, 250, 251},
   142  			[][]uint64{{201, 249, 250, 251}, {250, 251}},
   143  		},
   144  		{
   145  			[][2]uint64{{0, 250}, {250, 250}},
   146  			[]uint64{1, 2, 201, 249, 250, 251, 252},
   147  			[][]uint64{{251, 252}, {251, 252}},
   148  		},
   149  		{
   150  			[][2]uint64{{0, 200}, {250, 1}, {300, 1}},
   151  			[]uint64{1, 2, 201, 249, 250, 251, 300, 301},
   152  			[][]uint64{{201, 249, 250, 251, 300, 301}, {250, 251, 300, 301}, {300, 301}},
   153  		},
   154  		{
   155  			[][2]uint64{{0, 250}, {250, 250}, {300, 250}},
   156  			[]uint64{1, 2, 201, 249, 250, 251, 300, 301},
   157  			[][]uint64{{251, 300, 301}, {251, 300, 301}, {300, 301}},
   158  		},
   159  		{
   160  			[][2]uint64{{0, 300}, {250, 300}, {300, 300}},
   161  			[]uint64{1, 2, 201, 249, 250, 251, 300, 301},
   162  			[][]uint64{{301}, {301}, {301}},
   163  		},
   164  		{
   165  			[][2]uint64{{0, 400}, {250, 400}, {300, 400}},
   166  			[]uint64{1, 2, 201, 249, 250, 251, 300, 301, 400, 401},
   167  			[][]uint64{{401}, {401}, {401}},
   168  		},
   169  	}
   170  
   171  	for _, c := range cases {
   172  		t.Run("", func(t *testing.T) {
   173  			var indexers []blockdao.BlockIndexer
   174  			putBlocks := make([][]uint64, len(c.indexers))
   175  			indexersHeight := make([]uint64, len(c.indexers))
   176  			for id, indexer := range c.indexers {
   177  				idx := id
   178  				indexersHeight[idx] = indexer[1]
   179  				if indexer[0] > 0 {
   180  					mockIndexerWithStart := mock_blockdao.NewMockBlockIndexerWithStart(ctrl)
   181  					mockIndexerWithStart.EXPECT().Start(gomock.Any()).Return(nil).Times(1)
   182  					mockIndexerWithStart.EXPECT().StartHeight().Return(indexer[0]).Times(1)
   183  					mockIndexerWithStart.EXPECT().Height().DoAndReturn(func() (uint64, error) {
   184  						return indexersHeight[idx], nil
   185  					}).AnyTimes()
   186  					mockIndexerWithStart.EXPECT().PutBlock(gomock.Any(), gomock.Any()).DoAndReturn(func(ctx context.Context, blk *block.Block) error {
   187  						putBlocks[idx] = append(putBlocks[idx], blk.Height())
   188  						indexersHeight[idx] = blk.Height()
   189  						return nil
   190  					}).Times(len(c.expectBlocks[idx]))
   191  					indexers = append(indexers, mockIndexerWithStart)
   192  				} else {
   193  					mockIndexer := mock_blockdao.NewMockBlockIndexer(ctrl)
   194  					mockIndexer.EXPECT().Start(gomock.Any()).Return(nil).Times(1)
   195  					mockIndexer.EXPECT().Height().DoAndReturn(func() (uint64, error) {
   196  						return indexersHeight[idx], nil
   197  					}).AnyTimes()
   198  					mockIndexer.EXPECT().PutBlock(gomock.Any(), gomock.Any()).DoAndReturn(func(ctx context.Context, blk *block.Block) error {
   199  						putBlocks[idx] = append(putBlocks[idx], blk.Height())
   200  						indexersHeight[idx] = blk.Height()
   201  						return nil
   202  					}).Times(len(c.expectBlocks[idx]))
   203  					indexers = append(indexers, mockIndexer)
   204  				}
   205  			}
   206  			ig := NewSyncIndexers(indexers...)
   207  			err := ig.Start(context.Background())
   208  			require.NoError(err)
   209  			for _, blkHeight := range c.blocks {
   210  				blk, err := block.NewBuilder(block.RunnableActions{}).SetHeight(blkHeight).SignAndBuild(identityset.PrivateKey(0))
   211  				require.NoError(err)
   212  				err = ig.PutBlock(context.Background(), &blk)
   213  				require.NoError(err)
   214  			}
   215  			require.Equal(c.expectBlocks, putBlocks)
   216  		})
   217  	}
   218  }