Skip to content

Commit

Permalink
Merge PR cosmos#5445: Mock rpcclient in tests for votes pagination
Browse files Browse the repository at this point in the history
  • Loading branch information
dshulyak authored and alexanderbez committed Dec 24, 2019
1 parent 8353680 commit 5188c35
Show file tree
Hide file tree
Showing 4 changed files with 79 additions and 58 deletions.
3 changes: 1 addition & 2 deletions x/gov/client/cli/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import (
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/version"
"github.com/cosmos/cosmos-sdk/x/auth/client/utils"
gcutils "github.com/cosmos/cosmos-sdk/x/gov/client/utils"
"github.com/cosmos/cosmos-sdk/x/gov/types"
)
Expand Down Expand Up @@ -286,7 +285,7 @@ $ %[1]s query gov votes 1 --page=2 --limit=100

propStatus := proposal.Status
if !(propStatus == types.StatusVotingPeriod || propStatus == types.StatusDepositPeriod) {
res, err = gcutils.QueryVotesByTxQuery(cliCtx, params, utils.QueryTxsByEvents)
res, err = gcutils.QueryVotesByTxQuery(cliCtx, params)
} else {
res, _, err = cliCtx.QueryWithData(fmt.Sprintf("custom/%s/votes", queryRoute), bz)
}
Expand Down
3 changes: 1 addition & 2 deletions x/gov/client/rest/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import (
"github.com/cosmos/cosmos-sdk/client/context"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/rest"
"github.com/cosmos/cosmos-sdk/x/auth/client/utils"
gcutils "github.com/cosmos/cosmos-sdk/x/gov/client/utils"
"github.com/cosmos/cosmos-sdk/x/gov/types"
)
Expand Down Expand Up @@ -382,7 +381,7 @@ func queryVotesOnProposalHandlerFn(cliCtx context.CLIContext) http.HandlerFunc {
// as they're no longer in state.
propStatus := proposal.Status
if !(propStatus == types.StatusVotingPeriod || propStatus == types.StatusDepositPeriod) {
res, err = gcutils.QueryVotesByTxQuery(cliCtx, params, utils.QueryTxsByEvents)
res, err = gcutils.QueryVotesByTxQuery(cliCtx, params)
} else {
res, _, err = cliCtx.QueryWithData("custom/gov/votes", bz)
}
Expand Down
13 changes: 4 additions & 9 deletions x/gov/client/utils/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,15 +73,10 @@ func QueryDepositsByTxQuery(cliCtx context.CLIContext, params types.QueryProposa
return cliCtx.Codec.MarshalJSON(deposits)
}

// TxQuerier is a type that accepts query parameters (target events and pagination options) and returns sdk.SearchTxsResult.
// Mainly used for easier mocking of utils.QueryTxsByEvents in tests.
type TxQuerier func(cliCtx context.CLIContext, events []string, page, limit int) (*sdk.SearchTxsResult, error)

// QueryVotesByTxQuery will query for votes using provided TxQuerier implementation.
// In general utils.QueryTxsByEvents should be used that will do a direct tx query to a tendermint node.
// It will fetch and build votes directly from the returned txs and return a JSON
// QueryVotesByTxQuery will query for votes via a direct txs tags query. It
// will fetch and build votes directly from the returned txs and return a JSON
// marshalled result or any error that occurred.
func QueryVotesByTxQuery(cliCtx context.CLIContext, params types.QueryProposalVotesParams, querier TxQuerier) ([]byte, error) {
func QueryVotesByTxQuery(cliCtx context.CLIContext, params types.QueryProposalVotesParams) ([]byte, error) {
var (
events = []string{
fmt.Sprintf("%s.%s='%s'", sdk.EventTypeMessage, sdk.AttributeKeyAction, types.TypeMsgVote),
Expand All @@ -93,7 +88,7 @@ func QueryVotesByTxQuery(cliCtx context.CLIContext, params types.QueryProposalVo
)
// query interrupted either if we collected enough votes or tx indexer run out of relevant txs
for len(votes) < totalLimit {
searchResult, err := querier(cliCtx, events, nextTxPage, defaultLimit)
searchResult, err := utils.QueryTxsByEvents(cliCtx, events, nextTxPage, defaultLimit)
if err != nil {
return nil, err
}
Expand Down
118 changes: 73 additions & 45 deletions x/gov/client/utils/query_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,72 +7,87 @@ import (
"github.com/cosmos/cosmos-sdk/client/context"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
"github.com/cosmos/cosmos-sdk/x/gov/types"
"github.com/stretchr/testify/require"
"github.com/tendermint/tendermint/rpc/client/mock"
ctypes "github.com/tendermint/tendermint/rpc/core/types"
tmtypes "github.com/tendermint/tendermint/types"
)

type txMock struct {
address sdk.AccAddress
msgNum int
type TxSearchMock struct {
mock.Client
txs []tmtypes.Tx
}

func (tx txMock) ValidateBasic() sdk.Error {
return nil
func (mock TxSearchMock) TxSearch(query string, prove bool, page, perPage int) (*ctypes.ResultTxSearch, error) {
start, end := client.Paginate(len(mock.txs), page, perPage, 100)
if start < 0 || end < 0 {
// nil result with nil error crashes utils.QueryTxsByEvents
return &ctypes.ResultTxSearch{}, nil
}
txs := mock.txs[start:end]
rst := &ctypes.ResultTxSearch{Txs: make([]*ctypes.ResultTx, len(txs)), TotalCount: len(txs)}
for i := range txs {
rst.Txs[i] = &ctypes.ResultTx{Tx: txs[i]}
}
return rst, nil
}

func (tx txMock) GetMsgs() (msgs []sdk.Msg) {
for i := 0; i < tx.msgNum; i++ {
msgs = append(msgs, types.NewMsgVote(tx.address, 0, types.OptionYes))
}
return
func (mock TxSearchMock) Block(height *int64) (*ctypes.ResultBlock, error) {
// any non nil Block needs to be returned. used to get time value
return &ctypes.ResultBlock{Block: &tmtypes.Block{}}, nil
}

func makeQuerier(txs []sdk.Tx) TxQuerier {
return func(cliCtx context.CLIContext, events []string, page, limit int) (*sdk.SearchTxsResult, error) {
start, end := client.Paginate(len(txs), page, limit, 100)
if start < 0 || end < 0 {
return nil, nil
}
rst := &sdk.SearchTxsResult{
TotalCount: len(txs),
PageNumber: page,
PageTotal: len(txs) / limit,
Limit: limit,
Count: end - start,
}
for _, tx := range txs[start:end] {
rst.Txs = append(rst.Txs, sdk.TxResponse{Tx: tx})
}
return rst, nil
}
func newTestCodec() *codec.Codec {
cdc := codec.New()
sdk.RegisterCodec(cdc)
types.RegisterCodec(cdc)
authtypes.RegisterCodec(cdc)
return cdc
}

func TestGetPaginatedVotes(t *testing.T) {
type testCase struct {
description string
page, limit int
txs []sdk.Tx
txs []authtypes.StdTx
votes []types.Vote
}
acc1 := make(sdk.AccAddress, 20)
acc1[0] = 1
acc2 := make(sdk.AccAddress, 20)
acc2[0] = 2
acc1Msgs := []sdk.Msg{
types.NewMsgVote(acc1, 0, types.OptionYes),
types.NewMsgVote(acc1, 0, types.OptionYes),
}
acc2Msgs := []sdk.Msg{
types.NewMsgVote(acc2, 0, types.OptionYes),
types.NewMsgVote(acc2, 0, types.OptionYes),
}
for _, tc := range []testCase{
{
description: "1MsgPerTxAll",
page: 1,
limit: 2,
txs: []sdk.Tx{txMock{acc1, 1}, txMock{acc2, 1}},
txs: []authtypes.StdTx{
{Msgs: acc1Msgs[:1]},
{Msgs: acc2Msgs[:1]},
},
votes: []types.Vote{
types.NewVote(0, acc1, types.OptionYes),
types.NewVote(0, acc2, types.OptionYes)},
},

{
description: "2MsgPerTx1Chunk",
page: 1,
limit: 2,
txs: []sdk.Tx{txMock{acc1, 2}, txMock{acc2, 2}},
txs: []authtypes.StdTx{
{Msgs: acc1Msgs},
{Msgs: acc2Msgs},
},
votes: []types.Vote{
types.NewVote(0, acc1, types.OptionYes),
types.NewVote(0, acc1, types.OptionYes)},
Expand All @@ -81,7 +96,10 @@ func TestGetPaginatedVotes(t *testing.T) {
description: "2MsgPerTx2Chunk",
page: 2,
limit: 2,
txs: []sdk.Tx{txMock{acc1, 2}, txMock{acc2, 2}},
txs: []authtypes.StdTx{
{Msgs: acc1Msgs},
{Msgs: acc2Msgs},
},
votes: []types.Vote{
types.NewVote(0, acc2, types.OptionYes),
types.NewVote(0, acc2, types.OptionYes)},
Expand All @@ -90,33 +108,43 @@ func TestGetPaginatedVotes(t *testing.T) {
description: "IncompleteSearchTx",
page: 1,
limit: 2,
txs: []sdk.Tx{txMock{acc1, 1}},
votes: []types.Vote{types.NewVote(0, acc1, types.OptionYes)},
},
{
description: "IncompleteSearchTx",
page: 1,
limit: 2,
txs: []sdk.Tx{txMock{acc1, 1}},
votes: []types.Vote{types.NewVote(0, acc1, types.OptionYes)},
txs: []authtypes.StdTx{
{Msgs: acc1Msgs[:1]},
},
votes: []types.Vote{types.NewVote(0, acc1, types.OptionYes)},
},
{
description: "InvalidPage",
page: -1,
txs: []sdk.Tx{txMock{acc1, 1}},
txs: []authtypes.StdTx{
{Msgs: acc1Msgs[:1]},
},
},
{
description: "OutOfBounds",
page: 2,
limit: 10,
txs: []sdk.Tx{txMock{acc1, 1}},
txs: []authtypes.StdTx{
{Msgs: acc1Msgs[:1]},
},
},
} {
tc := tc
t.Run(tc.description, func(t *testing.T) {
ctx := context.CLIContext{}.WithCodec(codec.New())
var (
marshalled = make([]tmtypes.Tx, len(tc.txs))
cdc = newTestCodec()
)
for i := range tc.txs {
tx, err := cdc.MarshalBinaryLengthPrefixed(&tc.txs[i])
require.NoError(t, err)
marshalled[i] = tmtypes.Tx(tx)
}
client := TxSearchMock{txs: marshalled}
ctx := context.CLIContext{}.WithCodec(cdc).WithTrustNode(true).WithClient(client)

params := types.NewQueryProposalVotesParams(0, tc.page, tc.limit)
votesData, err := QueryVotesByTxQuery(ctx, params, makeQuerier(tc.txs))
votesData, err := QueryVotesByTxQuery(ctx, params)
require.NoError(t, err)
votes := []types.Vote{}
require.NoError(t, ctx.Codec.UnmarshalJSON(votesData, &votes))
Expand Down

0 comments on commit 5188c35

Please sign in to comment.