1package api_test 2 3import ( 4 "errors" 5 "fmt" 6 "io/ioutil" 7 "net/http" 8 "time" 9 10 "github.com/concourse/concourse/atc" 11 "github.com/concourse/concourse/atc/db" 12 "github.com/concourse/concourse/atc/db/dbfakes" 13 . "github.com/concourse/concourse/atc/testhelpers" 14 . "github.com/onsi/ginkgo" 15 . "github.com/onsi/gomega" 16) 17 18var _ = Describe("Versions API", func() { 19 var fakePipeline *dbfakes.FakePipeline 20 21 BeforeEach(func() { 22 fakePipeline = new(dbfakes.FakePipeline) 23 dbTeamFactory.FindTeamReturns(dbTeam, true, nil) 24 dbTeam.PipelineReturns(fakePipeline, true, nil) 25 }) 26 27 Describe("GET /api/v1/teams/:team_name/pipelines/:pipeline_name/resources/:resource_name/versions", func() { 28 var response *http.Response 29 var queryParams string 30 var fakeResource *dbfakes.FakeResource 31 32 BeforeEach(func() { 33 queryParams = "" 34 fakeResource = new(dbfakes.FakeResource) 35 }) 36 37 JustBeforeEach(func() { 38 var err error 39 40 request, err := http.NewRequest("GET", server.URL+"/api/v1/teams/a-team/pipelines/a-pipeline/resources/some-resource/versions"+queryParams, nil) 41 Expect(err).NotTo(HaveOccurred()) 42 43 response, err = client.Do(request) 44 Expect(err).NotTo(HaveOccurred()) 45 }) 46 47 Context("when not authorized", func() { 48 BeforeEach(func() { 49 fakeAccess.IsAuthorizedReturns(false) 50 }) 51 52 Context("and the pipeline is private", func() { 53 BeforeEach(func() { 54 fakePipeline.PublicReturns(false) 55 }) 56 57 Context("user is not authenticated", func() { 58 BeforeEach(func() { 59 fakeAccess.IsAuthenticatedReturns(false) 60 }) 61 62 It("returns 401", func() { 63 Expect(response.StatusCode).To(Equal(http.StatusUnauthorized)) 64 }) 65 }) 66 67 Context("user is authenticated", func() { 68 BeforeEach(func() { 69 fakeAccess.IsAuthenticatedReturns(true) 70 }) 71 72 It("returns 403", func() { 73 Expect(response.StatusCode).To(Equal(http.StatusForbidden)) 74 }) 75 }) 76 }) 77 78 Context("and the pipeline is public", func() { 79 BeforeEach(func() { 80 fakePipeline.PublicReturns(true) 81 fakePipeline.ResourceReturns(fakeResource, true, nil) 82 83 returnedVersions := []atc.ResourceVersion{ 84 { 85 ID: 4, 86 Enabled: true, 87 Version: atc.Version{ 88 "some": "version", 89 }, 90 Metadata: []atc.MetadataField{ 91 { 92 Name: "some", 93 Value: "metadata", 94 }, 95 }, 96 }, 97 { 98 ID: 2, 99 Enabled: false, 100 Version: atc.Version{ 101 "some": "version", 102 }, 103 Metadata: []atc.MetadataField{ 104 { 105 Name: "some", 106 Value: "metadata", 107 }, 108 }, 109 }, 110 } 111 112 fakeResource.VersionsReturns(returnedVersions, db.Pagination{}, true, nil) 113 }) 114 115 It("returns 200 OK", func() { 116 Expect(response.StatusCode).To(Equal(http.StatusOK)) 117 }) 118 119 It("returns content type application/json", func() { 120 expectedHeaderEntries := map[string]string{ 121 "Content-Type": "application/json", 122 } 123 Expect(response).Should(IncludeHeaderEntries(expectedHeaderEntries)) 124 }) 125 126 Context("when resource is public", func() { 127 BeforeEach(func() { 128 fakeResource.PublicReturns(true) 129 }) 130 131 It("returns the json", func() { 132 body, err := ioutil.ReadAll(response.Body) 133 Expect(err).NotTo(HaveOccurred()) 134 135 Expect(body).To(MatchJSON(`[ 136 { 137 "id": 4, 138 "enabled": true, 139 "version": {"some":"version"}, 140 "metadata": [ 141 { 142 "name":"some", 143 "value":"metadata" 144 } 145 ] 146 }, 147 { 148 "id":2, 149 "enabled": false, 150 "version": {"some":"version"}, 151 "metadata": [ 152 { 153 "name":"some", 154 "value":"metadata" 155 } 156 ] 157 } 158 ]`)) 159 }) 160 }) 161 162 Context("when resource is not public", func() { 163 Context("when the user is not authenticated", func() { 164 It("returns the json without version metadata", func() { 165 body, err := ioutil.ReadAll(response.Body) 166 Expect(err).NotTo(HaveOccurred()) 167 168 Expect(body).To(MatchJSON(`[ 169 { 170 "id": 4, 171 "enabled": true, 172 "version": {"some":"version"} 173 }, 174 { 175 "id":2, 176 "enabled": false, 177 "version": {"some":"version"} 178 } 179 ]`)) 180 }) 181 }) 182 183 Context("when the user is authenticated", func() { 184 BeforeEach(func() { 185 fakeAccess.IsAuthenticatedReturns(true) 186 }) 187 188 It("returns the json without version metadata", func() { 189 body, err := ioutil.ReadAll(response.Body) 190 Expect(err).NotTo(HaveOccurred()) 191 192 Expect(body).To(MatchJSON(`[ 193 { 194 "id": 4, 195 "enabled": true, 196 "version": {"some":"version"} 197 }, 198 { 199 "id":2, 200 "enabled": false, 201 "version": {"some":"version"} 202 } 203 ]`)) 204 }) 205 }) 206 }) 207 }) 208 }) 209 210 Context("when authorized", func() { 211 BeforeEach(func() { 212 fakeAccess.IsAuthenticatedReturns(true) 213 fakeAccess.IsAuthorizedReturns(true) 214 }) 215 216 It("finds the resource", func() { 217 Expect(fakePipeline.ResourceCallCount()).To(Equal(1)) 218 Expect(fakePipeline.ResourceArgsForCall(0)).To(Equal("some-resource")) 219 }) 220 221 Context("when finding the resource succeeds", func() { 222 BeforeEach(func() { 223 fakePipeline.ResourceReturns(fakeResource, true, nil) 224 }) 225 226 Context("when no params are passed", func() { 227 It("does not set defaults for since and until", func() { 228 Expect(fakeResource.VersionsCallCount()).To(Equal(1)) 229 230 page, versionFilter := fakeResource.VersionsArgsForCall(0) 231 Expect(page).To(Equal(db.Page{ 232 Limit: 100, 233 })) 234 Expect(versionFilter).To(Equal(atc.Version{})) 235 }) 236 }) 237 238 Context("when all the params are passed", func() { 239 BeforeEach(func() { 240 queryParams = "?from=5&to=7&limit=8&filter=ref:foo&filter=some-ref:blah" 241 }) 242 243 It("passes them through", func() { 244 Expect(fakeResource.VersionsCallCount()).To(Equal(1)) 245 246 page, versionFilter := fakeResource.VersionsArgsForCall(0) 247 Expect(page).To(Equal(db.Page{ 248 From: db.NewIntPtr(5), 249 To: db.NewIntPtr(7), 250 Limit: 8, 251 })) 252 Expect(versionFilter).To(Equal(atc.Version{ 253 "ref": "foo", 254 "some-ref": "blah", 255 })) 256 }) 257 }) 258 259 Context("when params includes version filter has special char", func() { 260 Context("space char", func() { 261 BeforeEach(func() { 262 queryParams = "?filter=some%20ref:some%20value" 263 }) 264 265 It("passes them through", func() { 266 Expect(fakeResource.VersionsCallCount()).To(Equal(1)) 267 268 _, versionFilter := fakeResource.VersionsArgsForCall(0) 269 Expect(versionFilter).To(Equal(atc.Version{ 270 "some ref": "some value", 271 })) 272 }) 273 }) 274 275 Context("% char", func() { 276 BeforeEach(func() { 277 queryParams = "?filter=ref:some%25value" 278 }) 279 280 It("passes them through", func() { 281 Expect(fakeResource.VersionsCallCount()).To(Equal(1)) 282 283 _, versionFilter := fakeResource.VersionsArgsForCall(0) 284 Expect(versionFilter).To(Equal(atc.Version{ 285 "ref": "some%value", 286 })) 287 }) 288 }) 289 290 Context(": char", func() { 291 BeforeEach(func() { 292 queryParams = "?filter=key%3Awith%3Acolon:abcdef" 293 }) 294 295 It("passes them through by splitting on first colon", func() { 296 Expect(fakeResource.VersionsCallCount()).To(Equal(1)) 297 298 _, versionFilter := fakeResource.VersionsArgsForCall(0) 299 Expect(versionFilter).To(Equal(atc.Version{ 300 "key": "with:colon:abcdef", 301 })) 302 }) 303 }) 304 305 Context("if there is no : ", func() { 306 BeforeEach(func() { 307 queryParams = "?filter=abcdef" 308 }) 309 310 It("set no filter when fetching versions", func() { 311 Expect(fakeResource.VersionsCallCount()).To(Equal(1)) 312 313 _, versionFilter := fakeResource.VersionsArgsForCall(0) 314 Expect(versionFilter).To(BeEmpty()) 315 }) 316 }) 317 }) 318 319 Context("when getting the versions succeeds", func() { 320 var returnedVersions []atc.ResourceVersion 321 322 BeforeEach(func() { 323 queryParams = "?since=5&limit=2" 324 returnedVersions = []atc.ResourceVersion{ 325 { 326 ID: 4, 327 Enabled: true, 328 Version: atc.Version{ 329 "some": "version", 330 "ref": "foo", 331 }, 332 Metadata: []atc.MetadataField{ 333 { 334 Name: "some", 335 Value: "metadata", 336 }, 337 }, 338 }, 339 { 340 ID: 2, 341 Enabled: false, 342 Version: atc.Version{ 343 "some": "version", 344 "ref": "blah", 345 }, 346 Metadata: []atc.MetadataField{ 347 { 348 Name: "some", 349 Value: "metadata", 350 }, 351 }, 352 }, 353 } 354 355 fakeResource.VersionsReturns(returnedVersions, db.Pagination{}, true, nil) 356 }) 357 358 It("returns 200 OK", func() { 359 Expect(response.StatusCode).To(Equal(http.StatusOK)) 360 }) 361 362 It("returns content type application/json", func() { 363 expectedHeaderEntries := map[string]string{ 364 "Content-Type": "application/json", 365 } 366 Expect(response).Should(IncludeHeaderEntries(expectedHeaderEntries)) 367 }) 368 369 It("returns the json", func() { 370 body, err := ioutil.ReadAll(response.Body) 371 Expect(err).NotTo(HaveOccurred()) 372 373 Expect(body).To(MatchJSON(`[ 374 { 375 "id": 4, 376 "enabled": true, 377 "version": {"some":"version", "ref":"foo"}, 378 "metadata": [ 379 { 380 "name":"some", 381 "value":"metadata" 382 } 383 ] 384 }, 385 { 386 "id":2, 387 "enabled": false, 388 "version": {"some":"version", "ref":"blah"}, 389 "metadata": [ 390 { 391 "name":"some", 392 "value":"metadata" 393 } 394 ] 395 } 396 ]`)) 397 }) 398 399 Context("when next/previous pages are available", func() { 400 BeforeEach(func() { 401 fakePipeline.NameReturns("some-pipeline") 402 fakeResource.VersionsReturns(returnedVersions, db.Pagination{ 403 Newer: &db.Page{From: db.NewIntPtr(4), Limit: 2}, 404 Older: &db.Page{To: db.NewIntPtr(2), Limit: 2}, 405 }, true, nil) 406 }) 407 408 It("returns Link headers per rfc5988", func() { 409 Expect(response.Header["Link"]).To(ConsistOf([]string{ 410 fmt.Sprintf(`<%s/api/v1/teams/a-team/pipelines/some-pipeline/resources/some-resource/versions?from=4&limit=2>; rel="previous"`, externalURL), 411 fmt.Sprintf(`<%s/api/v1/teams/a-team/pipelines/some-pipeline/resources/some-resource/versions?to=2&limit=2>; rel="next"`, externalURL), 412 })) 413 }) 414 }) 415 }) 416 417 Context("when the versions can't be found", func() { 418 BeforeEach(func() { 419 fakeResource.VersionsReturns(nil, db.Pagination{}, false, nil) 420 }) 421 422 It("returns 404 not found", func() { 423 Expect(response.StatusCode).To(Equal(http.StatusNotFound)) 424 }) 425 }) 426 427 Context("when getting the versions fails", func() { 428 BeforeEach(func() { 429 fakeResource.VersionsReturns(nil, db.Pagination{}, false, errors.New("oh no!")) 430 }) 431 432 It("returns 500 Internal Server Error", func() { 433 Expect(response.StatusCode).To(Equal(http.StatusInternalServerError)) 434 }) 435 }) 436 }) 437 438 Context("when finding the resource fails", func() { 439 BeforeEach(func() { 440 fakePipeline.ResourceReturns(nil, false, errors.New("oh no!")) 441 }) 442 443 It("returns 500 Internal Server Error", func() { 444 Expect(response.StatusCode).To(Equal(http.StatusInternalServerError)) 445 }) 446 }) 447 448 Context("when the resource is not found", func() { 449 BeforeEach(func() { 450 fakePipeline.ResourceReturns(nil, false, nil) 451 }) 452 453 It("returns 404 not found", func() { 454 Expect(response.StatusCode).To(Equal(http.StatusNotFound)) 455 }) 456 }) 457 }) 458 }) 459 460 Describe("PUT /api/v1/teams/:team_name/pipelines/:pipeline_name/resources/:resource_name/versions/:resource_version_id/enable", func() { 461 var response *http.Response 462 var fakeResource *dbfakes.FakeResource 463 464 JustBeforeEach(func() { 465 var err error 466 467 request, err := http.NewRequest("PUT", server.URL+"/api/v1/teams/a-team/pipelines/a-pipeline/resources/resource-name/versions/42/enable", nil) 468 Expect(err).NotTo(HaveOccurred()) 469 470 response, err = client.Do(request) 471 Expect(err).NotTo(HaveOccurred()) 472 }) 473 474 Context("when authenticated", func() { 475 BeforeEach(func() { 476 fakeAccess.IsAuthenticatedReturns(true) 477 }) 478 479 Context("when authorized", func() { 480 BeforeEach(func() { 481 fakeAccess.IsAuthorizedReturns(true) 482 }) 483 484 It("tries to find the resource", func() { 485 resourceName := fakePipeline.ResourceArgsForCall(0) 486 Expect(resourceName).To(Equal("resource-name")) 487 }) 488 489 Context("when finding the resource succeeds", func() { 490 BeforeEach(func() { 491 fakeResource = new(dbfakes.FakeResource) 492 fakeResource.IDReturns(1) 493 fakePipeline.ResourceReturns(fakeResource, true, nil) 494 }) 495 496 It("tries to enable the right resource config version", func() { 497 resourceConfigVersionID := fakeResource.EnableVersionArgsForCall(0) 498 Expect(resourceConfigVersionID).To(Equal(42)) 499 }) 500 501 Context("when enabling the resource succeeds", func() { 502 BeforeEach(func() { 503 fakeResource.EnableVersionReturns(nil) 504 }) 505 506 It("returns 200", func() { 507 Expect(response.StatusCode).To(Equal(http.StatusOK)) 508 }) 509 }) 510 511 Context("when enabling the resource fails", func() { 512 BeforeEach(func() { 513 fakeResource.EnableVersionReturns(errors.New("welp")) 514 }) 515 516 It("returns 500", func() { 517 Expect(response.StatusCode).To(Equal(http.StatusInternalServerError)) 518 }) 519 }) 520 }) 521 522 Context("when it fails to find the resource", func() { 523 BeforeEach(func() { 524 fakePipeline.ResourceReturns(nil, false, errors.New("welp")) 525 }) 526 527 It("returns Internal Server Error", func() { 528 Expect(response.StatusCode).To(Equal(http.StatusInternalServerError)) 529 }) 530 }) 531 532 Context("when the resource is not found", func() { 533 BeforeEach(func() { 534 fakePipeline.ResourceReturns(nil, false, nil) 535 }) 536 537 It("returns not found", func() { 538 Expect(response.StatusCode).To(Equal(http.StatusNotFound)) 539 }) 540 }) 541 }) 542 543 Context("when not authorized", func() { 544 BeforeEach(func() { 545 fakeAccess.IsAuthorizedReturns(false) 546 }) 547 It("returns Forbidden", func() { 548 Expect(response.StatusCode).To(Equal(http.StatusForbidden)) 549 }) 550 }) 551 }) 552 553 Context("when not authenticated", func() { 554 BeforeEach(func() { 555 fakeAccess.IsAuthenticatedReturns(false) 556 }) 557 558 It("returns Unauthorized", func() { 559 Expect(response.StatusCode).To(Equal(http.StatusUnauthorized)) 560 }) 561 }) 562 }) 563 564 Describe("PUT /api/v1/teams/:team_name/pipelines/:pipeline_name/resources/:resource_name/versions/:resource_version_id/disable", func() { 565 var response *http.Response 566 var fakeResource *dbfakes.FakeResource 567 568 JustBeforeEach(func() { 569 var err error 570 571 request, err := http.NewRequest("PUT", server.URL+"/api/v1/teams/a-team/pipelines/a-pipeline/resources/resource-name/versions/42/disable", nil) 572 Expect(err).NotTo(HaveOccurred()) 573 574 response, err = client.Do(request) 575 Expect(err).NotTo(HaveOccurred()) 576 }) 577 578 Context("when authenticated ", func() { 579 BeforeEach(func() { 580 fakeAccess.IsAuthenticatedReturns(true) 581 }) 582 583 Context("when authorized", func() { 584 BeforeEach(func() { 585 fakeAccess.IsAuthorizedReturns(true) 586 }) 587 588 It("tries to find the resource", func() { 589 resourceName := fakePipeline.ResourceArgsForCall(0) 590 Expect(resourceName).To(Equal("resource-name")) 591 }) 592 593 Context("when finding the resource succeeds", func() { 594 BeforeEach(func() { 595 fakeResource = new(dbfakes.FakeResource) 596 fakeResource.IDReturns(1) 597 fakePipeline.ResourceReturns(fakeResource, true, nil) 598 }) 599 600 It("tries to disable the right resource config version", func() { 601 resourceConfigVersionID := fakeResource.DisableVersionArgsForCall(0) 602 Expect(resourceConfigVersionID).To(Equal(42)) 603 }) 604 605 Context("when disabling the resource version succeeds", func() { 606 BeforeEach(func() { 607 fakeResource.DisableVersionReturns(nil) 608 }) 609 610 It("returns 200", func() { 611 Expect(response.StatusCode).To(Equal(http.StatusOK)) 612 }) 613 }) 614 615 Context("when disabling the resource fails", func() { 616 BeforeEach(func() { 617 fakeResource.DisableVersionReturns(errors.New("welp")) 618 }) 619 620 It("returns 500", func() { 621 Expect(response.StatusCode).To(Equal(http.StatusInternalServerError)) 622 }) 623 }) 624 }) 625 626 Context("when it fails to find the resource", func() { 627 BeforeEach(func() { 628 fakePipeline.ResourceReturns(nil, false, errors.New("welp")) 629 }) 630 631 It("returns Internal Server Error", func() { 632 Expect(response.StatusCode).To(Equal(http.StatusInternalServerError)) 633 }) 634 }) 635 636 Context("when the resource is not found", func() { 637 BeforeEach(func() { 638 fakePipeline.ResourceReturns(nil, false, nil) 639 }) 640 641 It("returns not found", func() { 642 Expect(response.StatusCode).To(Equal(http.StatusNotFound)) 643 }) 644 }) 645 }) 646 Context("when not authorized", func() { 647 BeforeEach(func() { 648 fakeAccess.IsAuthorizedReturns(false) 649 }) 650 651 It("returns Forbidden", func() { 652 Expect(response.StatusCode).To(Equal(http.StatusForbidden)) 653 }) 654 }) 655 }) 656 Context("when not authenticated", func() { 657 BeforeEach(func() { 658 fakeAccess.IsAuthenticatedReturns(false) 659 }) 660 661 It("returns Unauthorized", func() { 662 Expect(response.StatusCode).To(Equal(http.StatusUnauthorized)) 663 }) 664 }) 665 }) 666 667 Describe("PUT /api/v1/teams/:team_name/pipelines/:pipeline_name/resources/:resource_name/versions/:resource_version_id/pin", func() { 668 var response *http.Response 669 var fakeResource *dbfakes.FakeResource 670 671 JustBeforeEach(func() { 672 var err error 673 674 request, err := http.NewRequest("PUT", server.URL+"/api/v1/teams/a-team/pipelines/a-pipeline/resources/resource-name/versions/42/pin", nil) 675 Expect(err).NotTo(HaveOccurred()) 676 677 response, err = client.Do(request) 678 Expect(err).NotTo(HaveOccurred()) 679 }) 680 681 Context("when authenticated", func() { 682 BeforeEach(func() { 683 fakeAccess.IsAuthenticatedReturns(true) 684 }) 685 686 Context("when authorized", func() { 687 BeforeEach(func() { 688 fakeAccess.IsAuthorizedReturns(true) 689 }) 690 691 It("tries to find the resource", func() { 692 resourceName := fakePipeline.ResourceArgsForCall(0) 693 Expect(resourceName).To(Equal("resource-name")) 694 }) 695 696 Context("when finding the resource succeeds", func() { 697 BeforeEach(func() { 698 fakeResource = new(dbfakes.FakeResource) 699 fakeResource.IDReturns(1) 700 fakePipeline.ResourceReturns(fakeResource, true, nil) 701 }) 702 703 It("tries to pin the right resource config version", func() { 704 resourceConfigVersionID := fakeResource.PinVersionArgsForCall(0) 705 Expect(resourceConfigVersionID).To(Equal(42)) 706 }) 707 708 Context("when pinning the resource succeeds", func() { 709 BeforeEach(func() { 710 fakeResource.PinVersionReturns(true, nil) 711 }) 712 713 It("returns 200", func() { 714 Expect(response.StatusCode).To(Equal(http.StatusOK)) 715 }) 716 }) 717 718 Context("when pinning the resource fails by resource not exist", func() { 719 BeforeEach(func() { 720 fakeResource.PinVersionReturns(false, nil) 721 }) 722 723 It("returns 404", func() { 724 Expect(response.StatusCode).To(Equal(http.StatusNotFound)) 725 }) 726 }) 727 728 Context("when pinning the resource fails by error", func() { 729 BeforeEach(func() { 730 fakeResource.PinVersionReturns(false, errors.New("welp")) 731 }) 732 733 It("returns 500", func() { 734 Expect(response.StatusCode).To(Equal(http.StatusInternalServerError)) 735 }) 736 }) 737 }) 738 739 Context("when it fails to find the resource", func() { 740 BeforeEach(func() { 741 fakePipeline.ResourceReturns(nil, false, errors.New("welp")) 742 }) 743 744 It("returns Internal Server Error", func() { 745 Expect(response.StatusCode).To(Equal(http.StatusInternalServerError)) 746 }) 747 }) 748 749 Context("when the resource is not found", func() { 750 BeforeEach(func() { 751 fakePipeline.ResourceReturns(nil, false, nil) 752 }) 753 754 It("returns not found", func() { 755 Expect(response.StatusCode).To(Equal(http.StatusNotFound)) 756 }) 757 }) 758 }) 759 760 Context("when not authorized", func() { 761 BeforeEach(func() { 762 fakeAccess.IsAuthorizedReturns(false) 763 }) 764 It("returns Forbidden", func() { 765 Expect(response.StatusCode).To(Equal(http.StatusForbidden)) 766 }) 767 }) 768 }) 769 770 Context("when not authenticated", func() { 771 BeforeEach(func() { 772 fakeAccess.IsAuthenticatedReturns(false) 773 }) 774 775 It("returns Unauthorized", func() { 776 Expect(response.StatusCode).To(Equal(http.StatusUnauthorized)) 777 }) 778 }) 779 }) 780 781 Describe("GET /api/v1/teams/:team_name/pipelines/:pipeline_name/resources/:resource_name/versions/:resource_version_id/input_to", func() { 782 var response *http.Response 783 var stringVersionID string 784 var fakeResource *dbfakes.FakeResource 785 786 JustBeforeEach(func() { 787 var err error 788 789 request, err := http.NewRequest("GET", server.URL+"/api/v1/teams/a-team/pipelines/a-pipeline/resources/some-resource/versions/"+stringVersionID+"/input_to", nil) 790 Expect(err).NotTo(HaveOccurred()) 791 792 response, err = client.Do(request) 793 Expect(err).NotTo(HaveOccurred()) 794 }) 795 796 BeforeEach(func() { 797 fakeResource = new(dbfakes.FakeResource) 798 fakeResource.IDReturns(1) 799 stringVersionID = "123" 800 }) 801 802 Context("when not authorized", func() { 803 BeforeEach(func() { 804 fakeAccess.IsAuthorizedReturns(false) 805 }) 806 807 Context("and the pipeline is private", func() { 808 BeforeEach(func() { 809 fakePipeline.PublicReturns(false) 810 }) 811 812 Context("when authenticated", func() { 813 BeforeEach(func() { 814 fakeAccess.IsAuthenticatedReturns(true) 815 }) 816 817 It("returns 403", func() { 818 Expect(response.StatusCode).To(Equal(http.StatusForbidden)) 819 }) 820 }) 821 822 Context("when not authenticated", func() { 823 BeforeEach(func() { 824 fakeAccess.IsAuthenticatedReturns(false) 825 }) 826 827 It("returns 401", func() { 828 Expect(response.StatusCode).To(Equal(http.StatusUnauthorized)) 829 }) 830 }) 831 }) 832 833 Context("and the pipeline is public", func() { 834 BeforeEach(func() { 835 fakePipeline.PublicReturns(true) 836 fakePipeline.ResourceReturns(fakeResource, true, nil) 837 }) 838 839 It("returns 200 OK", func() { 840 Expect(response.StatusCode).To(Equal(http.StatusOK)) 841 }) 842 }) 843 }) 844 845 Context("when authorized", func() { 846 BeforeEach(func() { 847 fakeAccess.IsAuthenticatedReturns(true) 848 fakeAccess.IsAuthorizedReturns(true) 849 }) 850 851 Context("when not finding the resource", func() { 852 BeforeEach(func() { 853 fakePipeline.ResourceReturns(nil, false, nil) 854 }) 855 856 It("returns 404", func() { 857 Expect(response.StatusCode).To(Equal(http.StatusNotFound)) 858 }) 859 }) 860 861 Context("when failing to retrieve the resource", func() { 862 BeforeEach(func() { 863 fakePipeline.ResourceReturns(nil, false, errors.New("banana")) 864 }) 865 866 It("returns 500", func() { 867 Expect(response.StatusCode).To(Equal(http.StatusInternalServerError)) 868 }) 869 }) 870 871 It("looks for the resource", func() { 872 Expect(fakePipeline.ResourceCallCount()).To(Equal(1)) 873 Expect(fakePipeline.ResourceArgsForCall(0)).To(Equal("some-resource")) 874 }) 875 876 Context("when resource retrieval succeeds", func() { 877 BeforeEach(func() { 878 fakePipeline.ResourceReturns(fakeResource, true, nil) 879 }) 880 881 It("looks up the given version ID", func() { 882 Expect(fakePipeline.GetBuildsWithVersionAsInputCallCount()).To(Equal(1)) 883 resourceID, versionID := fakePipeline.GetBuildsWithVersionAsInputArgsForCall(0) 884 Expect(resourceID).To(Equal(1)) 885 Expect(versionID).To(Equal(123)) 886 }) 887 888 Context("when getting the builds succeeds", func() { 889 BeforeEach(func() { 890 build1 := new(dbfakes.FakeBuild) 891 build1.IDReturns(1024) 892 build1.NameReturns("5") 893 build1.JobNameReturns("some-job") 894 build1.PipelineNameReturns("a-pipeline") 895 build1.TeamNameReturns("a-team") 896 build1.StatusReturns(db.BuildStatusSucceeded) 897 build1.StartTimeReturns(time.Unix(1, 0)) 898 build1.EndTimeReturns(time.Unix(100, 0)) 899 900 build2 := new(dbfakes.FakeBuild) 901 build2.IDReturns(1025) 902 build2.NameReturns("6") 903 build2.JobNameReturns("some-job") 904 build2.PipelineNameReturns("a-pipeline") 905 build2.TeamNameReturns("a-team") 906 build2.StatusReturns(db.BuildStatusSucceeded) 907 build2.StartTimeReturns(time.Unix(200, 0)) 908 build2.EndTimeReturns(time.Unix(300, 0)) 909 910 fakePipeline.GetBuildsWithVersionAsInputReturns([]db.Build{build1, build2}, nil) 911 }) 912 913 It("returns 200 OK", func() { 914 Expect(response.StatusCode).To(Equal(http.StatusOK)) 915 }) 916 917 It("returns content type application/json", func() { 918 expectedHeaderEntries := map[string]string{ 919 "Content-Type": "application/json", 920 } 921 Expect(response).Should(IncludeHeaderEntries(expectedHeaderEntries)) 922 }) 923 924 It("returns the json", func() { 925 body, err := ioutil.ReadAll(response.Body) 926 Expect(err).NotTo(HaveOccurred()) 927 928 Expect(body).To(MatchJSON(`[ 929 { 930 "id": 1024, 931 "team_name": "a-team", 932 "name": "5", 933 "status": "succeeded", 934 "job_name": "some-job", 935 "api_url": "/api/v1/builds/1024", 936 "pipeline_name": "a-pipeline", 937 "start_time": 1, 938 "end_time": 100 939 }, 940 { 941 "id": 1025, 942 "name": "6", 943 "team_name": "a-team", 944 "status": "succeeded", 945 "job_name": "some-job", 946 "api_url": "/api/v1/builds/1025", 947 "pipeline_name": "a-pipeline", 948 "start_time": 200, 949 "end_time": 300 950 } 951 ]`)) 952 }) 953 }) 954 955 Context("when the version ID is invalid", func() { 956 BeforeEach(func() { 957 stringVersionID = "hello" 958 }) 959 960 It("returns an empty list", func() { 961 body, err := ioutil.ReadAll(response.Body) 962 Expect(err).NotTo(HaveOccurred()) 963 964 Expect(body).To(MatchJSON(`[]`)) 965 }) 966 }) 967 968 Context("when the call to get builds returns an error", func() { 969 BeforeEach(func() { 970 fakePipeline.GetBuildsWithVersionAsInputReturns(nil, errors.New("NOPE")) 971 }) 972 973 It("returns a 500 internal server error", func() { 974 Expect(response.StatusCode).To(Equal(http.StatusInternalServerError)) 975 }) 976 }) 977 }) 978 }) 979 }) 980 981 Describe("GET /api/v1/teams/:team_name/pipelines/:pipeline_name/resources/:resource_name/versions/:resource_version_id/output_of", func() { 982 var response *http.Response 983 var stringVersionID string 984 var fakeResource *dbfakes.FakeResource 985 986 JustBeforeEach(func() { 987 var err error 988 989 request, err := http.NewRequest("GET", server.URL+"/api/v1/teams/a-team/pipelines/a-pipeline/resources/some-resource/versions/"+stringVersionID+"/output_of", nil) 990 Expect(err).NotTo(HaveOccurred()) 991 992 response, err = client.Do(request) 993 Expect(err).NotTo(HaveOccurred()) 994 }) 995 996 BeforeEach(func() { 997 stringVersionID = "123" 998 fakeResource = new(dbfakes.FakeResource) 999 fakeResource.IDReturns(1) 1000 }) 1001 1002 Context("when not authorized", func() { 1003 BeforeEach(func() { 1004 fakeAccess.IsAuthorizedReturns(false) 1005 }) 1006 1007 Context("and the pipeline is private", func() { 1008 BeforeEach(func() { 1009 fakePipeline.PublicReturns(false) 1010 }) 1011 1012 Context("when authenticated", func() { 1013 BeforeEach(func() { 1014 fakeAccess.IsAuthenticatedReturns(true) 1015 }) 1016 1017 It("returns 403", func() { 1018 Expect(response.StatusCode).To(Equal(http.StatusForbidden)) 1019 }) 1020 }) 1021 1022 Context("when not authenticated", func() { 1023 BeforeEach(func() { 1024 fakeAccess.IsAuthenticatedReturns(false) 1025 }) 1026 1027 It("returns 401", func() { 1028 Expect(response.StatusCode).To(Equal(http.StatusUnauthorized)) 1029 }) 1030 }) 1031 }) 1032 1033 Context("and the pipeline is public", func() { 1034 BeforeEach(func() { 1035 fakePipeline.PublicReturns(true) 1036 fakePipeline.ResourceReturns(fakeResource, true, nil) 1037 }) 1038 1039 It("returns 200 OK", func() { 1040 Expect(response.StatusCode).To(Equal(http.StatusOK)) 1041 }) 1042 }) 1043 }) 1044 1045 Context("when authorized", func() { 1046 BeforeEach(func() { 1047 fakeAccess.IsAuthenticatedReturns(true) 1048 fakeAccess.IsAuthorizedReturns(true) 1049 }) 1050 1051 Context("when not finding the resource", func() { 1052 BeforeEach(func() { 1053 fakePipeline.ResourceReturns(nil, false, nil) 1054 }) 1055 1056 It("returns 404", func() { 1057 Expect(response.StatusCode).To(Equal(http.StatusNotFound)) 1058 }) 1059 }) 1060 1061 Context("when failing to retrieve the resource", func() { 1062 BeforeEach(func() { 1063 fakePipeline.ResourceReturns(nil, false, errors.New("banana")) 1064 }) 1065 1066 It("returns 500", func() { 1067 Expect(response.StatusCode).To(Equal(http.StatusInternalServerError)) 1068 }) 1069 }) 1070 1071 It("looks for the resource", func() { 1072 Expect(fakePipeline.ResourceCallCount()).To(Equal(1)) 1073 Expect(fakePipeline.ResourceArgsForCall(0)).To(Equal("some-resource")) 1074 }) 1075 1076 Context("when resource retrieval succeeds", func() { 1077 BeforeEach(func() { 1078 fakePipeline.ResourceReturns(fakeResource, true, nil) 1079 }) 1080 1081 It("looks up the given version ID", func() { 1082 Expect(fakePipeline.GetBuildsWithVersionAsOutputCallCount()).To(Equal(1)) 1083 resourceID, versionID := fakePipeline.GetBuildsWithVersionAsOutputArgsForCall(0) 1084 Expect(resourceID).To(Equal(1)) 1085 Expect(versionID).To(Equal(123)) 1086 }) 1087 1088 Context("when getting the builds succeeds", func() { 1089 BeforeEach(func() { 1090 build1 := new(dbfakes.FakeBuild) 1091 build1.IDReturns(1024) 1092 build1.NameReturns("5") 1093 build1.JobNameReturns("some-job") 1094 build1.PipelineNameReturns("a-pipeline") 1095 build1.TeamNameReturns("a-team") 1096 build1.StatusReturns(db.BuildStatusSucceeded) 1097 build1.StartTimeReturns(time.Unix(1, 0)) 1098 build1.EndTimeReturns(time.Unix(100, 0)) 1099 1100 build2 := new(dbfakes.FakeBuild) 1101 build2.IDReturns(1025) 1102 build2.NameReturns("6") 1103 build2.JobNameReturns("some-job") 1104 build2.PipelineNameReturns("a-pipeline") 1105 build2.TeamNameReturns("a-team") 1106 build2.StatusReturns(db.BuildStatusSucceeded) 1107 build2.StartTimeReturns(time.Unix(200, 0)) 1108 build2.EndTimeReturns(time.Unix(300, 0)) 1109 1110 fakePipeline.GetBuildsWithVersionAsOutputReturns([]db.Build{build1, build2}, nil) 1111 }) 1112 1113 It("returns 200 OK", func() { 1114 Expect(response.StatusCode).To(Equal(http.StatusOK)) 1115 }) 1116 1117 It("returns content type application/json", func() { 1118 expectedHeaderEntries := map[string]string{ 1119 "Content-Type": "application/json", 1120 } 1121 Expect(response).Should(IncludeHeaderEntries(expectedHeaderEntries)) 1122 }) 1123 1124 It("returns the json", func() { 1125 body, err := ioutil.ReadAll(response.Body) 1126 Expect(err).NotTo(HaveOccurred()) 1127 1128 Expect(body).To(MatchJSON(`[ 1129 { 1130 "id": 1024, 1131 "name": "5", 1132 "status": "succeeded", 1133 "job_name": "some-job", 1134 "api_url": "/api/v1/builds/1024", 1135 "pipeline_name": "a-pipeline", 1136 "team_name": "a-team", 1137 "start_time": 1, 1138 "end_time": 100 1139 }, 1140 { 1141 "id": 1025, 1142 "name": "6", 1143 "status": "succeeded", 1144 "job_name": "some-job", 1145 "api_url": "/api/v1/builds/1025", 1146 "pipeline_name": "a-pipeline", 1147 "team_name": "a-team", 1148 "start_time": 200, 1149 "end_time": 300 1150 } 1151 ]`)) 1152 }) 1153 }) 1154 1155 Context("when the version ID is invalid", func() { 1156 BeforeEach(func() { 1157 stringVersionID = "hello" 1158 }) 1159 1160 It("returns an empty list", func() { 1161 body, err := ioutil.ReadAll(response.Body) 1162 Expect(err).NotTo(HaveOccurred()) 1163 1164 Expect(body).To(MatchJSON(`[]`)) 1165 }) 1166 }) 1167 1168 Context("when the call to get builds returns an error", func() { 1169 BeforeEach(func() { 1170 fakePipeline.GetBuildsWithVersionAsOutputReturns(nil, errors.New("NOPE")) 1171 }) 1172 1173 It("returns a 500 internal server error", func() { 1174 Expect(response.StatusCode).To(Equal(http.StatusInternalServerError)) 1175 }) 1176 }) 1177 }) 1178 }) 1179 }) 1180}) 1181