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  }