github.com/yrj2011/jx-test-infra@v0.0.0-20190529031832-7a2065ee98eb/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 "context" 21 "crypto/tls" 22 "encoding/base64" 23 "encoding/json" 24 "fmt" 25 "io/ioutil" 26 "net/http" 27 "net/http/httptest" 28 "reflect" 29 "strconv" 30 "strings" 31 "sync/atomic" 32 "testing" 33 "time" 34 35 "k8s.io/apimachinery/pkg/util/sets" 36 ) 37 38 type testTime struct { 39 now time.Time 40 slept time.Duration 41 } 42 43 func (tt *testTime) Sleep(d time.Duration) { 44 tt.slept = d 45 } 46 func (tt *testTime) Until(t time.Time) time.Duration { 47 return t.Sub(tt.now) 48 } 49 50 func getClient(url string) *Client { 51 getToken := func() []byte { 52 return []byte("") 53 } 54 55 return &Client{ 56 time: &standardTime{}, 57 getToken: getToken, 58 client: &http.Client{ 59 Transport: &http.Transport{ 60 TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, 61 }, 62 }, 63 bases: []string{url}, 64 } 65 } 66 67 func TestRequestRateLimit(t *testing.T) { 68 tc := &testTime{now: time.Now()} 69 ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 70 if tc.slept == 0 { 71 w.Header().Set("X-RateLimit-Remaining", "0") 72 w.Header().Set("X-RateLimit-Reset", strconv.Itoa(int(tc.now.Add(time.Second).Unix()))) 73 http.Error(w, "403 Forbidden", http.StatusForbidden) 74 } 75 })) 76 defer ts.Close() 77 c := getClient(ts.URL) 78 c.time = tc 79 resp, err := c.requestRetry(http.MethodGet, "/", "", nil) 80 if err != nil { 81 t.Errorf("Error from request: %v", err) 82 } else if resp.StatusCode != 200 { 83 t.Errorf("Expected status code 200, got %d", resp.StatusCode) 84 } else if tc.slept < time.Second { 85 t.Errorf("Expected to sleep for at least a second, got %v", tc.slept) 86 } 87 } 88 89 func TestRetry404(t *testing.T) { 90 tc := &testTime{now: time.Now()} 91 ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 92 if tc.slept == 0 { 93 http.Error(w, "404 Not Found", http.StatusNotFound) 94 } 95 })) 96 defer ts.Close() 97 c := getClient(ts.URL) 98 c.time = tc 99 resp, err := c.requestRetry(http.MethodGet, "/", "", nil) 100 if err != nil { 101 t.Errorf("Error from request: %v", err) 102 } else if resp.StatusCode != 200 { 103 t.Errorf("Expected status code 200, got %d", resp.StatusCode) 104 } 105 } 106 107 func TestRetryBase(t *testing.T) { 108 defer func(orig time.Duration) { initialDelay = orig }(initialDelay) 109 initialDelay = time.Microsecond 110 111 ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})) 112 defer ts.Close() 113 c := getClient(ts.URL) 114 // One good endpoint: 115 c.bases = []string{c.bases[0]} 116 resp, err := c.requestRetry(http.MethodGet, "/", "", nil) 117 if err != nil { 118 t.Errorf("Error from request: %v", err) 119 } else if resp.StatusCode != 200 { 120 t.Errorf("Expected status code 200, got %d", resp.StatusCode) 121 } 122 // Bad endpoint followed by good endpoint: 123 c.bases = []string{"not-a-valid-base", c.bases[0]} 124 resp, err = c.requestRetry(http.MethodGet, "/", "", nil) 125 if err != nil { 126 t.Errorf("Error from request: %v", err) 127 } else if resp.StatusCode != 200 { 128 t.Errorf("Expected status code 200, got %d", resp.StatusCode) 129 } 130 // One bad endpoint: 131 c.bases = []string{"not-a-valid-base"} 132 resp, err = c.requestRetry(http.MethodGet, "/", "", nil) 133 if err == nil { 134 t.Error("Expected an error from a request to an invalid base, but succeeded!?") 135 } 136 } 137 138 func TestBotName(t *testing.T) { 139 ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 140 if r.Method != http.MethodGet { 141 t.Errorf("Bad method: %s", r.Method) 142 } 143 if r.URL.Path != "/user" { 144 t.Errorf("Bad request path: %s", r.URL.Path) 145 } 146 fmt.Fprint(w, "{\"login\": \"wowza\"}") 147 })) 148 c := getClient(ts.URL) 149 botName, err := c.BotName() 150 if err != nil { 151 t.Errorf("Didn't expect error: %v", err) 152 } else if botName != "wowza" { 153 t.Errorf("Wrong bot name. Got %s, expected wowza.", botName) 154 } 155 ts.Close() 156 botName, err = c.BotName() 157 if err != nil { 158 t.Errorf("Didn't expect error: %v", err) 159 } else if botName != "wowza" { 160 t.Errorf("Wrong bot name. Got %s, expected wowza.", botName) 161 } 162 } 163 164 func TestIsMember(t *testing.T) { 165 ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 166 if r.Method != http.MethodGet { 167 t.Errorf("Bad method: %s", r.Method) 168 } 169 if r.URL.Path != "/orgs/k8s/members/person" { 170 t.Errorf("Bad request path: %s", r.URL.Path) 171 } 172 http.Error(w, "204 No Content", http.StatusNoContent) 173 })) 174 defer ts.Close() 175 c := getClient(ts.URL) 176 mem, err := c.IsMember("k8s", "person") 177 if err != nil { 178 t.Errorf("Didn't expect error: %v", err) 179 } else if !mem { 180 t.Errorf("Should be member.") 181 } 182 } 183 184 func TestCreateComment(t *testing.T) { 185 ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 186 if r.Method != http.MethodPost { 187 t.Errorf("Bad method: %s", r.Method) 188 } 189 if r.URL.Path != "/repos/k8s/kuber/issues/5/comments" { 190 t.Errorf("Bad request path: %s", r.URL.Path) 191 } 192 b, err := ioutil.ReadAll(r.Body) 193 if err != nil { 194 t.Fatalf("Could not read request body: %v", err) 195 } 196 var ic IssueComment 197 if err := json.Unmarshal(b, &ic); err != nil { 198 t.Errorf("Could not unmarshal request: %v", err) 199 } else if ic.Body != "hello" { 200 t.Errorf("Wrong body: %s", ic.Body) 201 } 202 http.Error(w, "201 Created", http.StatusCreated) 203 })) 204 defer ts.Close() 205 c := getClient(ts.URL) 206 if err := c.CreateComment("k8s", "kuber", 5, "hello"); err != nil { 207 t.Errorf("Didn't expect error: %v", err) 208 } 209 } 210 211 func TestCreateCommentReaction(t *testing.T) { 212 ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 213 if r.Method != http.MethodPost { 214 t.Errorf("Bad method: %s", r.Method) 215 } 216 if r.URL.Path != "/repos/k8s/kuber/issues/comments/5/reactions" { 217 t.Errorf("Bad request path: %s", r.URL.Path) 218 } 219 if r.Header.Get("Accept") != "application/vnd.github.squirrel-girl-preview" { 220 t.Errorf("Bad Accept header: %s", r.Header.Get("Accept")) 221 } 222 http.Error(w, "201 Created", http.StatusCreated) 223 })) 224 defer ts.Close() 225 c := getClient(ts.URL) 226 if err := c.CreateCommentReaction("k8s", "kuber", 5, "+1"); err != nil { 227 t.Errorf("Didn't expect error: %v", err) 228 } 229 } 230 231 func TestDeleteComment(t *testing.T) { 232 ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 233 if r.Method != http.MethodDelete { 234 t.Errorf("Bad method: %s", r.Method) 235 } 236 if r.URL.Path != "/repos/k8s/kuber/issues/comments/123" { 237 t.Errorf("Bad request path: %s", r.URL.Path) 238 } 239 http.Error(w, "204 No Content", http.StatusNoContent) 240 })) 241 defer ts.Close() 242 c := getClient(ts.URL) 243 if err := c.DeleteComment("k8s", "kuber", 123); err != nil { 244 t.Errorf("Didn't expect error: %v", err) 245 } 246 } 247 248 func TestGetPullRequest(t *testing.T) { 249 ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 250 if r.Method != http.MethodGet { 251 t.Errorf("Bad method: %s", r.Method) 252 } 253 if r.URL.Path != "/repos/k8s/kuber/pulls/12" { 254 t.Errorf("Bad request path: %s", r.URL.Path) 255 } 256 pr := PullRequest{ 257 User: User{Login: "bla"}, 258 } 259 b, err := json.Marshal(&pr) 260 if err != nil { 261 t.Fatalf("Didn't expect error: %v", err) 262 } 263 fmt.Fprint(w, string(b)) 264 })) 265 defer ts.Close() 266 c := getClient(ts.URL) 267 pr, err := c.GetPullRequest("k8s", "kuber", 12) 268 if err != nil { 269 t.Errorf("Didn't expect error: %v", err) 270 } else if pr.User.Login != "bla" { 271 t.Errorf("Wrong user: %s", pr.User.Login) 272 } 273 } 274 275 func TestGetPullRequestChanges(t *testing.T) { 276 ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 277 if r.Method != http.MethodGet { 278 t.Errorf("Bad method: %s", r.Method) 279 } 280 if r.URL.Path != "/repos/k8s/kuber/pulls/12/files" { 281 t.Errorf("Bad request path: %s", r.URL.Path) 282 } 283 changes := []PullRequestChange{ 284 {Filename: "foo.txt"}, 285 } 286 b, err := json.Marshal(&changes) 287 if err != nil { 288 t.Fatalf("Didn't expect error: %v", err) 289 } 290 fmt.Fprint(w, string(b)) 291 })) 292 defer ts.Close() 293 c := getClient(ts.URL) 294 cs, err := c.GetPullRequestChanges("k8s", "kuber", 12) 295 if err != nil { 296 t.Errorf("Didn't expect error: %v", err) 297 } 298 if len(cs) != 1 || cs[0].Filename != "foo.txt" { 299 t.Errorf("Wrong result: %#v", cs) 300 } 301 } 302 303 func TestGetRef(t *testing.T) { 304 ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 305 if r.Method != http.MethodGet { 306 t.Errorf("Bad method: %s", r.Method) 307 } 308 if r.URL.Path != "/repos/k8s/kuber/git/refs/heads/mastah" { 309 t.Errorf("Bad request path: %s", r.URL.Path) 310 } 311 fmt.Fprint(w, `{"object": {"sha":"abcde"}}`) 312 })) 313 defer ts.Close() 314 c := getClient(ts.URL) 315 sha, err := c.GetRef("k8s", "kuber", "heads/mastah") 316 if err != nil { 317 t.Errorf("Didn't expect error: %v", err) 318 } else if sha != "abcde" { 319 t.Errorf("Wrong sha: %s", sha) 320 } 321 } 322 323 func TestCreateStatus(t *testing.T) { 324 ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 325 if r.Method != http.MethodPost { 326 t.Errorf("Bad method: %s", r.Method) 327 } 328 if r.URL.Path != "/repos/k8s/kuber/statuses/abcdef" { 329 t.Errorf("Bad request path: %s", r.URL.Path) 330 } 331 b, err := ioutil.ReadAll(r.Body) 332 if err != nil { 333 t.Fatalf("Could not read request body: %v", err) 334 } 335 var s Status 336 if err := json.Unmarshal(b, &s); err != nil { 337 t.Errorf("Could not unmarshal request: %v", err) 338 } else if s.Context != "c" { 339 t.Errorf("Wrong context: %s", s.Context) 340 } 341 http.Error(w, "201 Created", http.StatusCreated) 342 })) 343 defer ts.Close() 344 c := getClient(ts.URL) 345 if err := c.CreateStatus("k8s", "kuber", "abcdef", Status{ 346 Context: "c", 347 }); err != nil { 348 t.Errorf("Didn't expect error: %v", err) 349 } 350 } 351 352 func TestListIssueComments(t *testing.T) { 353 ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 354 if r.Method != http.MethodGet { 355 t.Errorf("Bad method: %s", r.Method) 356 } 357 if r.URL.Path == "/repos/k8s/kuber/issues/15/comments" { 358 ics := []IssueComment{{ID: 1}} 359 b, err := json.Marshal(ics) 360 if err != nil { 361 t.Fatalf("Didn't expect error: %v", err) 362 } 363 w.Header().Set("Link", fmt.Sprintf(`<blorp>; rel="first", <https://%s/someotherpath>; rel="next"`, r.Host)) 364 fmt.Fprint(w, string(b)) 365 } else if r.URL.Path == "/someotherpath" { 366 ics := []IssueComment{{ID: 2}} 367 b, err := json.Marshal(ics) 368 if err != nil { 369 t.Fatalf("Didn't expect error: %v", err) 370 } 371 fmt.Fprint(w, string(b)) 372 } else { 373 t.Errorf("Bad request path: %s", r.URL.Path) 374 } 375 })) 376 defer ts.Close() 377 c := getClient(ts.URL) 378 ics, err := c.ListIssueComments("k8s", "kuber", 15) 379 if err != nil { 380 t.Errorf("Didn't expect error: %v", err) 381 } else if len(ics) != 2 { 382 t.Errorf("Expected two issues, found %d: %v", len(ics), ics) 383 } else if ics[0].ID != 1 || ics[1].ID != 2 { 384 t.Errorf("Wrong issue IDs: %v", ics) 385 } 386 } 387 388 func TestAddLabel(t *testing.T) { 389 ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 390 if r.Method != http.MethodPost { 391 t.Errorf("Bad method: %s", r.Method) 392 } 393 if r.URL.Path != "/repos/k8s/kuber/issues/5/labels" { 394 t.Errorf("Bad request path: %s", r.URL.Path) 395 } 396 b, err := ioutil.ReadAll(r.Body) 397 if err != nil { 398 t.Fatalf("Could not read request body: %v", err) 399 } 400 var ls []string 401 if err := json.Unmarshal(b, &ls); err != nil { 402 t.Errorf("Could not unmarshal request: %v", err) 403 } else if len(ls) != 1 { 404 t.Errorf("Wrong length labels: %v", ls) 405 } else if ls[0] != "yay" { 406 t.Errorf("Wrong label: %s", ls[0]) 407 } 408 })) 409 defer ts.Close() 410 c := getClient(ts.URL) 411 if err := c.AddLabel("k8s", "kuber", 5, "yay"); err != nil { 412 t.Errorf("Didn't expect error: %v", err) 413 } 414 } 415 416 func TestRemoveLabel(t *testing.T) { 417 ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 418 if r.Method != http.MethodDelete { 419 t.Errorf("Bad method: %s", r.Method) 420 } 421 if r.URL.Path != "/repos/k8s/kuber/issues/5/labels/yay" { 422 t.Errorf("Bad request path: %s", r.URL.Path) 423 } 424 http.Error(w, "204 No Content", http.StatusNoContent) 425 })) 426 defer ts.Close() 427 c := getClient(ts.URL) 428 if err := c.RemoveLabel("k8s", "kuber", 5, "yay"); err != nil { 429 t.Errorf("Didn't expect error: %v", err) 430 } 431 } 432 433 func TestRemoveLabelFailsOnOtherThan404(t *testing.T) { 434 ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 435 if r.Method != http.MethodDelete { 436 t.Errorf("Bad method: %s", r.Method) 437 } 438 if r.URL.Path != "/repos/k8s/kuber/issues/5/labels/yay" { 439 t.Errorf("Bad request path: %s", r.URL.Path) 440 } 441 http.Error(w, "403 Forbidden", http.StatusForbidden) 442 })) 443 defer ts.Close() 444 c := getClient(ts.URL) 445 err := c.RemoveLabel("k8s", "kuber", 5, "yay") 446 if err == nil { 447 t.Errorf("Expected error but got none") 448 } 449 if _, ok := err.(*LabelNotFound); ok { 450 t.Fatalf("Expected error not to be a 404: %v", err) 451 } 452 } 453 454 func TestRemoveLabelNotFound(t *testing.T) { 455 ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 456 http.Error(w, `{"message": "Label does not exist"}`, 404) 457 })) 458 defer ts.Close() 459 c := getClient(ts.URL) 460 err := c.RemoveLabel("any", "old", 3, "label") 461 462 if err == nil { 463 t.Fatalf("RemoveLabel expected an error, got none") 464 } 465 466 if _, ok := err.(*LabelNotFound); !ok { 467 t.Fatalf("RemoveLabel expected LabelNotFound error, got %v", err) 468 } 469 } 470 471 func TestAssignIssue(t *testing.T) { 472 ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 473 if r.Method != http.MethodPost { 474 t.Errorf("Bad method: %s", r.Method) 475 } 476 if r.URL.Path != "/repos/k8s/kuber/issues/5/assignees" { 477 t.Errorf("Bad request path: %s", r.URL.Path) 478 } 479 b, err := ioutil.ReadAll(r.Body) 480 if err != nil { 481 t.Fatalf("Could not read request body: %v", err) 482 } 483 var ps map[string][]string 484 if err := json.Unmarshal(b, &ps); err != nil { 485 t.Errorf("Could not unmarshal request: %v", err) 486 } else if len(ps) != 1 { 487 t.Errorf("Wrong length patch: %v", ps) 488 } else if len(ps["assignees"]) == 3 { 489 if ps["assignees"][0] != "george" || ps["assignees"][1] != "jungle" || ps["assignees"][2] != "not-in-the-org" { 490 t.Errorf("Wrong assignees: %v", ps) 491 } 492 } else if len(ps["assignees"]) == 2 { 493 if ps["assignees"][0] != "george" || ps["assignees"][1] != "jungle" { 494 t.Errorf("Wrong assignees: %v", ps) 495 } 496 497 } else { 498 t.Errorf("Wrong assignees length: %v", ps) 499 } 500 w.WriteHeader(http.StatusCreated) 501 json.NewEncoder(w).Encode(Issue{ 502 Assignees: []User{{Login: "george"}, {Login: "jungle"}, {Login: "ignore-other"}}, 503 }) 504 })) 505 defer ts.Close() 506 c := getClient(ts.URL) 507 if err := c.AssignIssue("k8s", "kuber", 5, []string{"george", "jungle"}); err != nil { 508 t.Errorf("Unexpected error: %v", err) 509 } 510 if err := c.AssignIssue("k8s", "kuber", 5, []string{"george", "jungle", "not-in-the-org"}); err == nil { 511 t.Errorf("Expected an error") 512 } else if merr, ok := err.(MissingUsers); ok { 513 if len(merr.Users) != 1 || merr.Users[0] != "not-in-the-org" { 514 t.Errorf("Expected [not-in-the-org], not %v", merr.Users) 515 } 516 } else { 517 t.Errorf("Expected MissingUsers error") 518 } 519 } 520 521 func TestUnassignIssue(t *testing.T) { 522 ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 523 if r.Method != http.MethodDelete { 524 t.Errorf("Bad method: %s", r.Method) 525 } 526 if r.URL.Path != "/repos/k8s/kuber/issues/5/assignees" { 527 t.Errorf("Bad request path: %s", r.URL.Path) 528 } 529 b, err := ioutil.ReadAll(r.Body) 530 if err != nil { 531 t.Fatalf("Could not read request body: %v", err) 532 } 533 var ps map[string][]string 534 if err := json.Unmarshal(b, &ps); err != nil { 535 t.Errorf("Could not unmarshal request: %v", err) 536 } else if len(ps) != 1 { 537 t.Errorf("Wrong length patch: %v", ps) 538 } else if len(ps["assignees"]) == 3 { 539 if ps["assignees"][0] != "george" || ps["assignees"][1] != "jungle" || ps["assignees"][2] != "perma-assignee" { 540 t.Errorf("Wrong assignees: %v", ps) 541 } 542 } else if len(ps["assignees"]) == 2 { 543 if ps["assignees"][0] != "george" || ps["assignees"][1] != "jungle" { 544 t.Errorf("Wrong assignees: %v", ps) 545 } 546 547 } else { 548 t.Errorf("Wrong assignees length: %v", ps) 549 } 550 json.NewEncoder(w).Encode(Issue{ 551 Assignees: []User{{Login: "perma-assignee"}, {Login: "ignore-other"}}, 552 }) 553 })) 554 defer ts.Close() 555 c := getClient(ts.URL) 556 if err := c.UnassignIssue("k8s", "kuber", 5, []string{"george", "jungle"}); err != nil { 557 t.Errorf("Unexpected error: %v", err) 558 } 559 if err := c.UnassignIssue("k8s", "kuber", 5, []string{"george", "jungle", "perma-assignee"}); err == nil { 560 t.Errorf("Expected an error") 561 } else if merr, ok := err.(ExtraUsers); ok { 562 if len(merr.Users) != 1 || merr.Users[0] != "perma-assignee" { 563 t.Errorf("Expected [perma-assignee], not %v", merr.Users) 564 } 565 } else { 566 t.Errorf("Expected ExtraUsers error") 567 } 568 } 569 570 func TestReadPaginatedResults(t *testing.T) { 571 ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 572 if r.Method != http.MethodGet { 573 t.Errorf("Bad method: %s", r.Method) 574 } 575 if r.URL.Path == "/label/foo" { 576 objects := []Label{{Name: "foo"}} 577 b, err := json.Marshal(objects) 578 if err != nil { 579 t.Fatalf("Didn't expect error: %v", err) 580 } 581 w.Header().Set("Link", fmt.Sprintf(`<blorp>; rel="first", <https://%s/label/bar>; rel="next"`, r.Host)) 582 fmt.Fprint(w, string(b)) 583 } else if r.URL.Path == "/label/bar" { 584 objects := []Label{{Name: "bar"}} 585 b, err := json.Marshal(objects) 586 if err != nil { 587 t.Fatalf("Didn't expect error: %v", err) 588 } 589 fmt.Fprint(w, string(b)) 590 } else { 591 t.Errorf("Bad request path: %s", r.URL.Path) 592 } 593 })) 594 defer ts.Close() 595 c := getClient(ts.URL) 596 path := "/label/foo" 597 var labels []Label 598 err := c.readPaginatedResults( 599 path, 600 "", 601 func() interface{} { 602 return &[]Label{} 603 }, 604 func(obj interface{}) { 605 labels = append(labels, *(obj.(*[]Label))...) 606 }, 607 ) 608 if err != nil { 609 t.Errorf("Didn't expect error: %v", err) 610 } else if len(labels) != 2 { 611 t.Errorf("Expected two labels, found %d: %v", len(labels), labels) 612 } else if labels[0].Name != "foo" || labels[1].Name != "bar" { 613 t.Errorf("Wrong label names: %v", labels) 614 } 615 } 616 617 func TestListPullRequestComments(t *testing.T) { 618 ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 619 if r.Method != http.MethodGet { 620 t.Errorf("Bad method: %s", r.Method) 621 } 622 if r.URL.Path == "/repos/k8s/kuber/pulls/15/comments" { 623 prcs := []ReviewComment{{ID: 1}} 624 b, err := json.Marshal(prcs) 625 if err != nil { 626 t.Fatalf("Didn't expect error: %v", err) 627 } 628 w.Header().Set("Link", fmt.Sprintf(`<blorp>; rel="first", <https://%s/someotherpath>; rel="next"`, r.Host)) 629 fmt.Fprint(w, string(b)) 630 } else if r.URL.Path == "/someotherpath" { 631 prcs := []ReviewComment{{ID: 2}} 632 b, err := json.Marshal(prcs) 633 if err != nil { 634 t.Fatalf("Didn't expect error: %v", err) 635 } 636 fmt.Fprint(w, string(b)) 637 } else { 638 t.Errorf("Bad request path: %s", r.URL.Path) 639 } 640 })) 641 defer ts.Close() 642 c := getClient(ts.URL) 643 prcs, err := c.ListPullRequestComments("k8s", "kuber", 15) 644 if err != nil { 645 t.Errorf("Didn't expect error: %v", err) 646 } else if len(prcs) != 2 { 647 t.Errorf("Expected two comments, found %d: %v", len(prcs), prcs) 648 } else if prcs[0].ID != 1 || prcs[1].ID != 2 { 649 t.Errorf("Wrong issue IDs: %v", prcs) 650 } 651 } 652 653 func TestListReviews(t *testing.T) { 654 ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 655 if r.Method != http.MethodGet { 656 t.Errorf("Bad method: %s", r.Method) 657 } 658 if r.URL.Path == "/repos/k8s/kuber/pulls/15/reviews" { 659 reviews := []Review{{ID: 1}} 660 b, err := json.Marshal(reviews) 661 if err != nil { 662 t.Fatalf("Didn't expect error: %v", err) 663 } 664 w.Header().Set("Link", fmt.Sprintf(`<blorp>; rel="first", <https://%s/someotherpath>; rel="next"`, r.Host)) 665 fmt.Fprint(w, string(b)) 666 } else if r.URL.Path == "/someotherpath" { 667 reviews := []Review{{ID: 2}} 668 b, err := json.Marshal(reviews) 669 if err != nil { 670 t.Fatalf("Didn't expect error: %v", err) 671 } 672 fmt.Fprint(w, string(b)) 673 } else { 674 t.Errorf("Bad request path: %s", r.URL.Path) 675 } 676 })) 677 defer ts.Close() 678 c := getClient(ts.URL) 679 reviews, err := c.ListReviews("k8s", "kuber", 15) 680 if err != nil { 681 t.Errorf("Didn't expect error: %v", err) 682 } else if len(reviews) != 2 { 683 t.Errorf("Expected two reviews, found %d: %v", len(reviews), reviews) 684 } else if reviews[0].ID != 1 || reviews[1].ID != 2 { 685 t.Errorf("Wrong review IDs: %v", reviews) 686 } 687 } 688 689 func TestPrepareReviewersBody(t *testing.T) { 690 var tests = []struct { 691 name string 692 logins []string 693 expectedBody map[string][]string 694 }{ 695 { 696 name: "one reviewer", 697 logins: []string{"george"}, 698 expectedBody: map[string][]string{"reviewers": {"george"}}, 699 }, 700 { 701 name: "three reviewers", 702 logins: []string{"george", "jungle", "chimp"}, 703 expectedBody: map[string][]string{"reviewers": {"george", "jungle", "chimp"}}, 704 }, 705 { 706 name: "one team", 707 logins: []string{"kubernetes/sig-testing-misc"}, 708 expectedBody: map[string][]string{"team_reviewers": {"sig-testing-misc"}}, 709 }, 710 { 711 name: "two teams", 712 logins: []string{"kubernetes/sig-testing-misc", "kubernetes/sig-testing-bugs"}, 713 expectedBody: map[string][]string{"team_reviewers": {"sig-testing-misc", "sig-testing-bugs"}}, 714 }, 715 { 716 name: "one team not in org", 717 logins: []string{"kubernetes/sig-testing-misc", "other-org/sig-testing-bugs"}, 718 expectedBody: map[string][]string{"team_reviewers": {"sig-testing-misc"}}, 719 }, 720 { 721 name: "mixed single", 722 logins: []string{"george", "kubernetes/sig-testing-misc"}, 723 expectedBody: map[string][]string{"reviewers": {"george"}, "team_reviewers": {"sig-testing-misc"}}, 724 }, 725 { 726 name: "mixed multiple", 727 logins: []string{"george", "kubernetes/sig-testing-misc", "kubernetes/sig-testing-bugs", "jungle", "chimp"}, 728 expectedBody: map[string][]string{"reviewers": {"george", "jungle", "chimp"}, "team_reviewers": {"sig-testing-misc", "sig-testing-bugs"}}, 729 }, 730 } 731 for _, test := range tests { 732 body, _ := prepareReviewersBody(test.logins, "kubernetes") 733 if !reflect.DeepEqual(body, test.expectedBody) { 734 t.Errorf("%s: got %s instead of %s", test.name, body, test.expectedBody) 735 } 736 } 737 } 738 739 func TestRequestReview(t *testing.T) { 740 ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 741 if r.Method != http.MethodPost { 742 t.Errorf("Bad method: %s", r.Method) 743 } 744 if r.URL.Path != "/repos/k8s/kuber/pulls/5/requested_reviewers" { 745 t.Errorf("Bad request path: %s", r.URL.Path) 746 } 747 b, err := ioutil.ReadAll(r.Body) 748 if err != nil { 749 t.Fatalf("Could not read request body: %v", err) 750 } 751 var ps map[string][]string 752 if err := json.Unmarshal(b, &ps); err != nil { 753 t.Fatalf("Could not unmarshal request: %v", err) 754 } 755 if len(ps) < 1 || len(ps) > 2 { 756 t.Fatalf("Wrong length patch: %v", ps) 757 } 758 if sets.NewString(ps["reviewers"]...).Has("not-a-collaborator") { 759 w.WriteHeader(http.StatusUnprocessableEntity) 760 return 761 } 762 requestedReviewers := []User{} 763 for _, reviewers := range ps { 764 for _, reviewer := range reviewers { 765 requestedReviewers = append(requestedReviewers, User{Login: reviewer}) 766 } 767 } 768 w.WriteHeader(http.StatusCreated) 769 json.NewEncoder(w).Encode(PullRequest{ 770 RequestedReviewers: requestedReviewers, 771 }) 772 })) 773 defer ts.Close() 774 c := getClient(ts.URL) 775 if err := c.RequestReview("k8s", "kuber", 5, []string{"george", "jungle"}); err != nil { 776 t.Errorf("Unexpected error: %v", err) 777 } 778 if err := c.RequestReview("k8s", "kuber", 5, []string{"george", "jungle", "k8s/team1"}); err != nil { 779 t.Errorf("Unexpected error: %v", err) 780 } 781 if err := c.RequestReview("k8s", "kuber", 5, []string{"george", "jungle", "not-a-collaborator"}); err == nil { 782 t.Errorf("Expected an error") 783 } else if merr, ok := err.(MissingUsers); ok { 784 if len(merr.Users) != 1 || merr.Users[0] != "not-a-collaborator" { 785 t.Errorf("Expected [not-a-collaborator], not %v", merr.Users) 786 } 787 } else { 788 t.Errorf("Expected MissingUsers error") 789 } 790 if err := c.RequestReview("k8s", "kuber", 5, []string{"george", "jungle", "notk8s/team1"}); err == nil { 791 t.Errorf("Expected an error") 792 } else if merr, ok := err.(MissingUsers); ok { 793 if len(merr.Users) != 1 || merr.Users[0] != "notk8s/team1" { 794 t.Errorf("Expected [notk8s/team1], not %v", merr.Users) 795 } 796 } else { 797 t.Errorf("Expected MissingUsers error") 798 } 799 } 800 801 func TestUnrequestReview(t *testing.T) { 802 ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 803 if r.Method != http.MethodDelete { 804 t.Errorf("Bad method: %s", r.Method) 805 } 806 if r.URL.Path != "/repos/k8s/kuber/pulls/5/requested_reviewers" { 807 t.Errorf("Bad request path: %s", r.URL.Path) 808 } 809 b, err := ioutil.ReadAll(r.Body) 810 if err != nil { 811 t.Fatalf("Could not read request body: %v", err) 812 } 813 var ps map[string][]string 814 if err := json.Unmarshal(b, &ps); err != nil { 815 t.Errorf("Could not unmarshal request: %v", err) 816 } else if len(ps) != 1 { 817 t.Errorf("Wrong length patch: %v", ps) 818 } else if len(ps["reviewers"]) == 3 { 819 if ps["reviewers"][0] != "george" || ps["reviewers"][1] != "jungle" || ps["reviewers"][2] != "perma-reviewer" { 820 t.Errorf("Wrong reviewers: %v", ps) 821 } 822 } else if len(ps["reviewers"]) == 2 { 823 if ps["reviewers"][0] != "george" || ps["reviewers"][1] != "jungle" { 824 t.Errorf("Wrong reviewers: %v", ps) 825 } 826 } else { 827 t.Errorf("Wrong reviewers length: %v", ps) 828 } 829 json.NewEncoder(w).Encode(PullRequest{ 830 RequestedReviewers: []User{{Login: "perma-reviewer"}, {Login: "ignore-other"}}, 831 }) 832 })) 833 defer ts.Close() 834 c := getClient(ts.URL) 835 if err := c.UnrequestReview("k8s", "kuber", 5, []string{"george", "jungle"}); err != nil { 836 t.Errorf("Unexpected error: %v", err) 837 } 838 if err := c.UnrequestReview("k8s", "kuber", 5, []string{"george", "jungle", "perma-reviewer"}); err == nil { 839 t.Errorf("Expected an error") 840 } else if merr, ok := err.(ExtraUsers); ok { 841 if len(merr.Users) != 1 || merr.Users[0] != "perma-reviewer" { 842 t.Errorf("Expected [perma-reviewer], not %v", merr.Users) 843 } 844 } else { 845 t.Errorf("Expected ExtraUsers error") 846 } 847 } 848 849 func TestCloseIssue(t *testing.T) { 850 ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 851 if r.Method != http.MethodPatch { 852 t.Errorf("Bad method: %s", r.Method) 853 } 854 if r.URL.Path != "/repos/k8s/kuber/issues/5" { 855 t.Errorf("Bad request path: %s", r.URL.Path) 856 } 857 b, err := ioutil.ReadAll(r.Body) 858 if err != nil { 859 t.Fatalf("Could not read request body: %v", err) 860 } 861 var ps map[string]string 862 if err := json.Unmarshal(b, &ps); err != nil { 863 t.Errorf("Could not unmarshal request: %v", err) 864 } else if len(ps) != 1 { 865 t.Errorf("Wrong length patch: %v", ps) 866 } else if ps["state"] != "closed" { 867 t.Errorf("Wrong state: %s", ps["state"]) 868 } 869 })) 870 defer ts.Close() 871 c := getClient(ts.URL) 872 if err := c.CloseIssue("k8s", "kuber", 5); err != nil { 873 t.Errorf("Didn't expect error: %v", err) 874 } 875 } 876 877 func TestReopenIssue(t *testing.T) { 878 ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 879 if r.Method != http.MethodPatch { 880 t.Errorf("Bad method: %s", r.Method) 881 } 882 if r.URL.Path != "/repos/k8s/kuber/issues/5" { 883 t.Errorf("Bad request path: %s", r.URL.Path) 884 } 885 b, err := ioutil.ReadAll(r.Body) 886 if err != nil { 887 t.Fatalf("Could not read request body: %v", err) 888 } 889 var ps map[string]string 890 if err := json.Unmarshal(b, &ps); err != nil { 891 t.Errorf("Could not unmarshal request: %v", err) 892 } else if len(ps) != 1 { 893 t.Errorf("Wrong length patch: %v", ps) 894 } else if ps["state"] != "open" { 895 t.Errorf("Wrong state: %s", ps["state"]) 896 } 897 })) 898 defer ts.Close() 899 c := getClient(ts.URL) 900 if err := c.ReopenIssue("k8s", "kuber", 5); err != nil { 901 t.Errorf("Didn't expect error: %v", err) 902 } 903 } 904 905 func TestClosePR(t *testing.T) { 906 ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 907 if r.Method != http.MethodPatch { 908 t.Errorf("Bad method: %s", r.Method) 909 } 910 if r.URL.Path != "/repos/k8s/kuber/pulls/5" { 911 t.Errorf("Bad request path: %s", r.URL.Path) 912 } 913 b, err := ioutil.ReadAll(r.Body) 914 if err != nil { 915 t.Fatalf("Could not read request body: %v", err) 916 } 917 var ps map[string]string 918 if err := json.Unmarshal(b, &ps); err != nil { 919 t.Errorf("Could not unmarshal request: %v", err) 920 } else if len(ps) != 1 { 921 t.Errorf("Wrong length patch: %v", ps) 922 } else if ps["state"] != "closed" { 923 t.Errorf("Wrong state: %s", ps["state"]) 924 } 925 })) 926 defer ts.Close() 927 c := getClient(ts.URL) 928 if err := c.ClosePR("k8s", "kuber", 5); err != nil { 929 t.Errorf("Didn't expect error: %v", err) 930 } 931 } 932 933 func TestReopenPR(t *testing.T) { 934 ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 935 if r.Method != http.MethodPatch { 936 t.Errorf("Bad method: %s", r.Method) 937 } 938 if r.URL.Path != "/repos/k8s/kuber/pulls/5" { 939 t.Errorf("Bad request path: %s", r.URL.Path) 940 } 941 b, err := ioutil.ReadAll(r.Body) 942 if err != nil { 943 t.Fatalf("Could not read request body: %v", err) 944 } 945 var ps map[string]string 946 if err := json.Unmarshal(b, &ps); err != nil { 947 t.Errorf("Could not unmarshal request: %v", err) 948 } else if len(ps) != 1 { 949 t.Errorf("Wrong length patch: %v", ps) 950 } else if ps["state"] != "open" { 951 t.Errorf("Wrong state: %s", ps["state"]) 952 } 953 })) 954 defer ts.Close() 955 c := getClient(ts.URL) 956 if err := c.ReopenPR("k8s", "kuber", 5); err != nil { 957 t.Errorf("Didn't expect error: %v", err) 958 } 959 } 960 961 func TestFindIssues(t *testing.T) { 962 cases := []struct { 963 name string 964 sort bool 965 order bool 966 }{ 967 { 968 name: "simple query", 969 }, 970 { 971 name: "sort no order", 972 sort: true, 973 }, 974 { 975 name: "sort and order", 976 sort: true, 977 order: true, 978 }, 979 } 980 981 issueNum := 5 982 ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 983 if r.Method != http.MethodGet { 984 t.Errorf("Bad method: %s", r.Method) 985 } 986 if r.URL.Path != "/search/issues" { 987 t.Errorf("Bad request path: %s", r.URL.Path) 988 } 989 issueList := IssuesSearchResult{ 990 Total: 1, 991 Issues: []Issue{ 992 { 993 Number: issueNum, 994 Title: r.URL.RawQuery, 995 }, 996 }, 997 } 998 b, err := json.Marshal(&issueList) 999 if err != nil { 1000 t.Fatalf("Didn't expect error: %v", err) 1001 } 1002 fmt.Fprint(w, string(b)) 1003 })) 1004 defer ts.Close() 1005 c := getClient(ts.URL) 1006 1007 for _, tc := range cases { 1008 var result []Issue 1009 var err error 1010 sort := "" 1011 if tc.sort { 1012 sort = "sort-strategy" 1013 } 1014 if result, err = c.FindIssues("commit_hash", sort, tc.order); err != nil { 1015 t.Errorf("%s: didn't expect error: %v", tc.name, err) 1016 } 1017 if len(result) != 1 { 1018 t.Errorf("%s: unexpected number of results: %v", tc.name, len(result)) 1019 } 1020 if result[0].Number != issueNum { 1021 t.Errorf("%s: expected issue number %+v, got %+v", tc.name, issueNum, result[0].Number) 1022 } 1023 if tc.sort && !strings.Contains(result[0].Title, "sort="+sort) { 1024 t.Errorf("%s: missing sort=%s from query: %s", tc.name, sort, result[0].Title) 1025 } 1026 if tc.order && !strings.Contains(result[0].Title, "order=asc") { 1027 t.Errorf("%s: missing order=asc from query: %s", tc.name, result[0].Title) 1028 } 1029 } 1030 } 1031 1032 func TestGetFile(t *testing.T) { 1033 ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 1034 if r.Method != http.MethodGet { 1035 t.Errorf("Bad method: %s", r.Method) 1036 } 1037 if r.URL.Path != "/repos/k8s/kuber/contents/foo.txt" { 1038 t.Errorf("Bad request path: %s", r.URL.Path) 1039 } 1040 if r.URL.RawQuery != "" { 1041 t.Errorf("Bad request query: %s", r.URL.RawQuery) 1042 } 1043 c := &Content{ 1044 Content: base64.StdEncoding.EncodeToString([]byte("abcde")), 1045 } 1046 b, err := json.Marshal(&c) 1047 if err != nil { 1048 t.Fatalf("Didn't expect error: %v", err) 1049 } 1050 fmt.Fprint(w, string(b)) 1051 })) 1052 defer ts.Close() 1053 c := getClient(ts.URL) 1054 if content, err := c.GetFile("k8s", "kuber", "foo.txt", ""); err != nil { 1055 t.Errorf("Didn't expect error: %v", err) 1056 } else if string(content) != "abcde" { 1057 t.Errorf("Wrong content -- expect: abcde, got: %s", string(content)) 1058 } 1059 } 1060 1061 func TestGetFileRef(t *testing.T) { 1062 ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 1063 if r.Method != http.MethodGet { 1064 t.Errorf("Bad method: %s", r.Method) 1065 } 1066 if r.URL.Path != "/repos/k8s/kuber/contents/foo/bar.txt" { 1067 t.Errorf("Bad request path: %s", r.URL) 1068 } 1069 if r.URL.RawQuery != "ref=12345" { 1070 t.Errorf("Bad request query: %s", r.URL.RawQuery) 1071 } 1072 c := &Content{ 1073 Content: base64.StdEncoding.EncodeToString([]byte("abcde")), 1074 } 1075 b, err := json.Marshal(&c) 1076 if err != nil { 1077 t.Fatalf("Didn't expect error: %v", err) 1078 } 1079 fmt.Fprint(w, string(b)) 1080 })) 1081 defer ts.Close() 1082 c := getClient(ts.URL) 1083 if content, err := c.GetFile("k8s", "kuber", "foo/bar.txt", "12345"); err != nil { 1084 t.Errorf("Didn't expect error: %v", err) 1085 } else if string(content) != "abcde" { 1086 t.Errorf("Wrong content -- expect: abcde, got: %s", string(content)) 1087 } 1088 } 1089 1090 // TestGetLabels tests both GetRepoLabels and GetIssueLabels. 1091 func TestGetLabels(t *testing.T) { 1092 ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 1093 if r.Method != http.MethodGet { 1094 t.Errorf("Bad method: %s", r.Method) 1095 } 1096 var labels []Label 1097 switch r.URL.Path { 1098 case "/repos/k8s/kuber/issues/5/labels": 1099 labels = []Label{{Name: "issue-label"}} 1100 w.Header().Set("Link", fmt.Sprintf(`<blorp>; rel="first", <https://%s/someotherpath>; rel="next"`, r.Host)) 1101 case "/repos/k8s/kuber/labels": 1102 labels = []Label{{Name: "repo-label"}} 1103 w.Header().Set("Link", fmt.Sprintf(`<blorp>; rel="first", <https://%s/someotherpath>; rel="next"`, r.Host)) 1104 case "/someotherpath": 1105 labels = []Label{{Name: "label2"}} 1106 default: 1107 t.Errorf("Bad request path: %s", r.URL.Path) 1108 return 1109 } 1110 b, err := json.Marshal(labels) 1111 if err != nil { 1112 t.Fatalf("Didn't expect error: %v", err) 1113 } 1114 fmt.Fprint(w, string(b)) 1115 })) 1116 defer ts.Close() 1117 c := getClient(ts.URL) 1118 labels, err := c.GetIssueLabels("k8s", "kuber", 5) 1119 if err != nil { 1120 t.Errorf("Didn't expect error: %v", err) 1121 } else if len(labels) != 2 { 1122 t.Errorf("Expected two labels, found %d: %v", len(labels), labels) 1123 } else if labels[0].Name != "issue-label" || labels[1].Name != "label2" { 1124 t.Errorf("Wrong label names: %v", labels) 1125 } 1126 1127 labels, err = c.GetRepoLabels("k8s", "kuber") 1128 if err != nil { 1129 t.Errorf("Didn't expect error: %v", err) 1130 } else if len(labels) != 2 { 1131 t.Errorf("Expected two labels, found %d: %v", len(labels), labels) 1132 } else if labels[0].Name != "repo-label" || labels[1].Name != "label2" { 1133 t.Errorf("Wrong label names: %v", labels) 1134 } 1135 } 1136 1137 func simpleTestServer(t *testing.T, path string, v interface{}) *httptest.Server { 1138 return httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 1139 if r.URL.Path == path { 1140 b, err := json.Marshal(v) 1141 if err != nil { 1142 t.Fatalf("Didn't expect error: %v", err) 1143 } 1144 fmt.Fprint(w, string(b)) 1145 } else { 1146 t.Fatalf("Bad request path: %s", r.URL.Path) 1147 } 1148 })) 1149 } 1150 1151 func TestListTeams(t *testing.T) { 1152 ts := simpleTestServer(t, "/orgs/foo/teams", []Team{{ID: 1}}) 1153 defer ts.Close() 1154 c := getClient(ts.URL) 1155 teams, err := c.ListTeams("foo") 1156 if err != nil { 1157 t.Errorf("Didn't expect error: %v", err) 1158 } else if len(teams) != 1 { 1159 t.Errorf("Expected one team, found %d: %v", len(teams), teams) 1160 } else if teams[0].ID != 1 { 1161 t.Errorf("Wrong team names: %v", teams) 1162 } 1163 } 1164 1165 func TestCreateTeam(t *testing.T) { 1166 ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 1167 if r.Method != http.MethodPost { 1168 t.Errorf("Bad method: %s", r.Method) 1169 } 1170 if r.URL.Path != "/orgs/foo/teams" { 1171 t.Errorf("Bad request path: %s", r.URL.Path) 1172 } 1173 b, err := ioutil.ReadAll(r.Body) 1174 if err != nil { 1175 t.Fatalf("Could not read request body: %v", err) 1176 } 1177 var team Team 1178 switch err := json.Unmarshal(b, &team); { 1179 case err != nil: 1180 t.Errorf("Could not unmarshal request: %v", err) 1181 case team.Name == "": 1182 t.Errorf("client should reject empty names") 1183 case team.Name != "frobber": 1184 t.Errorf("Bad name: %s", team.Name) 1185 } 1186 team.Name = "hello" 1187 team.Description = "world" 1188 team.Privacy = "special" 1189 b, err = json.Marshal(team) 1190 if err != nil { 1191 t.Fatalf("Didn't expect error: %v", err) 1192 } 1193 w.WriteHeader(http.StatusCreated) // 201 1194 fmt.Fprint(w, string(b)) 1195 })) 1196 defer ts.Close() 1197 c := getClient(ts.URL) 1198 if _, err := c.CreateTeam("foo", Team{Name: ""}); err == nil { 1199 t.Errorf("client should reject empty name") 1200 } 1201 switch team, err := c.CreateTeam("foo", Team{Name: "frobber"}); { 1202 case err != nil: 1203 t.Errorf("unexpected error: %v", err) 1204 case team.Name != "hello": 1205 t.Errorf("bad name: %s", team.Name) 1206 case team.Description != "world": 1207 t.Errorf("bad description: %s", team.Description) 1208 case team.Privacy != "special": 1209 t.Errorf("bad privacy: %s", team.Privacy) 1210 } 1211 } 1212 1213 func TestEditTeam(t *testing.T) { 1214 ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 1215 if r.Method != http.MethodPatch { 1216 t.Errorf("Bad method: %s", r.Method) 1217 } 1218 if r.URL.Path != "/teams/63" { 1219 t.Errorf("Bad request path: %s", r.URL.Path) 1220 } 1221 b, err := ioutil.ReadAll(r.Body) 1222 if err != nil { 1223 t.Fatalf("Could not read request body: %v", err) 1224 } 1225 var team Team 1226 switch err := json.Unmarshal(b, &team); { 1227 case err != nil: 1228 t.Errorf("Could not unmarshal request: %v", err) 1229 case team.Name == "": 1230 t.Errorf("Bad name: %s", team.Name) 1231 } 1232 team.Name = "hello" 1233 team.Description = "world" 1234 team.Privacy = "special" 1235 b, err = json.Marshal(team) 1236 if err != nil { 1237 t.Fatalf("Didn't expect error: %v", err) 1238 } 1239 w.WriteHeader(http.StatusCreated) // 201 1240 fmt.Fprint(w, string(b)) 1241 })) 1242 defer ts.Close() 1243 c := getClient(ts.URL) 1244 if _, err := c.EditTeam(Team{ID: 0, Name: "frobber"}); err == nil { 1245 t.Errorf("client should reject id 0") 1246 } 1247 switch team, err := c.EditTeam(Team{ID: 63, Name: "frobber"}); { 1248 case err != nil: 1249 t.Errorf("unexpected error: %v", err) 1250 case team.Name != "hello": 1251 t.Errorf("bad name: %s", team.Name) 1252 case team.Description != "world": 1253 t.Errorf("bad description: %s", team.Description) 1254 case team.Privacy != "special": 1255 t.Errorf("bad privacy: %s", team.Privacy) 1256 } 1257 } 1258 1259 func TestListTeamMembers(t *testing.T) { 1260 ts := simpleTestServer(t, "/teams/1/members", []TeamMember{{Login: "foo"}}) 1261 defer ts.Close() 1262 c := getClient(ts.URL) 1263 teamMembers, err := c.ListTeamMembers(1, RoleAll) 1264 if err != nil { 1265 t.Errorf("Didn't expect error: %v", err) 1266 } else if len(teamMembers) != 1 { 1267 t.Errorf("Expected one team member, found %d: %v", len(teamMembers), teamMembers) 1268 } else if teamMembers[0].Login != "foo" { 1269 t.Errorf("Wrong team names: %v", teamMembers) 1270 } 1271 } 1272 1273 func TestIsCollaborator(t *testing.T) { 1274 ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 1275 if r.Method != http.MethodGet { 1276 t.Errorf("Bad method: %s", r.Method) 1277 } 1278 if r.URL.Path != "/repos/k8s/kuber/collaborators/person" { 1279 t.Errorf("Bad request path: %s", r.URL.Path) 1280 } 1281 http.Error(w, "204 No Content", http.StatusNoContent) 1282 })) 1283 defer ts.Close() 1284 c := getClient(ts.URL) 1285 mem, err := c.IsCollaborator("k8s", "kuber", "person") 1286 if err != nil { 1287 t.Errorf("Didn't expect error: %v", err) 1288 } else if !mem { 1289 t.Errorf("Should be member.") 1290 } 1291 } 1292 1293 func TestListCollaborators(t *testing.T) { 1294 ts := simpleTestServer(t, "/repos/org/repo/collaborators", []User{{Login: "foo"}, {Login: "bar"}}) 1295 defer ts.Close() 1296 c := getClient(ts.URL) 1297 users, err := c.ListCollaborators("org", "repo") 1298 if err != nil { 1299 t.Errorf("Didn't expect error: %v", err) 1300 } else if len(users) != 2 { 1301 t.Errorf("Expected two users, found %d: %v", len(users), users) 1302 return 1303 } 1304 if users[0].Login != "foo" { 1305 t.Errorf("Wrong user login for index 0: %v", users[0]) 1306 } 1307 if users[1].Login != "bar" { 1308 t.Errorf("Wrong user login for index 1: %v", users[1]) 1309 } 1310 } 1311 1312 func TestListIssueEvents(t *testing.T) { 1313 ts := simpleTestServer( 1314 t, 1315 "/repos/org/repo/issues/1/events", 1316 []ListedIssueEvent{ 1317 {Event: IssueActionLabeled}, 1318 {Event: IssueActionClosed}, 1319 }, 1320 ) 1321 defer ts.Close() 1322 c := getClient(ts.URL) 1323 events, err := c.ListIssueEvents("org", "repo", 1) 1324 if err != nil { 1325 t.Errorf("Didn't expect error: %v", err) 1326 } else if len(events) != 2 { 1327 t.Errorf("Expected two events, found %d: %v", len(events), events) 1328 return 1329 } 1330 if events[0].Event != IssueActionLabeled { 1331 t.Errorf("Wrong event for index 0: %v", events[0]) 1332 } 1333 if events[1].Event != IssueActionClosed { 1334 t.Errorf("Wrong event for index 1: %v", events[1]) 1335 } 1336 } 1337 1338 func TestThrottle(t *testing.T) { 1339 ts := simpleTestServer( 1340 t, 1341 "/repos/org/repo/issues/1/events", 1342 []ListedIssueEvent{ 1343 {Event: IssueActionOpened}, 1344 {Event: IssueActionClosed}, 1345 }, 1346 ) 1347 c := getClient(ts.URL) 1348 c.Throttle(1, 2) 1349 if c.client != &c.throttle { 1350 t.Errorf("Bad client %v, expecting %v", c.client, &c.throttle) 1351 } 1352 if len(c.throttle.throttle) != 2 { 1353 t.Fatalf("Expected two items in throttle channel, found %d", len(c.throttle.throttle)) 1354 } 1355 if cap(c.throttle.throttle) != 2 { 1356 t.Fatalf("Expected throttle channel capacity of two, found %d", cap(c.throttle.throttle)) 1357 } 1358 events, err := c.ListIssueEvents("org", "repo", 1) 1359 if err != nil { 1360 t.Errorf("Unexpected error: %v", err) 1361 } 1362 if len(events) != 2 { 1363 t.Errorf("Expected two events, found %d: %v", len(events), events) 1364 } 1365 if len(c.throttle.throttle) != 1 { 1366 t.Errorf("Expected one item in throttle channel, found %d", len(c.throttle.throttle)) 1367 } 1368 ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) 1369 go func() { 1370 if _, err := c.ListIssueEvents("org", "repo", 1); err != nil { 1371 t.Errorf("Unexpected error: %v", err) 1372 } 1373 if _, err := c.ListIssueEvents("org", "repo", 1); err != nil { 1374 t.Errorf("Unexpected error: %v", err) 1375 } 1376 cancel() 1377 }() 1378 slowed := false 1379 for ctx.Err() == nil { 1380 // Wait for the client to get throttled 1381 if atomic.LoadInt32(&c.throttle.slow) == 0 { 1382 continue 1383 } 1384 // Throttled, now add to the channel 1385 slowed = true 1386 select { 1387 case c.throttle.throttle <- time.Now(): // Add items to the channel 1388 case <-ctx.Done(): 1389 } 1390 } 1391 if !slowed { 1392 t.Errorf("Never throttled") 1393 } 1394 if err := ctx.Err(); err != context.Canceled { 1395 t.Errorf("Expected context cancellation did not happen: %v", err) 1396 } 1397 } 1398 1399 func TestGetBranches(t *testing.T) { 1400 ts := simpleTestServer(t, "/repos/org/repo/branches", []Branch{ 1401 {Name: "master", Protected: false}, 1402 {Name: "release-3.7", Protected: true}, 1403 }) 1404 defer ts.Close() 1405 c := getClient(ts.URL) 1406 branches, err := c.GetBranches("org", "repo", true) 1407 if err != nil { 1408 t.Errorf("Unexpected error: %v", err) 1409 } else if len(branches) != 2 { 1410 t.Errorf("Expected two branches, found %d, %v", len(branches), branches) 1411 return 1412 } 1413 switch { 1414 case branches[0].Name != "master": 1415 t.Errorf("Wrong branch name for index 0: %v", branches[0]) 1416 case branches[1].Name != "release-3.7": 1417 t.Errorf("Wrong branch name for index 1: %v", branches[1]) 1418 case branches[1].Protected == false: 1419 t.Errorf("Wrong branch protection for index 1: %v", branches[1]) 1420 } 1421 } 1422 1423 func TestRemoveBranchProtection(t *testing.T) { 1424 ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 1425 if r.Method != http.MethodDelete { 1426 t.Errorf("Bad method: %s", r.Method) 1427 } 1428 if r.URL.Path != "/repos/org/repo/branches/master/protection" { 1429 t.Errorf("Bad request path: %s", r.URL.Path) 1430 } 1431 http.Error(w, "204 No Content", http.StatusNoContent) 1432 })) 1433 defer ts.Close() 1434 c := getClient(ts.URL) 1435 if err := c.RemoveBranchProtection("org", "repo", "master"); err != nil { 1436 t.Errorf("Unexpected error: %v", err) 1437 } 1438 } 1439 1440 func TestUpdateBranchProtection(t *testing.T) { 1441 cases := []struct { 1442 name string 1443 // TODO(fejta): expand beyond contexts/pushers 1444 contexts []string 1445 pushers []string 1446 err bool 1447 }{ 1448 { 1449 name: "both", 1450 contexts: []string{"foo-pr-test", "other"}, 1451 pushers: []string{"movers", "awesome-team", "shakers"}, 1452 err: false, 1453 }, 1454 } 1455 1456 for _, tc := range cases { 1457 ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 1458 if r.Method != http.MethodPut { 1459 t.Errorf("Bad method: %s", r.Method) 1460 } 1461 if r.URL.Path != "/repos/org/repo/branches/master/protection" { 1462 t.Errorf("Bad request path: %s", r.URL.Path) 1463 } 1464 b, err := ioutil.ReadAll(r.Body) 1465 if err != nil { 1466 t.Fatalf("Could not read request body: %v", err) 1467 } 1468 var bpr BranchProtectionRequest 1469 if err := json.Unmarshal(b, &bpr); err != nil { 1470 t.Errorf("Could not unmarshal request: %v", err) 1471 } 1472 switch { 1473 case bpr.Restrictions != nil && bpr.Restrictions.Teams == nil: 1474 t.Errorf("Teams unset") 1475 case len(bpr.RequiredStatusChecks.Contexts) != len(tc.contexts): 1476 t.Errorf("Bad contexts: %v", bpr.RequiredStatusChecks.Contexts) 1477 case len(*bpr.Restrictions.Teams) != len(tc.pushers): 1478 t.Errorf("Bad teams: %v", *bpr.Restrictions.Teams) 1479 default: 1480 mc := map[string]bool{} 1481 for _, k := range tc.contexts { 1482 mc[k] = true 1483 } 1484 var missing []string 1485 for _, k := range bpr.RequiredStatusChecks.Contexts { 1486 if mc[k] != true { 1487 missing = append(missing, k) 1488 } 1489 } 1490 if n := len(missing); n > 0 { 1491 t.Errorf("%s: missing %d required contexts: %v", tc.name, n, missing) 1492 } 1493 mp := map[string]bool{} 1494 for _, k := range tc.pushers { 1495 mp[k] = true 1496 } 1497 missing = nil 1498 for _, k := range *bpr.Restrictions.Teams { 1499 if mp[k] != true { 1500 missing = append(missing, k) 1501 } 1502 } 1503 if n := len(missing); n > 0 { 1504 t.Errorf("%s: missing %d pushers: %v", tc.name, n, missing) 1505 } 1506 } 1507 http.Error(w, "200 OK", http.StatusOK) 1508 })) 1509 defer ts.Close() 1510 c := getClient(ts.URL) 1511 1512 err := c.UpdateBranchProtection("org", "repo", "master", BranchProtectionRequest{ 1513 RequiredStatusChecks: &RequiredStatusChecks{ 1514 Contexts: tc.contexts, 1515 }, 1516 Restrictions: &Restrictions{ 1517 Teams: &tc.pushers, 1518 }, 1519 }) 1520 if tc.err && err == nil { 1521 t.Errorf("%s: expected error failed to occur", tc.name) 1522 } 1523 if !tc.err && err != nil { 1524 t.Errorf("%s: received unexpected error: %v", tc.name, err) 1525 } 1526 } 1527 } 1528 1529 func TestClearMilestone(t *testing.T) { 1530 ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 1531 if r.Method != http.MethodPatch { 1532 t.Errorf("Bad method: %s", r.Method) 1533 } 1534 if r.URL.Path != "/repos/k8s/kuber/issues/5" { 1535 t.Errorf("Bad request path: %s", r.URL.Path) 1536 } 1537 b, err := ioutil.ReadAll(r.Body) 1538 if err != nil { 1539 t.Fatalf("Could not read request body: %v", err) 1540 } 1541 var issue Issue 1542 if err := json.Unmarshal(b, &issue); err != nil { 1543 t.Errorf("Could not unmarshal request: %v", err) 1544 } else if issue.Milestone.Title != "" { 1545 t.Errorf("Milestone title not empty: %v", issue.Milestone.Title) 1546 } 1547 })) 1548 defer ts.Close() 1549 c := getClient(ts.URL) 1550 if err := c.ClearMilestone("k8s", "kuber", 5); err != nil { 1551 t.Errorf("Didn't expect error: %v", err) 1552 } 1553 } 1554 1555 func TestSetMilestone(t *testing.T) { 1556 newMilestone := 42 1557 ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 1558 if r.Method != http.MethodPatch { 1559 t.Errorf("Bad method: %s", r.Method) 1560 } 1561 if r.URL.Path != "/repos/k8s/kuber/issues/5" { 1562 t.Errorf("Bad request path: %s", r.URL.Path) 1563 } 1564 b, err := ioutil.ReadAll(r.Body) 1565 if err != nil { 1566 t.Fatalf("Could not read request body: %v", err) 1567 } 1568 var issue struct { 1569 Milestone *int `json:"milestone,omitempty"` 1570 } 1571 if err := json.Unmarshal(b, &issue); err != nil { 1572 t.Fatalf("Could not unmarshal request: %v", err) 1573 } 1574 if issue.Milestone == nil { 1575 t.Fatal("Milestone was not set.") 1576 } 1577 if *issue.Milestone != newMilestone { 1578 t.Errorf("Expected milestone to be set to %d, but got %d.", newMilestone, *issue.Milestone) 1579 } 1580 })) 1581 defer ts.Close() 1582 c := getClient(ts.URL) 1583 if err := c.SetMilestone("k8s", "kuber", 5, newMilestone); err != nil { 1584 t.Errorf("Didn't expect error: %v", err) 1585 } 1586 } 1587 1588 func TestListMilestones(t *testing.T) { 1589 ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 1590 if r.Method != http.MethodGet { 1591 t.Errorf("Bad method: %s", r.Method) 1592 } 1593 if r.URL.Path != "/repos/k8s/kuber/milestones" { 1594 t.Errorf("Bad request path: %s", r.URL.Path) 1595 } 1596 })) 1597 defer ts.Close() 1598 c := getClient(ts.URL) 1599 if err, _ := c.ListMilestones("k8s", "kuber"); err != nil { 1600 t.Errorf("Didn't expect error: %v", err) 1601 } 1602 }