github.com/Finschia/finschia-sdk@v0.48.1/x/gov/keeper/querier_test.go (about)

     1  package keeper_test
     2  
     3  import (
     4  	"math/rand"
     5  	"strings"
     6  	"testing"
     7  	"time"
     8  
     9  	"github.com/stretchr/testify/require"
    10  	abci "github.com/tendermint/tendermint/abci/types"
    11  	tmproto "github.com/tendermint/tendermint/proto/tendermint/types"
    12  
    13  	"github.com/Finschia/finschia-sdk/codec"
    14  	"github.com/Finschia/finschia-sdk/simapp"
    15  	sdk "github.com/Finschia/finschia-sdk/types"
    16  	"github.com/Finschia/finschia-sdk/x/gov/keeper"
    17  	"github.com/Finschia/finschia-sdk/x/gov/types"
    18  )
    19  
    20  const custom = "custom"
    21  
    22  func getQueriedParams(t *testing.T, ctx sdk.Context, cdc *codec.LegacyAmino, querier sdk.Querier) (types.DepositParams, types.VotingParams, types.TallyParams) {
    23  	query := abci.RequestQuery{
    24  		Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryParams, types.ParamDeposit}, "/"),
    25  		Data: []byte{},
    26  	}
    27  
    28  	bz, err := querier(ctx, []string{types.QueryParams, types.ParamDeposit}, query)
    29  	require.NoError(t, err)
    30  	require.NotNil(t, bz)
    31  
    32  	var depositParams types.DepositParams
    33  	require.NoError(t, cdc.UnmarshalJSON(bz, &depositParams))
    34  
    35  	query = abci.RequestQuery{
    36  		Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryParams, types.ParamVoting}, "/"),
    37  		Data: []byte{},
    38  	}
    39  
    40  	bz, err = querier(ctx, []string{types.QueryParams, types.ParamVoting}, query)
    41  	require.NoError(t, err)
    42  	require.NotNil(t, bz)
    43  
    44  	var votingParams types.VotingParams
    45  	require.NoError(t, cdc.UnmarshalJSON(bz, &votingParams))
    46  
    47  	query = abci.RequestQuery{
    48  		Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryParams, types.ParamTallying}, "/"),
    49  		Data: []byte{},
    50  	}
    51  
    52  	bz, err = querier(ctx, []string{types.QueryParams, types.ParamTallying}, query)
    53  	require.NoError(t, err)
    54  	require.NotNil(t, bz)
    55  
    56  	var tallyParams types.TallyParams
    57  	require.NoError(t, cdc.UnmarshalJSON(bz, &tallyParams))
    58  
    59  	return depositParams, votingParams, tallyParams
    60  }
    61  
    62  func getQueriedProposals(
    63  	t *testing.T, ctx sdk.Context, cdc *codec.LegacyAmino, querier sdk.Querier,
    64  	depositor, voter sdk.AccAddress, status types.ProposalStatus, page, limit int,
    65  ) []types.Proposal {
    66  	query := abci.RequestQuery{
    67  		Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryProposals}, "/"),
    68  		Data: cdc.MustMarshalJSON(types.NewQueryProposalsParams(page, limit, status, voter, depositor)),
    69  	}
    70  
    71  	bz, err := querier(ctx, []string{types.QueryProposals}, query)
    72  	require.NoError(t, err)
    73  	require.NotNil(t, bz)
    74  
    75  	var proposals types.Proposals
    76  	require.NoError(t, cdc.UnmarshalJSON(bz, &proposals))
    77  
    78  	return proposals
    79  }
    80  
    81  func getQueriedDeposit(t *testing.T, ctx sdk.Context, cdc *codec.LegacyAmino, querier sdk.Querier, proposalID uint64, depositor sdk.AccAddress) types.Deposit {
    82  	query := abci.RequestQuery{
    83  		Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryDeposit}, "/"),
    84  		Data: cdc.MustMarshalJSON(types.NewQueryDepositParams(proposalID, depositor)),
    85  	}
    86  
    87  	bz, err := querier(ctx, []string{types.QueryDeposit}, query)
    88  	require.NoError(t, err)
    89  	require.NotNil(t, bz)
    90  
    91  	var deposit types.Deposit
    92  	require.NoError(t, cdc.UnmarshalJSON(bz, &deposit))
    93  
    94  	return deposit
    95  }
    96  
    97  func getQueriedDeposits(t *testing.T, ctx sdk.Context, cdc *codec.LegacyAmino, querier sdk.Querier, proposalID uint64) []types.Deposit {
    98  	query := abci.RequestQuery{
    99  		Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryDeposits}, "/"),
   100  		Data: cdc.MustMarshalJSON(types.NewQueryProposalParams(proposalID)),
   101  	}
   102  
   103  	bz, err := querier(ctx, []string{types.QueryDeposits}, query)
   104  	require.NoError(t, err)
   105  	require.NotNil(t, bz)
   106  
   107  	var deposits []types.Deposit
   108  	require.NoError(t, cdc.UnmarshalJSON(bz, &deposits))
   109  
   110  	return deposits
   111  }
   112  
   113  func getQueriedVote(t *testing.T, ctx sdk.Context, cdc *codec.LegacyAmino, querier sdk.Querier, proposalID uint64, voter sdk.AccAddress) types.Vote {
   114  	query := abci.RequestQuery{
   115  		Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryVote}, "/"),
   116  		Data: cdc.MustMarshalJSON(types.NewQueryVoteParams(proposalID, voter)),
   117  	}
   118  
   119  	bz, err := querier(ctx, []string{types.QueryVote}, query)
   120  	require.NoError(t, err)
   121  	require.NotNil(t, bz)
   122  
   123  	var vote types.Vote
   124  	require.NoError(t, cdc.UnmarshalJSON(bz, &vote))
   125  
   126  	return vote
   127  }
   128  
   129  func getQueriedVotes(t *testing.T, ctx sdk.Context, cdc *codec.LegacyAmino, querier sdk.Querier,
   130  	proposalID uint64, page, limit int,
   131  ) []types.Vote {
   132  	query := abci.RequestQuery{
   133  		Path: strings.Join([]string{custom, types.QuerierRoute, types.QueryVote}, "/"),
   134  		Data: cdc.MustMarshalJSON(types.NewQueryProposalVotesParams(proposalID, page, limit)),
   135  	}
   136  
   137  	bz, err := querier(ctx, []string{types.QueryVotes}, query)
   138  	require.NoError(t, err)
   139  	require.NotNil(t, bz)
   140  
   141  	var votes []types.Vote
   142  	require.NoError(t, cdc.UnmarshalJSON(bz, &votes))
   143  
   144  	return votes
   145  }
   146  
   147  func TestQueries(t *testing.T) {
   148  	app := simapp.Setup(false)
   149  	ctx := app.BaseApp.NewContext(false, tmproto.Header{})
   150  	legacyQuerierCdc := app.LegacyAmino()
   151  	querier := keeper.NewQuerier(app.GovKeeper, legacyQuerierCdc)
   152  
   153  	TestAddrs := simapp.AddTestAddrsIncremental(app, ctx, 2, sdk.NewInt(20000001))
   154  
   155  	oneCoins := sdk.NewCoins(sdk.NewInt64Coin(sdk.DefaultBondDenom, 1))
   156  	consCoins := sdk.NewCoins(sdk.NewCoin(sdk.DefaultBondDenom, app.StakingKeeper.TokensFromConsensusPower(ctx, 10)))
   157  
   158  	tp := TestProposal
   159  
   160  	depositParams, _, _ := getQueriedParams(t, ctx, legacyQuerierCdc, querier)
   161  
   162  	// TestAddrs[0] proposes (and deposits) proposals #1 and #2
   163  	proposal1, err := app.GovKeeper.SubmitProposal(ctx, tp)
   164  	require.NoError(t, err)
   165  	deposit1 := types.NewDeposit(proposal1.ProposalId, TestAddrs[0], oneCoins)
   166  	depositer1, err := sdk.AccAddressFromBech32(deposit1.Depositor)
   167  	require.NoError(t, err)
   168  	_, err = app.GovKeeper.AddDeposit(ctx, deposit1.ProposalId, depositer1, deposit1.Amount)
   169  	require.NoError(t, err)
   170  
   171  	proposal1.TotalDeposit = proposal1.TotalDeposit.Add(deposit1.Amount...)
   172  
   173  	proposal2, err := app.GovKeeper.SubmitProposal(ctx, tp)
   174  	require.NoError(t, err)
   175  	deposit2 := types.NewDeposit(proposal2.ProposalId, TestAddrs[0], consCoins)
   176  	depositer2, err := sdk.AccAddressFromBech32(deposit2.Depositor)
   177  	require.NoError(t, err)
   178  	_, err = app.GovKeeper.AddDeposit(ctx, deposit2.ProposalId, depositer2, deposit2.Amount)
   179  	require.NoError(t, err)
   180  
   181  	proposal2.TotalDeposit = proposal2.TotalDeposit.Add(deposit2.Amount...)
   182  
   183  	// TestAddrs[1] proposes (and deposits) on proposal #3
   184  	proposal3, err := app.GovKeeper.SubmitProposal(ctx, tp)
   185  	require.NoError(t, err)
   186  	deposit3 := types.NewDeposit(proposal3.ProposalId, TestAddrs[1], oneCoins)
   187  	depositer3, err := sdk.AccAddressFromBech32(deposit3.Depositor)
   188  	require.NoError(t, err)
   189  
   190  	_, err = app.GovKeeper.AddDeposit(ctx, deposit3.ProposalId, depositer3, deposit3.Amount)
   191  	require.NoError(t, err)
   192  
   193  	proposal3.TotalDeposit = proposal3.TotalDeposit.Add(deposit3.Amount...)
   194  
   195  	// TestAddrs[1] deposits on proposals #2 & #3
   196  	deposit4 := types.NewDeposit(proposal2.ProposalId, TestAddrs[1], depositParams.MinDeposit)
   197  	depositer4, err := sdk.AccAddressFromBech32(deposit4.Depositor)
   198  	require.NoError(t, err)
   199  	_, err = app.GovKeeper.AddDeposit(ctx, deposit4.ProposalId, depositer4, deposit4.Amount)
   200  	require.NoError(t, err)
   201  
   202  	proposal2.TotalDeposit = proposal2.TotalDeposit.Add(deposit4.Amount...)
   203  	proposal2.Status = types.StatusVotingPeriod
   204  	proposal2.VotingEndTime = proposal2.VotingEndTime.Add(types.DefaultPeriod)
   205  
   206  	deposit5 := types.NewDeposit(proposal3.ProposalId, TestAddrs[1], depositParams.MinDeposit)
   207  	depositer5, err := sdk.AccAddressFromBech32(deposit5.Depositor)
   208  	require.NoError(t, err)
   209  	_, err = app.GovKeeper.AddDeposit(ctx, deposit5.ProposalId, depositer5, deposit5.Amount)
   210  	require.NoError(t, err)
   211  
   212  	proposal3.TotalDeposit = proposal3.TotalDeposit.Add(deposit5.Amount...)
   213  	proposal3.Status = types.StatusVotingPeriod
   214  	proposal3.VotingEndTime = proposal3.VotingEndTime.Add(types.DefaultPeriod)
   215  	// total deposit of TestAddrs[1] on proposal #3 is worth the proposal deposit + individual deposit
   216  	deposit5.Amount = deposit5.Amount.Add(deposit3.Amount...)
   217  
   218  	// check deposits on proposal1 match individual deposits
   219  
   220  	deposits := getQueriedDeposits(t, ctx, legacyQuerierCdc, querier, proposal1.ProposalId)
   221  	require.Len(t, deposits, 1)
   222  	require.Equal(t, deposit1, deposits[0])
   223  
   224  	deposit := getQueriedDeposit(t, ctx, legacyQuerierCdc, querier, proposal1.ProposalId, TestAddrs[0])
   225  	require.Equal(t, deposit1, deposit)
   226  
   227  	// check deposits on proposal2 match individual deposits
   228  	deposits = getQueriedDeposits(t, ctx, legacyQuerierCdc, querier, proposal2.ProposalId)
   229  	require.Len(t, deposits, 2)
   230  	// NOTE order of deposits is determined by the addresses
   231  	require.Equal(t, deposit2, deposits[0])
   232  	require.Equal(t, deposit4, deposits[1])
   233  
   234  	// check deposits on proposal3 match individual deposits
   235  	deposits = getQueriedDeposits(t, ctx, legacyQuerierCdc, querier, proposal3.ProposalId)
   236  	require.Len(t, deposits, 1)
   237  	require.Equal(t, deposit5, deposits[0])
   238  
   239  	deposit = getQueriedDeposit(t, ctx, legacyQuerierCdc, querier, proposal3.ProposalId, TestAddrs[1])
   240  	require.Equal(t, deposit5, deposit)
   241  
   242  	// Only proposal #1 should be in types.Deposit Period
   243  	proposals := getQueriedProposals(t, ctx, legacyQuerierCdc, querier, nil, nil, types.StatusDepositPeriod, 1, 0)
   244  	require.Len(t, proposals, 1)
   245  	require.Equal(t, proposal1, proposals[0])
   246  
   247  	// Only proposals #2 and #3 should be in Voting Period
   248  	proposals = getQueriedProposals(t, ctx, legacyQuerierCdc, querier, nil, nil, types.StatusVotingPeriod, 1, 0)
   249  	require.Len(t, proposals, 2)
   250  	require.Equal(t, proposal2, proposals[0])
   251  	require.Equal(t, proposal3, proposals[1])
   252  
   253  	// Addrs[0] votes on proposals #2 & #3
   254  	vote1 := types.NewVote(proposal2.ProposalId, TestAddrs[0], types.NewNonSplitVoteOption(types.OptionYes))
   255  	vote2 := types.NewVote(proposal3.ProposalId, TestAddrs[0], types.NewNonSplitVoteOption(types.OptionYes))
   256  	app.GovKeeper.SetVote(ctx, vote1)
   257  	app.GovKeeper.SetVote(ctx, vote2)
   258  
   259  	// Addrs[1] votes on proposal #3
   260  	vote3 := types.NewVote(proposal3.ProposalId, TestAddrs[1], types.NewNonSplitVoteOption(types.OptionYes))
   261  	app.GovKeeper.SetVote(ctx, vote3)
   262  
   263  	// Test query voted by TestAddrs[0]
   264  	proposals = getQueriedProposals(t, ctx, legacyQuerierCdc, querier, nil, TestAddrs[0], types.StatusNil, 1, 0)
   265  	require.Equal(t, proposal2, proposals[0])
   266  	require.Equal(t, proposal3, proposals[1])
   267  
   268  	// Test query votes on types.Proposal 2
   269  	votes := getQueriedVotes(t, ctx, legacyQuerierCdc, querier, proposal2.ProposalId, 1, 0)
   270  	require.Len(t, votes, 1)
   271  	checkEqualVotes(t, vote1, votes[0])
   272  
   273  	vote := getQueriedVote(t, ctx, legacyQuerierCdc, querier, proposal2.ProposalId, TestAddrs[0])
   274  	checkEqualVotes(t, vote1, vote)
   275  
   276  	// Test query votes on types.Proposal 3
   277  	votes = getQueriedVotes(t, ctx, legacyQuerierCdc, querier, proposal3.ProposalId, 1, 0)
   278  	require.Len(t, votes, 2)
   279  	checkEqualVotes(t, vote2, votes[0])
   280  	checkEqualVotes(t, vote3, votes[1])
   281  
   282  	// Test query all proposals
   283  	proposals = getQueriedProposals(t, ctx, legacyQuerierCdc, querier, nil, nil, types.StatusNil, 1, 0)
   284  	require.Equal(t, proposal1, proposals[0])
   285  	require.Equal(t, proposal2, proposals[1])
   286  	require.Equal(t, proposal3, proposals[2])
   287  
   288  	// Test query voted by TestAddrs[1]
   289  	proposals = getQueriedProposals(t, ctx, legacyQuerierCdc, querier, nil, TestAddrs[1], types.StatusNil, 1, 0)
   290  	require.Equal(t, proposal3.ProposalId, proposals[0].ProposalId)
   291  
   292  	// Test query deposited by TestAddrs[0]
   293  	proposals = getQueriedProposals(t, ctx, legacyQuerierCdc, querier, TestAddrs[0], nil, types.StatusNil, 1, 0)
   294  	require.Equal(t, proposal1.ProposalId, proposals[0].ProposalId)
   295  
   296  	// Test query deposited by addr2
   297  	proposals = getQueriedProposals(t, ctx, legacyQuerierCdc, querier, TestAddrs[1], nil, types.StatusNil, 1, 0)
   298  	require.Equal(t, proposal2.ProposalId, proposals[0].ProposalId)
   299  	require.Equal(t, proposal3.ProposalId, proposals[1].ProposalId)
   300  
   301  	// Test query voted AND deposited by addr1
   302  	proposals = getQueriedProposals(t, ctx, legacyQuerierCdc, querier, TestAddrs[0], TestAddrs[0], types.StatusNil, 1, 0)
   303  	require.Equal(t, proposal2.ProposalId, proposals[0].ProposalId)
   304  }
   305  
   306  func TestPaginatedVotesQuery(t *testing.T) {
   307  	app := simapp.Setup(false)
   308  	ctx := app.BaseApp.NewContext(false, tmproto.Header{})
   309  	legacyQuerierCdc := app.LegacyAmino()
   310  
   311  	proposal := types.Proposal{
   312  		ProposalId: 100,
   313  		Status:     types.StatusVotingPeriod,
   314  	}
   315  
   316  	app.GovKeeper.SetProposal(ctx, proposal)
   317  
   318  	votes := make([]types.Vote, 20)
   319  	random := rand.New(rand.NewSource(time.Now().UnixNano()))
   320  	addrMap := make(map[string]struct{})
   321  	genAddr := func() string {
   322  		addr := make(sdk.AccAddress, 20)
   323  		for {
   324  			random.Read(addr)
   325  			addrStr := addr.String()
   326  			if _, ok := addrMap[addrStr]; !ok {
   327  				addrMap[addrStr] = struct{}{}
   328  				return addrStr
   329  			}
   330  		}
   331  	}
   332  	for i := range votes {
   333  		vote := types.Vote{
   334  			ProposalId: proposal.ProposalId,
   335  			Voter:      genAddr(),
   336  			Options:    types.NewNonSplitVoteOption(types.OptionYes),
   337  		}
   338  		votes[i] = vote
   339  		app.GovKeeper.SetVote(ctx, vote)
   340  	}
   341  
   342  	querier := keeper.NewQuerier(app.GovKeeper, legacyQuerierCdc)
   343  
   344  	// keeper preserves consistent order for each query, but this is not the insertion order
   345  	all := getQueriedVotes(t, ctx, legacyQuerierCdc, querier, proposal.ProposalId, 1, 0)
   346  	require.Equal(t, len(all), len(votes))
   347  
   348  	type testCase struct {
   349  		description string
   350  		page        int
   351  		limit       int
   352  		votes       []types.Vote
   353  	}
   354  	for _, tc := range []testCase{
   355  		{
   356  			description: "SkipAll",
   357  			page:        2,
   358  			limit:       len(all),
   359  		},
   360  		{
   361  			description: "GetFirstChunk",
   362  			page:        1,
   363  			limit:       10,
   364  			votes:       all[:10],
   365  		},
   366  		{
   367  			description: "GetSecondsChunk",
   368  			page:        2,
   369  			limit:       10,
   370  			votes:       all[10:],
   371  		},
   372  		{
   373  			description: "InvalidPage",
   374  			page:        -1,
   375  		},
   376  	} {
   377  		tc := tc
   378  		t.Run(tc.description, func(t *testing.T) {
   379  			votes := getQueriedVotes(t, ctx, legacyQuerierCdc, querier, proposal.ProposalId, tc.page, tc.limit)
   380  			require.Equal(t, len(tc.votes), len(votes))
   381  			for i := range votes {
   382  				require.Equal(t, tc.votes[i], votes[i])
   383  			}
   384  		})
   385  	}
   386  }
   387  
   388  // checkEqualVotes checks that two votes are equal, without taking into account
   389  // graceful fallback for `Option`.
   390  // When querying, the keeper populates the `vote.Option` field when there's
   391  // only 1 vote, this function checks equality of structs while skipping that
   392  // field.
   393  func checkEqualVotes(t *testing.T, vote1, vote2 types.Vote) {
   394  	require.Equal(t, vote1.Options, vote2.Options)
   395  	require.Equal(t, vote1.Voter, vote2.Voter)
   396  	require.Equal(t, vote1.ProposalId, vote2.ProposalId)
   397  }