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  })