github.com/pf-qiu/concourse/v6@v6.7.3-0.20201207032516-1f455d73275f/atc/api/builds_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 var _ = Describe("Builds API", func() { 21 22 Describe("POST /api/v1/builds", func() { 23 var plan atc.Plan 24 var response *http.Response 25 26 BeforeEach(func() { 27 plan = atc.Plan{ 28 Task: &atc.TaskPlan{ 29 Config: &atc.TaskConfig{ 30 Run: atc.TaskRunConfig{ 31 Path: "ls", 32 }, 33 }, 34 }, 35 } 36 }) 37 38 JustBeforeEach(func() { 39 reqPayload, err := json.Marshal(plan) 40 Expect(err).NotTo(HaveOccurred()) 41 42 req, err := http.NewRequest("POST", server.URL+"/api/v1/teams/some-team/builds", bytes.NewBuffer(reqPayload)) 43 Expect(err).NotTo(HaveOccurred()) 44 45 req.Header.Set("Content-Type", "application/json") 46 47 response, err = client.Do(req) 48 Expect(err).NotTo(HaveOccurred()) 49 }) 50 51 Context("when not authenticated", func() { 52 BeforeEach(func() { 53 fakeAccess.IsAuthenticatedReturns(false) 54 }) 55 56 It("returns 401", func() { 57 Expect(response.StatusCode).To(Equal(http.StatusUnauthorized)) 58 }) 59 60 It("does not trigger a build", func() { 61 Expect(dbTeam.CreateStartedBuildCallCount()).To(BeZero()) 62 }) 63 }) 64 65 Context("when authenticated", func() { 66 BeforeEach(func() { 67 fakeAccess.IsAuthenticatedReturns(true) 68 }) 69 70 Context("when not authorized", func() { 71 BeforeEach(func() { 72 fakeAccess.IsAuthorizedReturns(false) 73 }) 74 75 It("returns 403", func() { 76 Expect(response.StatusCode).To(Equal(http.StatusForbidden)) 77 }) 78 }) 79 80 Context("when authorized", func() { 81 BeforeEach(func() { 82 fakeAccess.IsAuthorizedReturns(true) 83 }) 84 85 Context("when creating a started build fails", func() { 86 BeforeEach(func() { 87 dbTeam.CreateStartedBuildReturns(nil, errors.New("oh no!")) 88 }) 89 90 It("returns 500 Internal Server Error", func() { 91 Expect(response.StatusCode).To(Equal(http.StatusInternalServerError)) 92 }) 93 }) 94 95 Context("when creating a started build succeeds", func() { 96 var fakeBuild *dbfakes.FakeBuild 97 98 BeforeEach(func() { 99 fakeBuild = new(dbfakes.FakeBuild) 100 fakeBuild.IDReturns(42) 101 fakeBuild.NameReturns("1") 102 fakeBuild.TeamNameReturns("some-team") 103 fakeBuild.StatusReturns("started") 104 fakeBuild.StartTimeReturns(time.Unix(1, 0)) 105 fakeBuild.EndTimeReturns(time.Unix(100, 0)) 106 fakeBuild.ReapTimeReturns(time.Unix(200, 0)) 107 108 dbTeam.CreateStartedBuildReturns(fakeBuild, nil) 109 }) 110 111 It("returns 201 Created", func() { 112 Expect(response.StatusCode).To(Equal(http.StatusCreated)) 113 }) 114 115 It("returns Content-Type 'application/json'", func() { 116 expectedHeaderEntries := map[string]string{ 117 "Content-Type": "application/json", 118 } 119 Expect(response).Should(IncludeHeaderEntries(expectedHeaderEntries)) 120 }) 121 122 It("creates a started build", func() { 123 Expect(dbTeam.CreateStartedBuildCallCount()).To(Equal(1)) 124 Expect(dbTeam.CreateStartedBuildArgsForCall(0)).To(Equal(plan)) 125 }) 126 127 It("returns the created build", func() { 128 body, err := ioutil.ReadAll(response.Body) 129 Expect(err).NotTo(HaveOccurred()) 130 131 Expect(body).To(MatchJSON(`{ 132 "id": 42, 133 "name": "1", 134 "team_name": "some-team", 135 "status": "started", 136 "api_url": "/api/v1/builds/42", 137 "start_time": 1, 138 "end_time": 100, 139 "reap_time": 200 140 }`)) 141 }) 142 143 }) 144 }) 145 }) 146 }) 147 148 Describe("GET /api/v1/builds", func() { 149 var response *http.Response 150 var queryParams string 151 var returnedBuilds []db.Build 152 153 BeforeEach(func() { 154 queryParams = "" 155 build1 := new(dbfakes.FakeBuild) 156 build1.IDReturns(4) 157 build1.NameReturns("2") 158 build1.JobNameReturns("job2") 159 build1.PipelineNameReturns("pipeline2") 160 build1.TeamNameReturns("some-team") 161 build1.StatusReturns(db.BuildStatusStarted) 162 build1.StartTimeReturns(time.Unix(1, 0)) 163 build1.EndTimeReturns(time.Unix(100, 0)) 164 build1.ReapTimeReturns(time.Unix(300, 0)) 165 166 build2 := new(dbfakes.FakeBuild) 167 build2.IDReturns(3) 168 build2.NameReturns("1") 169 build2.JobNameReturns("job1") 170 build2.PipelineNameReturns("pipeline1") 171 build2.TeamNameReturns("some-team") 172 build2.StatusReturns(db.BuildStatusSucceeded) 173 build2.StartTimeReturns(time.Unix(101, 0)) 174 build2.EndTimeReturns(time.Unix(200, 0)) 175 build2.ReapTimeReturns(time.Unix(400, 0)) 176 177 build3 := new(dbfakes.FakeBuild) 178 build3.IDReturns(5) 179 build3.NameReturns("1") 180 build3.ResourceNameReturns("resource1") 181 build3.PipelineNameReturns("pipeline1") 182 build3.TeamNameReturns("some-team") 183 build3.StatusReturns(db.BuildStatusSucceeded) 184 build3.StartTimeReturns(time.Unix(101, 0)) 185 186 returnedBuilds = []db.Build{build1, build2, build3} 187 fakeAccess.TeamNamesReturns([]string{"some-team"}) 188 }) 189 190 JustBeforeEach(func() { 191 var err error 192 193 response, err = client.Get(server.URL + "/api/v1/builds" + queryParams) 194 Expect(err).NotTo(HaveOccurred()) 195 }) 196 197 Context("when not authenticated", func() { 198 BeforeEach(func() { 199 fakeAccess.IsAuthenticatedReturns(false) 200 }) 201 202 Context("when no params are passed", func() { 203 BeforeEach(func() { 204 queryParams = "" 205 }) 206 207 It("does not set defaults for since and until", func() { 208 Expect(dbBuildFactory.VisibleBuildsCallCount()).To(Equal(1)) 209 210 teamName, page := dbBuildFactory.VisibleBuildsArgsForCall(0) 211 Expect(page).To(Equal(db.Page{ 212 Limit: 100, 213 })) 214 Expect(teamName).To(ConsistOf("some-team")) 215 }) 216 }) 217 218 Context("when all the params are passed", func() { 219 BeforeEach(func() { 220 queryParams = "?from=2&to=3&limit=8" 221 }) 222 223 It("passes them through", func() { 224 Expect(dbBuildFactory.VisibleBuildsCallCount()).To(Equal(1)) 225 226 _, page := dbBuildFactory.VisibleBuildsArgsForCall(0) 227 Expect(page).To(Equal(db.Page{ 228 From: db.NewIntPtr(2), 229 To: db.NewIntPtr(3), 230 Limit: 8, 231 })) 232 }) 233 234 Context("timestamp is provided", func() { 235 BeforeEach(func() { 236 queryParams = "?timestamps=true" 237 }) 238 239 It("calls AllBuilds", func() { 240 _, page := dbBuildFactory.VisibleBuildsArgsForCall(0) 241 Expect(page.UseDate).To(Equal(true)) 242 }) 243 }) 244 }) 245 246 Context("when getting the builds succeeds", func() { 247 BeforeEach(func() { 248 dbBuildFactory.VisibleBuildsReturns(returnedBuilds, db.Pagination{}, nil) 249 }) 250 251 It("returns 200 OK", func() { 252 Expect(response.StatusCode).To(Equal(http.StatusOK)) 253 }) 254 255 It("returns Content-Type 'application/json'", func() { 256 expectedHeaderEntries := map[string]string{ 257 "Content-Type": "application/json", 258 } 259 Expect(response).Should(IncludeHeaderEntries(expectedHeaderEntries)) 260 }) 261 262 It("returns all builds", func() { 263 body, err := ioutil.ReadAll(response.Body) 264 Expect(err).NotTo(HaveOccurred()) 265 266 Expect(body).To(MatchJSON(`[ 267 { 268 "id": 4, 269 "name": "2", 270 "job_name": "job2", 271 "pipeline_name": "pipeline2", 272 "team_name": "some-team", 273 "status": "started", 274 "api_url": "/api/v1/builds/4", 275 "start_time": 1, 276 "end_time": 100, 277 "reap_time": 300 278 }, 279 { 280 "id": 3, 281 "name": "1", 282 "job_name": "job1", 283 "pipeline_name": "pipeline1", 284 "team_name": "some-team", 285 "status": "succeeded", 286 "api_url": "/api/v1/builds/3", 287 "start_time": 101, 288 "end_time": 200, 289 "reap_time": 400 290 }, 291 { 292 "id": 5, 293 "name": "1", 294 "resource_name": "resource1", 295 "pipeline_name": "pipeline1", 296 "team_name": "some-team", 297 "status": "succeeded", 298 "api_url": "/api/v1/builds/5", 299 "start_time": 101 300 } 301 ]`)) 302 }) 303 }) 304 305 Context("when next/previous pages are available", func() { 306 BeforeEach(func() { 307 dbBuildFactory.VisibleBuildsReturns(returnedBuilds, db.Pagination{ 308 Newer: &db.Page{From: db.NewIntPtr(4), Limit: 2}, 309 Older: &db.Page{To: db.NewIntPtr(3), Limit: 2}, 310 }, nil) 311 }) 312 313 It("returns Link headers per rfc5988", func() { 314 Expect(response.Header["Link"]).To(ConsistOf([]string{ 315 fmt.Sprintf(`<%s/api/v1/builds?from=4&limit=2>; rel="previous"`, externalURL), 316 fmt.Sprintf(`<%s/api/v1/builds?to=3&limit=2>; rel="next"`, externalURL), 317 })) 318 }) 319 }) 320 321 Context("when getting all builds fails", func() { 322 BeforeEach(func() { 323 dbBuildFactory.VisibleBuildsReturns(nil, db.Pagination{}, errors.New("oh no!")) 324 }) 325 326 It("returns 500 Internal Server Error", func() { 327 Expect(response.StatusCode).To(Equal(http.StatusInternalServerError)) 328 }) 329 }) 330 }) 331 332 Context("when authenticated", func() { 333 BeforeEach(func() { 334 fakeAccess.IsAuthenticatedReturns(true) 335 }) 336 337 Context("when user has the admin privilege", func() { 338 BeforeEach(func() { 339 fakeAccess.IsAdminReturns(true) 340 }) 341 342 It("calls AllBuilds", func() { 343 Expect(dbBuildFactory.AllBuildsCallCount()).To(Equal(1)) 344 Expect(dbBuildFactory.VisibleBuildsCallCount()).To(Equal(0)) 345 }) 346 347 }) 348 349 Context("when no params are passed", func() { 350 BeforeEach(func() { 351 queryParams = "" 352 }) 353 354 It("does not set defaults for since and until", func() { 355 Expect(dbBuildFactory.VisibleBuildsCallCount()).To(Equal(1)) 356 357 _, page := dbBuildFactory.VisibleBuildsArgsForCall(0) 358 Expect(page).To(Equal(db.Page{ 359 Limit: 100, 360 })) 361 }) 362 }) 363 364 Context("when all the params are passed", func() { 365 BeforeEach(func() { 366 queryParams = "?from=2&to=3&limit=8" 367 }) 368 369 It("passes them through", func() { 370 Expect(dbBuildFactory.VisibleBuildsCallCount()).To(Equal(1)) 371 372 _, page := dbBuildFactory.VisibleBuildsArgsForCall(0) 373 Expect(page).To(Equal(db.Page{ 374 From: db.NewIntPtr(2), 375 To: db.NewIntPtr(3), 376 Limit: 8, 377 })) 378 }) 379 }) 380 381 Context("when getting the builds succeeds", func() { 382 BeforeEach(func() { 383 dbBuildFactory.VisibleBuildsReturns(returnedBuilds, db.Pagination{}, nil) 384 }) 385 386 It("returns 200 OK", func() { 387 Expect(response.StatusCode).To(Equal(http.StatusOK)) 388 }) 389 390 It("returns Content-Type 'application/json'", func() { 391 expectedHeaderEntries := map[string]string{ 392 "Content-Type": "application/json", 393 } 394 Expect(response).Should(IncludeHeaderEntries(expectedHeaderEntries)) 395 }) 396 397 It("returns all builds", func() { 398 body, err := ioutil.ReadAll(response.Body) 399 Expect(err).NotTo(HaveOccurred()) 400 401 Expect(body).To(MatchJSON(`[ 402 { 403 "id": 4, 404 "name": "2", 405 "job_name": "job2", 406 "pipeline_name": "pipeline2", 407 "team_name": "some-team", 408 "status": "started", 409 "api_url": "/api/v1/builds/4", 410 "start_time": 1, 411 "end_time": 100, 412 "reap_time": 300 413 }, 414 { 415 "id": 3, 416 "name": "1", 417 "job_name": "job1", 418 "pipeline_name": "pipeline1", 419 "team_name": "some-team", 420 "status": "succeeded", 421 "api_url": "/api/v1/builds/3", 422 "start_time": 101, 423 "end_time": 200, 424 "reap_time": 400 425 }, 426 { 427 "id": 5, 428 "name": "1", 429 "resource_name": "resource1", 430 "pipeline_name": "pipeline1", 431 "team_name": "some-team", 432 "status": "succeeded", 433 "api_url": "/api/v1/builds/5", 434 "start_time": 101 435 } 436 ]`)) 437 }) 438 439 It("returns builds for teams from the token", func() { 440 Expect(dbBuildFactory.VisibleBuildsCallCount()).To(Equal(1)) 441 teamName, _ := dbBuildFactory.VisibleBuildsArgsForCall(0) 442 Expect(teamName).To(ConsistOf("some-team")) 443 }) 444 }) 445 446 Context("when next/previous pages are available", func() { 447 BeforeEach(func() { 448 dbBuildFactory.VisibleBuildsReturns(returnedBuilds, db.Pagination{ 449 Newer: &db.Page{From: db.NewIntPtr(4), Limit: 2}, 450 Older: &db.Page{To: db.NewIntPtr(3), Limit: 2}, 451 }, nil) 452 }) 453 454 It("returns Link headers per rfc5988", func() { 455 Expect(response.Header["Link"]).To(ConsistOf([]string{ 456 fmt.Sprintf(`<%s/api/v1/builds?from=4&limit=2>; rel="previous"`, externalURL), 457 fmt.Sprintf(`<%s/api/v1/builds?to=3&limit=2>; rel="next"`, externalURL), 458 })) 459 }) 460 }) 461 462 Context("when getting all builds fails", func() { 463 BeforeEach(func() { 464 dbBuildFactory.VisibleBuildsReturns(nil, db.Pagination{}, errors.New("oh no!")) 465 }) 466 467 It("returns 500 Internal Server Error", func() { 468 Expect(response.StatusCode).To(Equal(http.StatusInternalServerError)) 469 }) 470 }) 471 }) 472 }) 473 474 Describe("GET /api/v1/builds/:build_id", func() { 475 var response *http.Response 476 477 Context("when parsing the build_id fails", func() { 478 BeforeEach(func() { 479 var err error 480 481 response, err = client.Get(server.URL + "/api/v1/builds/nope") 482 Expect(err).NotTo(HaveOccurred()) 483 }) 484 485 It("returns Bad Request", func() { 486 Expect(response.StatusCode).To(Equal(http.StatusBadRequest)) 487 }) 488 }) 489 490 Context("when parsing the build_id succeeds", func() { 491 JustBeforeEach(func() { 492 var err error 493 494 response, err = client.Get(server.URL + "/api/v1/builds/1") 495 Expect(err).NotTo(HaveOccurred()) 496 }) 497 498 Context("when calling the database fails", func() { 499 BeforeEach(func() { 500 dbBuildFactory.BuildReturns(nil, false, errors.New("disaster")) 501 }) 502 503 It("returns 500 Internal Server Error", func() { 504 Expect(response.StatusCode).To(Equal(http.StatusInternalServerError)) 505 }) 506 }) 507 508 Context("when the build cannot be found", func() { 509 BeforeEach(func() { 510 dbBuildFactory.BuildReturns(nil, false, nil) 511 }) 512 513 It("returns Not Found", func() { 514 Expect(response.StatusCode).To(Equal(http.StatusNotFound)) 515 }) 516 }) 517 518 Context("when the build can be found", func() { 519 BeforeEach(func() { 520 build.IDReturns(1) 521 build.NameReturns("1") 522 build.TeamIDReturns(2) 523 build.TeamNameReturns("some-team") 524 build.PipelineIDReturns(123) 525 build.PipelineNameReturns("pipeline1") 526 build.JobIDReturns(456) 527 build.JobNameReturns("job1") 528 build.StatusReturns(db.BuildStatusSucceeded) 529 build.StartTimeReturns(time.Unix(1, 0)) 530 build.EndTimeReturns(time.Unix(100, 0)) 531 build.ReapTimeReturns(time.Unix(200, 0)) 532 dbBuildFactory.BuildReturns(build, true, nil) 533 build.PipelineReturns(fakePipeline, true, nil) 534 fakePipeline.PublicReturns(true) 535 }) 536 537 Context("when not authenticated", func() { 538 BeforeEach(func() { 539 fakeAccess.IsAuthenticatedReturns(false) 540 fakeAccess.IsAuthorizedReturns(false) 541 }) 542 543 Context("and build is one off", func() { 544 BeforeEach(func() { 545 build.PipelineIDReturns(0) 546 }) 547 548 It("returns 401", func() { 549 Expect(response.StatusCode).To(Equal(http.StatusUnauthorized)) 550 }) 551 }) 552 553 Context("and the pipeline is not found", func() { 554 BeforeEach(func() { 555 build.PipelineReturns(nil, false, nil) 556 }) 557 558 It("returns 404", func() { 559 Expect(response.StatusCode).To(Equal(http.StatusNotFound)) 560 }) 561 }) 562 563 Context("and the pipeline is private", func() { 564 BeforeEach(func() { 565 fakePipeline.PublicReturns(false) 566 build.PipelineReturns(fakePipeline, true, nil) 567 }) 568 569 It("returns 401", func() { 570 Expect(response.StatusCode).To(Equal(http.StatusUnauthorized)) 571 }) 572 }) 573 574 Context("and the pipeline is public", func() { 575 BeforeEach(func() { 576 fakePipeline.PublicReturns(true) 577 build.PipelineReturns(fakePipeline, true, nil) 578 }) 579 580 It("returns 200", func() { 581 Expect(response.StatusCode).To(Equal(http.StatusOK)) 582 }) 583 }) 584 }) 585 586 Context("when authenticated", func() { 587 BeforeEach(func() { 588 fakeAccess.IsAuthenticatedReturns(true) 589 }) 590 591 Context("when user is not authorized", func() { 592 BeforeEach(func() { 593 fakeAccess.IsAuthorizedReturns(false) 594 595 }) 596 It("returns 200 OK", func() { 597 Expect(response.StatusCode).To(Equal(http.StatusOK)) 598 }) 599 }) 600 601 Context("when user is authorized", func() { 602 BeforeEach(func() { 603 fakeAccess.IsAuthorizedReturns(true) 604 }) 605 606 It("returns 200 OK", func() { 607 Expect(response.StatusCode).To(Equal(http.StatusOK)) 608 }) 609 610 It("returns Content-Type 'application/json'", func() { 611 expectedHeaderEntries := map[string]string{ 612 "Content-Type": "application/json", 613 } 614 Expect(response).Should(IncludeHeaderEntries(expectedHeaderEntries)) 615 }) 616 617 It("returns the build with the given build_id", func() { 618 Expect(dbBuildFactory.BuildCallCount()).To(Equal(1)) 619 buildID := dbBuildFactory.BuildArgsForCall(0) 620 Expect(buildID).To(Equal(1)) 621 622 body, err := ioutil.ReadAll(response.Body) 623 Expect(err).NotTo(HaveOccurred()) 624 625 Expect(body).To(MatchJSON(`{ 626 "id": 1, 627 "name": "1", 628 "status": "succeeded", 629 "job_name": "job1", 630 "pipeline_id": 123, 631 "pipeline_name": "pipeline1", 632 "team_name": "some-team", 633 "api_url": "/api/v1/builds/1", 634 "start_time": 1, 635 "end_time": 100, 636 "reap_time": 200 637 }`)) 638 }) 639 }) 640 }) 641 }) 642 }) 643 }) 644 645 Describe("GET /api/v1/builds/:build_id/resources", func() { 646 var response *http.Response 647 648 Context("when the build is found", func() { 649 BeforeEach(func() { 650 build.TeamNameReturns("some-team") 651 build.JobIDReturns(42) 652 build.JobNameReturns("job1") 653 build.PipelineIDReturns(42) 654 build.PipelineReturns(fakePipeline, true, nil) 655 dbBuildFactory.BuildReturns(build, true, nil) 656 }) 657 658 JustBeforeEach(func() { 659 var err error 660 661 response, err = client.Get(server.URL + "/api/v1/builds/3/resources") 662 Expect(err).NotTo(HaveOccurred()) 663 }) 664 665 Context("when not authenticated", func() { 666 BeforeEach(func() { 667 fakeAccess.IsAuthenticatedReturns(false) 668 }) 669 670 Context("and build is one off", func() { 671 BeforeEach(func() { 672 build.PipelineIDReturns(0) 673 }) 674 675 It("returns 401", func() { 676 Expect(response.StatusCode).To(Equal(http.StatusUnauthorized)) 677 }) 678 }) 679 680 Context("and the pipeline is private", func() { 681 BeforeEach(func() { 682 fakePipeline.PublicReturns(false) 683 build.PipelineReturns(fakePipeline, true, nil) 684 }) 685 686 It("returns 401", func() { 687 Expect(response.StatusCode).To(Equal(http.StatusUnauthorized)) 688 }) 689 }) 690 691 Context("and the pipeline is public", func() { 692 BeforeEach(func() { 693 fakePipeline.PublicReturns(true) 694 build.PipelineReturns(fakePipeline, true, nil) 695 }) 696 697 It("returns 200", func() { 698 Expect(response.StatusCode).To(Equal(http.StatusOK)) 699 }) 700 }) 701 }) 702 703 Context("when authenticated, but not authorized", func() { 704 BeforeEach(func() { 705 fakeAccess.IsAuthenticatedReturns(true) 706 fakeAccess.IsAuthorizedReturns(false) 707 }) 708 709 It("returns 403", func() { 710 Expect(response.StatusCode).To(Equal(http.StatusForbidden)) 711 }) 712 }) 713 714 Context("when authenticated and authorized", func() { 715 BeforeEach(func() { 716 fakeAccess.IsAuthenticatedReturns(true) 717 fakeAccess.IsAuthorizedReturns(true) 718 }) 719 720 It("returns 200 OK", func() { 721 Expect(response.StatusCode).To(Equal(http.StatusOK)) 722 }) 723 724 Context("when the build inputs/outputs are not empty", func() { 725 BeforeEach(func() { 726 build.ResourcesReturns([]db.BuildInput{ 727 { 728 Name: "input1", 729 Version: atc.Version{"version": "value1"}, 730 ResourceID: 1, 731 FirstOccurrence: true, 732 }, 733 { 734 Name: "input2", 735 Version: atc.Version{"version": "value2"}, 736 ResourceID: 2, 737 FirstOccurrence: false, 738 }, 739 }, 740 []db.BuildOutput{ 741 { 742 Name: "myresource3", 743 Version: atc.Version{"version": "value3"}, 744 }, 745 { 746 Name: "myresource4", 747 Version: atc.Version{"version": "value4"}, 748 }, 749 }, nil) 750 }) 751 752 It("returns Content-Type 'application/json'", func() { 753 expectedHeaderEntries := map[string]string{ 754 "Content-Type": "application/json", 755 } 756 Expect(response).Should(IncludeHeaderEntries(expectedHeaderEntries)) 757 }) 758 759 It("returns the build with it's input and output versioned resources", func() { 760 body, err := ioutil.ReadAll(response.Body) 761 Expect(err).NotTo(HaveOccurred()) 762 763 Expect(body).To(MatchJSON(`{ 764 "inputs": [ 765 { 766 "name": "input1", 767 "version": {"version": "value1"}, 768 "pipeline_id": 42, 769 "first_occurrence": true 770 }, 771 { 772 "name": "input2", 773 "version": {"version": "value2"}, 774 "pipeline_id": 42, 775 "first_occurrence": false 776 } 777 ], 778 "outputs": [ 779 { 780 "name": "myresource3", 781 "version": {"version": "value3"} 782 }, 783 { 784 "name": "myresource4", 785 "version": {"version": "value4"} 786 } 787 ] 788 }`)) 789 }) 790 }) 791 792 Context("when the build resources error", func() { 793 BeforeEach(func() { 794 build.ResourcesReturns([]db.BuildInput{}, []db.BuildOutput{}, errors.New("where are my feedback?")) 795 }) 796 797 It("returns internal server error", func() { 798 Expect(response.StatusCode).To(Equal(http.StatusInternalServerError)) 799 }) 800 }) 801 802 Context("with an invalid build", func() { 803 Context("when the lookup errors", func() { 804 BeforeEach(func() { 805 dbBuildFactory.BuildReturns(build, false, errors.New("Freakin' out man, I'm freakin' out!")) 806 }) 807 808 It("returns internal server error", func() { 809 Expect(response.StatusCode).To(Equal(http.StatusInternalServerError)) 810 }) 811 }) 812 813 Context("when the build does not exist", func() { 814 BeforeEach(func() { 815 dbBuildFactory.BuildReturns(nil, false, nil) 816 }) 817 818 It("returns internal server error", func() { 819 Expect(response.StatusCode).To(Equal(http.StatusNotFound)) 820 }) 821 }) 822 }) 823 }) 824 }) 825 826 Context("with an invalid build_id", func() { 827 JustBeforeEach(func() { 828 var err error 829 830 response, err = client.Get(server.URL + "/api/v1/builds/nope/resources") 831 Expect(err).NotTo(HaveOccurred()) 832 }) 833 834 It("returns internal server error", func() { 835 Expect(response.StatusCode).To(Equal(http.StatusBadRequest)) 836 }) 837 }) 838 }) 839 840 Describe("GET /api/v1/builds/:build_id/events", func() { 841 var ( 842 request *http.Request 843 response *http.Response 844 ) 845 846 BeforeEach(func() { 847 var err error 848 849 request, err = http.NewRequest("GET", server.URL+"/api/v1/builds/128/events", nil) 850 Expect(err).NotTo(HaveOccurred()) 851 }) 852 853 JustBeforeEach(func() { 854 var err error 855 856 response, err = client.Do(request) 857 Expect(err).NotTo(HaveOccurred()) 858 }) 859 860 Context("when the build can be found", func() { 861 BeforeEach(func() { 862 build.TeamNameReturns("some-team") 863 build.JobIDReturns(42) 864 build.JobNameReturns("job1") 865 build.PipelineIDReturns(42) 866 build.PipelineReturns(fakePipeline, true, nil) 867 dbBuildFactory.BuildReturns(build, true, nil) 868 }) 869 870 Context("when authenticated, but not authorized", func() { 871 BeforeEach(func() { 872 fakeAccess.IsAuthenticatedReturns(true) 873 fakeAccess.IsAuthorizedReturns(false) 874 }) 875 876 It("returns 403", func() { 877 Expect(response.StatusCode).To(Equal(http.StatusForbidden)) 878 }) 879 }) 880 881 Context("when authorized", func() { 882 BeforeEach(func() { 883 fakeAccess.IsAuthenticatedReturns(true) 884 fakeAccess.IsAuthorizedReturns(true) 885 }) 886 887 It("returns 200", func() { 888 Expect(response.StatusCode).To(Equal(200)) 889 }) 890 891 It("serves the request via the event handler", func() { 892 body, err := ioutil.ReadAll(response.Body) 893 Expect(err).NotTo(HaveOccurred()) 894 895 Expect(string(body)).To(Equal("fake event handler factory was here")) 896 897 Expect(constructedEventHandler.build).To(Equal(build)) 898 Expect(dbBuildFactory.BuildCallCount()).To(Equal(1)) 899 buildID := dbBuildFactory.BuildArgsForCall(0) 900 Expect(buildID).To(Equal(128)) 901 }) 902 }) 903 904 Context("when not authenticated", func() { 905 BeforeEach(func() { 906 fakeAccess.IsAuthenticatedReturns(false) 907 }) 908 909 Context("and the pipeline is private", func() { 910 BeforeEach(func() { 911 build.PipelineReturns(fakePipeline, true, nil) 912 fakePipeline.PublicReturns(false) 913 }) 914 915 It("returns 401", func() { 916 Expect(response.StatusCode).To(Equal(http.StatusUnauthorized)) 917 }) 918 }) 919 920 Context("and the pipeline is public", func() { 921 BeforeEach(func() { 922 build.PipelineReturns(fakePipeline, true, nil) 923 fakePipeline.PublicReturns(true) 924 }) 925 926 Context("when the job is found", func() { 927 var fakeJob *dbfakes.FakeJob 928 929 BeforeEach(func() { 930 fakeJob = new(dbfakes.FakeJob) 931 fakePipeline.JobReturns(fakeJob, true, nil) 932 }) 933 934 Context("and the job is private", func() { 935 BeforeEach(func() { 936 fakeJob.PublicReturns(false) 937 }) 938 939 It("returns 401", func() { 940 Expect(response.StatusCode).To(Equal(http.StatusUnauthorized)) 941 }) 942 }) 943 944 Context("and the job is public", func() { 945 BeforeEach(func() { 946 fakeJob.PublicReturns(true) 947 }) 948 949 It("returns 200", func() { 950 Expect(response.StatusCode).To(Equal(200)) 951 }) 952 953 It("serves the request via the event handler", func() { 954 body, err := ioutil.ReadAll(response.Body) 955 Expect(err).NotTo(HaveOccurred()) 956 957 Expect(string(body)).To(Equal("fake event handler factory was here")) 958 959 Expect(constructedEventHandler.build).To(Equal(build)) 960 Expect(dbBuildFactory.BuildCallCount()).To(Equal(1)) 961 buildID := dbBuildFactory.BuildArgsForCall(0) 962 Expect(buildID).To(Equal(128)) 963 }) 964 }) 965 }) 966 967 Context("when finding the job fails", func() { 968 BeforeEach(func() { 969 fakePipeline.JobReturns(nil, false, errors.New("nope")) 970 }) 971 972 It("returns Internal Server Error", func() { 973 Expect(response.StatusCode).To(Equal(http.StatusInternalServerError)) 974 }) 975 }) 976 977 Context("when the job cannot be found", func() { 978 BeforeEach(func() { 979 fakePipeline.JobReturns(nil, false, nil) 980 }) 981 982 It("returns Not Found", func() { 983 Expect(response.StatusCode).To(Equal(http.StatusNotFound)) 984 }) 985 }) 986 }) 987 988 Context("when the build can not be found", func() { 989 BeforeEach(func() { 990 dbBuildFactory.BuildReturns(nil, false, nil) 991 }) 992 993 It("returns Not Found", func() { 994 Expect(response.StatusCode).To(Equal(http.StatusNotFound)) 995 }) 996 }) 997 998 Context("when calling the database fails", func() { 999 BeforeEach(func() { 1000 dbBuildFactory.BuildReturns(nil, false, errors.New("nope")) 1001 }) 1002 1003 It("returns Internal Server Error", func() { 1004 Expect(response.StatusCode).To(Equal(http.StatusInternalServerError)) 1005 }) 1006 }) 1007 }) 1008 }) 1009 1010 Context("when calling the database fails", func() { 1011 BeforeEach(func() { 1012 dbBuildFactory.BuildReturns(nil, false, errors.New("nope")) 1013 }) 1014 1015 It("returns Internal Server Error", func() { 1016 Expect(response.StatusCode).To(Equal(http.StatusInternalServerError)) 1017 }) 1018 }) 1019 }) 1020 1021 Describe("PUT /api/v1/builds/:build_id/abort", func() { 1022 var ( 1023 response *http.Response 1024 ) 1025 1026 JustBeforeEach(func() { 1027 var err error 1028 1029 req, err := http.NewRequest("PUT", server.URL+"/api/v1/builds/128/abort", nil) 1030 Expect(err).NotTo(HaveOccurred()) 1031 1032 response, err = client.Do(req) 1033 Expect(err).NotTo(HaveOccurred()) 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 Context("when authenticated", func() { 1047 BeforeEach(func() { 1048 fakeAccess.IsAuthenticatedReturns(true) 1049 }) 1050 1051 Context("when looking up the build fails", func() { 1052 BeforeEach(func() { 1053 dbBuildFactory.BuildReturns(nil, false, errors.New("nope")) 1054 }) 1055 1056 It("returns 500", func() { 1057 Expect(response.StatusCode).To(Equal(http.StatusInternalServerError)) 1058 }) 1059 }) 1060 1061 Context("when the build can not be found", func() { 1062 BeforeEach(func() { 1063 dbBuildFactory.BuildReturns(nil, false, nil) 1064 }) 1065 1066 It("returns 404", func() { 1067 Expect(response.StatusCode).To(Equal(http.StatusNotFound)) 1068 }) 1069 }) 1070 1071 Context("when the build is found", func() { 1072 BeforeEach(func() { 1073 build.TeamNameReturns("some-team") 1074 dbBuildFactory.BuildReturns(build, true, nil) 1075 }) 1076 1077 Context("when not authorized", func() { 1078 BeforeEach(func() { 1079 fakeAccess.IsAuthorizedReturns(false) 1080 }) 1081 1082 It("returns 403", func() { 1083 Expect(response.StatusCode).To(Equal(http.StatusForbidden)) 1084 }) 1085 }) 1086 1087 Context("when authorized", func() { 1088 BeforeEach(func() { 1089 fakeAccess.IsAuthorizedReturns(true) 1090 }) 1091 1092 Context("when aborting the build fails", func() { 1093 BeforeEach(func() { 1094 build.MarkAsAbortedReturns(errors.New("nope")) 1095 }) 1096 1097 It("returns 500", func() { 1098 Expect(response.StatusCode).To(Equal(http.StatusInternalServerError)) 1099 }) 1100 }) 1101 1102 Context("when aborting succeeds", func() { 1103 BeforeEach(func() { 1104 build.MarkAsAbortedReturns(nil) 1105 }) 1106 1107 It("returns 204", func() { 1108 Expect(response.StatusCode).To(Equal(http.StatusNoContent)) 1109 }) 1110 }) 1111 }) 1112 }) 1113 }) 1114 }) 1115 1116 Describe("GET /api/v1/builds/:build_id/preparation", func() { 1117 var response *http.Response 1118 1119 JustBeforeEach(func() { 1120 var err error 1121 response, err = http.Get(server.URL + "/api/v1/builds/42/preparation") 1122 Expect(err).NotTo(HaveOccurred()) 1123 }) 1124 1125 Context("when the build is found", func() { 1126 var buildPrep db.BuildPreparation 1127 1128 BeforeEach(func() { 1129 buildPrep = db.BuildPreparation{ 1130 BuildID: 42, 1131 PausedPipeline: db.BuildPreparationStatusNotBlocking, 1132 PausedJob: db.BuildPreparationStatusNotBlocking, 1133 MaxRunningBuilds: db.BuildPreparationStatusBlocking, 1134 Inputs: map[string]db.BuildPreparationStatus{ 1135 "foo": db.BuildPreparationStatusNotBlocking, 1136 "bar": db.BuildPreparationStatusBlocking, 1137 }, 1138 InputsSatisfied: db.BuildPreparationStatusBlocking, 1139 MissingInputReasons: db.MissingInputReasons{"some-input": "some-reason"}, 1140 } 1141 dbBuildFactory.BuildReturns(build, true, nil) 1142 build.TeamNameReturns("some-team") 1143 build.JobIDReturns(42) 1144 build.JobNameReturns("job1") 1145 build.PipelineIDReturns(42) 1146 build.PreparationReturns(buildPrep, true, nil) 1147 }) 1148 1149 Context("when authenticated, but not authorized", func() { 1150 BeforeEach(func() { 1151 fakeAccess.IsAuthenticatedReturns(true) 1152 fakeAccess.IsAuthorizedReturns(false) 1153 build.PipelineReturns(fakePipeline, true, nil) 1154 }) 1155 1156 It("returns 403", func() { 1157 Expect(response.StatusCode).To(Equal(http.StatusForbidden)) 1158 }) 1159 }) 1160 1161 Context("when not authenticated", func() { 1162 BeforeEach(func() { 1163 fakeAccess.IsAuthenticatedReturns(false) 1164 }) 1165 1166 Context("and build is one off", func() { 1167 BeforeEach(func() { 1168 build.PipelineIDReturns(0) 1169 }) 1170 1171 It("returns 401", func() { 1172 Expect(response.StatusCode).To(Equal(http.StatusUnauthorized)) 1173 }) 1174 }) 1175 1176 Context("and the pipeline is private", func() { 1177 BeforeEach(func() { 1178 build.PipelineReturns(fakePipeline, true, nil) 1179 fakePipeline.PublicReturns(false) 1180 }) 1181 1182 It("returns 401", func() { 1183 Expect(response.StatusCode).To(Equal(http.StatusUnauthorized)) 1184 }) 1185 }) 1186 1187 Context("and the pipeline is public", func() { 1188 BeforeEach(func() { 1189 build.PipelineReturns(fakePipeline, true, nil) 1190 fakePipeline.PublicReturns(true) 1191 }) 1192 1193 Context("when the job is found", func() { 1194 var fakeJob *dbfakes.FakeJob 1195 BeforeEach(func() { 1196 fakeJob = new(dbfakes.FakeJob) 1197 fakePipeline.JobReturns(fakeJob, true, nil) 1198 }) 1199 1200 Context("when job is private", func() { 1201 BeforeEach(func() { 1202 fakeJob.PublicReturns(false) 1203 }) 1204 1205 It("returns 401", func() { 1206 Expect(response.StatusCode).To(Equal(http.StatusUnauthorized)) 1207 }) 1208 }) 1209 1210 Context("when job is public", func() { 1211 BeforeEach(func() { 1212 fakeJob.PublicReturns(true) 1213 }) 1214 1215 It("returns 200", func() { 1216 Expect(response.StatusCode).To(Equal(http.StatusOK)) 1217 }) 1218 }) 1219 }) 1220 1221 Context("when finding the job fails", func() { 1222 BeforeEach(func() { 1223 fakePipeline.JobReturns(nil, false, errors.New("nope")) 1224 }) 1225 1226 It("returns Internal Server Error", func() { 1227 Expect(response.StatusCode).To(Equal(http.StatusInternalServerError)) 1228 }) 1229 }) 1230 1231 Context("when the job cannot be found", func() { 1232 BeforeEach(func() { 1233 fakePipeline.JobReturns(nil, false, nil) 1234 }) 1235 1236 It("returns Not Found", func() { 1237 Expect(response.StatusCode).To(Equal(http.StatusNotFound)) 1238 }) 1239 }) 1240 }) 1241 }) 1242 1243 Context("when authenticated", func() { 1244 BeforeEach(func() { 1245 fakeAccess.IsAuthenticatedReturns(true) 1246 fakeAccess.IsAuthorizedReturns(true) 1247 }) 1248 1249 It("fetches data from the db", func() { 1250 Expect(build.PreparationCallCount()).To(Equal(1)) 1251 }) 1252 1253 It("returns OK", func() { 1254 Expect(response.StatusCode).To(Equal(http.StatusOK)) 1255 }) 1256 1257 It("returns Content-Type 'application/json'", func() { 1258 expectedHeaderEntries := map[string]string{ 1259 "Content-Type": "application/json", 1260 } 1261 Expect(response).Should(IncludeHeaderEntries(expectedHeaderEntries)) 1262 }) 1263 1264 It("returns the build preparation", func() { 1265 body, err := ioutil.ReadAll(response.Body) 1266 Expect(err).NotTo(HaveOccurred()) 1267 1268 Expect(body).To(MatchJSON(`{ 1269 "build_id": 42, 1270 "paused_pipeline": "not_blocking", 1271 "paused_job": "not_blocking", 1272 "max_running_builds": "blocking", 1273 "inputs": { 1274 "foo": "not_blocking", 1275 "bar": "blocking" 1276 }, 1277 "inputs_satisfied": "blocking", 1278 "missing_input_reasons": { 1279 "some-input": "some-reason" 1280 } 1281 }`)) 1282 }) 1283 1284 Context("when the build preparation is not found", func() { 1285 BeforeEach(func() { 1286 dbBuildFactory.BuildReturns(build, true, nil) 1287 build.PreparationReturns(db.BuildPreparation{}, false, nil) 1288 }) 1289 1290 It("returns Not Found", func() { 1291 Expect(response.StatusCode).To(Equal(http.StatusNotFound)) 1292 }) 1293 }) 1294 1295 Context("when looking up the build preparation fails", func() { 1296 BeforeEach(func() { 1297 dbBuildFactory.BuildReturns(build, true, nil) 1298 build.PreparationReturns(db.BuildPreparation{}, false, errors.New("ho ho ho merry festivus")) 1299 }) 1300 1301 It("returns 500 Internal Server Error", func() { 1302 Expect(response.StatusCode).To(Equal(http.StatusInternalServerError)) 1303 }) 1304 }) 1305 }) 1306 }) 1307 1308 Context("when looking up the build fails", func() { 1309 BeforeEach(func() { 1310 dbBuildFactory.BuildReturns(nil, false, errors.New("ho ho ho merry festivus")) 1311 }) 1312 1313 It("returns 500 Internal Server Error", func() { 1314 Expect(response.StatusCode).To(Equal(http.StatusInternalServerError)) 1315 }) 1316 }) 1317 1318 Context("when build is not found", func() { 1319 BeforeEach(func() { 1320 dbBuildFactory.BuildReturns(nil, false, nil) 1321 }) 1322 1323 It("returns 404", func() { 1324 Expect(response.StatusCode).To(Equal(http.StatusNotFound)) 1325 }) 1326 }) 1327 }) 1328 1329 Describe("GET /api/v1/builds/:build_id/plan", func() { 1330 var plan *json.RawMessage 1331 1332 var response *http.Response 1333 1334 BeforeEach(func() { 1335 data := []byte(`{"some":"plan"}`) 1336 plan = (*json.RawMessage)(&data) 1337 }) 1338 1339 JustBeforeEach(func() { 1340 var err error 1341 response, err = http.Get(server.URL + "/api/v1/builds/42/plan") 1342 Expect(err).NotTo(HaveOccurred()) 1343 }) 1344 1345 Context("when the build is found", func() { 1346 BeforeEach(func() { 1347 build.TeamNameReturns("some-team") 1348 build.JobIDReturns(42) 1349 build.JobNameReturns("job1") 1350 build.PipelineIDReturns(42) 1351 dbBuildFactory.BuildReturns(build, true, nil) 1352 }) 1353 1354 Context("when authenticated, but not authorized", func() { 1355 BeforeEach(func() { 1356 fakeAccess.IsAuthenticatedReturns(true) 1357 fakeAccess.IsAuthorizedReturns(false) 1358 1359 build.PipelineReturns(fakePipeline, true, nil) 1360 }) 1361 1362 It("returns 403", func() { 1363 Expect(response.StatusCode).To(Equal(http.StatusForbidden)) 1364 }) 1365 }) 1366 1367 Context("when not authenticated", func() { 1368 BeforeEach(func() { 1369 fakeAccess.IsAuthenticatedReturns(false) 1370 }) 1371 1372 Context("and build is one off", func() { 1373 BeforeEach(func() { 1374 build.PipelineIDReturns(0) 1375 }) 1376 1377 It("returns 401", func() { 1378 Expect(response.StatusCode).To(Equal(http.StatusUnauthorized)) 1379 }) 1380 }) 1381 1382 Context("and the pipeline is private", func() { 1383 BeforeEach(func() { 1384 build.PipelineReturns(fakePipeline, true, nil) 1385 fakePipeline.PublicReturns(false) 1386 }) 1387 1388 It("returns 401", func() { 1389 Expect(response.StatusCode).To(Equal(http.StatusUnauthorized)) 1390 }) 1391 }) 1392 1393 Context("and the pipeline is public", func() { 1394 BeforeEach(func() { 1395 build.PipelineReturns(fakePipeline, true, nil) 1396 fakePipeline.PublicReturns(true) 1397 }) 1398 1399 Context("when finding the job fails", func() { 1400 BeforeEach(func() { 1401 fakePipeline.JobReturns(nil, false, errors.New("nope")) 1402 }) 1403 It("returns 500", func() { 1404 Expect(response.StatusCode).To(Equal(http.StatusInternalServerError)) 1405 }) 1406 }) 1407 1408 Context("when the job does not exist", func() { 1409 BeforeEach(func() { 1410 fakePipeline.JobReturns(nil, false, nil) 1411 }) 1412 It("returns 404", func() { 1413 Expect(response.StatusCode).To(Equal(http.StatusNotFound)) 1414 }) 1415 }) 1416 1417 Context("when the job exists", func() { 1418 var fakeJob *dbfakes.FakeJob 1419 1420 BeforeEach(func() { 1421 fakeJob = new(dbfakes.FakeJob) 1422 fakePipeline.JobReturns(fakeJob, true, nil) 1423 }) 1424 1425 Context("and the job is public", func() { 1426 BeforeEach(func() { 1427 fakeJob.PublicReturns(true) 1428 }) 1429 Context("and the build has a plan", func() { 1430 BeforeEach(func() { 1431 build.HasPlanReturns(true) 1432 }) 1433 It("returns 200", func() { 1434 Expect(response.StatusCode).To(Equal(http.StatusOK)) 1435 }) 1436 }) 1437 Context("and the build has no plan", func() { 1438 BeforeEach(func() { 1439 build.HasPlanReturns(false) 1440 }) 1441 It("returns 404", func() { 1442 Expect(response.StatusCode).To(Equal(http.StatusNotFound)) 1443 }) 1444 }) 1445 }) 1446 1447 Context("and the job is private", func() { 1448 BeforeEach(func() { 1449 fakeJob.PublicReturns(false) 1450 }) 1451 It("returns 401", func() { 1452 Expect(response.StatusCode).To(Equal(http.StatusUnauthorized)) 1453 }) 1454 }) 1455 }) 1456 }) 1457 }) 1458 1459 Context("when authenticated", func() { 1460 BeforeEach(func() { 1461 fakeAccess.IsAuthenticatedReturns(true) 1462 fakeAccess.IsAuthorizedReturns(true) 1463 }) 1464 1465 Context("when the build returns a plan", func() { 1466 BeforeEach(func() { 1467 build.HasPlanReturns(true) 1468 build.PublicPlanReturns(plan) 1469 build.SchemaReturns("some-schema") 1470 }) 1471 1472 It("returns OK", func() { 1473 Expect(response.StatusCode).To(Equal(http.StatusOK)) 1474 }) 1475 1476 It("returns Content-Type 'application/json'", func() { 1477 expectedHeaderEntries := map[string]string{ 1478 "Content-Type": "application/json", 1479 } 1480 Expect(response).Should(IncludeHeaderEntries(expectedHeaderEntries)) 1481 }) 1482 1483 It("returns the plan", func() { 1484 body, err := ioutil.ReadAll(response.Body) 1485 Expect(err).NotTo(HaveOccurred()) 1486 1487 Expect(body).To(MatchJSON(`{ 1488 "schema": "some-schema", 1489 "plan": {"some":"plan"} 1490 }`)) 1491 }) 1492 }) 1493 1494 Context("when the build has no plan", func() { 1495 BeforeEach(func() { 1496 build.HasPlanReturns(false) 1497 }) 1498 1499 It("returns no Content-Type header", func() { 1500 expectedHeaderEntries := map[string]string{ 1501 "Content-Type": "", 1502 } 1503 Expect(response).ShouldNot(IncludeHeaderEntries(expectedHeaderEntries)) 1504 }) 1505 1506 It("returns not found", func() { 1507 Expect(response.StatusCode).To(Equal(http.StatusNotFound)) 1508 }) 1509 }) 1510 }) 1511 }) 1512 1513 Context("when the build is not found", func() { 1514 BeforeEach(func() { 1515 dbBuildFactory.BuildReturns(nil, false, nil) 1516 }) 1517 1518 It("returns Not Found", func() { 1519 Expect(response.StatusCode).To(Equal(http.StatusNotFound)) 1520 }) 1521 }) 1522 1523 Context("when looking up the build fails", func() { 1524 BeforeEach(func() { 1525 dbBuildFactory.BuildReturns(nil, false, errors.New("oh no!")) 1526 }) 1527 1528 It("returns 500 Internal Server Error", func() { 1529 Expect(response.StatusCode).To(Equal(http.StatusInternalServerError)) 1530 }) 1531 }) 1532 }) 1533 })