github.com/cosmos/cosmos-sdk@v0.50.10/types/query/pagination_test.go (about) 1 package query_test 2 3 import ( 4 gocontext "context" 5 "fmt" 6 "testing" 7 8 cmtproto "github.com/cometbft/cometbft/proto/tendermint/types" 9 "github.com/stretchr/testify/suite" 10 11 "cosmossdk.io/depinject" 12 "cosmossdk.io/log" 13 "cosmossdk.io/math" 14 "cosmossdk.io/store/prefix" 15 16 "github.com/cosmos/cosmos-sdk/baseapp" 17 "github.com/cosmos/cosmos-sdk/codec" 18 codectypes "github.com/cosmos/cosmos-sdk/codec/types" 19 "github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1" 20 "github.com/cosmos/cosmos-sdk/runtime" 21 "github.com/cosmos/cosmos-sdk/testutil/configurator" 22 testutilsims "github.com/cosmos/cosmos-sdk/testutil/sims" 23 sdk "github.com/cosmos/cosmos-sdk/types" 24 "github.com/cosmos/cosmos-sdk/types/address" 25 "github.com/cosmos/cosmos-sdk/types/query" 26 _ "github.com/cosmos/cosmos-sdk/x/auth" 27 authkeeper "github.com/cosmos/cosmos-sdk/x/auth/keeper" 28 _ "github.com/cosmos/cosmos-sdk/x/bank" 29 bankkeeper "github.com/cosmos/cosmos-sdk/x/bank/keeper" 30 "github.com/cosmos/cosmos-sdk/x/bank/testutil" 31 "github.com/cosmos/cosmos-sdk/x/bank/types" 32 _ "github.com/cosmos/cosmos-sdk/x/consensus" 33 _ "github.com/cosmos/cosmos-sdk/x/params" 34 ) 35 36 const ( 37 holder = "holder" 38 multiPerm = "multiple permissions account" 39 randomPerm = "random permission" 40 numBalances = 235 41 defaultLimit = 100 42 overLimit = 101 43 underLimit = 10 44 lastPageRecords = 35 45 ) 46 47 type paginationTestSuite struct { 48 suite.Suite 49 ctx sdk.Context 50 bankKeeper bankkeeper.Keeper 51 accountKeeper authkeeper.AccountKeeper 52 cdc codec.Codec 53 interfaceReg codectypes.InterfaceRegistry 54 app *runtime.App 55 } 56 57 func TestPaginationTestSuite(t *testing.T) { 58 suite.Run(t, new(paginationTestSuite)) 59 } 60 61 func (s *paginationTestSuite) SetupTest() { 62 var ( 63 bankKeeper bankkeeper.Keeper 64 accountKeeper authkeeper.AccountKeeper 65 reg codectypes.InterfaceRegistry 66 cdc codec.Codec 67 ) 68 69 app, err := testutilsims.Setup( 70 depinject.Configs( 71 configurator.NewAppConfig( 72 configurator.AuthModule(), 73 configurator.BankModule(), 74 configurator.ParamsModule(), 75 configurator.ConsensusModule(), 76 configurator.OmitInitGenesis(), 77 ), 78 depinject.Supply(log.NewNopLogger()), 79 ), 80 &bankKeeper, &accountKeeper, ®, &cdc) 81 82 s.NoError(err) 83 84 ctx := app.BaseApp.NewContextLegacy(false, cmtproto.Header{Height: 1}) 85 86 s.ctx, s.bankKeeper, s.accountKeeper, s.cdc, s.app, s.interfaceReg = ctx, bankKeeper, accountKeeper, cdc, app, reg 87 } 88 89 func (s *paginationTestSuite) TestParsePagination() { 90 s.T().Log("verify default values for empty page request") 91 pageReq := &query.PageRequest{} 92 page, limit, err := query.ParsePagination(pageReq) 93 s.Require().NoError(err) 94 s.Require().Equal(limit, query.DefaultLimit) 95 s.Require().Equal(page, 1) 96 97 s.T().Log("verify with custom values") 98 pageReq = &query.PageRequest{ 99 Offset: 0, 100 Limit: 10, 101 } 102 page, limit, err = query.ParsePagination(pageReq) 103 s.Require().NoError(err) 104 s.Require().Equal(page, 1) 105 s.Require().Equal(limit, 10) 106 } 107 108 func (s *paginationTestSuite) TestPagination() { 109 queryHelper := baseapp.NewQueryServerTestHelper(s.ctx, s.interfaceReg) 110 types.RegisterQueryServer(queryHelper, s.bankKeeper) 111 queryClient := types.NewQueryClient(queryHelper) 112 113 var balances sdk.Coins 114 115 for i := 0; i < numBalances; i++ { 116 denom := fmt.Sprintf("foo%ddenom", i) 117 balances = append(balances, sdk.NewInt64Coin(denom, 100)) 118 } 119 120 balances = balances.Sort() 121 addr1 := sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()) 122 acc1 := s.accountKeeper.NewAccountWithAddress(s.ctx, addr1) 123 s.accountKeeper.SetAccount(s.ctx, acc1) 124 s.Require().NoError(testutil.FundAccount(s.ctx, s.bankKeeper, addr1, balances)) 125 126 s.T().Log("verify empty page request results a max of defaultLimit records and counts total records") 127 pageReq := &query.PageRequest{} 128 request := types.NewQueryAllBalancesRequest(addr1, pageReq, false) 129 res, err := queryClient.AllBalances(gocontext.Background(), request) 130 s.Require().NoError(err) 131 s.Require().Equal(res.Pagination.Total, uint64(numBalances)) 132 s.Require().NotNil(res.Pagination.NextKey) 133 s.Require().LessOrEqual(res.Balances.Len(), defaultLimit) 134 135 s.T().Log("verify page request with limit > defaultLimit, returns less or equal to `limit` records") 136 pageReq = &query.PageRequest{Limit: overLimit} 137 request = types.NewQueryAllBalancesRequest(addr1, pageReq, false) 138 res, err = queryClient.AllBalances(gocontext.Background(), request) 139 s.Require().NoError(err) 140 s.Require().Equal(res.Pagination.Total, uint64(0)) 141 s.Require().NotNil(res.Pagination.NextKey) 142 s.Require().LessOrEqual(res.Balances.Len(), overLimit) 143 144 s.T().Log("verify paginate with custom limit and countTotal true") 145 pageReq = &query.PageRequest{Limit: underLimit, CountTotal: true} 146 request = types.NewQueryAllBalancesRequest(addr1, pageReq, false) 147 res, err = queryClient.AllBalances(gocontext.Background(), request) 148 s.Require().NoError(err) 149 s.Require().Equal(res.Balances.Len(), underLimit) 150 s.Require().NotNil(res.Pagination.NextKey) 151 s.Require().Equal(res.Pagination.Total, uint64(numBalances)) 152 153 s.T().Log("verify paginate with custom limit and countTotal false") 154 pageReq = &query.PageRequest{Limit: defaultLimit, CountTotal: false} 155 request = types.NewQueryAllBalancesRequest(addr1, pageReq, false) 156 res, err = queryClient.AllBalances(gocontext.Background(), request) 157 s.Require().NoError(err) 158 s.Require().Equal(res.Balances.Len(), defaultLimit) 159 s.Require().NotNil(res.Pagination.NextKey) 160 s.Require().Equal(res.Pagination.Total, uint64(0)) 161 162 s.T().Log("verify paginate with custom limit, key and countTotal false") 163 pageReq = &query.PageRequest{Key: res.Pagination.NextKey, Limit: defaultLimit, CountTotal: false} 164 request = types.NewQueryAllBalancesRequest(addr1, pageReq, false) 165 res, err = queryClient.AllBalances(gocontext.Background(), request) 166 s.Require().NoError(err) 167 s.Require().Equal(res.Balances.Len(), defaultLimit) 168 s.Require().NotNil(res.Pagination.NextKey) 169 s.Require().Equal(res.Pagination.Total, uint64(0)) 170 171 s.T().Log("verify paginate for last page, results in records less than max limit") 172 pageReq = &query.PageRequest{Key: res.Pagination.NextKey, Limit: defaultLimit, CountTotal: false} 173 request = types.NewQueryAllBalancesRequest(addr1, pageReq, false) 174 res, err = queryClient.AllBalances(gocontext.Background(), request) 175 s.Require().NoError(err) 176 s.Require().LessOrEqual(res.Balances.Len(), defaultLimit) 177 s.Require().Equal(res.Balances.Len(), lastPageRecords) 178 s.Require().Nil(res.Pagination.NextKey) 179 s.Require().Equal(res.Pagination.Total, uint64(0)) 180 181 s.T().Log("verify paginate with offset and limit") 182 pageReq = &query.PageRequest{Offset: 200, Limit: defaultLimit, CountTotal: false} 183 request = types.NewQueryAllBalancesRequest(addr1, pageReq, false) 184 res, err = queryClient.AllBalances(gocontext.Background(), request) 185 s.Require().NoError(err) 186 s.Require().LessOrEqual(res.Balances.Len(), defaultLimit) 187 s.Require().Equal(res.Balances.Len(), lastPageRecords) 188 s.Require().Nil(res.Pagination.NextKey) 189 s.Require().Equal(res.Pagination.Total, uint64(0)) 190 191 s.T().Log("verify paginate with offset and limit") 192 pageReq = &query.PageRequest{Offset: 100, Limit: defaultLimit, CountTotal: false} 193 request = types.NewQueryAllBalancesRequest(addr1, pageReq, false) 194 res, err = queryClient.AllBalances(gocontext.Background(), request) 195 s.Require().NoError(err) 196 s.Require().LessOrEqual(res.Balances.Len(), defaultLimit) 197 s.Require().NotNil(res.Pagination.NextKey) 198 s.Require().Equal(res.Pagination.Total, uint64(0)) 199 200 s.T().Log("verify paginate with offset and key - error") 201 pageReq = &query.PageRequest{Key: res.Pagination.NextKey, Offset: 100, Limit: defaultLimit, CountTotal: false} 202 request = types.NewQueryAllBalancesRequest(addr1, pageReq, false) 203 _, err = queryClient.AllBalances(gocontext.Background(), request) 204 s.Require().Error(err) 205 s.Require().Equal("rpc error: code = InvalidArgument desc = paginate: invalid request, either offset or key is expected, got both", err.Error()) 206 207 s.T().Log("verify paginate with offset greater than total results") 208 pageReq = &query.PageRequest{Offset: 300, Limit: defaultLimit, CountTotal: false} 209 request = types.NewQueryAllBalancesRequest(addr1, pageReq, false) 210 res, err = queryClient.AllBalances(gocontext.Background(), request) 211 s.Require().NoError(err) 212 s.Require().LessOrEqual(res.Balances.Len(), 0) 213 s.Require().Nil(res.Pagination.NextKey) 214 } 215 216 func (s *paginationTestSuite) TestReversePagination() { 217 queryHelper := baseapp.NewQueryServerTestHelper(s.ctx, s.interfaceReg) 218 types.RegisterQueryServer(queryHelper, s.bankKeeper) 219 queryClient := types.NewQueryClient(queryHelper) 220 221 var balances sdk.Coins 222 223 for i := 0; i < numBalances; i++ { 224 denom := fmt.Sprintf("foo%ddenom", i) 225 balances = append(balances, sdk.NewInt64Coin(denom, 100)) 226 } 227 228 balances = balances.Sort() 229 addr1 := sdk.AccAddress(secp256k1.GenPrivKey().PubKey().Address()) 230 acc1 := s.accountKeeper.NewAccountWithAddress(s.ctx, addr1) 231 s.accountKeeper.SetAccount(s.ctx, acc1) 232 s.Require().NoError(testutil.FundAccount(s.ctx, s.bankKeeper, addr1, balances)) 233 234 s.T().Log("verify paginate with custom limit and countTotal, Reverse false") 235 pageReq := &query.PageRequest{Limit: 2, CountTotal: true, Reverse: true, Key: nil} 236 request := types.NewQueryAllBalancesRequest(addr1, pageReq, false) 237 res1, err := queryClient.AllBalances(gocontext.Background(), request) 238 s.Require().NoError(err) 239 s.Require().Equal(2, res1.Balances.Len()) 240 s.Require().NotNil(res1.Pagination.NextKey) 241 242 s.T().Log("verify paginate with custom limit and countTotal, Reverse false") 243 pageReq = &query.PageRequest{Limit: 150} 244 request = types.NewQueryAllBalancesRequest(addr1, pageReq, false) 245 res1, err = queryClient.AllBalances(gocontext.Background(), request) 246 s.Require().NoError(err) 247 s.Require().Equal(res1.Balances.Len(), 150) 248 s.Require().NotNil(res1.Pagination.NextKey) 249 s.Require().Equal(res1.Pagination.Total, uint64(0)) 250 251 s.T().Log("verify paginate with custom limit, key and Reverse true") 252 pageReq = &query.PageRequest{Limit: defaultLimit, Reverse: true} 253 request = types.NewQueryAllBalancesRequest(addr1, pageReq, false) 254 res, err := queryClient.AllBalances(gocontext.Background(), request) 255 s.Require().NoError(err) 256 s.Require().Equal(res.Balances.Len(), defaultLimit) 257 s.Require().NotNil(res.Pagination.NextKey) 258 s.Require().Equal(res.Pagination.Total, uint64(0)) 259 260 s.T().Log("verify paginate with custom limit, key and Reverse true") 261 pageReq = &query.PageRequest{Offset: 100, Limit: defaultLimit, Reverse: true} 262 request = types.NewQueryAllBalancesRequest(addr1, pageReq, false) 263 res, err = queryClient.AllBalances(gocontext.Background(), request) 264 s.Require().NoError(err) 265 s.Require().Equal(res.Balances.Len(), defaultLimit) 266 s.Require().NotNil(res.Pagination.NextKey) 267 s.Require().Equal(res.Pagination.Total, uint64(0)) 268 269 s.T().Log("verify paginate for last page, Reverse true") 270 pageReq = &query.PageRequest{Offset: 200, Limit: defaultLimit, Reverse: true} 271 request = types.NewQueryAllBalancesRequest(addr1, pageReq, false) 272 res, err = queryClient.AllBalances(gocontext.Background(), request) 273 s.Require().NoError(err) 274 s.Require().Equal(res.Balances.Len(), lastPageRecords) 275 s.Require().Nil(res.Pagination.NextKey) 276 s.Require().Equal(res.Pagination.Total, uint64(0)) 277 278 s.T().Log("verify page request with limit > defaultLimit, returns less or equal to `limit` records") 279 pageReq = &query.PageRequest{Limit: overLimit, Reverse: true} 280 request = types.NewQueryAllBalancesRequest(addr1, pageReq, false) 281 res, err = queryClient.AllBalances(gocontext.Background(), request) 282 s.Require().NoError(err) 283 s.Require().Equal(res.Pagination.Total, uint64(0)) 284 s.Require().NotNil(res.Pagination.NextKey) 285 s.Require().LessOrEqual(res.Balances.Len(), overLimit) 286 287 s.T().Log("verify paginate with custom limit, key, countTotal false and Reverse true") 288 pageReq = &query.PageRequest{Key: res1.Pagination.NextKey, Limit: 50, Reverse: true} 289 request = types.NewQueryAllBalancesRequest(addr1, pageReq, false) 290 res, err = queryClient.AllBalances(gocontext.Background(), request) 291 s.Require().NoError(err) 292 s.Require().Equal(res.Balances.Len(), 50) 293 s.Require().NotNil(res.Pagination.NextKey) 294 s.Require().Equal(res.Pagination.Total, uint64(0)) 295 296 s.T().Log("verify Reverse pagination returns valid result") 297 s.Require().Equal(balances[101:151].String(), res.Balances.Sort().String()) 298 299 s.T().Log("verify paginate with custom limit, key, countTotal false and Reverse true") 300 pageReq = &query.PageRequest{Key: res.Pagination.NextKey, Limit: 50, Reverse: true} 301 request = types.NewQueryAllBalancesRequest(addr1, pageReq, false) 302 res, err = queryClient.AllBalances(gocontext.Background(), request) 303 s.Require().NoError(err) 304 s.Require().Equal(res.Balances.Len(), 50) 305 s.Require().NotNil(res.Pagination.NextKey) 306 s.Require().Equal(res.Pagination.Total, uint64(0)) 307 308 s.T().Log("verify Reverse pagination returns valid result") 309 s.Require().Equal(balances[51:101].String(), res.Balances.Sort().String()) 310 311 s.T().Log("verify paginate for last page Reverse true") 312 pageReq = &query.PageRequest{Key: res.Pagination.NextKey, Limit: defaultLimit, Reverse: true} 313 request = types.NewQueryAllBalancesRequest(addr1, pageReq, false) 314 res, err = queryClient.AllBalances(gocontext.Background(), request) 315 s.Require().NoError(err) 316 s.Require().Equal(res.Balances.Len(), 51) 317 s.Require().Nil(res.Pagination.NextKey) 318 s.Require().Equal(res.Pagination.Total, uint64(0)) 319 320 s.T().Log("verify Reverse pagination returns valid result") 321 s.Require().Equal(balances[0:51].String(), res.Balances.Sort().String()) 322 323 s.T().Log("verify paginate with offset and key - error") 324 pageReq = &query.PageRequest{Key: res1.Pagination.NextKey, Offset: 100, Limit: defaultLimit, CountTotal: false} 325 request = types.NewQueryAllBalancesRequest(addr1, pageReq, false) 326 _, err = queryClient.AllBalances(gocontext.Background(), request) 327 s.Require().Error(err) 328 s.Require().Equal("rpc error: code = InvalidArgument desc = paginate: invalid request, either offset or key is expected, got both", err.Error()) 329 330 s.T().Log("verify paginate with offset greater than total results") 331 pageReq = &query.PageRequest{Offset: 300, Limit: defaultLimit, CountTotal: false, Reverse: true} 332 request = types.NewQueryAllBalancesRequest(addr1, pageReq, false) 333 res, err = queryClient.AllBalances(gocontext.Background(), request) 334 s.Require().NoError(err) 335 s.Require().LessOrEqual(res.Balances.Len(), 0) 336 s.Require().Nil(res.Pagination.NextKey) 337 } 338 339 func (s *paginationTestSuite) TestPaginate() { 340 var balances sdk.Coins 341 342 for i := 0; i < 2; i++ { 343 denom := fmt.Sprintf("foo%ddenom", i) 344 balances = append(balances, sdk.NewInt64Coin(denom, 100)) 345 } 346 347 balances = balances.Sort() 348 addr1 := sdk.AccAddress([]byte("addr1")) 349 acc1 := s.accountKeeper.NewAccountWithAddress(s.ctx, addr1) 350 s.accountKeeper.SetAccount(s.ctx, acc1) 351 err := testutil.FundAccount(s.ctx, s.bankKeeper, addr1, balances) 352 if err != nil { // should return no error 353 fmt.Println(err) 354 } 355 // Paginate example 356 pageReq := &query.PageRequest{Key: nil, Limit: 1, CountTotal: true} 357 request := types.NewQueryAllBalancesRequest(addr1, pageReq, false) 358 balResult := sdk.NewCoins() 359 authStore := s.ctx.KVStore(s.app.UnsafeFindStoreKey(types.StoreKey)) 360 balancesStore := prefix.NewStore(authStore, types.BalancesPrefix) 361 accountStore := prefix.NewStore(balancesStore, address.MustLengthPrefix(addr1)) 362 pageRes, err := query.Paginate(accountStore, request.Pagination, func(key, value []byte) error { 363 var amount math.Int 364 err := amount.Unmarshal(value) 365 if err != nil { 366 return err 367 } 368 balResult = append(balResult, sdk.NewCoin(string(key), amount)) 369 return nil 370 }) 371 if err != nil { // should return no error 372 fmt.Println(err) 373 } 374 fmt.Println(&types.QueryAllBalancesResponse{Balances: balResult, Pagination: pageRes}) 375 // Output: 376 // balances:<denom:"foo0denom" amount:"100" > pagination:<next_key:"foo1denom" total:2 > 377 }