/* Copyright 2017 The Kubernetes Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package pager import ( "context" "fmt" "reflect" "testing" "k8s.io/apimachinery/pkg/api/errors" metainternalversion "k8s.io/apimachinery/pkg/apis/meta/internalversion" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1beta1 "k8s.io/apimachinery/pkg/apis/meta/v1beta1" "k8s.io/apimachinery/pkg/runtime" ) func list(count int, rv string) *metainternalversion.List { var list metainternalversion.List for i := 0; i < count; i++ { list.Items = append(list.Items, &metav1beta1.PartialObjectMetadata{ ObjectMeta: metav1.ObjectMeta{ Name: fmt.Sprintf("%d", i), }, }) } list.ResourceVersion = rv return &list } type testPager struct { t *testing.T rv string index int remaining int last int continuing bool done bool expectPage int64 } func (p *testPager) reset() { p.continuing = false p.remaining += p.index p.index = 0 p.last = 0 p.done = false } func (p *testPager) PagedList(ctx context.Context, options metav1.ListOptions) (runtime.Object, error) { if p.done { p.t.Errorf("did not expect additional call to paged list") return nil, fmt.Errorf("unexpected list call") } expectedContinue := fmt.Sprintf("%s:%d", p.rv, p.last) if options.Limit != p.expectPage || (p.continuing && options.Continue != expectedContinue) { p.t.Errorf("invariant violated, expected limit %d and continue %s, got %#v", p.expectPage, expectedContinue, options) return nil, fmt.Errorf("invariant violated") } var list metainternalversion.List total := options.Limit if total == 0 { total = int64(p.remaining) } for i := int64(0); i < total; i++ { if p.remaining <= 0 { break } list.Items = append(list.Items, &metav1beta1.PartialObjectMetadata{ ObjectMeta: metav1.ObjectMeta{ Name: fmt.Sprintf("%d", p.index), }, }) p.remaining-- p.index++ } p.last = p.index if p.remaining > 0 { list.Continue = fmt.Sprintf("%s:%d", p.rv, p.last) p.continuing = true } else { p.done = true } list.ResourceVersion = p.rv return &list, nil } func (p *testPager) ExpiresOnSecondPage(ctx context.Context, options metav1.ListOptions) (runtime.Object, error) { if p.continuing { p.done = true return nil, errors.NewResourceExpired("this list has expired") } return p.PagedList(ctx, options) } func (p *testPager) ExpiresOnSecondPageThenFullList(ctx context.Context, options metav1.ListOptions) (runtime.Object, error) { if p.continuing { p.reset() p.expectPage = 0 return nil, errors.NewResourceExpired("this list has expired") } return p.PagedList(ctx, options) } func TestListPager_List(t *testing.T) { type fields struct { PageSize int64 PageFn ListPageFunc FullListIfExpired bool } type args struct { ctx context.Context options metav1.ListOptions } tests := []struct { name string fields fields args args want runtime.Object wantErr bool isExpired bool }{ { name: "empty page", fields: fields{PageSize: 10, PageFn: (&testPager{t: t, expectPage: 10, remaining: 0, rv: "rv:20"}).PagedList}, args: args{}, want: list(0, "rv:20"), }, { name: "one page", fields: fields{PageSize: 10, PageFn: (&testPager{t: t, expectPage: 10, remaining: 9, rv: "rv:20"}).PagedList}, args: args{}, want: list(9, "rv:20"), }, { name: "one full page", fields: fields{PageSize: 10, PageFn: (&testPager{t: t, expectPage: 10, remaining: 10, rv: "rv:20"}).PagedList}, args: args{}, want: list(10, "rv:20"), }, { name: "two pages", fields: fields{PageSize: 10, PageFn: (&testPager{t: t, expectPage: 10, remaining: 11, rv: "rv:20"}).PagedList}, args: args{}, want: list(11, "rv:20"), }, { name: "three pages", fields: fields{PageSize: 10, PageFn: (&testPager{t: t, expectPage: 10, remaining: 21, rv: "rv:20"}).PagedList}, args: args{}, want: list(21, "rv:20"), }, { name: "expires on second page", fields: fields{PageSize: 10, PageFn: (&testPager{t: t, expectPage: 10, remaining: 21, rv: "rv:20"}).ExpiresOnSecondPage}, args: args{}, wantErr: true, isExpired: true, }, { name: "expires on second page and then lists", fields: fields{ FullListIfExpired: true, PageSize: 10, PageFn: (&testPager{t: t, expectPage: 10, remaining: 21, rv: "rv:20"}).ExpiresOnSecondPageThenFullList, }, args: args{}, want: list(21, "rv:20"), }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { p := &ListPager{ PageSize: tt.fields.PageSize, PageFn: tt.fields.PageFn, FullListIfExpired: tt.fields.FullListIfExpired, } got, err := p.List(tt.args.ctx, tt.args.options) if (err != nil) != tt.wantErr { t.Errorf("ListPager.List() error = %v, wantErr %v", err, tt.wantErr) return } if tt.isExpired != errors.IsResourceExpired(err) { t.Errorf("ListPager.List() error = %v, isExpired %v", err, tt.isExpired) return } if !reflect.DeepEqual(got, tt.want) { t.Errorf("ListPager.List() = %v, want %v", got, tt.want) } }) } }