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