github.com/munnerz/test-infra@v0.0.0-20190108210205-ce3d181dc989/pkg/ghclient/core_test.go (about)

     1  /*
     2  Copyright 2017 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package ghclient
    18  
    19  import (
    20  	"fmt"
    21  	"testing"
    22  	"time"
    23  
    24  	"github.com/google/go-github/github"
    25  )
    26  
    27  func setForTest(client *Client) {
    28  	client.retries = 5
    29  	client.retryInitialBackoff = time.Nanosecond
    30  	client.tokenReserve = 50
    31  }
    32  
    33  // fakeGithub is a fake go-github client that doubles as a test instance representation. This fake
    34  // client keeps track of the number of API calls made in order to test retry behavior and also allows
    35  // the number of pages of results to be configured.
    36  type fakeGithub struct {
    37  	// name is a string representation of this fakeGithub instance.
    38  	name string
    39  	// hits is a count of the number of API calls made to fakeGithub.
    40  	hits int
    41  	// hitsBeforeResponse is the number of hits that should be received before fakeGithub responds without error.
    42  	hitsBeforeResponse int
    43  	// shouldSucceed indicates if the githubutil client should get a valid response.
    44  	shouldSucceed bool
    45  	// pages is the number of pages to return for paginated data. (Should be 1 for non-paginated data)
    46  	pages int
    47  	// listOpts is the ListOptions that would be used in the call if the call were real.
    48  	listOpts github.ListOptions
    49  }
    50  
    51  // checkHits verifies that the githubutil client made the correct number of retries before returning.
    52  func (f *fakeGithub) checkHits() bool {
    53  	if f.shouldSucceed {
    54  		return f.hits-f.pages+1 == f.hitsBeforeResponse
    55  	}
    56  	return f.hitsBeforeResponse > f.hits
    57  }
    58  
    59  func (f *fakeGithub) call() ([]interface{}, *github.Response, error) {
    60  	f.hits++
    61  	if f.hits >= f.hitsBeforeResponse {
    62  		return []interface{}{f.listOpts.Page},
    63  			&github.Response{Rate: github.Rate{Limit: 5000, Remaining: 1000, Reset: github.Timestamp{Time: time.Now()}}, LastPage: f.pages},
    64  			nil
    65  	}
    66  	return nil, nil, fmt.Errorf("some error that forces a retry")
    67  }
    68  
    69  func TestRetryAndPagination(t *testing.T) {
    70  	tests := []*fakeGithub{
    71  		{name: "no retries", hitsBeforeResponse: 1, shouldSucceed: true, pages: 1},
    72  		{name: "max retries", hitsBeforeResponse: 6, shouldSucceed: true, pages: 1},
    73  		{name: "1 too many retries needed", hitsBeforeResponse: 7, shouldSucceed: false, pages: 1},
    74  		{name: "3 too many retries needed", hitsBeforeResponse: 10, shouldSucceed: false, pages: 1},
    75  		{name: "2 pages", hitsBeforeResponse: 1, shouldSucceed: true, pages: 2},
    76  		{name: "10 pages", hitsBeforeResponse: 1, shouldSucceed: true, pages: 10},
    77  		{name: "2 pages 2 retries", hitsBeforeResponse: 3, shouldSucceed: true, pages: 2},
    78  		{name: "10 pages max retries", hitsBeforeResponse: 6, shouldSucceed: true, pages: 10},
    79  		{name: "10 pages one too many retries", hitsBeforeResponse: 7, shouldSucceed: false, pages: 10},
    80  	}
    81  	for _, test := range tests {
    82  		client := &Client{}
    83  		setForTest(client)
    84  		pages, err := client.depaginate("retry test", &test.listOpts, test.call)
    85  		if (err == nil) != test.shouldSucceed {
    86  			t.Errorf("Retry+Pagination test '%s' failed because the error value was unexpected: %v", test.name, err)
    87  		}
    88  		if !test.checkHits() {
    89  			t.Errorf("Retry+Pagination test '%s' failed with the wrong number of hits: %d", test.name, test.hits)
    90  		}
    91  		if test.shouldSucceed && len(pages) != test.pages {
    92  			t.Errorf("Retry+Pagination test '%s' failed because the number of pages returned was %d instead of %d. Pages returned: %#v", test.name, len(pages), test.pages, pages)
    93  		}
    94  	}
    95  }