@@ -147,7 +147,7 @@ func TestClientPaginateBasic(t *testing.T) {
147147
148148 var gotItems []* Item
149149 var iterationErr error
150- seq := paginate (ctx , params , listFunc , func (r * ListTestResult ) []* Item { return r .Items })
150+ seq := paginate (ctx , params , 0 , listFunc , func (r * ListTestResult ) []* Item { return r .Items })
151151 for item , err := range seq {
152152 if err != nil {
153153 iterationErr = err
@@ -200,7 +200,7 @@ func TestClientPaginateVariousPageSizes(t *testing.T) {
200200 return res , nil
201201 }
202202 var gotItems []* Item
203- seq := paginate (ctx , & ListTestParams {}, listFunc , func (r * ListTestResult ) []* Item { return r .Items })
203+ seq := paginate (ctx , & ListTestParams {}, 0 , listFunc , func (r * ListTestResult ) []* Item { return r .Items })
204204 for item , err := range seq {
205205 if err != nil {
206206 t .Fatalf ("paginate() unexpected error during iteration: %v" , err )
@@ -214,6 +214,188 @@ func TestClientPaginateVariousPageSizes(t *testing.T) {
214214 }
215215}
216216
217+ func TestPaginateMaxPages (t * testing.T ) {
218+ ctx := context .Background ()
219+ // Create 10 pages of results (1 item each, all with next cursor).
220+ all := allItems [:10 ]
221+ results := generatePaginatedResults (all , 1 )
222+
223+ listFunc := func (ctx context.Context , params * ListTestParams ) (* ListTestResult , error ) {
224+ if len (results ) == 0 {
225+ t .Fatal ("listFunc called more times than expected" )
226+ }
227+ res := results [0 ]
228+ results = results [1 :]
229+ return res , nil
230+ }
231+
232+ t .Run ("DefaultLimit64" , func (t * testing.T ) {
233+ // With maxPages=0 (default 64), 10 pages should succeed.
234+ results = generatePaginatedResults (all , 1 )
235+ var got []* Item
236+ var iterErr error
237+ seq := paginate (ctx , & ListTestParams {}, 0 , listFunc , func (r * ListTestResult ) []* Item { return r .Items })
238+ for item , err := range seq {
239+ if err != nil {
240+ iterErr = err
241+ break
242+ }
243+ got = append (got , item )
244+ }
245+ if iterErr != nil {
246+ t .Fatalf ("unexpected error: %v" , iterErr )
247+ }
248+ if len (got ) != len (all ) {
249+ t .Fatalf ("got %d items, want %d" , len (got ), len (all ))
250+ }
251+ })
252+
253+ t .Run ("MaxPagesExceeded" , func (t * testing.T ) {
254+ results = generatePaginatedResults (all , 1 )
255+ var got []* Item
256+ var iterErr error
257+ // maxPages=3: should stop after 3 pages with an error.
258+ seq := paginate (ctx , & ListTestParams {}, 3 , listFunc , func (r * ListTestResult ) []* Item { return r .Items })
259+ for item , err := range seq {
260+ if err != nil {
261+ iterErr = err
262+ break
263+ }
264+ got = append (got , item )
265+ }
266+ if iterErr == nil {
267+ t .Fatal ("expected pagination error, got nil" )
268+ }
269+ if len (got ) != 3 {
270+ t .Fatalf ("got %d items, want 3" , len (got ))
271+ }
272+ })
273+
274+ t .Run ("UnlimitedWithNegative" , func (t * testing.T ) {
275+ results = generatePaginatedResults (all , 1 )
276+ var got []* Item
277+ var iterErr error
278+ // maxPages=-1: unlimited, should get all items.
279+ seq := paginate (ctx , & ListTestParams {}, - 1 , listFunc , func (r * ListTestResult ) []* Item { return r .Items })
280+ for item , err := range seq {
281+ if err != nil {
282+ iterErr = err
283+ break
284+ }
285+ got = append (got , item )
286+ }
287+ if iterErr != nil {
288+ t .Fatalf ("unexpected error: %v" , iterErr )
289+ }
290+ if len (got ) != len (all ) {
291+ t .Fatalf ("got %d items, want %d" , len (got ), len (all ))
292+ }
293+ })
294+
295+ t .Run ("DefaultCapAt64" , func (t * testing.T ) {
296+ // maxPages=0 defaults to DefaultListMaxPages (64). Create 100 pages; should stop at 64.
297+ bigAll := make ([]* Item , 100 )
298+ for i := range bigAll {
299+ bigAll [i ] = & Item {Name : fmt .Sprintf ("item-%d" , i )}
300+ }
301+ results := generatePaginatedResults (bigAll , 1 )
302+ listFunc := func (ctx context.Context , params * ListTestParams ) (* ListTestResult , error ) {
303+ if len (results ) == 0 {
304+ t .Fatal ("listFunc called more times than expected" )
305+ }
306+ res := results [0 ]
307+ results = results [1 :]
308+ return res , nil
309+ }
310+ var got []* Item
311+ var iterErr error
312+ seq := paginate (ctx , & ListTestParams {}, 0 , listFunc , func (r * ListTestResult ) []* Item { return r .Items })
313+ for item , err := range seq {
314+ if err != nil {
315+ iterErr = err
316+ break
317+ }
318+ got = append (got , item )
319+ }
320+ if iterErr == nil {
321+ t .Fatal ("expected pagination error at default cap, got nil" )
322+ }
323+ if len (got ) != DefaultListMaxPages {
324+ t .Fatalf ("got %d items, want %d (DefaultListMaxPages)" , len (got ), DefaultListMaxPages )
325+ }
326+ })
327+ }
328+
329+ func TestPaginateCursorCycle (t * testing.T ) {
330+ ctx := context .Background ()
331+ callCount := 0
332+ // Server returns a cursor cycle: page1 → cursorA, page2 → cursorB, page3 → cursorA again.
333+ listFunc := func (ctx context.Context , params * ListTestParams ) (* ListTestResult , error ) {
334+ callCount ++
335+ switch callCount {
336+ case 1 :
337+ return & ListTestResult {Items : []* Item {{Name : "a" }}, NextCursor : "cursorA" }, nil
338+ case 2 :
339+ return & ListTestResult {Items : []* Item {{Name : "b" }}, NextCursor : "cursorB" }, nil
340+ case 3 :
341+ // Cycle: cursorA was already seen.
342+ return & ListTestResult {Items : []* Item {{Name : "c" }}, NextCursor : "cursorA" }, nil
343+ default :
344+ t .Fatal ("listFunc called after cycle should have been detected" )
345+ return nil , fmt .Errorf ("unreachable" )
346+ }
347+ }
348+
349+ var got []* Item
350+ var iterErr error
351+ seq := paginate (ctx , & ListTestParams {}, - 1 , listFunc , func (r * ListTestResult ) []* Item { return r .Items })
352+ for item , err := range seq {
353+ if err != nil {
354+ iterErr = err
355+ break
356+ }
357+ got = append (got , item )
358+ }
359+ if iterErr == nil {
360+ t .Fatal ("expected cursor cycle error, got nil" )
361+ }
362+ if len (got ) != 3 {
363+ t .Fatalf ("got %d items, want 3 (items on cycling page are yielded before detection)" , len (got ))
364+ }
365+ }
366+
367+ func TestPaginateNoMutation (t * testing.T ) {
368+ ctx := context .Background ()
369+ params := & ListTestParams {Cursor : "initial" }
370+ results := generatePaginatedResults (allItems [:3 ], 1 )
371+
372+ listFunc := func (ctx context.Context , p * ListTestParams ) (* ListTestResult , error ) {
373+ if len (results ) == 0 {
374+ return & ListTestResult {Items : nil , NextCursor : "" }, nil
375+ }
376+ res := results [0 ]
377+ results = results [1 :]
378+ return res , nil
379+ }
380+
381+ var got []* Item
382+ seq := paginate (ctx , params , - 1 , listFunc , func (r * ListTestResult ) []* Item { return r .Items })
383+ for item , err := range seq {
384+ if err != nil {
385+ t .Fatalf ("unexpected error: %v" , err )
386+ }
387+ got = append (got , item )
388+ }
389+
390+ // The original params should not have been mutated.
391+ if params .Cursor != "initial" {
392+ t .Errorf ("params.Cursor was mutated to %q, want %q" , params .Cursor , "initial" )
393+ }
394+ if len (got ) != 3 {
395+ t .Fatalf ("got %d items, want 3" , len (got ))
396+ }
397+ }
398+
217399func TestClientCapabilities (t * testing.T ) {
218400 testCases := []struct {
219401 name string
0 commit comments