From 4c381f1e77b137f75051abf9eae0bade2c5c232f Mon Sep 17 00:00:00 2001 From: William Banfield Date: Mon, 23 Aug 2021 15:44:48 -0400 Subject: [PATCH] add tests --- inspect/inspect.go | 2 +- inspect/inspect_test.go | 368 +++++++++++++++++++++++++++++++++++++++- inspect/rpc/rpc.go | 3 +- 3 files changed, 369 insertions(+), 4 deletions(-) diff --git a/inspect/inspect.go b/inspect/inspect.go index 6c756ff85..dd44ac920 100644 --- a/inspect/inspect.go +++ b/inspect/inspect.go @@ -43,7 +43,7 @@ type Inspect struct { /// //nolint:lll func New(cfg *config.RPCConfig, bs state.BlockStore, ss state.Store, es []indexer.EventSink, logger log.Logger) *Inspect { - routes := rpc.Routes(*cfg, ss, bs, es) + routes := rpc.Routes(*cfg, ss, bs, es, logger) eb := types.NewEventBus() eb.SetLogger(logger.With("module", "events")) is := indexer.NewIndexerService(es, eb) diff --git a/inspect/inspect_test.go b/inspect/inspect_test.go index 2839965e6..f6ba72932 100644 --- a/inspect/inspect_test.go +++ b/inspect/inspect_test.go @@ -18,6 +18,7 @@ import ( "github.com/tendermint/tendermint/inspect" "github.com/tendermint/tendermint/libs/log" "github.com/tendermint/tendermint/libs/pubsub/query" + "github.com/tendermint/tendermint/proto/tendermint/state" httpclient "github.com/tendermint/tendermint/rpc/client/http" "github.com/tendermint/tendermint/state/indexer" indexermocks "github.com/tendermint/tendermint/state/indexer/mocks" @@ -57,7 +58,7 @@ func TestInspectRun(t *testing.T) { } -func TestInspectServeInfoRPC(t *testing.T) { +func TestBlock(t *testing.T) { testHeight := int64(1) testBlock := new(types.Block) testBlock.Header.Height = testHeight @@ -98,7 +99,7 @@ func TestInspectServeInfoRPC(t *testing.T) { stateStoreMock.AssertExpectations(t) } -func TestInspectTxSearch(t *testing.T) { +func TestTxSearch(t *testing.T) { testHash := []byte("test") testTx := []byte("tx") testQuery := fmt.Sprintf("tx.hash='%s'", string(testHash)) @@ -144,6 +145,369 @@ func TestInspectTxSearch(t *testing.T) { stateStoreMock.AssertExpectations(t) blockStoreMock.AssertExpectations(t) } +func TestTx(t *testing.T) { + testHash := []byte("test") + testTx := []byte("tx") + + stateStoreMock := &statemocks.Store{} + blockStoreMock := &statemocks.BlockStore{} + eventSinkMock := &indexermocks.EventSink{} + eventSinkMock.On("Stop").Return(nil) + eventSinkMock.On("Type").Return(indexer.KV) + eventSinkMock.On("GetTxByHash", testHash).Return(&abcitypes.TxResult{ + Tx: testTx, + }, nil) + + rpcConfig := config.TestRPCConfig() + l := log.TestingLogger() + d := inspect.New(rpcConfig, blockStoreMock, stateStoreMock, []indexer.EventSink{eventSinkMock}, l) + ctx, cancel := context.WithCancel(context.Background()) + wg := &sync.WaitGroup{} + wg.Add(1) + go func() { + defer wg.Done() + require.NoError(t, d.Run(ctx)) + }() + requireConnect(t, rpcConfig.ListenAddress, 15) + cli, err := httpclient.New(rpcConfig.ListenAddress) + require.NoError(t, err) + + res, err := cli.Tx(context.Background(), testHash, false) + require.NoError(t, err) + require.Equal(t, types.Tx(testTx), res.Tx) + + cancel() + wg.Wait() + + eventSinkMock.AssertExpectations(t) + stateStoreMock.AssertExpectations(t) + blockStoreMock.AssertExpectations(t) +} +func TestConsensusParams(t *testing.T) { + testHeight := int64(1) + testMaxGas := int64(55) + stateStoreMock := &statemocks.Store{} + blockStoreMock := &statemocks.BlockStore{} + blockStoreMock.On("Height").Return(testHeight) + blockStoreMock.On("Base").Return(int64(0)) + stateStoreMock.On("LoadConsensusParams", testHeight).Return(types.ConsensusParams{ + Block: types.BlockParams{ + MaxGas: testMaxGas, + }, + }, nil) + eventSinkMock := &indexermocks.EventSink{} + eventSinkMock.On("Stop").Return(nil) + rpcConfig := config.TestRPCConfig() + l := log.TestingLogger() + d := inspect.New(rpcConfig, blockStoreMock, stateStoreMock, []indexer.EventSink{eventSinkMock}, l) + + ctx, cancel := context.WithCancel(context.Background()) + wg := &sync.WaitGroup{} + wg.Add(1) + go func() { + defer wg.Done() + require.NoError(t, d.Run(ctx)) + }() + // force context switch + time.Sleep(10 * time.Millisecond) + requireConnect(t, rpcConfig.ListenAddress, 15) + cli, err := httpclient.New(rpcConfig.ListenAddress) + require.NoError(t, err) + params, err := cli.ConsensusParams(context.Background(), &testHeight) + require.NoError(t, err) + require.Equal(t, params.ConsensusParams.Block.MaxGas, testMaxGas) + + cancel() + wg.Wait() + + blockStoreMock.AssertExpectations(t) + stateStoreMock.AssertExpectations(t) +} + +func TestBlockResults(t *testing.T) { + testHeight := int64(1) + testGasUsed := int64(100) + stateStoreMock := &statemocks.Store{} + // tmstate "github.com/tendermint/tendermint/proto/tendermint/state" + stateStoreMock.On("LoadABCIResponses", testHeight).Return(&state.ABCIResponses{ + DeliverTxs: []*abcitypes.ResponseDeliverTx{ + { + GasUsed: testGasUsed, + }, + }, + EndBlock: &abcitypes.ResponseEndBlock{}, + BeginBlock: &abcitypes.ResponseBeginBlock{}, + }, nil) + blockStoreMock := &statemocks.BlockStore{} + blockStoreMock.On("Base").Return(int64(0)) + blockStoreMock.On("Height").Return(testHeight) + eventSinkMock := &indexermocks.EventSink{} + eventSinkMock.On("Stop").Return(nil) + rpcConfig := config.TestRPCConfig() + l := log.TestingLogger() + d := inspect.New(rpcConfig, blockStoreMock, stateStoreMock, []indexer.EventSink{eventSinkMock}, l) + + ctx, cancel := context.WithCancel(context.Background()) + wg := &sync.WaitGroup{} + wg.Add(1) + go func() { + defer wg.Done() + require.NoError(t, d.Run(ctx)) + }() + // force context switch + time.Sleep(10 * time.Millisecond) + requireConnect(t, rpcConfig.ListenAddress, 15) + cli, err := httpclient.New(rpcConfig.ListenAddress) + require.NoError(t, err) + res, err := cli.BlockResults(context.Background(), &testHeight) + require.NoError(t, err) + require.Equal(t, res.TotalGasUsed, testGasUsed) + + cancel() + wg.Wait() + + blockStoreMock.AssertExpectations(t) + stateStoreMock.AssertExpectations(t) +} + +func TestCommit(t *testing.T) { + testHeight := int64(1) + testRound := int32(101) + stateStoreMock := &statemocks.Store{} + blockStoreMock := &statemocks.BlockStore{} + blockStoreMock.On("Base").Return(int64(0)) + blockStoreMock.On("Height").Return(testHeight) + blockStoreMock.On("LoadBlockMeta", testHeight).Return(&types.BlockMeta{}, nil) + blockStoreMock.On("LoadSeenCommit").Return(&types.Commit{ + Height: testHeight, + Round: testRound, + }, nil) + eventSinkMock := &indexermocks.EventSink{} + eventSinkMock.On("Stop").Return(nil) + rpcConfig := config.TestRPCConfig() + l := log.TestingLogger() + d := inspect.New(rpcConfig, blockStoreMock, stateStoreMock, []indexer.EventSink{eventSinkMock}, l) + + ctx, cancel := context.WithCancel(context.Background()) + wg := &sync.WaitGroup{} + wg.Add(1) + go func() { + defer wg.Done() + require.NoError(t, d.Run(ctx)) + }() + // force context switch + time.Sleep(10 * time.Millisecond) + requireConnect(t, rpcConfig.ListenAddress, 15) + cli, err := httpclient.New(rpcConfig.ListenAddress) + require.NoError(t, err) + res, err := cli.Commit(context.Background(), &testHeight) + require.NoError(t, err) + require.NotNil(t, res) + require.Equal(t, res.SignedHeader.Commit.Round, testRound) + + cancel() + wg.Wait() + + blockStoreMock.AssertExpectations(t) + stateStoreMock.AssertExpectations(t) +} + +func TestBlockByHash(t *testing.T) { + testHeight := int64(1) + testHash := []byte("test hash") + testBlock := new(types.Block) + testBlock.Header.Height = testHeight + testBlock.Header.LastCommitHash = testHash + stateStoreMock := &statemocks.Store{} + blockStoreMock := &statemocks.BlockStore{} + blockStoreMock.On("LoadBlockMeta", testHeight).Return(&types.BlockMeta{ + BlockID: types.BlockID{ + Hash: testHash, + }, + Header: types.Header{ + Height: testHeight, + }, + }, nil) + blockStoreMock.On("LoadBlockByHash", testHash).Return(testBlock, nil) + eventSinkMock := &indexermocks.EventSink{} + eventSinkMock.On("Stop").Return(nil) + rpcConfig := config.TestRPCConfig() + l := log.TestingLogger() + d := inspect.New(rpcConfig, blockStoreMock, stateStoreMock, []indexer.EventSink{eventSinkMock}, l) + + ctx, cancel := context.WithCancel(context.Background()) + wg := &sync.WaitGroup{} + wg.Add(1) + go func() { + defer wg.Done() + require.NoError(t, d.Run(ctx)) + }() + // force context switch + time.Sleep(10 * time.Millisecond) + requireConnect(t, rpcConfig.ListenAddress, 15) + cli, err := httpclient.New(rpcConfig.ListenAddress) + require.NoError(t, err) + res, err := cli.BlockByHash(context.Background(), testHash) + require.NoError(t, err) + require.NotNil(t, res) + require.Equal(t, []byte(res.BlockID.Hash), testHash) + + cancel() + wg.Wait() + + blockStoreMock.AssertExpectations(t) + stateStoreMock.AssertExpectations(t) +} + +func TestBlockchain(t *testing.T) { + testHeight := int64(1) + testBlock := new(types.Block) + testBlockHash := []byte("test hash") + testBlock.Header.Height = testHeight + testBlock.Header.LastCommitHash = testBlockHash + stateStoreMock := &statemocks.Store{} + + blockStoreMock := &statemocks.BlockStore{} + blockStoreMock.On("Height").Return(testHeight) + blockStoreMock.On("Base").Return(int64(0)) + blockStoreMock.On("LoadBlockMeta", testHeight).Return(&types.BlockMeta{ + BlockID: types.BlockID{ + Hash: testBlockHash, + }, + }) + eventSinkMock := &indexermocks.EventSink{} + eventSinkMock.On("Stop").Return(nil) + rpcConfig := config.TestRPCConfig() + l := log.TestingLogger() + d := inspect.New(rpcConfig, blockStoreMock, stateStoreMock, []indexer.EventSink{eventSinkMock}, l) + + ctx, cancel := context.WithCancel(context.Background()) + wg := &sync.WaitGroup{} + wg.Add(1) + go func() { + defer wg.Done() + require.NoError(t, d.Run(ctx)) + }() + // force context switch + time.Sleep(10 * time.Millisecond) + requireConnect(t, rpcConfig.ListenAddress, 15) + cli, err := httpclient.New(rpcConfig.ListenAddress) + require.NoError(t, err) + res, err := cli.BlockchainInfo(context.Background(), 0, 100) + require.NoError(t, err) + require.NotNil(t, res) + require.Equal(t, testBlockHash, []byte(res.BlockMetas[0].BlockID.Hash)) + + cancel() + wg.Wait() + + blockStoreMock.AssertExpectations(t) + stateStoreMock.AssertExpectations(t) +} + +func TestValidators(t *testing.T) { + testHeight := int64(1) + testVotingPower := int64(100) + testValidators := types.ValidatorSet{ + Validators: []*types.Validator{ + { + VotingPower: testVotingPower, + }, + }, + } + stateStoreMock := &statemocks.Store{} + stateStoreMock.On("LoadValidators", testHeight).Return(&testValidators, nil) + + blockStoreMock := &statemocks.BlockStore{} + blockStoreMock.On("Height").Return(testHeight) + blockStoreMock.On("Base").Return(int64(0)) + eventSinkMock := &indexermocks.EventSink{} + eventSinkMock.On("Stop").Return(nil) + rpcConfig := config.TestRPCConfig() + l := log.TestingLogger() + d := inspect.New(rpcConfig, blockStoreMock, stateStoreMock, []indexer.EventSink{eventSinkMock}, l) + + ctx, cancel := context.WithCancel(context.Background()) + wg := &sync.WaitGroup{} + wg.Add(1) + go func() { + defer wg.Done() + require.NoError(t, d.Run(ctx)) + }() + // force context switch + time.Sleep(10 * time.Millisecond) + requireConnect(t, rpcConfig.ListenAddress, 15) + cli, err := httpclient.New(rpcConfig.ListenAddress) + require.NoError(t, err) + + testPage := 1 + testPerPage := 100 + res, err := cli.Validators(context.Background(), &testHeight, &testPage, &testPerPage) + require.NoError(t, err) + require.NotNil(t, res) + require.Equal(t, testVotingPower, res.Validators[0].VotingPower) + + cancel() + wg.Wait() + + blockStoreMock.AssertExpectations(t) + stateStoreMock.AssertExpectations(t) +} + +func TestBlockSearch(t *testing.T) { + testHeight := int64(1) + testBlockHash := []byte("test hash") + testQuery := "block.height = 1" + stateStoreMock := &statemocks.Store{} + + blockStoreMock := &statemocks.BlockStore{} + eventSinkMock := &indexermocks.EventSink{} + eventSinkMock.On("Stop").Return(nil) + eventSinkMock.On("Type").Return(indexer.KV) + blockStoreMock.On("LoadBlock", testHeight).Return(&types.Block{ + Header: types.Header{ + Height: testHeight, + }, + }, nil) + blockStoreMock.On("LoadBlockMeta", testHeight).Return(&types.BlockMeta{ + BlockID: types.BlockID{ + Hash: testBlockHash, + }, + }) + eventSinkMock.On("SearchBlockEvents", mock.Anything, + mock.MatchedBy(func(q *query.Query) bool { return testQuery == q.String() })). + Return([]int64{testHeight}, nil) + rpcConfig := config.TestRPCConfig() + l := log.TestingLogger() + d := inspect.New(rpcConfig, blockStoreMock, stateStoreMock, []indexer.EventSink{eventSinkMock}, l) + + ctx, cancel := context.WithCancel(context.Background()) + wg := &sync.WaitGroup{} + wg.Add(1) + go func() { + defer wg.Done() + require.NoError(t, d.Run(ctx)) + }() + // force context switch + time.Sleep(10 * time.Millisecond) + requireConnect(t, rpcConfig.ListenAddress, 15) + cli, err := httpclient.New(rpcConfig.ListenAddress) + require.NoError(t, err) + + testPage := 1 + testPerPage := 100 + testOrderBy := "desc" + res, err := cli.BlockSearch(context.Background(), testQuery, &testPage, &testPerPage, testOrderBy) + require.NoError(t, err) + require.NotNil(t, res) + require.Equal(t, testBlockHash, []byte(res.Blocks[0].BlockID.Hash)) + + cancel() + wg.Wait() + + blockStoreMock.AssertExpectations(t) + stateStoreMock.AssertExpectations(t) +} func requireConnect(t testing.TB, addr string, retries int) { parts := strings.SplitN(addr, "://", 2) diff --git a/inspect/rpc/rpc.go b/inspect/rpc/rpc.go index 18b19ee93..e582c4487 100644 --- a/inspect/rpc/rpc.go +++ b/inspect/rpc/rpc.go @@ -27,13 +27,14 @@ type Server struct { } // Routes returns the set of routes used by the Inspect server. -func Routes(cfg config.RPCConfig, s state.Store, bs state.BlockStore, es []indexer.EventSink) core.RoutesMap { +func Routes(cfg config.RPCConfig, s state.Store, bs state.BlockStore, es []indexer.EventSink, logger log.Logger) core.RoutesMap { env := &core.Environment{ Config: cfg, EventSinks: es, StateStore: s, BlockStore: bs, ConsensusReactor: waitSyncCheckerImpl{}, + Logger: logger, } return core.RoutesMap{ "blockchain": server.NewRPCFunc(env.BlockchainInfo, "minHeight,maxHeight", true),