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 }