342 lines
9.3 KiB
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))
|
|
}
|
|
})
|
|
}
|
|
}
|