github.com/pf-qiu/concourse/v6@v6.7.3-0.20201207032516-1f455d73275f/atc/api/teams_test.go (about) 1 package api_test 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "errors" 7 "fmt" 8 "io/ioutil" 9 "net/http" 10 "time" 11 12 "github.com/pf-qiu/concourse/v6/atc" 13 "github.com/pf-qiu/concourse/v6/atc/db" 14 "github.com/pf-qiu/concourse/v6/atc/db/dbfakes" 15 . "github.com/pf-qiu/concourse/v6/atc/testhelpers" 16 . "github.com/onsi/ginkgo" 17 . "github.com/onsi/gomega" 18 ) 19 20 func jsonEncode(object interface{}) *bytes.Buffer { 21 reqPayload, err := json.Marshal(object) 22 Expect(err).NotTo(HaveOccurred()) 23 24 return bytes.NewBuffer(reqPayload) 25 } 26 27 var _ = Describe("Teams API", func() { 28 var ( 29 fakeTeam *dbfakes.FakeTeam 30 ) 31 32 BeforeEach(func() { 33 fakeTeam = new(dbfakes.FakeTeam) 34 }) 35 36 Describe("GET /api/v1/teams", func() { 37 var ( 38 response *http.Response 39 fakeTeamOne *dbfakes.FakeTeam 40 fakeTeamTwo *dbfakes.FakeTeam 41 fakeTeamThree *dbfakes.FakeTeam 42 teamNames []string 43 ) 44 45 JustBeforeEach(func() { 46 path := fmt.Sprintf("%s/api/v1/teams", server.URL) 47 48 request, err := http.NewRequest("GET", path, nil) 49 Expect(err).NotTo(HaveOccurred()) 50 51 response, err = client.Do(request) 52 Expect(err).NotTo(HaveOccurred()) 53 }) 54 55 BeforeEach(func() { 56 fakeTeamOne = new(dbfakes.FakeTeam) 57 fakeTeamTwo = new(dbfakes.FakeTeam) 58 fakeTeamThree = new(dbfakes.FakeTeam) 59 60 teamNames = []string{"avengers", "aliens", "predators"} 61 62 fakeTeamOne.IDReturns(5) 63 fakeTeamOne.NameReturns(teamNames[0]) 64 fakeTeamOne.AuthReturns(atc.TeamAuth{ 65 "owner": map[string][]string{ 66 "groups": []string{}, "users": []string{"local:username"}, 67 }, 68 }) 69 70 fakeTeamTwo.IDReturns(9) 71 fakeTeamTwo.NameReturns(teamNames[1]) 72 fakeTeamTwo.AuthReturns(atc.TeamAuth{ 73 "owner": map[string][]string{ 74 "groups": []string{}, "users": []string{"local:username"}, 75 }, 76 }) 77 78 fakeTeamThree.IDReturns(22) 79 fakeTeamThree.NameReturns(teamNames[2]) 80 fakeTeamThree.AuthReturns(atc.TeamAuth{ 81 "owner": map[string][]string{ 82 "groups": []string{}, "users": []string{"local:username"}, 83 }, 84 }) 85 86 fakeAccess.IsAuthorizedReturnsOnCall(0, true) 87 fakeAccess.IsAuthorizedReturnsOnCall(1, false) 88 fakeAccess.IsAuthorizedReturnsOnCall(2, true) 89 }) 90 91 Context("when the database call succeeds", func() { 92 BeforeEach(func() { 93 dbTeamFactory.GetTeamsReturns([]db.Team{fakeTeamOne, fakeTeamTwo, fakeTeamThree}, nil) 94 }) 95 96 It("should return the teams the user is authorized for", func() { 97 body, err := ioutil.ReadAll(response.Body) 98 Expect(err).NotTo(HaveOccurred()) 99 100 Expect(body).To(MatchJSON(`[ 101 { 102 "id": 5, 103 "name": "avengers", 104 "auth": { "owner":{"users":["local:username"],"groups":[]}} 105 }, 106 { 107 "id": 22, 108 "name": "predators", 109 "auth": { "owner":{"users":["local:username"],"groups":[]}} 110 } 111 ]`)) 112 }) 113 }) 114 115 Context("when the database call returns an error", func() { 116 var disaster error 117 118 BeforeEach(func() { 119 disaster = errors.New("some error") 120 dbTeamFactory.GetTeamsReturns(nil, disaster) 121 }) 122 123 It("returns 500 Internal Server Error", func() { 124 Expect(response.StatusCode).To(Equal(http.StatusInternalServerError)) 125 }) 126 }) 127 }) 128 129 Describe("GET /api/v1/teams/:team_name", func() { 130 var response *http.Response 131 var fakeTeam *dbfakes.FakeTeam 132 133 BeforeEach(func() { 134 fakeTeam = new(dbfakes.FakeTeam) 135 fakeTeam.IDReturns(1) 136 fakeTeam.NameReturns("a-team") 137 fakeTeam.AuthReturns(atc.TeamAuth{ 138 "owner": map[string][]string{ 139 "groups": {}, "users": {"local:username"}, 140 }, 141 }) 142 }) 143 144 JustBeforeEach(func() { 145 req, err := http.NewRequest("GET", fmt.Sprintf("%s/api/v1/teams/a-team", server.URL), nil) 146 Expect(err).NotTo(HaveOccurred()) 147 148 req.Header.Set("Content-Type", "application/json") 149 150 response, err = client.Do(req) 151 Expect(err).NotTo(HaveOccurred()) 152 }) 153 154 Context("when not authenticated and not admin", func() { 155 BeforeEach(func() { 156 fakeAccess.IsAuthorizedReturns(false) 157 fakeAccess.IsAdminReturns(false) 158 }) 159 160 It("returns 401", func() { 161 Expect(response.StatusCode).To(Equal(http.StatusUnauthorized)) 162 }) 163 }) 164 165 Context("when not authenticated to specified team, but have admin authority", func() { 166 BeforeEach(func() { 167 dbTeamFactory.FindTeamReturns(fakeTeam, true, nil) 168 fakeAccess.IsAuthenticatedReturns(true) 169 fakeAccess.IsAdminReturns(true) 170 fakeAccess.IsAuthorizedReturns(false) 171 }) 172 173 It("returns 200 ok", func() { 174 Expect(response.StatusCode).To(Equal(http.StatusOK)) 175 }) 176 177 It("returns application/json", func() { 178 expectedHeaderEntries := map[string]string{ 179 "Content-Type": "application/json", 180 } 181 Expect(response).Should(IncludeHeaderEntries(expectedHeaderEntries)) 182 }) 183 184 It("returns a team JSON", func() { 185 body, err := ioutil.ReadAll(response.Body) 186 Expect(err).NotTo(HaveOccurred()) 187 188 Expect(body).To(MatchJSON(` 189 { 190 "id": 1, 191 "name": "a-team", 192 "auth": { 193 "owner": { 194 "groups": [], 195 "users": [ 196 "local:username" 197 ] 198 } 199 } 200 }`)) 201 }) 202 }) 203 204 Context("when authenticated to specified team", func() { 205 BeforeEach(func() { 206 dbTeamFactory.FindTeamReturns(fakeTeam, true, nil) 207 fakeAccess.IsAuthenticatedReturns(true) 208 fakeAccess.IsAdminReturns(false) 209 fakeAccess.IsAuthorizedReturns(true) 210 }) 211 212 It("returns 200 ok", func() { 213 Expect(response.StatusCode).To(Equal(http.StatusOK)) 214 }) 215 216 It("returns application/json", func() { 217 expectedHeaderEntries := map[string]string{ 218 "Content-Type": "application/json", 219 } 220 Expect(response).Should(IncludeHeaderEntries(expectedHeaderEntries)) 221 }) 222 223 It("returns a team JSON", func() { 224 body, err := ioutil.ReadAll(response.Body) 225 Expect(err).NotTo(HaveOccurred()) 226 227 Expect(body).To(MatchJSON(` 228 { 229 "id": 1, 230 "name": "a-team", 231 "auth": { 232 "owner": { 233 "groups": [], 234 "users": [ 235 "local:username" 236 ] 237 } 238 } 239 }`)) 240 }) 241 }) 242 243 Context("when authenticated as another team", func() { 244 BeforeEach(func() { 245 dbTeamFactory.FindTeamReturns(fakeTeam, true, nil) 246 fakeAccess.IsAuthenticatedReturns(true) 247 }) 248 249 It("return 403", func() { 250 Expect(response.StatusCode).To(Equal(http.StatusForbidden)) 251 }) 252 }) 253 }) 254 Describe("PUT /api/v1/teams/:team_name", func() { 255 var ( 256 response *http.Response 257 teamAuth atc.TeamAuth 258 atcTeam atc.Team 259 path string 260 ) 261 262 BeforeEach(func() { 263 fakeTeam.IDReturns(5) 264 fakeTeam.NameReturns("some-team") 265 266 teamAuth = atc.TeamAuth{ 267 "owner": map[string][]string{ 268 "groups": {}, "users": {"local:username"}, 269 }, 270 } 271 atcTeam = atc.Team{Auth: teamAuth} 272 path = fmt.Sprintf("%s/api/v1/teams/some-team", server.URL) 273 }) 274 275 JustBeforeEach(func() { 276 var err error 277 request, err := http.NewRequest("PUT", path, jsonEncode(atcTeam)) 278 Expect(err).NotTo(HaveOccurred()) 279 280 response, err = client.Do(request) 281 Expect(err).NotTo(HaveOccurred()) 282 }) 283 284 authorizedTeamTests := func() { 285 Context("when the team exists", func() { 286 BeforeEach(func() { 287 atcTeam = atc.Team{ 288 Auth: atc.TeamAuth{ 289 "owner": map[string][]string{ 290 "users": []string{"local:username"}, 291 }, 292 }, 293 } 294 dbTeamFactory.FindTeamReturns(fakeTeam, true, nil) 295 }) 296 297 It("updates provider auth", func() { 298 Expect(response.StatusCode).To(Equal(http.StatusOK)) 299 Expect(fakeTeam.UpdateProviderAuthCallCount()).To(Equal(1)) 300 301 updatedProviderAuth := fakeTeam.UpdateProviderAuthArgsForCall(0) 302 Expect(updatedProviderAuth).To(Equal(atcTeam.Auth)) 303 }) 304 305 Context("when updating provider auth fails", func() { 306 BeforeEach(func() { 307 fakeTeam.UpdateProviderAuthReturns(errors.New("stop trying to make fetch happen")) 308 }) 309 310 It("returns 500 Internal Server error", func() { 311 Expect(response.StatusCode).To(Equal(http.StatusInternalServerError)) 312 }) 313 }) 314 Context("when provider auth is empty", func() { 315 BeforeEach(func() { 316 atcTeam = atc.Team{} 317 dbTeamFactory.FindTeamReturns(fakeTeam, true, nil) 318 }) 319 320 It("does not update provider auth", func() { 321 Expect(response.StatusCode).To(Equal(http.StatusBadRequest)) 322 Expect(fakeTeam.UpdateProviderAuthCallCount()).To(Equal(0)) 323 }) 324 }) 325 326 Context("when provider auth is invalid", func() { 327 BeforeEach(func() { 328 atcTeam = atc.Team{ 329 Auth: atc.TeamAuth{ 330 "owner": { 331 //"users": []string{}, 332 //"groups": []string{}, 333 }, 334 }, 335 } 336 dbTeamFactory.FindTeamReturns(fakeTeam, true, nil) 337 }) 338 339 It("does not update provider auth", func() { 340 Expect(response.StatusCode).To(Equal(http.StatusBadRequest)) 341 Expect(fakeTeam.UpdateProviderAuthCallCount()).To(Equal(0)) 342 }) 343 }) 344 }) 345 } 346 347 Context("when the requester team is authorized as an admin team", func() { 348 BeforeEach(func() { 349 fakeAccess.IsAuthenticatedReturns(true) 350 fakeAccess.IsAuthenticatedReturns(true) 351 fakeAccess.IsAdminReturns(true) 352 }) 353 354 authorizedTeamTests() 355 356 Context("when the team is not found", func() { 357 BeforeEach(func() { 358 dbTeamFactory.FindTeamReturns(nil, false, nil) 359 dbTeamFactory.CreateTeamReturns(fakeTeam, nil) 360 }) 361 362 It("creates the team", func() { 363 Expect(response.StatusCode).To(Equal(http.StatusCreated)) 364 Expect(dbTeamFactory.CreateTeamCallCount()).To(Equal(1)) 365 366 createdTeam := dbTeamFactory.CreateTeamArgsForCall(0) 367 Expect(createdTeam).To(Equal(atc.Team{ 368 Name: "some-team", 369 Auth: teamAuth, 370 })) 371 }) 372 373 It("delete the teams in cache", func() { 374 Expect(dbTeamFactory.NotifyCacherCallCount()).To(Equal(1)) 375 }) 376 377 Context("when it fails to create team", func() { 378 BeforeEach(func() { 379 dbTeamFactory.CreateTeamReturns(nil, errors.New("it is never going to happen")) 380 }) 381 382 It("returns a 500 Internal Server error", func() { 383 Expect(response.StatusCode).To(Equal(http.StatusInternalServerError)) 384 }) 385 386 It("does not delete the teams in cache", func() { 387 Expect(dbTeamFactory.NotifyCacherCallCount()).To(Equal(0)) 388 }) 389 }) 390 391 Context("when the team's name is an invalid identifier", func() { 392 BeforeEach(func() { 393 path = fmt.Sprintf("%s/api/v1/teams/_some-team", server.URL) 394 fakeTeam.NameReturns("_some-team") 395 }) 396 397 It("returns a warning in the response body", func() { 398 Expect(ioutil.ReadAll(response.Body)).To(MatchJSON(` 399 { 400 "warnings": [ 401 { 402 "type": "invalid_identifier", 403 "message": "team: '_some-team' is not a valid identifier: must start with a lowercase letter" 404 } 405 ], 406 "team": { 407 "id": 5, 408 "name": "_some-team" 409 } 410 }`)) 411 }) 412 }) 413 }) 414 }) 415 416 Context("when the requester team is authorized as the team being set", func() { 417 BeforeEach(func() { 418 fakeAccess.IsAuthenticatedReturns(true) 419 fakeAccess.IsAuthorizedReturns(true) 420 }) 421 422 authorizedTeamTests() 423 424 Context("when the team is not found", func() { 425 BeforeEach(func() { 426 dbTeamFactory.FindTeamReturns(nil, false, nil) 427 dbTeamFactory.CreateTeamReturns(fakeTeam, nil) 428 }) 429 430 It("does not create the team", func() { 431 Expect(response.StatusCode).To(Equal(http.StatusForbidden)) 432 Expect(dbTeamFactory.CreateTeamCallCount()).To(Equal(0)) 433 }) 434 }) 435 }) 436 }) 437 438 Describe("DELETE /api/v1/teams/:team_name", func() { 439 var request *http.Request 440 var response *http.Response 441 442 var teamName string 443 444 BeforeEach(func() { 445 teamName = "team venture" 446 447 fakeTeam.IDReturns(2) 448 fakeTeam.NameReturns(teamName) 449 }) 450 451 Context("when the requester is authenticated for some admin team", func() { 452 JustBeforeEach(func() { 453 path := fmt.Sprintf("%s/api/v1/teams/%s", server.URL, teamName) 454 455 var err error 456 request, err = http.NewRequest("DELETE", path, nil) 457 Expect(err).NotTo(HaveOccurred()) 458 459 response, err = client.Do(request) 460 Expect(err).NotTo(HaveOccurred()) 461 }) 462 463 BeforeEach(func() { 464 fakeAccess.IsAuthenticatedReturns(true) 465 fakeAccess.IsAdminReturns(true) 466 }) 467 468 Context("when there's a problem finding teams", func() { 469 BeforeEach(func() { 470 dbTeamFactory.FindTeamReturns(nil, false, errors.New("a dingo ate my baby!")) 471 }) 472 473 It("returns 500 Internal Server Error", func() { 474 Expect(response.StatusCode).To(Equal(http.StatusInternalServerError)) 475 }) 476 }) 477 478 Context("when team exists", func() { 479 BeforeEach(func() { 480 dbTeamFactory.FindTeamReturns(fakeTeam, true, nil) 481 }) 482 483 It("returns 204 No Content", func() { 484 Expect(response.StatusCode).To(Equal(http.StatusNoContent)) 485 }) 486 487 It("receives the correct team name", func() { 488 Expect(dbTeamFactory.FindTeamCallCount()).To(Equal(1)) 489 Expect(dbTeamFactory.FindTeamArgsForCall(0)).To(Equal(teamName)) 490 }) 491 It("deletes the team from the DB", func() { 492 Expect(fakeTeam.DeleteCallCount()).To(Equal(1)) 493 //TODO delete the build events via a table drop rather 494 }) 495 496 Context("when trying to delete the admin team", func() { 497 BeforeEach(func() { 498 teamName = atc.DefaultTeamName 499 fakeTeam.AdminReturns(true) 500 dbTeamFactory.FindTeamReturns(fakeTeam, true, nil) 501 dbTeamFactory.GetTeamsReturns([]db.Team{fakeTeam}, nil) 502 }) 503 504 It("returns 403 Forbidden and backs off", func() { 505 Expect(response.StatusCode).To(Equal(http.StatusForbidden)) 506 Expect(fakeTeam.DeleteCallCount()).To(Equal(0)) 507 }) 508 }) 509 510 Context("when there's a problem deleting the team", func() { 511 BeforeEach(func() { 512 fakeTeam.DeleteReturns(errors.New("disaster")) 513 }) 514 515 It("returns 500 Internal Server Error", func() { 516 Expect(response.StatusCode).To(Equal(http.StatusInternalServerError)) 517 }) 518 }) 519 }) 520 521 Context("when team does not exist", func() { 522 BeforeEach(func() { 523 dbTeamFactory.FindTeamReturns(nil, false, nil) 524 }) 525 526 It("returns 404 Not Found", func() { 527 Expect(response.StatusCode).To(Equal(http.StatusNotFound)) 528 }) 529 }) 530 }) 531 532 Context("when the requester belongs to a non-admin team", func() { 533 JustBeforeEach(func() { 534 path := fmt.Sprintf("%s/api/v1/teams/%s", server.URL, "non-admin-team") 535 536 var err error 537 request, err = http.NewRequest("DELETE", path, nil) 538 Expect(err).NotTo(HaveOccurred()) 539 540 response, err = client.Do(request) 541 Expect(err).NotTo(HaveOccurred()) 542 543 }) 544 545 BeforeEach(func() { 546 fakeAccess.IsAuthenticatedReturns(true) 547 fakeAccess.IsAdminReturns(false) 548 }) 549 550 It("returns 403 forbidden", func() { 551 Expect(response.StatusCode).To(Equal(http.StatusForbidden)) 552 }) 553 }) 554 }) 555 556 Describe("PUT /api/v1/teams/:team_name/rename", func() { 557 var response *http.Response 558 var requestBody string 559 var teamName string 560 561 JustBeforeEach(func() { 562 request, err := http.NewRequest( 563 "PUT", 564 server.URL+"/api/v1/teams/"+teamName+"/rename", 565 bytes.NewBufferString(requestBody), 566 ) 567 Expect(err).NotTo(HaveOccurred()) 568 569 response, err = client.Do(request) 570 Expect(err).NotTo(HaveOccurred()) 571 }) 572 573 BeforeEach(func() { 574 requestBody = `{"name":"some-new-name"}` 575 fakeTeam.IDReturns(2) 576 }) 577 578 Context("when authenticated", func() { 579 BeforeEach(func() { 580 fakeAccess.IsAuthenticatedReturns(true) 581 }) 582 Context("when requester belongs to an admin team", func() { 583 BeforeEach(func() { 584 teamName = "a-team" 585 fakeTeam.NameReturns(teamName) 586 fakeAccess.IsAdminReturns(true) 587 dbTeamFactory.FindTeamReturns(fakeTeam, true, nil) 588 }) 589 590 It("constructs teamDB with provided team name", func() { 591 Expect(dbTeamFactory.FindTeamCallCount()).To(Equal(1)) 592 Expect(dbTeamFactory.FindTeamArgsForCall(0)).To(Equal("a-team")) 593 }) 594 595 It("renames the team to the name provided", func() { 596 Expect(fakeTeam.RenameCallCount()).To(Equal(1)) 597 Expect(fakeTeam.RenameArgsForCall(0)).To(Equal("some-new-name")) 598 }) 599 600 It("returns 200", func() { 601 Expect(response.StatusCode).To(Equal(http.StatusOK)) 602 }) 603 604 Context("when a warning occurs", func() { 605 606 BeforeEach(func() { 607 requestBody = `{"name":"_some-new-name"}` 608 }) 609 610 It("returns a warning in the response body", func() { 611 Expect(ioutil.ReadAll(response.Body)).To(MatchJSON(` 612 { 613 "warnings": [ 614 { 615 "type": "invalid_identifier", 616 "message": "team: '_some-new-name' is not a valid identifier: must start with a lowercase letter" 617 } 618 ] 619 }`)) 620 }) 621 }) 622 }) 623 624 Context("when requester belongs to the team", func() { 625 BeforeEach(func() { 626 teamName = "a-team" 627 fakeTeam.NameReturns(teamName) 628 fakeAccess.IsAuthorizedReturns(true) 629 dbTeamFactory.FindTeamReturns(fakeTeam, true, nil) 630 }) 631 632 It("constructs teamDB with provided team name", func() { 633 Expect(dbTeamFactory.FindTeamCallCount()).To(Equal(1)) 634 Expect(dbTeamFactory.FindTeamArgsForCall(0)).To(Equal("a-team")) 635 }) 636 637 It("renames the team to the name provided", func() { 638 Expect(fakeTeam.RenameCallCount()).To(Equal(1)) 639 Expect(fakeTeam.RenameArgsForCall(0)).To(Equal("some-new-name")) 640 }) 641 642 It("returns 200", func() { 643 Expect(response.StatusCode).To(Equal(http.StatusOK)) 644 }) 645 }) 646 647 Context("when requester does not belong to the team", func() { 648 BeforeEach(func() { 649 teamName = "a-team" 650 fakeTeam.NameReturns(teamName) 651 fakeAccess.IsAuthorizedReturns(false) 652 dbTeamFactory.FindTeamReturns(fakeTeam, true, nil) 653 }) 654 655 It("returns 403 Forbidden", func() { 656 Expect(response.StatusCode).To(Equal(http.StatusForbidden)) 657 Expect(fakeTeam.RenameCallCount()).To(Equal(0)) 658 }) 659 }) 660 }) 661 662 Context("when not authenticated", func() { 663 BeforeEach(func() { 664 fakeAccess.IsAuthenticatedReturns(false) 665 }) 666 667 It("returns 401 Unauthorized", func() { 668 Expect(response.StatusCode).To(Equal(http.StatusUnauthorized)) 669 Expect(fakeTeam.RenameCallCount()).To(Equal(0)) 670 }) 671 }) 672 }) 673 674 Describe("GET /api/v1/teams/:team_name/builds", func() { 675 var ( 676 response *http.Response 677 queryParams string 678 teamName string 679 ) 680 681 BeforeEach(func() { 682 teamName = "some-team" 683 }) 684 685 JustBeforeEach(func() { 686 var err error 687 688 response, err = client.Get(server.URL + "/api/v1/teams/" + teamName + "/builds" + queryParams) 689 Expect(err).NotTo(HaveOccurred()) 690 }) 691 692 Context("when not authenticated", func() { 693 BeforeEach(func() { 694 fakeAccess.IsAuthenticatedReturns(false) 695 dbTeamFactory.FindTeamReturns(fakeTeam, true, nil) 696 }) 697 698 It("returns 401", func() { 699 Expect(response.StatusCode).To(Equal(http.StatusUnauthorized)) 700 Expect(fakeTeam.BuildsCallCount()).To(Equal(0)) 701 }) 702 }) 703 704 Context("when authenticated", func() { 705 BeforeEach(func() { 706 fakeAccess.IsAuthenticatedReturns(true) 707 dbTeamFactory.FindTeamReturns(fakeTeam, true, nil) 708 }) 709 710 Context("when no params are passed", func() { 711 It("does not set defaults for since and until", func() { 712 Expect(fakeTeam.BuildsCallCount()).To(Equal(1)) 713 714 page := fakeTeam.BuildsArgsForCall(0) 715 Expect(page).To(Equal(db.Page{ 716 Limit: 100, 717 })) 718 }) 719 }) 720 721 Context("when all the params are passed", func() { 722 BeforeEach(func() { 723 queryParams = "?from=2&to=3&limit=8" 724 }) 725 726 It("passes them through", func() { 727 Expect(fakeTeam.BuildsCallCount()).To(Equal(1)) 728 729 page := fakeTeam.BuildsArgsForCall(0) 730 Expect(page).To(Equal(db.Page{ 731 From: db.NewIntPtr(2), 732 To: db.NewIntPtr(3), 733 Limit: 8, 734 })) 735 }) 736 }) 737 738 Context("when getting the builds succeeds", func() { 739 var returnedBuilds []db.Build 740 741 BeforeEach(func() { 742 queryParams = "?since=5&limit=2" 743 744 build1 := new(dbfakes.FakeBuild) 745 build1.IDReturns(4) 746 build1.NameReturns("2") 747 build1.JobNameReturns("some-job") 748 build1.PipelineNameReturns("some-pipeline") 749 build1.TeamNameReturns("some-team") 750 build1.StatusReturns(db.BuildStatusStarted) 751 build1.StartTimeReturns(time.Unix(1, 0)) 752 build1.EndTimeReturns(time.Unix(100, 0)) 753 754 build2 := new(dbfakes.FakeBuild) 755 build2.IDReturns(2) 756 build2.NameReturns("1") 757 build2.JobNameReturns("some-job") 758 build2.PipelineNameReturns("some-pipeline") 759 build2.TeamNameReturns("some-team") 760 build2.StatusReturns(db.BuildStatusSucceeded) 761 build2.StartTimeReturns(time.Unix(101, 0)) 762 build2.EndTimeReturns(time.Unix(200, 0)) 763 764 returnedBuilds = []db.Build{build1, build2} 765 fakeTeam.BuildsReturns(returnedBuilds, db.Pagination{}, nil) 766 }) 767 768 It("returns 200 OK", func() { 769 Expect(response.StatusCode).To(Equal(http.StatusOK)) 770 }) 771 772 It("returns Content-Type 'application/json'", func() { 773 expectedHeaderEntries := map[string]string{ 774 "Content-Type": "application/json", 775 } 776 Expect(response).Should(IncludeHeaderEntries(expectedHeaderEntries)) 777 }) 778 779 It("returns the builds", func() { 780 body, err := ioutil.ReadAll(response.Body) 781 Expect(err).NotTo(HaveOccurred()) 782 783 Expect(body).To(MatchJSON(`[ 784 { 785 "id": 4, 786 "name": "2", 787 "job_name": "some-job", 788 "status": "started", 789 "api_url": "/api/v1/builds/4", 790 "pipeline_name":"some-pipeline", 791 "team_name": "some-team", 792 "start_time": 1, 793 "end_time": 100 794 }, 795 { 796 "id": 2, 797 "name": "1", 798 "job_name": "some-job", 799 "status": "succeeded", 800 "api_url": "/api/v1/builds/2", 801 "pipeline_name": "some-pipeline", 802 "team_name": "some-team", 803 "start_time": 101, 804 "end_time": 200 805 } 806 ]`)) 807 }) 808 809 Context("when next/previous pages are available", func() { 810 BeforeEach(func() { 811 fakeTeam.BuildsReturns(returnedBuilds, db.Pagination{ 812 Newer: &db.Page{From: db.NewIntPtr(4), Limit: 2}, 813 Older: &db.Page{To: db.NewIntPtr(2), Limit: 2}, 814 }, nil) 815 }) 816 817 It("returns Link headers per rfc5988", func() { 818 Expect(response.Header["Link"]).To(ConsistOf([]string{ 819 fmt.Sprintf(`<%s/api/v1/teams/some-team/builds?from=4&limit=2>; rel="previous"`, externalURL), 820 fmt.Sprintf(`<%s/api/v1/teams/some-team/builds?to=2&limit=2>; rel="next"`, externalURL), 821 })) 822 }) 823 }) 824 }) 825 826 Context("when getting the build fails", func() { 827 BeforeEach(func() { 828 fakeTeam.BuildsReturns(nil, db.Pagination{}, errors.New("oh no!")) 829 }) 830 831 It("returns 404 Not Found", func() { 832 Expect(response.StatusCode).To(Equal(http.StatusNotFound)) 833 }) 834 }) 835 }) 836 }) 837 })