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 }