package commands
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"testing"
|
|
|
|
"github.com/spf13/cobra"
|
|
"github.com/stretchr/testify/mock"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
dbm "github.com/tendermint/tm-db"
|
|
|
|
abcitypes "github.com/tendermint/tendermint/abci/types"
|
|
"github.com/tendermint/tendermint/config"
|
|
"github.com/tendermint/tendermint/internal/state/indexer"
|
|
"github.com/tendermint/tendermint/internal/state/mocks"
|
|
"github.com/tendermint/tendermint/libs/log"
|
|
prototmstate "github.com/tendermint/tendermint/proto/tendermint/state"
|
|
"github.com/tendermint/tendermint/types"
|
|
|
|
_ "github.com/lib/pq" // for the psql sink
|
|
)
|
|
|
|
const (
|
|
height int64 = 10
|
|
base int64 = 2
|
|
)
|
|
|
|
func setupReIndexEventCmd(ctx context.Context, conf *config.Config, logger log.Logger) *cobra.Command {
|
|
cmd := MakeReindexEventCommand(conf, logger)
|
|
|
|
reIndexEventCmd := &cobra.Command{
|
|
Use: cmd.Use,
|
|
Run: func(cmd *cobra.Command, args []string) {},
|
|
}
|
|
|
|
_ = reIndexEventCmd.ExecuteContext(ctx)
|
|
|
|
return reIndexEventCmd
|
|
}
|
|
|
|
func TestReIndexEventCheckHeight(t *testing.T) {
|
|
mockBlockStore := &mocks.BlockStore{}
|
|
mockBlockStore.
|
|
On("Base").Return(base).
|
|
On("Height").Return(height)
|
|
|
|
testCases := []struct {
|
|
startHeight int64
|
|
endHeight int64
|
|
validHeight bool
|
|
}{
|
|
{0, 0, true},
|
|
{0, base, true},
|
|
{0, base - 1, false},
|
|
{0, height, true},
|
|
{0, height + 1, true},
|
|
{0, 0, true},
|
|
{base - 1, 0, false},
|
|
{base, 0, true},
|
|
{base, base, true},
|
|
{base, base - 1, false},
|
|
{base, height, true},
|
|
{base, height + 1, true},
|
|
{height, 0, true},
|
|
{height, base, false},
|
|
{height, height - 1, false},
|
|
{height, height, true},
|
|
{height, height + 1, true},
|
|
{height + 1, 0, false},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
err := checkValidHeight(mockBlockStore, checkValidHeightArgs{startHeight: tc.startHeight, endHeight: tc.endHeight})
|
|
if tc.validHeight {
|
|
require.NoError(t, err)
|
|
} else {
|
|
require.Error(t, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestLoadEventSink(t *testing.T) {
|
|
testCases := []struct {
|
|
sinks []string
|
|
connURL string
|
|
loadErr bool
|
|
}{
|
|
{[]string{}, "", true},
|
|
{[]string{"NULL"}, "", true},
|
|
{[]string{"KV"}, "", false},
|
|
{[]string{"KV", "KV"}, "", true},
|
|
{[]string{"PSQL"}, "", true}, // true because empty connect url
|
|
{[]string{"PSQL"}, "wrongUrl", true}, // true because wrong connect url
|
|
// skip to test PSQL connect with correct url
|
|
{[]string{"UnsupportedSinkType"}, "wrongUrl", true},
|
|
}
|
|
|
|
for _, tc := range testCases {
|
|
cfg := config.TestConfig()
|
|
cfg.TxIndex.Indexer = tc.sinks
|
|
cfg.TxIndex.PsqlConn = tc.connURL
|
|
_, err := loadEventSinks(cfg)
|
|
if tc.loadErr {
|
|
require.Error(t, err)
|
|
} else {
|
|
require.NoError(t, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestLoadBlockStore(t *testing.T) {
|
|
testCfg, err := config.ResetTestRoot(t.TempDir(), t.Name())
|
|
require.NoError(t, err)
|
|
testCfg.DBBackend = "goleveldb"
|
|
_, _, err = loadStateAndBlockStore(testCfg)
|
|
// we should return an error because the state store and block store
|
|
// don't yet exist
|
|
require.Error(t, err)
|
|
|
|
dbType := dbm.BackendType(testCfg.DBBackend)
|
|
bsdb, err := dbm.NewDB("blockstore", dbType, testCfg.DBDir())
|
|
require.NoError(t, err)
|
|
bsdb.Close()
|
|
|
|
ssdb, err := dbm.NewDB("state", dbType, testCfg.DBDir())
|
|
require.NoError(t, err)
|
|
ssdb.Close()
|
|
|
|
bs, ss, err := loadStateAndBlockStore(testCfg)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, bs)
|
|
require.NotNil(t, ss)
|
|
}
|
|
|
|
func TestReIndexEvent(t *testing.T) {
|
|
mockBlockStore := &mocks.BlockStore{}
|
|
mockStateStore := &mocks.Store{}
|
|
mockEventSink := &mocks.EventSink{}
|
|
|
|
mockBlockStore.
|
|
On("Base").Return(base).
|
|
On("Height").Return(height).
|
|
On("LoadBlock", base).Return(nil).Once().
|
|
On("LoadBlock", base).Return(&types.Block{Data: types.Data{Txs: types.Txs{make(types.Tx, 1)}}}).
|
|
On("LoadBlock", height).Return(&types.Block{Data: types.Data{Txs: types.Txs{make(types.Tx, 1)}}})
|
|
|
|
mockEventSink.
|
|
On("Type").Return(indexer.KV).
|
|
On("IndexBlockEvents", mock.AnythingOfType("types.EventDataNewBlockHeader")).Return(errors.New("")).Once().
|
|
On("IndexBlockEvents", mock.AnythingOfType("types.EventDataNewBlockHeader")).Return(nil).
|
|
On("IndexTxEvents", mock.AnythingOfType("[]*types.TxResult")).Return(errors.New("")).Once().
|
|
On("IndexTxEvents", mock.AnythingOfType("[]*types.TxResult")).Return(nil)
|
|
|
|
dtx := abcitypes.ResponseDeliverTx{}
|
|
abciResp := &prototmstate.ABCIResponses{
|
|
FinalizeBlock: &abcitypes.ResponseFinalizeBlock{
|
|
Txs: []*abcitypes.ResponseDeliverTx{&dtx},
|
|
},
|
|
}
|
|
|
|
mockStateStore.
|
|
On("LoadABCIResponses", base).Return(nil, errors.New("")).Once().
|
|
On("LoadABCIResponses", base).Return(abciResp, nil).
|
|
On("LoadABCIResponses", height).Return(abciResp, nil)
|
|
|
|
testCases := []struct {
|
|
startHeight int64
|
|
endHeight int64
|
|
reIndexErr bool
|
|
}{
|
|
{base, height, true}, // LoadBlock error
|
|
{base, height, true}, // LoadABCIResponses error
|
|
{base, height, true}, // index block event error
|
|
{base, height, true}, // index tx event error
|
|
{base, base, false},
|
|
{height, height, false},
|
|
}
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
logger := log.NewNopLogger()
|
|
conf := config.DefaultConfig()
|
|
|
|
for _, tc := range testCases {
|
|
err := eventReIndex(
|
|
setupReIndexEventCmd(ctx, conf, logger),
|
|
eventReIndexArgs{
|
|
sinks: []indexer.EventSink{mockEventSink},
|
|
blockStore: mockBlockStore,
|
|
stateStore: mockStateStore,
|
|
startHeight: tc.startHeight,
|
|
endHeight: tc.endHeight,
|
|
})
|
|
|
|
if tc.reIndexErr {
|
|
require.Error(t, err)
|
|
} else {
|
|
require.NoError(t, err)
|
|
}
|
|
}
|
|
}
|