github.com/shashidharatd/test-infra@v0.0.0-20171006011030-71304e1ca560/prow/github/client_test.go (about)

     1  /*
     2  Copyright 2016 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 github
    18  
    19  import (
    20  	"crypto/tls"
    21  	"encoding/base64"
    22  	"encoding/json"
    23  	"fmt"
    24  	"io/ioutil"
    25  	"net/http"
    26  	"net/http/httptest"
    27  	"strconv"
    28  	"strings"
    29  	"testing"
    30  	"time"
    31  )
    32  
    33  func getClient(url string) *Client {
    34  	return &Client{
    35  		client: &http.Client{
    36  			Transport: &http.Transport{
    37  				TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
    38  			},
    39  		},
    40  		base: url,
    41  	}
    42  }
    43  
    44  func TestRequestRateLimit(t *testing.T) {
    45  	var slept time.Duration
    46  	timeSleep = func(d time.Duration) { slept = d }
    47  	defer func() { timeSleep = time.Sleep }()
    48  	ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    49  		if slept == 0 {
    50  			w.Header().Set("X-RateLimit-Remaining", "0")
    51  			w.Header().Set("X-RateLimit-Reset", strconv.Itoa(int(time.Now().Add(time.Second).Unix())))
    52  			http.Error(w, "403 Forbidden", http.StatusForbidden)
    53  		}
    54  	}))
    55  	defer ts.Close()
    56  	c := getClient(ts.URL)
    57  	resp, err := c.requestRetry(http.MethodGet, c.base, "", nil)
    58  	if err != nil {
    59  		t.Errorf("Error from request: %v", err)
    60  	} else if resp.StatusCode != 200 {
    61  		t.Errorf("Expected status code 200, got %d", resp.StatusCode)
    62  	} else if slept < time.Second {
    63  		t.Errorf("Expected to sleep for at least a second, got %v", slept)
    64  	}
    65  }
    66  
    67  func TestRetry404(t *testing.T) {
    68  	var slept int
    69  	timeSleep = func(d time.Duration) { slept++ }
    70  	defer func() { timeSleep = time.Sleep }()
    71  	ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    72  		if slept == 0 {
    73  			http.Error(w, "404 Not Found", http.StatusNotFound)
    74  		}
    75  	}))
    76  	defer ts.Close()
    77  	c := getClient(ts.URL)
    78  	resp, err := c.requestRetry(http.MethodGet, c.base, "", nil)
    79  	if err != nil {
    80  		t.Errorf("Error from request: %v", err)
    81  	} else if resp.StatusCode != 200 {
    82  		t.Errorf("Expected status code 200, got %d", resp.StatusCode)
    83  	}
    84  }
    85  
    86  func TestBotName(t *testing.T) {
    87  	ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    88  		if r.Method != http.MethodGet {
    89  			t.Errorf("Bad method: %s", r.Method)
    90  		}
    91  		if r.URL.Path != "/user" {
    92  			t.Errorf("Bad request path: %s", r.URL.Path)
    93  		}
    94  		fmt.Fprint(w, "{\"login\": \"wowza\"}")
    95  	}))
    96  	c := getClient(ts.URL)
    97  	botName, err := c.BotName()
    98  	if err != nil {
    99  		t.Errorf("Didn't expect error: %v", err)
   100  	} else if botName != "wowza" {
   101  		t.Errorf("Wrong bot name. Got %s, expected wowza.", botName)
   102  	}
   103  	ts.Close()
   104  	botName, err = c.BotName()
   105  	if err != nil {
   106  		t.Errorf("Didn't expect error: %v", err)
   107  	} else if botName != "wowza" {
   108  		t.Errorf("Wrong bot name. Got %s, expected wowza.", botName)
   109  	}
   110  }
   111  
   112  func TestIsMember(t *testing.T) {
   113  	ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   114  		if r.Method != http.MethodGet {
   115  			t.Errorf("Bad method: %s", r.Method)
   116  		}
   117  		if r.URL.Path != "/orgs/k8s/members/person" {
   118  			t.Errorf("Bad request path: %s", r.URL.Path)
   119  		}
   120  		http.Error(w, "204 No Content", http.StatusNoContent)
   121  	}))
   122  	defer ts.Close()
   123  	c := getClient(ts.URL)
   124  	mem, err := c.IsMember("k8s", "person")
   125  	if err != nil {
   126  		t.Errorf("Didn't expect error: %v", err)
   127  	} else if !mem {
   128  		t.Errorf("Should be member.")
   129  	}
   130  }
   131  
   132  func TestCreateComment(t *testing.T) {
   133  	ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   134  		if r.Method != http.MethodPost {
   135  			t.Errorf("Bad method: %s", r.Method)
   136  		}
   137  		if r.URL.Path != "/repos/k8s/kuber/issues/5/comments" {
   138  			t.Errorf("Bad request path: %s", r.URL.Path)
   139  		}
   140  		b, err := ioutil.ReadAll(r.Body)
   141  		if err != nil {
   142  			t.Fatalf("Could not read request body: %v", err)
   143  		}
   144  		var ic IssueComment
   145  		if err := json.Unmarshal(b, &ic); err != nil {
   146  			t.Errorf("Could not unmarshal request: %v", err)
   147  		} else if ic.Body != "hello" {
   148  			t.Errorf("Wrong body: %s", ic.Body)
   149  		}
   150  		http.Error(w, "201 Created", http.StatusCreated)
   151  	}))
   152  	defer ts.Close()
   153  	c := getClient(ts.URL)
   154  	if err := c.CreateComment("k8s", "kuber", 5, "hello"); err != nil {
   155  		t.Errorf("Didn't expect error: %v", err)
   156  	}
   157  }
   158  
   159  func TestCreateCommentReaction(t *testing.T) {
   160  	ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   161  		if r.Method != http.MethodPost {
   162  			t.Errorf("Bad method: %s", r.Method)
   163  		}
   164  		if r.URL.Path != "/repos/k8s/kuber/issues/comments/5/reactions" {
   165  			t.Errorf("Bad request path: %s", r.URL.Path)
   166  		}
   167  		if r.Header.Get("Accept") != "application/vnd.github.squirrel-girl-preview" {
   168  			t.Errorf("Bad Accept header: %s", r.Header.Get("Accept"))
   169  		}
   170  		http.Error(w, "201 Created", http.StatusCreated)
   171  	}))
   172  	defer ts.Close()
   173  	c := getClient(ts.URL)
   174  	if err := c.CreateCommentReaction("k8s", "kuber", 5, "+1"); err != nil {
   175  		t.Errorf("Didn't expect error: %v", err)
   176  	}
   177  }
   178  
   179  func TestDeleteComment(t *testing.T) {
   180  	ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   181  		if r.Method != http.MethodDelete {
   182  			t.Errorf("Bad method: %s", r.Method)
   183  		}
   184  		if r.URL.Path != "/repos/k8s/kuber/issues/comments/123" {
   185  			t.Errorf("Bad request path: %s", r.URL.Path)
   186  		}
   187  		http.Error(w, "204 No Content", http.StatusNoContent)
   188  	}))
   189  	defer ts.Close()
   190  	c := getClient(ts.URL)
   191  	if err := c.DeleteComment("k8s", "kuber", 123); err != nil {
   192  		t.Errorf("Didn't expect error: %v", err)
   193  	}
   194  }
   195  
   196  func TestGetPullRequest(t *testing.T) {
   197  	ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   198  		if r.Method != http.MethodGet {
   199  			t.Errorf("Bad method: %s", r.Method)
   200  		}
   201  		if r.URL.Path != "/repos/k8s/kuber/pulls/12" {
   202  			t.Errorf("Bad request path: %s", r.URL.Path)
   203  		}
   204  		pr := PullRequest{
   205  			User: User{Login: "bla"},
   206  		}
   207  		b, err := json.Marshal(&pr)
   208  		if err != nil {
   209  			t.Fatalf("Didn't expect error: %v", err)
   210  		}
   211  		fmt.Fprint(w, string(b))
   212  	}))
   213  	defer ts.Close()
   214  	c := getClient(ts.URL)
   215  	pr, err := c.GetPullRequest("k8s", "kuber", 12)
   216  	if err != nil {
   217  		t.Errorf("Didn't expect error: %v", err)
   218  	} else if pr.User.Login != "bla" {
   219  		t.Errorf("Wrong user: %s", pr.User.Login)
   220  	}
   221  }
   222  
   223  func TestGetPullRequestChanges(t *testing.T) {
   224  	ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   225  		if r.Method != http.MethodGet {
   226  			t.Errorf("Bad method: %s", r.Method)
   227  		}
   228  		if r.URL.Path != "/repos/k8s/kuber/pulls/12/files" {
   229  			t.Errorf("Bad request path: %s", r.URL.Path)
   230  		}
   231  		changes := []PullRequestChange{
   232  			{Filename: "foo.txt"},
   233  		}
   234  		b, err := json.Marshal(&changes)
   235  		if err != nil {
   236  			t.Fatalf("Didn't expect error: %v", err)
   237  		}
   238  		fmt.Fprint(w, string(b))
   239  	}))
   240  	defer ts.Close()
   241  	c := getClient(ts.URL)
   242  	cs, err := c.GetPullRequestChanges("k8s", "kuber", 12)
   243  	if err != nil {
   244  		t.Errorf("Didn't expect error: %v", err)
   245  	}
   246  	if len(cs) != 1 || cs[0].Filename != "foo.txt" {
   247  		t.Errorf("Wrong result: %#v", cs)
   248  	}
   249  }
   250  
   251  func TestGetRef(t *testing.T) {
   252  	ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   253  		if r.Method != http.MethodGet {
   254  			t.Errorf("Bad method: %s", r.Method)
   255  		}
   256  		if r.URL.Path != "/repos/k8s/kuber/git/refs/heads/mastah" {
   257  			t.Errorf("Bad request path: %s", r.URL.Path)
   258  		}
   259  		fmt.Fprint(w, `{"object": {"sha":"abcde"}}`)
   260  	}))
   261  	defer ts.Close()
   262  	c := getClient(ts.URL)
   263  	sha, err := c.GetRef("k8s", "kuber", "heads/mastah")
   264  	if err != nil {
   265  		t.Errorf("Didn't expect error: %v", err)
   266  	} else if sha != "abcde" {
   267  		t.Errorf("Wrong sha: %s", sha)
   268  	}
   269  }
   270  
   271  func TestCreateStatus(t *testing.T) {
   272  	ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   273  		if r.Method != http.MethodPost {
   274  			t.Errorf("Bad method: %s", r.Method)
   275  		}
   276  		if r.URL.Path != "/repos/k8s/kuber/statuses/abcdef" {
   277  			t.Errorf("Bad request path: %s", r.URL.Path)
   278  		}
   279  		b, err := ioutil.ReadAll(r.Body)
   280  		if err != nil {
   281  			t.Fatalf("Could not read request body: %v", err)
   282  		}
   283  		var s Status
   284  		if err := json.Unmarshal(b, &s); err != nil {
   285  			t.Errorf("Could not unmarshal request: %v", err)
   286  		} else if s.Context != "c" {
   287  			t.Errorf("Wrong context: %s", s.Context)
   288  		}
   289  		http.Error(w, "201 Created", http.StatusCreated)
   290  	}))
   291  	defer ts.Close()
   292  	c := getClient(ts.URL)
   293  	if err := c.CreateStatus("k8s", "kuber", "abcdef", Status{
   294  		Context: "c",
   295  	}); err != nil {
   296  		t.Errorf("Didn't expect error: %v", err)
   297  	}
   298  }
   299  
   300  func TestListIssueComments(t *testing.T) {
   301  	ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   302  		if r.Method != http.MethodGet {
   303  			t.Errorf("Bad method: %s", r.Method)
   304  		}
   305  		if r.URL.Path == "/repos/k8s/kuber/issues/15/comments" {
   306  			ics := []IssueComment{{ID: 1}}
   307  			b, err := json.Marshal(ics)
   308  			if err != nil {
   309  				t.Fatalf("Didn't expect error: %v", err)
   310  			}
   311  			w.Header().Set("Link", fmt.Sprintf(`<blorp>; rel="first", <https://%s/someotherpath>; rel="next"`, r.Host))
   312  			fmt.Fprint(w, string(b))
   313  		} else if r.URL.Path == "/someotherpath" {
   314  			ics := []IssueComment{{ID: 2}}
   315  			b, err := json.Marshal(ics)
   316  			if err != nil {
   317  				t.Fatalf("Didn't expect error: %v", err)
   318  			}
   319  			fmt.Fprint(w, string(b))
   320  		} else {
   321  			t.Errorf("Bad request path: %s", r.URL.Path)
   322  		}
   323  	}))
   324  	defer ts.Close()
   325  	c := getClient(ts.URL)
   326  	ics, err := c.ListIssueComments("k8s", "kuber", 15)
   327  	if err != nil {
   328  		t.Errorf("Didn't expect error: %v", err)
   329  	} else if len(ics) != 2 {
   330  		t.Errorf("Expected two issues, found %d: %v", len(ics), ics)
   331  	} else if ics[0].ID != 1 || ics[1].ID != 2 {
   332  		t.Errorf("Wrong issue IDs: %v", ics)
   333  	}
   334  }
   335  
   336  func TestAddLabel(t *testing.T) {
   337  	ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   338  		if r.Method != http.MethodPost {
   339  			t.Errorf("Bad method: %s", r.Method)
   340  		}
   341  		if r.URL.Path != "/repos/k8s/kuber/issues/5/labels" {
   342  			t.Errorf("Bad request path: %s", r.URL.Path)
   343  		}
   344  		b, err := ioutil.ReadAll(r.Body)
   345  		if err != nil {
   346  			t.Fatalf("Could not read request body: %v", err)
   347  		}
   348  		var ls []string
   349  		if err := json.Unmarshal(b, &ls); err != nil {
   350  			t.Errorf("Could not unmarshal request: %v", err)
   351  		} else if len(ls) != 1 {
   352  			t.Errorf("Wrong length labels: %v", ls)
   353  		} else if ls[0] != "yay" {
   354  			t.Errorf("Wrong label: %s", ls[0])
   355  		}
   356  	}))
   357  	defer ts.Close()
   358  	c := getClient(ts.URL)
   359  	if err := c.AddLabel("k8s", "kuber", 5, "yay"); err != nil {
   360  		t.Errorf("Didn't expect error: %v", err)
   361  	}
   362  }
   363  
   364  func TestRemoveLabel(t *testing.T) {
   365  	ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   366  		if r.Method != http.MethodDelete {
   367  			t.Errorf("Bad method: %s", r.Method)
   368  		}
   369  		if r.URL.Path != "/repos/k8s/kuber/issues/5/labels/yay" {
   370  			t.Errorf("Bad request path: %s", r.URL.Path)
   371  		}
   372  		http.Error(w, "204 No Content", http.StatusNoContent)
   373  	}))
   374  	defer ts.Close()
   375  	c := getClient(ts.URL)
   376  	if err := c.RemoveLabel("k8s", "kuber", 5, "yay"); err != nil {
   377  		t.Errorf("Didn't expect error: %v", err)
   378  	}
   379  }
   380  
   381  func TestAssignIssue(t *testing.T) {
   382  	ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   383  		if r.Method != http.MethodPost {
   384  			t.Errorf("Bad method: %s", r.Method)
   385  		}
   386  		if r.URL.Path != "/repos/k8s/kuber/issues/5/assignees" {
   387  			t.Errorf("Bad request path: %s", r.URL.Path)
   388  		}
   389  		b, err := ioutil.ReadAll(r.Body)
   390  		if err != nil {
   391  			t.Fatalf("Could not read request body: %v", err)
   392  		}
   393  		var ps map[string][]string
   394  		if err := json.Unmarshal(b, &ps); err != nil {
   395  			t.Errorf("Could not unmarshal request: %v", err)
   396  		} else if len(ps) != 1 {
   397  			t.Errorf("Wrong length patch: %v", ps)
   398  		} else if len(ps["assignees"]) == 3 {
   399  			if ps["assignees"][0] != "george" || ps["assignees"][1] != "jungle" || ps["assignees"][2] != "not-in-the-org" {
   400  				t.Errorf("Wrong assignees: %v", ps)
   401  			}
   402  		} else if len(ps["assignees"]) == 2 {
   403  			if ps["assignees"][0] != "george" || ps["assignees"][1] != "jungle" {
   404  				t.Errorf("Wrong assignees: %v", ps)
   405  			}
   406  
   407  		} else {
   408  			t.Errorf("Wrong assignees length: %v", ps)
   409  		}
   410  		w.WriteHeader(http.StatusCreated)
   411  		json.NewEncoder(w).Encode(Issue{
   412  			Assignees: []User{{Login: "george"}, {Login: "jungle"}, {Login: "ignore-other"}},
   413  		})
   414  	}))
   415  	defer ts.Close()
   416  	c := getClient(ts.URL)
   417  	if err := c.AssignIssue("k8s", "kuber", 5, []string{"george", "jungle"}); err != nil {
   418  		t.Errorf("Unexpected error: %v", err)
   419  	}
   420  	if err := c.AssignIssue("k8s", "kuber", 5, []string{"george", "jungle", "not-in-the-org"}); err == nil {
   421  		t.Errorf("Expected an error")
   422  	} else if merr, ok := err.(MissingUsers); ok {
   423  		if len(merr.Users) != 1 || merr.Users[0] != "not-in-the-org" {
   424  			t.Errorf("Expected [not-in-the-org], not %v", merr.Users)
   425  		}
   426  	} else {
   427  		t.Errorf("Expected MissingUsers error")
   428  	}
   429  }
   430  
   431  func TestUnassignIssue(t *testing.T) {
   432  	ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   433  		if r.Method != http.MethodDelete {
   434  			t.Errorf("Bad method: %s", r.Method)
   435  		}
   436  		if r.URL.Path != "/repos/k8s/kuber/issues/5/assignees" {
   437  			t.Errorf("Bad request path: %s", r.URL.Path)
   438  		}
   439  		b, err := ioutil.ReadAll(r.Body)
   440  		if err != nil {
   441  			t.Fatalf("Could not read request body: %v", err)
   442  		}
   443  		var ps map[string][]string
   444  		if err := json.Unmarshal(b, &ps); err != nil {
   445  			t.Errorf("Could not unmarshal request: %v", err)
   446  		} else if len(ps) != 1 {
   447  			t.Errorf("Wrong length patch: %v", ps)
   448  		} else if len(ps["assignees"]) == 3 {
   449  			if ps["assignees"][0] != "george" || ps["assignees"][1] != "jungle" || ps["assignees"][2] != "perma-assignee" {
   450  				t.Errorf("Wrong assignees: %v", ps)
   451  			}
   452  		} else if len(ps["assignees"]) == 2 {
   453  			if ps["assignees"][0] != "george" || ps["assignees"][1] != "jungle" {
   454  				t.Errorf("Wrong assignees: %v", ps)
   455  			}
   456  
   457  		} else {
   458  			t.Errorf("Wrong assignees length: %v", ps)
   459  		}
   460  		json.NewEncoder(w).Encode(Issue{
   461  			Assignees: []User{{Login: "perma-assignee"}, {Login: "ignore-other"}},
   462  		})
   463  	}))
   464  	defer ts.Close()
   465  	c := getClient(ts.URL)
   466  	if err := c.UnassignIssue("k8s", "kuber", 5, []string{"george", "jungle"}); err != nil {
   467  		t.Errorf("Unexpected error: %v", err)
   468  	}
   469  	if err := c.UnassignIssue("k8s", "kuber", 5, []string{"george", "jungle", "perma-assignee"}); err == nil {
   470  		t.Errorf("Expected an error")
   471  	} else if merr, ok := err.(ExtraUsers); ok {
   472  		if len(merr.Users) != 1 || merr.Users[0] != "perma-assignee" {
   473  			t.Errorf("Expected [perma-assignee], not %v", merr.Users)
   474  		}
   475  	} else {
   476  		t.Errorf("Expected ExtraUsers error")
   477  	}
   478  }
   479  
   480  func TestReadPaginatedResults(t *testing.T) {
   481  	ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   482  		if r.Method != http.MethodGet {
   483  			t.Errorf("Bad method: %s", r.Method)
   484  		}
   485  		if r.URL.Path == "/label/foo" {
   486  			objects := []Label{{Name: "foo"}}
   487  			b, err := json.Marshal(objects)
   488  			if err != nil {
   489  				t.Fatalf("Didn't expect error: %v", err)
   490  			}
   491  			w.Header().Set("Link", fmt.Sprintf(`<blorp>; rel="first", <https://%s/label/bar>; rel="next"`, r.Host))
   492  			fmt.Fprint(w, string(b))
   493  		} else if r.URL.Path == "/label/bar" {
   494  			objects := []Label{{Name: "bar"}}
   495  			b, err := json.Marshal(objects)
   496  			if err != nil {
   497  				t.Fatalf("Didn't expect error: %v", err)
   498  			}
   499  			fmt.Fprint(w, string(b))
   500  		} else {
   501  			t.Errorf("Bad request path: %s", r.URL.Path)
   502  		}
   503  	}))
   504  	defer ts.Close()
   505  	c := getClient(ts.URL)
   506  	path := "/label/foo"
   507  	var labels []Label
   508  	err := c.readPaginatedResults(path,
   509  		func() interface{} {
   510  			return &[]Label{}
   511  		},
   512  		func(obj interface{}) {
   513  			labels = append(labels, *(obj.(*[]Label))...)
   514  		},
   515  	)
   516  	if err != nil {
   517  		t.Errorf("Didn't expect error: %v", err)
   518  	} else if len(labels) != 2 {
   519  		t.Errorf("Expected two labels, found %d: %v", len(labels), labels)
   520  	} else if labels[0].Name != "foo" || labels[1].Name != "bar" {
   521  		t.Errorf("Wrong label names: %v", labels)
   522  	}
   523  }
   524  
   525  func TestListPullRequestComments(t *testing.T) {
   526  	ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   527  		if r.Method != http.MethodGet {
   528  			t.Errorf("Bad method: %s", r.Method)
   529  		}
   530  		if r.URL.Path == "/repos/k8s/kuber/pulls/15/comments" {
   531  			prcs := []ReviewComment{{ID: 1}}
   532  			b, err := json.Marshal(prcs)
   533  			if err != nil {
   534  				t.Fatalf("Didn't expect error: %v", err)
   535  			}
   536  			w.Header().Set("Link", fmt.Sprintf(`<blorp>; rel="first", <https://%s/someotherpath>; rel="next"`, r.Host))
   537  			fmt.Fprint(w, string(b))
   538  		} else if r.URL.Path == "/someotherpath" {
   539  			prcs := []ReviewComment{{ID: 2}}
   540  			b, err := json.Marshal(prcs)
   541  			if err != nil {
   542  				t.Fatalf("Didn't expect error: %v", err)
   543  			}
   544  			fmt.Fprint(w, string(b))
   545  		} else {
   546  			t.Errorf("Bad request path: %s", r.URL.Path)
   547  		}
   548  	}))
   549  	defer ts.Close()
   550  	c := getClient(ts.URL)
   551  	prcs, err := c.ListPullRequestComments("k8s", "kuber", 15)
   552  	if err != nil {
   553  		t.Errorf("Didn't expect error: %v", err)
   554  	} else if len(prcs) != 2 {
   555  		t.Errorf("Expected two comments, found %d: %v", len(prcs), prcs)
   556  	} else if prcs[0].ID != 1 || prcs[1].ID != 2 {
   557  		t.Errorf("Wrong issue IDs: %v", prcs)
   558  	}
   559  }
   560  
   561  func TestRequestReview(t *testing.T) {
   562  	ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   563  		if r.Method != http.MethodPost {
   564  			t.Errorf("Bad method: %s", r.Method)
   565  		}
   566  		if r.URL.Path != "/repos/k8s/kuber/pulls/5/requested_reviewers" {
   567  			t.Errorf("Bad request path: %s", r.URL.Path)
   568  		}
   569  		b, err := ioutil.ReadAll(r.Body)
   570  		if err != nil {
   571  			t.Fatalf("Could not read request body: %v", err)
   572  		}
   573  		var ps map[string][]string
   574  		if err := json.Unmarshal(b, &ps); err != nil {
   575  			t.Fatalf("Could not unmarshal request: %v", err)
   576  		}
   577  		if len(ps) != 1 {
   578  			t.Fatalf("Wrong length patch: %v", ps)
   579  		}
   580  		switch len(ps["reviewers"]) {
   581  		case 3:
   582  			if ps["reviewers"][0] != "george" || ps["reviewers"][1] != "jungle" || ps["reviewers"][2] != "not-a-collaborator" {
   583  				t.Errorf("Wrong reviewers: %v", ps)
   584  			}
   585  			//fall out of switch statement to bad reviewer case
   586  		case 2:
   587  			if ps["reviewers"][0] != "george" || ps["reviewers"][1] != "jungle" {
   588  				t.Errorf("Wrong reviewers: %v", ps)
   589  			}
   590  			w.WriteHeader(http.StatusCreated)
   591  			json.NewEncoder(w).Encode(PullRequest{
   592  				RequestedReviewers: []User{{Login: "george"}, {Login: "jungle"}, {Login: "ignore-other"}},
   593  			})
   594  			return
   595  		case 1:
   596  			if ps["reviewers"][0] != "not-a-collaborator" {
   597  				w.WriteHeader(http.StatusCreated)
   598  				json.NewEncoder(w).Encode(PullRequest{
   599  					RequestedReviewers: []User{{Login: ps["reviewers"][0]}, {Login: "ignore-other"}},
   600  				})
   601  				return
   602  			}
   603  			//fall out of switch statement to bad reviewer case
   604  		default:
   605  			t.Errorf("Wrong reviewers length: %v", ps)
   606  		}
   607  		//bad reviewer case
   608  		w.WriteHeader(http.StatusUnprocessableEntity)
   609  	}))
   610  	defer ts.Close()
   611  	c := getClient(ts.URL)
   612  	if err := c.RequestReview("k8s", "kuber", 5, []string{"george", "jungle"}); err != nil {
   613  		t.Errorf("Unexpected error: %v", err)
   614  	}
   615  	if err := c.RequestReview("k8s", "kuber", 5, []string{"george", "jungle", "not-a-collaborator"}); err == nil {
   616  		t.Errorf("Expected an error")
   617  	} else if merr, ok := err.(MissingUsers); ok {
   618  		if len(merr.Users) != 1 || merr.Users[0] != "not-a-collaborator" {
   619  			t.Errorf("Expected [not-a-collaborator], not %v", merr.Users)
   620  		}
   621  	} else {
   622  		t.Errorf("Expected MissingUsers error")
   623  	}
   624  }
   625  
   626  func TestUnrequestReview(t *testing.T) {
   627  	ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   628  		if r.Method != http.MethodDelete {
   629  			t.Errorf("Bad method: %s", r.Method)
   630  		}
   631  		if r.URL.Path != "/repos/k8s/kuber/pulls/5/requested_reviewers" {
   632  			t.Errorf("Bad request path: %s", r.URL.Path)
   633  		}
   634  		b, err := ioutil.ReadAll(r.Body)
   635  		if err != nil {
   636  			t.Fatalf("Could not read request body: %v", err)
   637  		}
   638  		var ps map[string][]string
   639  		if err := json.Unmarshal(b, &ps); err != nil {
   640  			t.Errorf("Could not unmarshal request: %v", err)
   641  		} else if len(ps) != 1 {
   642  			t.Errorf("Wrong length patch: %v", ps)
   643  		} else if len(ps["reviewers"]) == 3 {
   644  			if ps["reviewers"][0] != "george" || ps["reviewers"][1] != "jungle" || ps["reviewers"][2] != "perma-reviewer" {
   645  				t.Errorf("Wrong reviewers: %v", ps)
   646  			}
   647  		} else if len(ps["reviewers"]) == 2 {
   648  			if ps["reviewers"][0] != "george" || ps["reviewers"][1] != "jungle" {
   649  				t.Errorf("Wrong reviewers: %v", ps)
   650  			}
   651  		} else {
   652  			t.Errorf("Wrong reviewers length: %v", ps)
   653  		}
   654  		json.NewEncoder(w).Encode(PullRequest{
   655  			RequestedReviewers: []User{{Login: "perma-reviewer"}, {Login: "ignore-other"}},
   656  		})
   657  	}))
   658  	defer ts.Close()
   659  	c := getClient(ts.URL)
   660  	if err := c.UnrequestReview("k8s", "kuber", 5, []string{"george", "jungle"}); err != nil {
   661  		t.Errorf("Unexpected error: %v", err)
   662  	}
   663  	if err := c.UnrequestReview("k8s", "kuber", 5, []string{"george", "jungle", "perma-reviewer"}); err == nil {
   664  		t.Errorf("Expected an error")
   665  	} else if merr, ok := err.(ExtraUsers); ok {
   666  		if len(merr.Users) != 1 || merr.Users[0] != "perma-reviewer" {
   667  			t.Errorf("Expected [perma-reviewer], not %v", merr.Users)
   668  		}
   669  	} else {
   670  		t.Errorf("Expected ExtraUsers error")
   671  	}
   672  }
   673  
   674  func TestCloseIssue(t *testing.T) {
   675  	ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   676  		if r.Method != http.MethodPatch {
   677  			t.Errorf("Bad method: %s", r.Method)
   678  		}
   679  		if r.URL.Path != "/repos/k8s/kuber/issues/5" {
   680  			t.Errorf("Bad request path: %s", r.URL.Path)
   681  		}
   682  		b, err := ioutil.ReadAll(r.Body)
   683  		if err != nil {
   684  			t.Fatalf("Could not read request body: %v", err)
   685  		}
   686  		var ps map[string]string
   687  		if err := json.Unmarshal(b, &ps); err != nil {
   688  			t.Errorf("Could not unmarshal request: %v", err)
   689  		} else if len(ps) != 1 {
   690  			t.Errorf("Wrong length patch: %v", ps)
   691  		} else if ps["state"] != "closed" {
   692  			t.Errorf("Wrong state: %s", ps["state"])
   693  		}
   694  	}))
   695  	defer ts.Close()
   696  	c := getClient(ts.URL)
   697  	if err := c.CloseIssue("k8s", "kuber", 5); err != nil {
   698  		t.Errorf("Didn't expect error: %v", err)
   699  	}
   700  }
   701  
   702  func TestReopenIssue(t *testing.T) {
   703  	ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   704  		if r.Method != http.MethodPatch {
   705  			t.Errorf("Bad method: %s", r.Method)
   706  		}
   707  		if r.URL.Path != "/repos/k8s/kuber/issues/5" {
   708  			t.Errorf("Bad request path: %s", r.URL.Path)
   709  		}
   710  		b, err := ioutil.ReadAll(r.Body)
   711  		if err != nil {
   712  			t.Fatalf("Could not read request body: %v", err)
   713  		}
   714  		var ps map[string]string
   715  		if err := json.Unmarshal(b, &ps); err != nil {
   716  			t.Errorf("Could not unmarshal request: %v", err)
   717  		} else if len(ps) != 1 {
   718  			t.Errorf("Wrong length patch: %v", ps)
   719  		} else if ps["state"] != "open" {
   720  			t.Errorf("Wrong state: %s", ps["state"])
   721  		}
   722  	}))
   723  	defer ts.Close()
   724  	c := getClient(ts.URL)
   725  	if err := c.ReopenIssue("k8s", "kuber", 5); err != nil {
   726  		t.Errorf("Didn't expect error: %v", err)
   727  	}
   728  }
   729  
   730  func TestClosePR(t *testing.T) {
   731  	ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   732  		if r.Method != http.MethodPatch {
   733  			t.Errorf("Bad method: %s", r.Method)
   734  		}
   735  		if r.URL.Path != "/repos/k8s/kuber/pulls/5" {
   736  			t.Errorf("Bad request path: %s", r.URL.Path)
   737  		}
   738  		b, err := ioutil.ReadAll(r.Body)
   739  		if err != nil {
   740  			t.Fatalf("Could not read request body: %v", err)
   741  		}
   742  		var ps map[string]string
   743  		if err := json.Unmarshal(b, &ps); err != nil {
   744  			t.Errorf("Could not unmarshal request: %v", err)
   745  		} else if len(ps) != 1 {
   746  			t.Errorf("Wrong length patch: %v", ps)
   747  		} else if ps["state"] != "closed" {
   748  			t.Errorf("Wrong state: %s", ps["state"])
   749  		}
   750  	}))
   751  	defer ts.Close()
   752  	c := getClient(ts.URL)
   753  	if err := c.ClosePR("k8s", "kuber", 5); err != nil {
   754  		t.Errorf("Didn't expect error: %v", err)
   755  	}
   756  }
   757  
   758  func TestReopenPR(t *testing.T) {
   759  	ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   760  		if r.Method != http.MethodPatch {
   761  			t.Errorf("Bad method: %s", r.Method)
   762  		}
   763  		if r.URL.Path != "/repos/k8s/kuber/pulls/5" {
   764  			t.Errorf("Bad request path: %s", r.URL.Path)
   765  		}
   766  		b, err := ioutil.ReadAll(r.Body)
   767  		if err != nil {
   768  			t.Fatalf("Could not read request body: %v", err)
   769  		}
   770  		var ps map[string]string
   771  		if err := json.Unmarshal(b, &ps); err != nil {
   772  			t.Errorf("Could not unmarshal request: %v", err)
   773  		} else if len(ps) != 1 {
   774  			t.Errorf("Wrong length patch: %v", ps)
   775  		} else if ps["state"] != "open" {
   776  			t.Errorf("Wrong state: %s", ps["state"])
   777  		}
   778  	}))
   779  	defer ts.Close()
   780  	c := getClient(ts.URL)
   781  	if err := c.ReopenPR("k8s", "kuber", 5); err != nil {
   782  		t.Errorf("Didn't expect error: %v", err)
   783  	}
   784  }
   785  
   786  func TestFindIssues(t *testing.T) {
   787  	cases := []struct {
   788  		name  string
   789  		sort  bool
   790  		order bool
   791  	}{
   792  		{
   793  			name: "simple query",
   794  		},
   795  		{
   796  			name: "sort no order",
   797  			sort: true,
   798  		},
   799  		{
   800  			name:  "sort and order",
   801  			sort:  true,
   802  			order: true,
   803  		},
   804  	}
   805  
   806  	issueNum := 5
   807  	ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   808  		if r.Method != http.MethodGet {
   809  			t.Errorf("Bad method: %s", r.Method)
   810  		}
   811  		if r.URL.Path != "/search/issues" {
   812  			t.Errorf("Bad request path: %s", r.URL.Path)
   813  		}
   814  		issueList := IssuesSearchResult{
   815  			Total: 1,
   816  			Issues: []Issue{
   817  				{
   818  					Number: issueNum,
   819  					Title:  r.URL.RawQuery,
   820  				},
   821  			},
   822  		}
   823  		b, err := json.Marshal(&issueList)
   824  		if err != nil {
   825  			t.Fatalf("Didn't expect error: %v", err)
   826  		}
   827  		fmt.Fprint(w, string(b))
   828  	}))
   829  	defer ts.Close()
   830  	c := getClient(ts.URL)
   831  
   832  	for _, tc := range cases {
   833  		var result []Issue
   834  		var err error
   835  		sort := ""
   836  		if tc.sort {
   837  			sort = "sort-strategy"
   838  		}
   839  		if result, err = c.FindIssues("commit_hash", sort, tc.order); err != nil {
   840  			t.Errorf("%s: didn't expect error: %v", tc.name, err)
   841  		}
   842  		if len(result) != 1 {
   843  			t.Errorf("%s: unexpected number of results: %v", tc.name, len(result))
   844  		}
   845  		if result[0].Number != issueNum {
   846  			t.Errorf("%s: expected issue number %+v, got %+v", tc.name, issueNum, result[0].Number)
   847  		}
   848  		if tc.sort && !strings.Contains(result[0].Title, "sort="+sort) {
   849  			t.Errorf("%s: missing sort=%s from query: %s", tc.name, sort, result[0].Title)
   850  		}
   851  		if tc.order && !strings.Contains(result[0].Title, "order=asc") {
   852  			t.Errorf("%s: missing order=asc from query: %s", tc.name, result[0].Title)
   853  		}
   854  	}
   855  }
   856  
   857  func TestGetFile(t *testing.T) {
   858  	ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   859  		if r.Method != http.MethodGet {
   860  			t.Errorf("Bad method: %s", r.Method)
   861  		}
   862  		if r.URL.Path != "/repos/k8s/kuber/contents/foo.txt" {
   863  			t.Errorf("Bad request path: %s", r.URL.Path)
   864  		}
   865  		if r.URL.RawQuery != "" {
   866  			t.Errorf("Bad request query: %s", r.URL.RawQuery)
   867  		}
   868  		c := &Content{
   869  			Content: base64.StdEncoding.EncodeToString([]byte("abcde")),
   870  		}
   871  		b, err := json.Marshal(&c)
   872  		if err != nil {
   873  			t.Fatalf("Didn't expect error: %v", err)
   874  		}
   875  		fmt.Fprint(w, string(b))
   876  	}))
   877  	defer ts.Close()
   878  	c := getClient(ts.URL)
   879  	if content, err := c.GetFile("k8s", "kuber", "foo.txt", ""); err != nil {
   880  		t.Errorf("Didn't expect error: %v", err)
   881  	} else if string(content) != "abcde" {
   882  		t.Errorf("Wrong content -- expect: abcde, got: %s", string(content))
   883  	}
   884  }
   885  
   886  func TestGetFileRef(t *testing.T) {
   887  	ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   888  		if r.Method != http.MethodGet {
   889  			t.Errorf("Bad method: %s", r.Method)
   890  		}
   891  		if r.URL.Path != "/repos/k8s/kuber/contents/foo/bar.txt" {
   892  			t.Errorf("Bad request path: %s", r.URL)
   893  		}
   894  		if r.URL.RawQuery != "ref=12345" {
   895  			t.Errorf("Bad request query: %s", r.URL.RawQuery)
   896  		}
   897  		c := &Content{
   898  			Content: base64.StdEncoding.EncodeToString([]byte("abcde")),
   899  		}
   900  		b, err := json.Marshal(&c)
   901  		if err != nil {
   902  			t.Fatalf("Didn't expect error: %v", err)
   903  		}
   904  		fmt.Fprint(w, string(b))
   905  	}))
   906  	defer ts.Close()
   907  	c := getClient(ts.URL)
   908  	if content, err := c.GetFile("k8s", "kuber", "foo/bar.txt", "12345"); err != nil {
   909  		t.Errorf("Didn't expect error: %v", err)
   910  	} else if string(content) != "abcde" {
   911  		t.Errorf("Wrong content -- expect: abcde, got: %s", string(content))
   912  	}
   913  }
   914  
   915  // TestGetLabels tests both GetRepoLabels and GetIssueLabels.
   916  func TestGetLabels(t *testing.T) {
   917  	ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   918  		if r.Method != http.MethodGet {
   919  			t.Errorf("Bad method: %s", r.Method)
   920  		}
   921  		var labels []Label
   922  		switch r.URL.Path {
   923  		case "/repos/k8s/kuber/issues/5/labels":
   924  			labels = []Label{{Name: "issue-label"}}
   925  			w.Header().Set("Link", fmt.Sprintf(`<blorp>; rel="first", <https://%s/someotherpath>; rel="next"`, r.Host))
   926  		case "/repos/k8s/kuber/labels":
   927  			labels = []Label{{Name: "repo-label"}}
   928  			w.Header().Set("Link", fmt.Sprintf(`<blorp>; rel="first", <https://%s/someotherpath>; rel="next"`, r.Host))
   929  		case "/someotherpath":
   930  			labels = []Label{{Name: "label2"}}
   931  		default:
   932  			t.Errorf("Bad request path: %s", r.URL.Path)
   933  			return
   934  		}
   935  		b, err := json.Marshal(labels)
   936  		if err != nil {
   937  			t.Fatalf("Didn't expect error: %v", err)
   938  		}
   939  		fmt.Fprint(w, string(b))
   940  	}))
   941  	defer ts.Close()
   942  	c := getClient(ts.URL)
   943  	labels, err := c.GetIssueLabels("k8s", "kuber", 5)
   944  	if err != nil {
   945  		t.Errorf("Didn't expect error: %v", err)
   946  	} else if len(labels) != 2 {
   947  		t.Errorf("Expected two labels, found %d: %v", len(labels), labels)
   948  	} else if labels[0].Name != "issue-label" || labels[1].Name != "label2" {
   949  		t.Errorf("Wrong label names: %v", labels)
   950  	}
   951  
   952  	labels, err = c.GetRepoLabels("k8s", "kuber")
   953  	if err != nil {
   954  		t.Errorf("Didn't expect error: %v", err)
   955  	} else if len(labels) != 2 {
   956  		t.Errorf("Expected two labels, found %d: %v", len(labels), labels)
   957  	} else if labels[0].Name != "repo-label" || labels[1].Name != "label2" {
   958  		t.Errorf("Wrong label names: %v", labels)
   959  	}
   960  }
   961  
   962  func simpleTestServer(t *testing.T, path string, v interface{}) *httptest.Server {
   963  	return httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   964  		if r.URL.Path == path {
   965  			b, err := json.Marshal(v)
   966  			if err != nil {
   967  				t.Fatalf("Didn't expect error: %v", err)
   968  			}
   969  			fmt.Fprint(w, string(b))
   970  		} else {
   971  			t.Fatalf("Bad request path: %s", r.URL.Path)
   972  		}
   973  	}))
   974  }
   975  
   976  func TestListTeams(t *testing.T) {
   977  	ts := simpleTestServer(t, "/orgs/foo/teams", []Team{{ID: 1}})
   978  	defer ts.Close()
   979  	c := getClient(ts.URL)
   980  	teams, err := c.ListTeams("foo")
   981  	if err != nil {
   982  		t.Errorf("Didn't expect error: %v", err)
   983  	} else if len(teams) != 1 {
   984  		t.Errorf("Expected one team, found %d: %v", len(teams), teams)
   985  	} else if teams[0].ID != 1 {
   986  		t.Errorf("Wrong team names: %v", teams)
   987  	}
   988  }
   989  
   990  func TestListTeamMembers(t *testing.T) {
   991  	ts := simpleTestServer(t, "/teams/1/members", []TeamMember{{Login: "foo"}})
   992  	defer ts.Close()
   993  	c := getClient(ts.URL)
   994  	teamMembers, err := c.ListTeamMembers(1)
   995  	if err != nil {
   996  		t.Errorf("Didn't expect error: %v", err)
   997  	} else if len(teamMembers) != 1 {
   998  		t.Errorf("Expected one team member, found %d: %v", len(teamMembers), teamMembers)
   999  	} else if teamMembers[0].Login != "foo" {
  1000  		t.Errorf("Wrong team names: %v", teamMembers)
  1001  	}
  1002  }