cosmos-sdk/x/gov/client/utils/query_test.go

342 lines
9.3 KiB
Go

package utils_test
import (
"fmt"
"strconv"
"strings"
"testing"
"github.com/stretchr/testify/require"
sdkmath "cosmossdk.io/math"
"cosmossdk.io/x/gov"
"cosmossdk.io/x/gov/client/utils"
v1 "cosmossdk.io/x/gov/types/v1"
"cosmossdk.io/x/gov/types/v1beta1"
"github.com/cosmos/cosmos-sdk/client"
codectestutil "github.com/cosmos/cosmos-sdk/codec/testutil"
clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli"
sdk "github.com/cosmos/cosmos-sdk/types"
moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil"
)
type TxSearchMock struct {
clitestutil.MockCometTxSearchRPC
// use for filter tx with query conditions
msgsSet [][]sdk.Msg
}
// mock applying the query string in TxSearch
func filterTxs(mock *TxSearchMock) clitestutil.FilterTxsFn {
return func(query string, start, end int) ([][]byte, error) {
filterTxs := [][]byte{}
proposalIdStr, senderAddr := getQueryAttributes(query)
txs := mock.Txs()
if senderAddr != "" {
proposalId, err := strconv.ParseUint(proposalIdStr, 10, 64)
if err != nil {
return nil, err
}
for i, msgs := range mock.msgsSet {
for _, msg := range msgs {
if voteMsg, ok := msg.(*v1beta1.MsgVote); ok {
if voteMsg.Voter == senderAddr && voteMsg.ProposalId == proposalId {
filterTxs = append(filterTxs, txs[i])
continue
}
}
if voteMsg, ok := msg.(*v1.MsgVote); ok {
if voteMsg.Voter == senderAddr && voteMsg.ProposalId == proposalId {
filterTxs = append(filterTxs, txs[i])
continue
}
}
if voteWeightedMsg, ok := msg.(*v1beta1.MsgVoteWeighted); ok {
if voteWeightedMsg.Voter == senderAddr && voteWeightedMsg.ProposalId == proposalId {
filterTxs = append(filterTxs, txs[i])
continue
}
}
if voteWeightedMsg, ok := msg.(*v1.MsgVoteWeighted); ok {
if voteWeightedMsg.Voter == senderAddr && voteWeightedMsg.ProposalId == proposalId {
filterTxs = append(filterTxs, txs[i])
continue
}
}
}
}
} else {
for _, tx := range txs {
filterTxs = append(filterTxs, tx)
}
}
if len(filterTxs) < end {
return filterTxs, nil
}
return filterTxs[start:end], nil
}
}
// getQueryAttributes extracts value from query string
func getQueryAttributes(q string) (proposalId, senderAddr string) {
splitStr := strings.Split(q, " OR ")
if len(splitStr) >= 2 {
keySender := strings.Trim(splitStr[1], ")")
senderAddr = strings.Trim(strings.Split(keySender, "=")[1], "'")
keyProposal := strings.Split(q, " AND ")[0]
proposalId = strings.Trim(strings.Split(keyProposal, "=")[1], "'")
} else {
proposalId = strings.Trim(strings.Split(splitStr[0], "=")[1], "'")
}
return proposalId, senderAddr
}
func TestGetPaginatedVotes(t *testing.T) {
cdcOpts := codectestutil.CodecOptions{}
encCfg := moduletestutil.MakeTestEncodingConfig(cdcOpts, gov.AppModule{})
type testCase struct {
description string
page, limit int
msgs [][]sdk.Msg
votes []v1.Vote
}
acc1 := make(sdk.AccAddress, 20)
acc1[0] = 1
acc1Str, err := cdcOpts.GetAddressCodec().BytesToString(acc1)
require.NoError(t, err)
acc2 := make(sdk.AccAddress, 20)
acc2[0] = 2
acc2Str, err := cdcOpts.GetAddressCodec().BytesToString(acc2)
require.NoError(t, err)
acc1Msgs := []sdk.Msg{
v1.NewMsgVote(acc1Str, 0, v1.OptionYes, ""),
v1.NewMsgVote(acc1Str, 0, v1.OptionYes, ""),
v1.NewMsgDeposit(acc1Str, 0, sdk.NewCoins(sdk.NewCoin("stake", sdkmath.NewInt(10)))), // should be ignored
}
acc2Msgs := []sdk.Msg{
v1.NewMsgVote(acc2Str, 0, v1.OptionYes, ""),
v1.NewMsgVoteWeighted(acc2Str, 0, v1.NewNonSplitVoteOption(v1.OptionYes), ""),
}
for _, tc := range []testCase{
{
description: "1MsgPerTxAll",
page: 1,
limit: 2,
msgs: [][]sdk.Msg{
acc1Msgs[:1],
acc2Msgs[:1],
},
votes: []v1.Vote{
v1.NewVote(0, acc1Str, v1.NewNonSplitVoteOption(v1.OptionYes), ""),
v1.NewVote(0, acc2Str, v1.NewNonSplitVoteOption(v1.OptionYes), ""),
},
},
{
description: "2MsgPerTx1Chunk",
page: 1,
limit: 2,
msgs: [][]sdk.Msg{
acc1Msgs,
acc2Msgs,
},
votes: []v1.Vote{
v1.NewVote(0, acc1Str, v1.NewNonSplitVoteOption(v1.OptionYes), ""),
v1.NewVote(0, acc1Str, v1.NewNonSplitVoteOption(v1.OptionYes), ""),
},
},
{
description: "2MsgPerTx2Chunk",
page: 2,
limit: 2,
msgs: [][]sdk.Msg{
acc1Msgs,
acc2Msgs,
},
votes: []v1.Vote{
v1.NewVote(0, acc2Str, v1.NewNonSplitVoteOption(v1.OptionYes), ""),
v1.NewVote(0, acc2Str, v1.NewNonSplitVoteOption(v1.OptionYes), ""),
},
},
{
description: "IncompleteSearchTx",
page: 1,
limit: 2,
msgs: [][]sdk.Msg{
acc1Msgs[:1],
},
votes: []v1.Vote{v1.NewVote(0, acc1Str, v1.NewNonSplitVoteOption(v1.OptionYes), "")},
},
{
description: "InvalidPage",
page: -1,
msgs: [][]sdk.Msg{
acc1Msgs[:1],
},
},
{
description: "OutOfBounds",
page: 2,
limit: 10,
msgs: [][]sdk.Msg{
acc1Msgs[:1],
},
},
} {
t.Run(tc.description, func(t *testing.T) {
marshaled := make([][]byte, len(tc.msgs))
clientCtx := client.Context{}.
WithLegacyAmino(encCfg.Amino).
WithTxConfig(encCfg.TxConfig)
for i := range tc.msgs {
txBuilder := clientCtx.TxConfig.NewTxBuilder()
err := txBuilder.SetMsgs(tc.msgs[i]...)
require.NoError(t, err)
tx, err := clientCtx.TxConfig.TxEncoder()(txBuilder.GetTx())
require.NoError(t, err)
marshaled[i] = tx
}
cli := &TxSearchMock{msgsSet: tc.msgs}
cli.WithTxs(marshaled)
cli.WithTxConfig(encCfg.TxConfig)
cli.WithFilterTxsFn(filterTxs(cli))
clientCtx = clientCtx.WithClient(cli)
params := utils.QueryProposalVotesParams{0, tc.page, tc.limit}
votesData, err := utils.QueryVotesByTxQuery(clientCtx, params)
require.NoError(t, err)
votes := []v1.Vote{}
require.NoError(t, clientCtx.LegacyAmino.UnmarshalJSON(votesData, &votes))
require.Equal(t, len(tc.votes), len(votes))
for i := range votes {
require.Equal(t, tc.votes[i], votes[i])
}
})
}
}
func TestGetSingleVote(t *testing.T) {
cdcOpts := codectestutil.CodecOptions{}
encCfg := moduletestutil.MakeTestEncodingConfig(cdcOpts, gov.AppModule{})
type testCase struct {
description string
msgs [][]sdk.Msg
votes []v1.Vote
expErr string
}
acc1 := make(sdk.AccAddress, 20)
acc1[0] = 1
acc1Str, err := cdcOpts.GetAddressCodec().BytesToString(acc1)
require.NoError(t, err)
acc2 := make(sdk.AccAddress, 20)
acc2[0] = 2
acc2Str, err := cdcOpts.GetAddressCodec().BytesToString(acc2)
require.NoError(t, err)
acc1Msgs := []sdk.Msg{
v1.NewMsgVote(acc1Str, 0, v1.OptionYes, ""),
v1.NewMsgVote(acc1Str, 0, v1.OptionYes, ""),
v1.NewMsgDeposit(acc1Str, 0, sdk.NewCoins(sdk.NewCoin("stake", sdkmath.NewInt(10)))), // should be ignored
}
acc2Msgs := []sdk.Msg{
v1.NewMsgVote(acc2Str, 0, v1.OptionYes, ""),
v1.NewMsgVoteWeighted(acc2Str, 0, v1.NewNonSplitVoteOption(v1.OptionYes), ""),
v1beta1.NewMsgVoteWeighted(acc2Str, 0, v1beta1.NewNonSplitVoteOption(v1beta1.OptionYes)),
}
for _, tc := range []testCase{
{
description: "no vote found: no msgVote",
msgs: [][]sdk.Msg{
acc1Msgs[:1],
},
votes: []v1.Vote{},
expErr: "did not vote on proposalID",
},
{
description: "no vote found: wrong proposal ID",
msgs: [][]sdk.Msg{
acc1Msgs[:1],
},
votes: []v1.Vote{},
expErr: "did not vote on proposalID",
},
{
description: "query 2 voter vote",
msgs: [][]sdk.Msg{
acc1Msgs,
acc2Msgs[:1],
},
votes: []v1.Vote{
v1.NewVote(0, acc1Str, v1.NewNonSplitVoteOption(v1.OptionYes), ""),
v1.NewVote(0, acc2Str, v1.NewNonSplitVoteOption(v1.OptionYes), ""),
},
},
{
description: "query 2 voter vote with v1beta1",
msgs: [][]sdk.Msg{
acc1Msgs,
acc2Msgs[2:],
},
votes: []v1.Vote{
v1.NewVote(0, acc1Str, v1.NewNonSplitVoteOption(v1.OptionYes), ""),
v1.NewVote(0, acc2Str, v1.NewNonSplitVoteOption(v1.OptionYes), ""),
},
},
} {
t.Run(tc.description, func(t *testing.T) {
marshaled := make([][]byte, len(tc.msgs))
clientCtx := client.Context{}.
WithLegacyAmino(encCfg.Amino).
WithTxConfig(encCfg.TxConfig).
WithAddressCodec(cdcOpts.GetAddressCodec()).
WithCodec(encCfg.Codec)
for i := range tc.msgs {
txBuilder := clientCtx.TxConfig.NewTxBuilder()
err := txBuilder.SetMsgs(tc.msgs[i]...)
require.NoError(t, err)
tx, err := clientCtx.TxConfig.TxEncoder()(txBuilder.GetTx())
require.NoError(t, err)
marshaled[i] = tx
}
cli := &TxSearchMock{msgsSet: tc.msgs}
cli.WithTxs(marshaled)
cli.WithTxConfig(encCfg.TxConfig)
cli.WithFilterTxsFn(filterTxs(cli))
clientCtx = clientCtx.WithClient(cli)
// testing query single vote
for i, v := range tc.votes {
accAddr, err := clientCtx.AddressCodec.StringToBytes(v.Voter)
require.NoError(t, err)
voteParams := utils.QueryVoteParams{ProposalID: 0, Voter: accAddr}
voteData, err := utils.QueryVoteByTxQuery(clientCtx, voteParams)
if tc.expErr != "" {
require.Error(t, err)
require.True(t, strings.Contains(err.Error(), tc.expErr))
continue
}
require.NoError(t, err)
vote := v1.Vote{}
require.NoError(t, clientCtx.Codec.UnmarshalJSON(voteData, &vote))
require.Equal(t, v, vote, fmt.Sprintf("vote should be equal at entry %v", i))
}
})
}
}