github.com/anth0d/nomad@v0.0.0-20221214183521-ae3a0a2cad06/nomad/state/paginator/paginator_test.go (about)

     1  package paginator
     2  
     3  import (
     4  	"errors"
     5  	"testing"
     6  
     7  	"github.com/hashicorp/nomad/ci"
     8  	"github.com/hashicorp/nomad/nomad/structs"
     9  	"github.com/stretchr/testify/require"
    10  )
    11  
    12  func TestPaginator(t *testing.T) {
    13  	ci.Parallel(t)
    14  	ids := []string{"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"}
    15  
    16  	cases := []struct {
    17  		name              string
    18  		perPage           int32
    19  		nextToken         string
    20  		expected          []string
    21  		expectedNextToken string
    22  		expectedError     string
    23  	}{
    24  		{
    25  			name:              "size-3 page-1",
    26  			perPage:           3,
    27  			expected:          []string{"0", "1", "2"},
    28  			expectedNextToken: "3",
    29  		},
    30  		{
    31  			name:              "size-5 page-2 stop before end",
    32  			perPage:           5,
    33  			nextToken:         "3",
    34  			expected:          []string{"3", "4", "5", "6", "7"},
    35  			expectedNextToken: "8",
    36  		},
    37  		{
    38  			name:              "page-2 reading off the end",
    39  			perPage:           10,
    40  			nextToken:         "5",
    41  			expected:          []string{"5", "6", "7", "8", "9"},
    42  			expectedNextToken: "",
    43  		},
    44  		{
    45  			name:              "starting off the end",
    46  			perPage:           5,
    47  			nextToken:         "a",
    48  			expected:          []string{},
    49  			expectedNextToken: "",
    50  		},
    51  		{
    52  			name:          "error during append",
    53  			expectedError: "failed to append",
    54  		},
    55  	}
    56  
    57  	for _, tc := range cases {
    58  		t.Run(tc.name, func(t *testing.T) {
    59  
    60  			iter := newTestIterator(ids)
    61  			tokenizer := testTokenizer{}
    62  			opts := structs.QueryOptions{
    63  				PerPage:   tc.perPage,
    64  				NextToken: tc.nextToken,
    65  			}
    66  
    67  			results := []string{}
    68  			paginator, err := NewPaginator(iter, tokenizer, nil, opts,
    69  				func(raw interface{}) error {
    70  					if tc.expectedError != "" {
    71  						return errors.New(tc.expectedError)
    72  					}
    73  
    74  					result := raw.(*mockObject)
    75  					results = append(results, result.id)
    76  					return nil
    77  				},
    78  			)
    79  			require.NoError(t, err)
    80  
    81  			nextToken, err := paginator.Page()
    82  			if tc.expectedError == "" {
    83  				require.NoError(t, err)
    84  				require.Equal(t, tc.expected, results)
    85  				require.Equal(t, tc.expectedNextToken, nextToken)
    86  			} else {
    87  				require.Error(t, err)
    88  				require.Contains(t, err.Error(), tc.expectedError)
    89  			}
    90  		})
    91  	}
    92  
    93  }
    94  
    95  // helpers for pagination tests
    96  
    97  // implements Iterator interface
    98  type testResultIterator struct {
    99  	results chan interface{}
   100  }
   101  
   102  func (i testResultIterator) Next() interface{} {
   103  	select {
   104  	case raw := <-i.results:
   105  		if raw == nil {
   106  			return nil
   107  		}
   108  
   109  		m := raw.(*mockObject)
   110  		return m
   111  	default:
   112  		return nil
   113  	}
   114  }
   115  
   116  type mockObject struct {
   117  	id        string
   118  	namespace string
   119  }
   120  
   121  func (m *mockObject) GetNamespace() string {
   122  	return m.namespace
   123  }
   124  
   125  func newTestIterator(ids []string) testResultIterator {
   126  	iter := testResultIterator{results: make(chan interface{}, 20)}
   127  	for _, id := range ids {
   128  		iter.results <- &mockObject{id: id}
   129  	}
   130  	return iter
   131  }
   132  
   133  func newTestIteratorWithMocks(mocks []*mockObject) testResultIterator {
   134  	iter := testResultIterator{results: make(chan interface{}, 20)}
   135  	for _, m := range mocks {
   136  		iter.results <- m
   137  	}
   138  	return iter
   139  }
   140  
   141  // implements Tokenizer interface
   142  type testTokenizer struct{}
   143  
   144  func (t testTokenizer) GetToken(raw interface{}) string {
   145  	return raw.(*mockObject).id
   146  }