github.com/cosmos/cosmos-sdk@v0.50.10/x/group/internal/orm/iterator_test.go (about)

     1  package orm
     2  
     3  import (
     4  	"testing"
     5  
     6  	"github.com/cosmos/gogoproto/proto"
     7  	"github.com/stretchr/testify/assert"
     8  	"github.com/stretchr/testify/require"
     9  
    10  	errorsmod "cosmossdk.io/errors"
    11  	storetypes "cosmossdk.io/store/types"
    12  
    13  	"github.com/cosmos/cosmos-sdk/codec"
    14  	"github.com/cosmos/cosmos-sdk/codec/types"
    15  	"github.com/cosmos/cosmos-sdk/testutil/testdata"
    16  	sdk "github.com/cosmos/cosmos-sdk/types"
    17  	"github.com/cosmos/cosmos-sdk/types/query"
    18  	"github.com/cosmos/cosmos-sdk/x/group/errors"
    19  )
    20  
    21  func TestReadAll(t *testing.T) {
    22  	specs := map[string]struct {
    23  		srcIT     Iterator
    24  		destSlice func() ModelSlicePtr
    25  		expErr    *errorsmod.Error
    26  		expIDs    []RowID
    27  		expResult ModelSlicePtr
    28  	}{
    29  		"all good with object slice": {
    30  			srcIT: mockIter(EncodeSequence(1), &testdata.TableModel{Name: "test"}),
    31  			destSlice: func() ModelSlicePtr {
    32  				x := make([]testdata.TableModel, 1)
    33  				return &x
    34  			},
    35  			expIDs:    []RowID{EncodeSequence(1)},
    36  			expResult: &[]testdata.TableModel{{Name: "test"}},
    37  		},
    38  		"all good with pointer slice": {
    39  			srcIT: mockIter(EncodeSequence(1), &testdata.TableModel{Name: "test"}),
    40  			destSlice: func() ModelSlicePtr {
    41  				x := make([]*testdata.TableModel, 1)
    42  				return &x
    43  			},
    44  			expIDs:    []RowID{EncodeSequence(1)},
    45  			expResult: &[]*testdata.TableModel{{Name: "test"}},
    46  		},
    47  		"dest slice empty": {
    48  			srcIT: mockIter(EncodeSequence(1), &testdata.TableModel{}),
    49  			destSlice: func() ModelSlicePtr {
    50  				x := make([]testdata.TableModel, 0)
    51  				return &x
    52  			},
    53  			expIDs:    []RowID{EncodeSequence(1)},
    54  			expResult: &[]testdata.TableModel{{}},
    55  		},
    56  		"dest pointer with nil value": {
    57  			srcIT: mockIter(EncodeSequence(1), &testdata.TableModel{}),
    58  			destSlice: func() ModelSlicePtr {
    59  				return (*[]testdata.TableModel)(nil)
    60  			},
    61  			expErr: errors.ErrORMInvalidArgument,
    62  		},
    63  		"iterator is nil": {
    64  			srcIT:     nil,
    65  			destSlice: func() ModelSlicePtr { return new([]testdata.TableModel) },
    66  			expErr:    errors.ErrORMInvalidArgument,
    67  		},
    68  		"dest slice is nil": {
    69  			srcIT:     noopIter(),
    70  			destSlice: func() ModelSlicePtr { return nil },
    71  			expErr:    errors.ErrORMInvalidArgument,
    72  		},
    73  		"dest slice is not a pointer": {
    74  			srcIT:     IteratorFunc(nil),
    75  			destSlice: func() ModelSlicePtr { return make([]testdata.TableModel, 1) },
    76  			expErr:    errors.ErrORMInvalidArgument,
    77  		},
    78  		"error on loadNext is returned": {
    79  			srcIT: NewInvalidIterator(),
    80  			destSlice: func() ModelSlicePtr {
    81  				x := make([]testdata.TableModel, 1)
    82  				return &x
    83  			},
    84  			expErr: errors.ErrORMInvalidIterator,
    85  		},
    86  	}
    87  	for msg, spec := range specs {
    88  		t.Run(msg, func(t *testing.T) {
    89  			loaded := spec.destSlice()
    90  			ids, err := ReadAll(spec.srcIT, loaded)
    91  			require.True(t, spec.expErr.Is(err), "expected %s but got %s", spec.expErr, err)
    92  			assert.Equal(t, spec.expIDs, ids)
    93  			if err == nil {
    94  				assert.Equal(t, spec.expResult, loaded)
    95  			}
    96  		})
    97  	}
    98  }
    99  
   100  func TestLimitedIterator(t *testing.T) {
   101  	specs := map[string]struct {
   102  		parent      Iterator
   103  		max         int
   104  		expectErr   bool
   105  		expectedErr string
   106  		exp         []testdata.TableModel
   107  	}{
   108  		"nil parent": {
   109  			parent:      nil,
   110  			max:         0,
   111  			expectErr:   true,
   112  			expectedErr: "parent iterator must not be nil",
   113  		},
   114  		"negative max": {
   115  			parent:      mockIter(EncodeSequence(1), &testdata.TableModel{Name: "test"}),
   116  			max:         -1,
   117  			expectErr:   true,
   118  			expectedErr: "quantity must not be negative",
   119  		},
   120  		"all from range with max > length": {
   121  			parent: mockIter(EncodeSequence(1), &testdata.TableModel{Name: "test"}),
   122  			max:    2,
   123  			exp:    []testdata.TableModel{{Name: "test"}},
   124  		},
   125  		"up to max": {
   126  			parent: mockIter(EncodeSequence(1), &testdata.TableModel{Name: "test"}),
   127  			max:    1,
   128  			exp:    []testdata.TableModel{{Name: "test"}},
   129  		},
   130  		"none when max = 0": {
   131  			parent: mockIter(EncodeSequence(1), &testdata.TableModel{Name: "test"}),
   132  			max:    0,
   133  			exp:    []testdata.TableModel{},
   134  		},
   135  	}
   136  	for msg, spec := range specs {
   137  		t.Run(msg, func(t *testing.T) {
   138  			src, err := LimitIterator(spec.parent, spec.max)
   139  			if spec.expectErr {
   140  				require.Error(t, err)
   141  				require.Contains(t, err.Error(), spec.expectedErr)
   142  			} else {
   143  				require.NoError(t, err)
   144  				var loaded []testdata.TableModel
   145  				_, err := ReadAll(src, &loaded)
   146  				require.NoError(t, err)
   147  				assert.EqualValues(t, spec.exp, loaded)
   148  			}
   149  		})
   150  	}
   151  }
   152  
   153  func TestFirst(t *testing.T) {
   154  	testCases := []struct {
   155  		name          string
   156  		iterator      Iterator
   157  		dest          proto.Message
   158  		expectErr     bool
   159  		expectedErr   string
   160  		expectedRowID RowID
   161  		expectedDest  proto.Message
   162  	}{
   163  		{
   164  			name:        "nil iterator",
   165  			iterator:    nil,
   166  			dest:        &testdata.TableModel{},
   167  			expectErr:   true,
   168  			expectedErr: "iterator must not be nil",
   169  		},
   170  		{
   171  			name:        "nil dest",
   172  			iterator:    mockIter(EncodeSequence(1), &testdata.TableModel{Name: "test"}),
   173  			dest:        nil,
   174  			expectErr:   true,
   175  			expectedErr: "destination object must not be nil",
   176  		},
   177  		{
   178  			name:          "all not nil",
   179  			iterator:      mockIter(EncodeSequence(1), &testdata.TableModel{Name: "test"}),
   180  			dest:          &testdata.TableModel{},
   181  			expectErr:     false,
   182  			expectedRowID: EncodeSequence(1),
   183  			expectedDest:  &testdata.TableModel{Name: "test"},
   184  		},
   185  	}
   186  	for _, tc := range testCases {
   187  		t.Run(tc.name, func(t *testing.T) {
   188  			rowID, err := First(tc.iterator, tc.dest)
   189  			if tc.expectErr {
   190  				require.Error(t, err)
   191  				require.Contains(t, err.Error(), tc.expectedErr)
   192  			} else {
   193  				require.NoError(t, err)
   194  				require.Equal(t, tc.expectedRowID, rowID)
   195  				require.Equal(t, tc.expectedDest, tc.dest)
   196  			}
   197  		})
   198  	}
   199  }
   200  
   201  func TestPaginate(t *testing.T) {
   202  	interfaceRegistry := types.NewInterfaceRegistry()
   203  	cdc := codec.NewProtoCodec(interfaceRegistry)
   204  
   205  	tb, err := NewAutoUInt64Table(AutoUInt64TablePrefix, AutoUInt64TableSeqPrefix, &testdata.TableModel{}, cdc)
   206  	require.NoError(t, err)
   207  	idx, err := NewIndex(tb, AutoUInt64TableModelByMetadataPrefix, func(val interface{}) ([]interface{}, error) {
   208  		return []interface{}{val.(*testdata.TableModel).Metadata}, nil
   209  	}, testdata.TableModel{}.Metadata)
   210  	require.NoError(t, err)
   211  
   212  	ctx := NewMockContext()
   213  	store := ctx.KVStore(storetypes.NewKVStoreKey("test"))
   214  
   215  	metadata := []byte("metadata")
   216  	t1 := testdata.TableModel{
   217  		Id:       1,
   218  		Name:     "my test 1",
   219  		Metadata: metadata,
   220  	}
   221  	t2 := testdata.TableModel{
   222  		Id:       2,
   223  		Name:     "my test 2",
   224  		Metadata: metadata,
   225  	}
   226  	t3 := testdata.TableModel{
   227  		Id:       3,
   228  		Name:     "my test 3",
   229  		Metadata: []byte("other-metadata"),
   230  	}
   231  	t4 := testdata.TableModel{
   232  		Id:       4,
   233  		Name:     "my test 4",
   234  		Metadata: metadata,
   235  	}
   236  	t5 := testdata.TableModel{
   237  		Id:       5,
   238  		Name:     "my test 5",
   239  		Metadata: []byte("other-metadata"),
   240  	}
   241  
   242  	for _, g := range []testdata.TableModel{t1, t2, t3, t4, t5} {
   243  		g := g
   244  		_, err := tb.Create(store, &g)
   245  		require.NoError(t, err)
   246  	}
   247  
   248  	specs := map[string]struct {
   249  		pageReq    *query.PageRequest
   250  		expPageRes *query.PageResponse
   251  		exp        []testdata.TableModel
   252  		key        []byte
   253  		expErr     bool
   254  	}{
   255  		"one item": {
   256  			pageReq:    &query.PageRequest{Key: nil, Limit: 1},
   257  			exp:        []testdata.TableModel{t1},
   258  			expPageRes: &query.PageResponse{Total: 0, NextKey: EncodeSequence(2)},
   259  			key:        metadata,
   260  		},
   261  		"with both key and offset": {
   262  			pageReq: &query.PageRequest{Key: EncodeSequence(2), Offset: 1},
   263  			expErr:  true,
   264  			key:     metadata,
   265  		},
   266  		"up to max": {
   267  			pageReq:    &query.PageRequest{Key: nil, Limit: 3, CountTotal: true},
   268  			exp:        []testdata.TableModel{t1, t2, t4},
   269  			expPageRes: &query.PageResponse{Total: 3, NextKey: nil},
   270  			key:        metadata,
   271  		},
   272  		"no results": {
   273  			pageReq:    &query.PageRequest{Key: nil, Limit: 2, CountTotal: true},
   274  			exp:        []testdata.TableModel{},
   275  			expPageRes: &query.PageResponse{Total: 0, NextKey: nil},
   276  			key:        sdk.AccAddress([]byte("no-group-address")),
   277  		},
   278  		"with offset and count total": {
   279  			pageReq:    &query.PageRequest{Key: nil, Offset: 1, Limit: 2, CountTotal: true},
   280  			exp:        []testdata.TableModel{t2, t4},
   281  			expPageRes: &query.PageResponse{Total: 3, NextKey: nil},
   282  			key:        metadata,
   283  		},
   284  		"nil/default page req (limit = 100 > number of items)": {
   285  			pageReq:    nil,
   286  			exp:        []testdata.TableModel{t1, t2, t4},
   287  			expPageRes: &query.PageResponse{Total: 3, NextKey: nil},
   288  			key:        metadata,
   289  		},
   290  		"with key and limit < number of elem (count total is ignored in this case)": {
   291  			pageReq:    &query.PageRequest{Key: EncodeSequence(2), Limit: 1, CountTotal: true},
   292  			exp:        []testdata.TableModel{t2},
   293  			expPageRes: &query.PageResponse{Total: 0, NextKey: EncodeSequence(4)},
   294  			key:        metadata,
   295  		},
   296  		"with key and limit >= number of elem": {
   297  			pageReq:    &query.PageRequest{Key: EncodeSequence(2), Limit: 2},
   298  			exp:        []testdata.TableModel{t2, t4},
   299  			expPageRes: &query.PageResponse{Total: 0, NextKey: nil},
   300  			key:        metadata,
   301  		},
   302  		"with nothing left to iterate from key": {
   303  			pageReq:    &query.PageRequest{Key: EncodeSequence(5)},
   304  			exp:        []testdata.TableModel{},
   305  			expPageRes: &query.PageResponse{Total: 0, NextKey: nil},
   306  			key:        metadata,
   307  		},
   308  	}
   309  	for msg, spec := range specs {
   310  		t.Run(msg, func(t *testing.T) {
   311  			var loaded []testdata.TableModel
   312  
   313  			it, err := idx.GetPaginated(store, spec.key, spec.pageReq)
   314  			require.NoError(t, err)
   315  
   316  			res, err := Paginate(it, spec.pageReq, &loaded)
   317  			if spec.expErr {
   318  				require.Error(t, err)
   319  			} else {
   320  				require.NoError(t, err)
   321  				assert.EqualValues(t, spec.exp, loaded)
   322  				assert.EqualValues(t, spec.expPageRes.Total, res.Total)
   323  				assert.EqualValues(t, spec.expPageRes.NextKey, res.NextKey)
   324  			}
   325  		})
   326  	}
   327  
   328  	t.Run("nil iterator", func(t *testing.T) {
   329  		var loaded []testdata.TableModel
   330  		res, err := Paginate(nil, &query.PageRequest{}, &loaded)
   331  		require.Error(t, err)
   332  		require.Contains(t, err.Error(), "iterator must not be nil")
   333  		require.Nil(t, res)
   334  	})
   335  
   336  	t.Run("non-slice destination", func(t *testing.T) {
   337  		var loaded testdata.TableModel
   338  		res, err := Paginate(
   339  			mockIter(EncodeSequence(1), &testdata.TableModel{Name: "test"}),
   340  			&query.PageRequest{},
   341  			&loaded,
   342  		)
   343  		require.Error(t, err)
   344  		require.Contains(t, err.Error(), "destination must point to a slice")
   345  		require.Nil(t, res)
   346  	})
   347  }
   348  
   349  // mockIter encodes + decodes value object.
   350  func mockIter(rowID RowID, val proto.Message) Iterator {
   351  	b, err := proto.Marshal(val)
   352  	if err != nil {
   353  		panic(err)
   354  	}
   355  	return NewSingleValueIterator(rowID, b)
   356  }
   357  
   358  func noopIter() Iterator {
   359  	return IteratorFunc(func(dest proto.Message) (RowID, error) {
   360  		return nil, nil
   361  	})
   362  }