github.com/pf-qiu/concourse/v6@v6.7.3-0.20201207032516-1f455d73275f/atc/api/versions_test.go (about) 1 package api_test 2 3 import ( 4 "errors" 5 "fmt" 6 "io/ioutil" 7 "net/http" 8 "time" 9 10 "github.com/pf-qiu/concourse/v6/atc" 11 "github.com/pf-qiu/concourse/v6/atc/db" 12 "github.com/pf-qiu/concourse/v6/atc/db/dbfakes" 13 . "github.com/pf-qiu/concourse/v6/atc/testhelpers" 14 . "github.com/onsi/ginkgo" 15 . "github.com/onsi/gomega" 16 ) 17 18 var _ = 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 Context("and resource is on an instanced pipeline", func() { 416 BeforeEach(func() { 417 fakePipeline.InstanceVarsReturns(atc.InstanceVars{"branch": "master"}) 418 }) 419 420 It("returns Link headers per rfc5988", func() { 421 link := fmt.Sprintf(`<%s/api/v1/teams/a-team/pipelines/some-pipeline/resources/some-resource/versions?`, externalURL) 422 Expect(response.Header["Link"]).To(ConsistOf([]string{ 423 link + `to=2&limit=2&instance_vars=%7B%22branch%22%3A%22master%22%7D>; rel="next"`, 424 link + `from=4&limit=2&instance_vars=%7B%22branch%22%3A%22master%22%7D>; rel="previous"`, 425 })) 426 }) 427 }) 428 }) 429 }) 430 431 Context("when the versions can't be found", func() { 432 BeforeEach(func() { 433 fakeResource.VersionsReturns(nil, db.Pagination{}, false, nil) 434 }) 435 436 It("returns 404 not found", func() { 437 Expect(response.StatusCode).To(Equal(http.StatusNotFound)) 438 }) 439 }) 440 441 Context("when getting the versions fails", func() { 442 BeforeEach(func() { 443 fakeResource.VersionsReturns(nil, db.Pagination{}, false, errors.New("oh no!")) 444 }) 445 446 It("returns 500 Internal Server Error", func() { 447 Expect(response.StatusCode).To(Equal(http.StatusInternalServerError)) 448 }) 449 }) 450 }) 451 452 Context("when finding the resource fails", func() { 453 BeforeEach(func() { 454 fakePipeline.ResourceReturns(nil, false, errors.New("oh no!")) 455 }) 456 457 It("returns 500 Internal Server Error", func() { 458 Expect(response.StatusCode).To(Equal(http.StatusInternalServerError)) 459 }) 460 }) 461 462 Context("when the resource is not found", func() { 463 BeforeEach(func() { 464 fakePipeline.ResourceReturns(nil, false, nil) 465 }) 466 467 It("returns 404 not found", func() { 468 Expect(response.StatusCode).To(Equal(http.StatusNotFound)) 469 }) 470 }) 471 }) 472 }) 473 474 Describe("PUT /api/v1/teams/:team_name/pipelines/:pipeline_name/resources/:resource_name/versions/:resource_version_id/enable", func() { 475 var response *http.Response 476 var fakeResource *dbfakes.FakeResource 477 478 JustBeforeEach(func() { 479 var err error 480 481 request, err := http.NewRequest("PUT", server.URL+"/api/v1/teams/a-team/pipelines/a-pipeline/resources/resource-name/versions/42/enable", nil) 482 Expect(err).NotTo(HaveOccurred()) 483 484 response, err = client.Do(request) 485 Expect(err).NotTo(HaveOccurred()) 486 }) 487 488 Context("when authenticated", func() { 489 BeforeEach(func() { 490 fakeAccess.IsAuthenticatedReturns(true) 491 }) 492 493 Context("when authorized", func() { 494 BeforeEach(func() { 495 fakeAccess.IsAuthorizedReturns(true) 496 }) 497 498 It("tries to find the resource", func() { 499 resourceName := fakePipeline.ResourceArgsForCall(0) 500 Expect(resourceName).To(Equal("resource-name")) 501 }) 502 503 Context("when finding the resource succeeds", func() { 504 BeforeEach(func() { 505 fakeResource = new(dbfakes.FakeResource) 506 fakeResource.IDReturns(1) 507 fakePipeline.ResourceReturns(fakeResource, true, nil) 508 }) 509 510 It("tries to enable the right resource config version", func() { 511 resourceConfigVersionID := fakeResource.EnableVersionArgsForCall(0) 512 Expect(resourceConfigVersionID).To(Equal(42)) 513 }) 514 515 Context("when enabling the resource succeeds", func() { 516 BeforeEach(func() { 517 fakeResource.EnableVersionReturns(nil) 518 }) 519 520 It("returns 200", func() { 521 Expect(response.StatusCode).To(Equal(http.StatusOK)) 522 }) 523 }) 524 525 Context("when enabling the resource fails", func() { 526 BeforeEach(func() { 527 fakeResource.EnableVersionReturns(errors.New("welp")) 528 }) 529 530 It("returns 500", func() { 531 Expect(response.StatusCode).To(Equal(http.StatusInternalServerError)) 532 }) 533 }) 534 }) 535 536 Context("when it fails to find the resource", func() { 537 BeforeEach(func() { 538 fakePipeline.ResourceReturns(nil, false, errors.New("welp")) 539 }) 540 541 It("returns Internal Server Error", func() { 542 Expect(response.StatusCode).To(Equal(http.StatusInternalServerError)) 543 }) 544 }) 545 546 Context("when the resource is not found", func() { 547 BeforeEach(func() { 548 fakePipeline.ResourceReturns(nil, false, nil) 549 }) 550 551 It("returns not found", func() { 552 Expect(response.StatusCode).To(Equal(http.StatusNotFound)) 553 }) 554 }) 555 }) 556 557 Context("when not authorized", func() { 558 BeforeEach(func() { 559 fakeAccess.IsAuthorizedReturns(false) 560 }) 561 It("returns Forbidden", func() { 562 Expect(response.StatusCode).To(Equal(http.StatusForbidden)) 563 }) 564 }) 565 }) 566 567 Context("when not authenticated", func() { 568 BeforeEach(func() { 569 fakeAccess.IsAuthenticatedReturns(false) 570 }) 571 572 It("returns Unauthorized", func() { 573 Expect(response.StatusCode).To(Equal(http.StatusUnauthorized)) 574 }) 575 }) 576 }) 577 578 Describe("PUT /api/v1/teams/:team_name/pipelines/:pipeline_name/resources/:resource_name/versions/:resource_version_id/disable", func() { 579 var response *http.Response 580 var fakeResource *dbfakes.FakeResource 581 582 JustBeforeEach(func() { 583 var err error 584 585 request, err := http.NewRequest("PUT", server.URL+"/api/v1/teams/a-team/pipelines/a-pipeline/resources/resource-name/versions/42/disable", nil) 586 Expect(err).NotTo(HaveOccurred()) 587 588 response, err = client.Do(request) 589 Expect(err).NotTo(HaveOccurred()) 590 }) 591 592 Context("when authenticated ", func() { 593 BeforeEach(func() { 594 fakeAccess.IsAuthenticatedReturns(true) 595 }) 596 597 Context("when authorized", func() { 598 BeforeEach(func() { 599 fakeAccess.IsAuthorizedReturns(true) 600 }) 601 602 It("tries to find the resource", func() { 603 resourceName := fakePipeline.ResourceArgsForCall(0) 604 Expect(resourceName).To(Equal("resource-name")) 605 }) 606 607 Context("when finding the resource succeeds", func() { 608 BeforeEach(func() { 609 fakeResource = new(dbfakes.FakeResource) 610 fakeResource.IDReturns(1) 611 fakePipeline.ResourceReturns(fakeResource, true, nil) 612 }) 613 614 It("tries to disable the right resource config version", func() { 615 resourceConfigVersionID := fakeResource.DisableVersionArgsForCall(0) 616 Expect(resourceConfigVersionID).To(Equal(42)) 617 }) 618 619 Context("when disabling the resource version succeeds", func() { 620 BeforeEach(func() { 621 fakeResource.DisableVersionReturns(nil) 622 }) 623 624 It("returns 200", func() { 625 Expect(response.StatusCode).To(Equal(http.StatusOK)) 626 }) 627 }) 628 629 Context("when disabling the resource fails", func() { 630 BeforeEach(func() { 631 fakeResource.DisableVersionReturns(errors.New("welp")) 632 }) 633 634 It("returns 500", func() { 635 Expect(response.StatusCode).To(Equal(http.StatusInternalServerError)) 636 }) 637 }) 638 }) 639 640 Context("when it fails to find the resource", func() { 641 BeforeEach(func() { 642 fakePipeline.ResourceReturns(nil, false, errors.New("welp")) 643 }) 644 645 It("returns Internal Server Error", func() { 646 Expect(response.StatusCode).To(Equal(http.StatusInternalServerError)) 647 }) 648 }) 649 650 Context("when the resource is not found", func() { 651 BeforeEach(func() { 652 fakePipeline.ResourceReturns(nil, false, nil) 653 }) 654 655 It("returns not found", func() { 656 Expect(response.StatusCode).To(Equal(http.StatusNotFound)) 657 }) 658 }) 659 }) 660 Context("when not authorized", func() { 661 BeforeEach(func() { 662 fakeAccess.IsAuthorizedReturns(false) 663 }) 664 665 It("returns Forbidden", func() { 666 Expect(response.StatusCode).To(Equal(http.StatusForbidden)) 667 }) 668 }) 669 }) 670 Context("when not authenticated", func() { 671 BeforeEach(func() { 672 fakeAccess.IsAuthenticatedReturns(false) 673 }) 674 675 It("returns Unauthorized", func() { 676 Expect(response.StatusCode).To(Equal(http.StatusUnauthorized)) 677 }) 678 }) 679 }) 680 681 Describe("PUT /api/v1/teams/:team_name/pipelines/:pipeline_name/resources/:resource_name/versions/:resource_version_id/pin", func() { 682 var response *http.Response 683 var fakeResource *dbfakes.FakeResource 684 685 JustBeforeEach(func() { 686 var err error 687 688 request, err := http.NewRequest("PUT", server.URL+"/api/v1/teams/a-team/pipelines/a-pipeline/resources/resource-name/versions/42/pin", nil) 689 Expect(err).NotTo(HaveOccurred()) 690 691 response, err = client.Do(request) 692 Expect(err).NotTo(HaveOccurred()) 693 }) 694 695 Context("when authenticated", func() { 696 BeforeEach(func() { 697 fakeAccess.IsAuthenticatedReturns(true) 698 }) 699 700 Context("when authorized", func() { 701 BeforeEach(func() { 702 fakeAccess.IsAuthorizedReturns(true) 703 }) 704 705 It("tries to find the resource", func() { 706 resourceName := fakePipeline.ResourceArgsForCall(0) 707 Expect(resourceName).To(Equal("resource-name")) 708 }) 709 710 Context("when finding the resource succeeds", func() { 711 BeforeEach(func() { 712 fakeResource = new(dbfakes.FakeResource) 713 fakeResource.IDReturns(1) 714 fakePipeline.ResourceReturns(fakeResource, true, nil) 715 }) 716 717 It("tries to pin the right resource config version", func() { 718 resourceConfigVersionID := fakeResource.PinVersionArgsForCall(0) 719 Expect(resourceConfigVersionID).To(Equal(42)) 720 }) 721 722 Context("when pinning the resource succeeds", func() { 723 BeforeEach(func() { 724 fakeResource.PinVersionReturns(true, nil) 725 }) 726 727 It("returns 200", func() { 728 Expect(response.StatusCode).To(Equal(http.StatusOK)) 729 }) 730 }) 731 732 Context("when pinning the resource fails by resource not exist", func() { 733 BeforeEach(func() { 734 fakeResource.PinVersionReturns(false, nil) 735 }) 736 737 It("returns 404", func() { 738 Expect(response.StatusCode).To(Equal(http.StatusNotFound)) 739 }) 740 }) 741 742 Context("when pinning the resource fails by error", func() { 743 BeforeEach(func() { 744 fakeResource.PinVersionReturns(false, errors.New("welp")) 745 }) 746 747 It("returns 500", func() { 748 Expect(response.StatusCode).To(Equal(http.StatusInternalServerError)) 749 }) 750 }) 751 }) 752 753 Context("when it fails to find the resource", func() { 754 BeforeEach(func() { 755 fakePipeline.ResourceReturns(nil, false, errors.New("welp")) 756 }) 757 758 It("returns Internal Server Error", func() { 759 Expect(response.StatusCode).To(Equal(http.StatusInternalServerError)) 760 }) 761 }) 762 763 Context("when the resource is not found", func() { 764 BeforeEach(func() { 765 fakePipeline.ResourceReturns(nil, false, nil) 766 }) 767 768 It("returns not found", func() { 769 Expect(response.StatusCode).To(Equal(http.StatusNotFound)) 770 }) 771 }) 772 }) 773 774 Context("when not authorized", func() { 775 BeforeEach(func() { 776 fakeAccess.IsAuthorizedReturns(false) 777 }) 778 It("returns Forbidden", func() { 779 Expect(response.StatusCode).To(Equal(http.StatusForbidden)) 780 }) 781 }) 782 }) 783 784 Context("when not authenticated", func() { 785 BeforeEach(func() { 786 fakeAccess.IsAuthenticatedReturns(false) 787 }) 788 789 It("returns Unauthorized", func() { 790 Expect(response.StatusCode).To(Equal(http.StatusUnauthorized)) 791 }) 792 }) 793 }) 794 795 Describe("GET /api/v1/teams/:team_name/pipelines/:pipeline_name/resources/:resource_name/versions/:resource_version_id/input_to", func() { 796 var response *http.Response 797 var stringVersionID string 798 var fakeResource *dbfakes.FakeResource 799 800 JustBeforeEach(func() { 801 var err error 802 803 request, err := http.NewRequest("GET", server.URL+"/api/v1/teams/a-team/pipelines/a-pipeline/resources/some-resource/versions/"+stringVersionID+"/input_to", nil) 804 Expect(err).NotTo(HaveOccurred()) 805 806 response, err = client.Do(request) 807 Expect(err).NotTo(HaveOccurred()) 808 }) 809 810 BeforeEach(func() { 811 fakeResource = new(dbfakes.FakeResource) 812 fakeResource.IDReturns(1) 813 stringVersionID = "123" 814 }) 815 816 Context("when not authorized", func() { 817 BeforeEach(func() { 818 fakeAccess.IsAuthorizedReturns(false) 819 }) 820 821 Context("and the pipeline is private", func() { 822 BeforeEach(func() { 823 fakePipeline.PublicReturns(false) 824 }) 825 826 Context("when authenticated", func() { 827 BeforeEach(func() { 828 fakeAccess.IsAuthenticatedReturns(true) 829 }) 830 831 It("returns 403", func() { 832 Expect(response.StatusCode).To(Equal(http.StatusForbidden)) 833 }) 834 }) 835 836 Context("when not authenticated", func() { 837 BeforeEach(func() { 838 fakeAccess.IsAuthenticatedReturns(false) 839 }) 840 841 It("returns 401", func() { 842 Expect(response.StatusCode).To(Equal(http.StatusUnauthorized)) 843 }) 844 }) 845 }) 846 847 Context("and the pipeline is public", func() { 848 BeforeEach(func() { 849 fakePipeline.PublicReturns(true) 850 fakePipeline.ResourceReturns(fakeResource, true, nil) 851 }) 852 853 It("returns 200 OK", func() { 854 Expect(response.StatusCode).To(Equal(http.StatusOK)) 855 }) 856 }) 857 }) 858 859 Context("when authorized", func() { 860 BeforeEach(func() { 861 fakeAccess.IsAuthenticatedReturns(true) 862 fakeAccess.IsAuthorizedReturns(true) 863 }) 864 865 Context("when not finding the resource", func() { 866 BeforeEach(func() { 867 fakePipeline.ResourceReturns(nil, false, nil) 868 }) 869 870 It("returns 404", func() { 871 Expect(response.StatusCode).To(Equal(http.StatusNotFound)) 872 }) 873 }) 874 875 Context("when failing to retrieve the resource", func() { 876 BeforeEach(func() { 877 fakePipeline.ResourceReturns(nil, false, errors.New("banana")) 878 }) 879 880 It("returns 500", func() { 881 Expect(response.StatusCode).To(Equal(http.StatusInternalServerError)) 882 }) 883 }) 884 885 It("looks for the resource", func() { 886 Expect(fakePipeline.ResourceCallCount()).To(Equal(1)) 887 Expect(fakePipeline.ResourceArgsForCall(0)).To(Equal("some-resource")) 888 }) 889 890 Context("when resource retrieval succeeds", func() { 891 BeforeEach(func() { 892 fakePipeline.ResourceReturns(fakeResource, true, nil) 893 }) 894 895 It("looks up the given version ID", func() { 896 Expect(fakePipeline.GetBuildsWithVersionAsInputCallCount()).To(Equal(1)) 897 resourceID, versionID := fakePipeline.GetBuildsWithVersionAsInputArgsForCall(0) 898 Expect(resourceID).To(Equal(1)) 899 Expect(versionID).To(Equal(123)) 900 }) 901 902 Context("when getting the builds succeeds", func() { 903 BeforeEach(func() { 904 build1 := new(dbfakes.FakeBuild) 905 build1.IDReturns(1024) 906 build1.NameReturns("5") 907 build1.JobNameReturns("some-job") 908 build1.PipelineNameReturns("a-pipeline") 909 build1.TeamNameReturns("a-team") 910 build1.StatusReturns(db.BuildStatusSucceeded) 911 build1.StartTimeReturns(time.Unix(1, 0)) 912 build1.EndTimeReturns(time.Unix(100, 0)) 913 914 build2 := new(dbfakes.FakeBuild) 915 build2.IDReturns(1025) 916 build2.NameReturns("6") 917 build2.JobNameReturns("some-job") 918 build2.PipelineNameReturns("a-pipeline") 919 build2.TeamNameReturns("a-team") 920 build2.StatusReturns(db.BuildStatusSucceeded) 921 build2.StartTimeReturns(time.Unix(200, 0)) 922 build2.EndTimeReturns(time.Unix(300, 0)) 923 924 fakePipeline.GetBuildsWithVersionAsInputReturns([]db.Build{build1, build2}, nil) 925 }) 926 927 It("returns 200 OK", func() { 928 Expect(response.StatusCode).To(Equal(http.StatusOK)) 929 }) 930 931 It("returns content type application/json", func() { 932 expectedHeaderEntries := map[string]string{ 933 "Content-Type": "application/json", 934 } 935 Expect(response).Should(IncludeHeaderEntries(expectedHeaderEntries)) 936 }) 937 938 It("returns the json", func() { 939 body, err := ioutil.ReadAll(response.Body) 940 Expect(err).NotTo(HaveOccurred()) 941 942 Expect(body).To(MatchJSON(`[ 943 { 944 "id": 1024, 945 "team_name": "a-team", 946 "name": "5", 947 "status": "succeeded", 948 "job_name": "some-job", 949 "api_url": "/api/v1/builds/1024", 950 "pipeline_name": "a-pipeline", 951 "start_time": 1, 952 "end_time": 100 953 }, 954 { 955 "id": 1025, 956 "name": "6", 957 "team_name": "a-team", 958 "status": "succeeded", 959 "job_name": "some-job", 960 "api_url": "/api/v1/builds/1025", 961 "pipeline_name": "a-pipeline", 962 "start_time": 200, 963 "end_time": 300 964 } 965 ]`)) 966 }) 967 }) 968 969 Context("when the version ID is invalid", func() { 970 BeforeEach(func() { 971 stringVersionID = "hello" 972 }) 973 974 It("returns an empty list", func() { 975 body, err := ioutil.ReadAll(response.Body) 976 Expect(err).NotTo(HaveOccurred()) 977 978 Expect(body).To(MatchJSON(`[]`)) 979 }) 980 }) 981 982 Context("when the call to get builds returns an error", func() { 983 BeforeEach(func() { 984 fakePipeline.GetBuildsWithVersionAsInputReturns(nil, errors.New("NOPE")) 985 }) 986 987 It("returns a 500 internal server error", func() { 988 Expect(response.StatusCode).To(Equal(http.StatusInternalServerError)) 989 }) 990 }) 991 }) 992 }) 993 }) 994 995 Describe("GET /api/v1/teams/:team_name/pipelines/:pipeline_name/resources/:resource_name/versions/:resource_version_id/output_of", func() { 996 var response *http.Response 997 var stringVersionID string 998 var fakeResource *dbfakes.FakeResource 999 1000 JustBeforeEach(func() { 1001 var err error 1002 1003 request, err := http.NewRequest("GET", server.URL+"/api/v1/teams/a-team/pipelines/a-pipeline/resources/some-resource/versions/"+stringVersionID+"/output_of", nil) 1004 Expect(err).NotTo(HaveOccurred()) 1005 1006 response, err = client.Do(request) 1007 Expect(err).NotTo(HaveOccurred()) 1008 }) 1009 1010 BeforeEach(func() { 1011 stringVersionID = "123" 1012 fakeResource = new(dbfakes.FakeResource) 1013 fakeResource.IDReturns(1) 1014 }) 1015 1016 Context("when not authorized", func() { 1017 BeforeEach(func() { 1018 fakeAccess.IsAuthorizedReturns(false) 1019 }) 1020 1021 Context("and the pipeline is private", func() { 1022 BeforeEach(func() { 1023 fakePipeline.PublicReturns(false) 1024 }) 1025 1026 Context("when authenticated", func() { 1027 BeforeEach(func() { 1028 fakeAccess.IsAuthenticatedReturns(true) 1029 }) 1030 1031 It("returns 403", func() { 1032 Expect(response.StatusCode).To(Equal(http.StatusForbidden)) 1033 }) 1034 }) 1035 1036 Context("when not authenticated", func() { 1037 BeforeEach(func() { 1038 fakeAccess.IsAuthenticatedReturns(false) 1039 }) 1040 1041 It("returns 401", func() { 1042 Expect(response.StatusCode).To(Equal(http.StatusUnauthorized)) 1043 }) 1044 }) 1045 }) 1046 1047 Context("and the pipeline is public", func() { 1048 BeforeEach(func() { 1049 fakePipeline.PublicReturns(true) 1050 fakePipeline.ResourceReturns(fakeResource, true, nil) 1051 }) 1052 1053 It("returns 200 OK", func() { 1054 Expect(response.StatusCode).To(Equal(http.StatusOK)) 1055 }) 1056 }) 1057 }) 1058 1059 Context("when authorized", func() { 1060 BeforeEach(func() { 1061 fakeAccess.IsAuthenticatedReturns(true) 1062 fakeAccess.IsAuthorizedReturns(true) 1063 }) 1064 1065 Context("when not finding the resource", func() { 1066 BeforeEach(func() { 1067 fakePipeline.ResourceReturns(nil, false, nil) 1068 }) 1069 1070 It("returns 404", func() { 1071 Expect(response.StatusCode).To(Equal(http.StatusNotFound)) 1072 }) 1073 }) 1074 1075 Context("when failing to retrieve the resource", func() { 1076 BeforeEach(func() { 1077 fakePipeline.ResourceReturns(nil, false, errors.New("banana")) 1078 }) 1079 1080 It("returns 500", func() { 1081 Expect(response.StatusCode).To(Equal(http.StatusInternalServerError)) 1082 }) 1083 }) 1084 1085 It("looks for the resource", func() { 1086 Expect(fakePipeline.ResourceCallCount()).To(Equal(1)) 1087 Expect(fakePipeline.ResourceArgsForCall(0)).To(Equal("some-resource")) 1088 }) 1089 1090 Context("when resource retrieval succeeds", func() { 1091 BeforeEach(func() { 1092 fakePipeline.ResourceReturns(fakeResource, true, nil) 1093 }) 1094 1095 It("looks up the given version ID", func() { 1096 Expect(fakePipeline.GetBuildsWithVersionAsOutputCallCount()).To(Equal(1)) 1097 resourceID, versionID := fakePipeline.GetBuildsWithVersionAsOutputArgsForCall(0) 1098 Expect(resourceID).To(Equal(1)) 1099 Expect(versionID).To(Equal(123)) 1100 }) 1101 1102 Context("when getting the builds succeeds", func() { 1103 BeforeEach(func() { 1104 build1 := new(dbfakes.FakeBuild) 1105 build1.IDReturns(1024) 1106 build1.NameReturns("5") 1107 build1.JobNameReturns("some-job") 1108 build1.PipelineNameReturns("a-pipeline") 1109 build1.TeamNameReturns("a-team") 1110 build1.StatusReturns(db.BuildStatusSucceeded) 1111 build1.StartTimeReturns(time.Unix(1, 0)) 1112 build1.EndTimeReturns(time.Unix(100, 0)) 1113 1114 build2 := new(dbfakes.FakeBuild) 1115 build2.IDReturns(1025) 1116 build2.NameReturns("6") 1117 build2.JobNameReturns("some-job") 1118 build2.PipelineNameReturns("a-pipeline") 1119 build2.TeamNameReturns("a-team") 1120 build2.StatusReturns(db.BuildStatusSucceeded) 1121 build2.StartTimeReturns(time.Unix(200, 0)) 1122 build2.EndTimeReturns(time.Unix(300, 0)) 1123 1124 fakePipeline.GetBuildsWithVersionAsOutputReturns([]db.Build{build1, build2}, nil) 1125 }) 1126 1127 It("returns 200 OK", func() { 1128 Expect(response.StatusCode).To(Equal(http.StatusOK)) 1129 }) 1130 1131 It("returns content type application/json", func() { 1132 expectedHeaderEntries := map[string]string{ 1133 "Content-Type": "application/json", 1134 } 1135 Expect(response).Should(IncludeHeaderEntries(expectedHeaderEntries)) 1136 }) 1137 1138 It("returns the json", func() { 1139 body, err := ioutil.ReadAll(response.Body) 1140 Expect(err).NotTo(HaveOccurred()) 1141 1142 Expect(body).To(MatchJSON(`[ 1143 { 1144 "id": 1024, 1145 "name": "5", 1146 "status": "succeeded", 1147 "job_name": "some-job", 1148 "api_url": "/api/v1/builds/1024", 1149 "pipeline_name": "a-pipeline", 1150 "team_name": "a-team", 1151 "start_time": 1, 1152 "end_time": 100 1153 }, 1154 { 1155 "id": 1025, 1156 "name": "6", 1157 "status": "succeeded", 1158 "job_name": "some-job", 1159 "api_url": "/api/v1/builds/1025", 1160 "pipeline_name": "a-pipeline", 1161 "team_name": "a-team", 1162 "start_time": 200, 1163 "end_time": 300 1164 } 1165 ]`)) 1166 }) 1167 }) 1168 1169 Context("when the version ID is invalid", func() { 1170 BeforeEach(func() { 1171 stringVersionID = "hello" 1172 }) 1173 1174 It("returns an empty list", func() { 1175 body, err := ioutil.ReadAll(response.Body) 1176 Expect(err).NotTo(HaveOccurred()) 1177 1178 Expect(body).To(MatchJSON(`[]`)) 1179 }) 1180 }) 1181 1182 Context("when the call to get builds returns an error", func() { 1183 BeforeEach(func() { 1184 fakePipeline.GetBuildsWithVersionAsOutputReturns(nil, errors.New("NOPE")) 1185 }) 1186 1187 It("returns a 500 internal server error", func() { 1188 Expect(response.StatusCode).To(Equal(http.StatusInternalServerError)) 1189 }) 1190 }) 1191 }) 1192 }) 1193 }) 1194 })