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