github.com/pf-qiu/concourse/v6@v6.7.3-0.20201207032516-1f455d73275f/atc/api/jobs_test.go (about)

     1  package api_test
     2  
     3  import (
     4  	"encoding/json"
     5  	"errors"
     6  	"fmt"
     7  	"io/ioutil"
     8  	"net/http"
     9  	"strings"
    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("Jobs API", func() {
    21  	var fakeJob *dbfakes.FakeJob
    22  	var versionedResourceTypes atc.VersionedResourceTypes
    23  	var fakePipeline *dbfakes.FakePipeline
    24  
    25  	BeforeEach(func() {
    26  		fakeJob = new(dbfakes.FakeJob)
    27  		fakePipeline = new(dbfakes.FakePipeline)
    28  		dbTeamFactory.FindTeamReturns(dbTeam, true, nil)
    29  		dbTeam.PipelineReturns(fakePipeline, true, nil)
    30  
    31  		versionedResourceTypes = atc.VersionedResourceTypes{
    32  			atc.VersionedResourceType{
    33  				ResourceType: atc.ResourceType{
    34  					Name:   "some-resource-1",
    35  					Type:   "some-base-type-1",
    36  					Source: atc.Source{"some": "source-1"},
    37  				},
    38  				Version: atc.Version{"some": "version-1"},
    39  			},
    40  			atc.VersionedResourceType{
    41  				ResourceType: atc.ResourceType{
    42  					Name:   "some-resource-2",
    43  					Type:   "some-base-type-2",
    44  					Source: atc.Source{"some": "source-2"},
    45  				},
    46  				Version: atc.Version{"some": "version-2"},
    47  			},
    48  			atc.VersionedResourceType{
    49  				ResourceType: atc.ResourceType{
    50  					Name:   "some-resource-3",
    51  					Type:   "some-base-type-3",
    52  					Source: atc.Source{"some": "source-3"},
    53  				},
    54  				Version: atc.Version{"some": "version-3"},
    55  			},
    56  		}
    57  
    58  		fakePipeline.ResourceTypesReturns([]db.ResourceType{
    59  			fakeDBResourceType(versionedResourceTypes[0]),
    60  			fakeDBResourceType(versionedResourceTypes[1]),
    61  			fakeDBResourceType(versionedResourceTypes[2]),
    62  		}, nil)
    63  	})
    64  
    65  	Describe("GET /api/v1/jobs", func() {
    66  		var response *http.Response
    67  
    68  		JustBeforeEach(func() {
    69  			req, err := http.NewRequest("GET", server.URL+"/api/v1/jobs", nil)
    70  			Expect(err).NotTo(HaveOccurred())
    71  
    72  			req.Header.Set("Content-Type", "application/json")
    73  
    74  			response, err = client.Do(req)
    75  			Expect(err).NotTo(HaveOccurred())
    76  		})
    77  
    78  		BeforeEach(func() {
    79  			dbJobFactory.VisibleJobsReturns([]atc.JobSummary{
    80  				{
    81  					ID:           1,
    82  					Name:         "some-job",
    83  					Paused:       true,
    84  					PipelineID:   1,
    85  					PipelineName: "some-pipeline",
    86  					TeamName:     "some-team",
    87  
    88  					Inputs: []atc.JobInputSummary{
    89  						{
    90  							Name:     "some-input",
    91  							Resource: "some-input",
    92  							Trigger:  false,
    93  						},
    94  						{
    95  							Name:     "some-name",
    96  							Resource: "some-other-input",
    97  							Passed:   []string{"a", "b"},
    98  							Trigger:  true,
    99  						},
   100  					},
   101  
   102  					NextBuild: &atc.BuildSummary{
   103  						ID:           3,
   104  						Name:         "2",
   105  						JobName:      "some-job",
   106  						PipelineID:   1,
   107  						PipelineName: "some-pipeline",
   108  						TeamName:     "some-team",
   109  						Status:       "started",
   110  					},
   111  					FinishedBuild: &atc.BuildSummary{
   112  						ID:           1,
   113  						Name:         "1",
   114  						JobName:      "some-job",
   115  						PipelineID:   1,
   116  						PipelineName: "some-pipeline",
   117  						TeamName:     "some-team",
   118  						Status:       "succeeded",
   119  						StartTime:    1,
   120  						EndTime:      100,
   121  					},
   122  
   123  					Groups: []string{"group-1", "group-2"},
   124  				},
   125  			}, nil)
   126  		})
   127  
   128  		It("returns 200 OK", func() {
   129  			Expect(response.StatusCode).To(Equal(http.StatusOK))
   130  		})
   131  
   132  		It("returns application/json", func() {
   133  			expectedHeaderEntries := map[string]string{
   134  				"Content-Type": "application/json",
   135  			}
   136  			Expect(response).Should(IncludeHeaderEntries(expectedHeaderEntries))
   137  		})
   138  
   139  		It("returns all jobs from public pipelines and pipelines in authenticated teams", func() {
   140  			body, err := ioutil.ReadAll(response.Body)
   141  			Expect(err).NotTo(HaveOccurred())
   142  
   143  			Expect(body).To(MatchJSON(`[
   144  			{
   145  				"id": 1,
   146  				"name": "some-job",
   147  				"pipeline_id": 1,
   148  				"pipeline_name": "some-pipeline",
   149  				"team_name": "some-team",
   150  				"paused": true,
   151  				"next_build": {
   152  					"id": 3,
   153  					"team_name": "some-team",
   154  					"name": "2",
   155  					"status": "started",
   156  					"job_name": "some-job",
   157  					"pipeline_id": 1,
   158  					"pipeline_name": "some-pipeline"
   159  				},
   160  				"finished_build": {
   161  					"id": 1,
   162  					"team_name": "some-team",
   163  					"name": "1",
   164  					"status": "succeeded",
   165  					"job_name": "some-job",
   166  					"pipeline_id": 1,
   167  					"pipeline_name": "some-pipeline",
   168  					"start_time": 1,
   169  					"end_time": 100
   170  				},
   171  				"inputs": [
   172  					{
   173  						"name": "some-input",
   174  						"resource": "some-input"
   175  					},
   176  					{
   177  						"name": "some-name",
   178  						"resource": "some-other-input",
   179  						"passed": [
   180  							"a",
   181  							"b"
   182  						],
   183  						"trigger": true
   184  					}
   185  				],
   186  				"groups": ["group-1", "group-2"]
   187  			}
   188  			]`))
   189  		})
   190  
   191  		Context("when getting the jobs fails", func() {
   192  			BeforeEach(func() {
   193  				dbJobFactory.VisibleJobsReturns(nil, errors.New("nope"))
   194  			})
   195  
   196  			It("returns 500", func() {
   197  				Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
   198  			})
   199  		})
   200  
   201  		Context("when there are no visible jobs", func() {
   202  			BeforeEach(func() {
   203  				dbJobFactory.VisibleJobsReturns(nil, nil)
   204  			})
   205  
   206  			It("returns empty array", func() {
   207  				body, err := ioutil.ReadAll(response.Body)
   208  				Expect(err).NotTo(HaveOccurred())
   209  
   210  				Expect(body).To(MatchJSON(`[]`))
   211  			})
   212  		})
   213  
   214  		Context("when not authenticated", func() {
   215  			It("populates job factory with no team names", func() {
   216  				Expect(dbJobFactory.VisibleJobsCallCount()).To(Equal(1))
   217  				Expect(dbJobFactory.VisibleJobsArgsForCall(0)).To(BeEmpty())
   218  			})
   219  		})
   220  
   221  		Context("when authenticated", func() {
   222  			BeforeEach(func() {
   223  				fakeAccess.TeamNamesReturns([]string{"some-team"})
   224  			})
   225  
   226  			It("constructs job factory with provided team names", func() {
   227  				Expect(dbJobFactory.VisibleJobsCallCount()).To(Equal(1))
   228  				Expect(dbJobFactory.VisibleJobsArgsForCall(0)).To(ContainElement("some-team"))
   229  			})
   230  
   231  			Context("user has the admin privilege", func() {
   232  				BeforeEach(func() {
   233  					fakeAccess.IsAdminReturns(true)
   234  				})
   235  
   236  				It("returns all jobs from public and private pipelines from unauthenticated teams", func() {
   237  					Expect(dbJobFactory.AllActiveJobsCallCount()).To(Equal(1))
   238  				})
   239  			})
   240  		})
   241  	})
   242  
   243  	Describe("GET /api/v1/teams/:team_name/pipelines/:pipeline_name/jobs/:job_name", func() {
   244  		var response *http.Response
   245  
   246  		JustBeforeEach(func() {
   247  			var err error
   248  
   249  			response, err = client.Get(server.URL + "/api/v1/teams/some-team/pipelines/some-pipeline/jobs/some-job")
   250  			Expect(err).NotTo(HaveOccurred())
   251  		})
   252  
   253  		Context("when not authenticated", func() {
   254  			BeforeEach(func() {
   255  				fakeAccess.IsAuthenticatedReturns(false)
   256  			})
   257  
   258  			Context("and the pipeline is private", func() {
   259  				BeforeEach(func() {
   260  					fakePipeline.PublicReturns(false)
   261  				})
   262  
   263  				It("returns 401", func() {
   264  					Expect(response.StatusCode).To(Equal(http.StatusUnauthorized))
   265  				})
   266  			})
   267  
   268  			Context("and the pipeline is public", func() {
   269  				BeforeEach(func() {
   270  					fakePipeline.JobReturns(fakeJob, true, nil)
   271  					fakePipeline.PublicReturns(true)
   272  					fakeJob.FinishedAndNextBuildReturns(nil, nil, nil)
   273  				})
   274  
   275  				It("returns 200 OK", func() {
   276  					Expect(response.StatusCode).To(Equal(http.StatusOK))
   277  				})
   278  			})
   279  		})
   280  
   281  		Context("when authenticated and not authorized", func() {
   282  			BeforeEach(func() {
   283  				fakeAccess.IsAuthenticatedReturns(true)
   284  				fakeAccess.IsAuthorizedReturns(false)
   285  			})
   286  
   287  			Context("and the pipeline is private", func() {
   288  				BeforeEach(func() {
   289  					fakePipeline.PublicReturns(false)
   290  				})
   291  
   292  				It("returns 403", func() {
   293  					Expect(response.StatusCode).To(Equal(http.StatusForbidden))
   294  				})
   295  			})
   296  
   297  			Context("and the pipeline is public", func() {
   298  				BeforeEach(func() {
   299  					fakePipeline.JobReturns(fakeJob, true, nil)
   300  					fakePipeline.PublicReturns(true)
   301  					fakeJob.FinishedAndNextBuildReturns(nil, nil, nil)
   302  				})
   303  
   304  				It("returns 200 OK", func() {
   305  					Expect(response.StatusCode).To(Equal(http.StatusOK))
   306  				})
   307  			})
   308  		})
   309  
   310  		Context("when authenticated and authorized", func() {
   311  			var build1 *dbfakes.FakeBuild
   312  			var build2 *dbfakes.FakeBuild
   313  
   314  			BeforeEach(func() {
   315  				fakeAccess.IsAuthenticatedReturns(true)
   316  				fakeAccess.IsAuthorizedReturns(true)
   317  			})
   318  
   319  			Context("when getting the build succeeds", func() {
   320  				BeforeEach(func() {
   321  					build1 = new(dbfakes.FakeBuild)
   322  					build1.IDReturns(1)
   323  					build1.NameReturns("1")
   324  					build1.JobNameReturns("some-job")
   325  					build1.PipelineIDReturns(1)
   326  					build1.PipelineNameReturns("some-pipeline")
   327  					build1.TeamNameReturns("some-team")
   328  					build1.StatusReturns(db.BuildStatusSucceeded)
   329  					build1.StartTimeReturns(time.Unix(1, 0))
   330  					build1.EndTimeReturns(time.Unix(100, 0))
   331  
   332  					build2 = new(dbfakes.FakeBuild)
   333  					build2.IDReturns(3)
   334  					build2.NameReturns("2")
   335  					build2.JobNameReturns("some-job")
   336  					build2.PipelineIDReturns(1)
   337  					build2.PipelineNameReturns("some-pipeline")
   338  					build2.TeamNameReturns("some-team")
   339  					build2.StatusReturns(db.BuildStatusStarted)
   340  				})
   341  
   342  				Context("when getting the job fails", func() {
   343  					BeforeEach(func() {
   344  						fakePipeline.JobReturns(nil, false, errors.New("nope"))
   345  					})
   346  
   347  					It("returns 500", func() {
   348  						Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
   349  					})
   350  				})
   351  
   352  				Context("when getting the job succeeds", func() {
   353  					BeforeEach(func() {
   354  						fakeJob.IDReturns(1)
   355  						fakeJob.PausedReturns(true)
   356  						fakeJob.FirstLoggedBuildIDReturns(99)
   357  						fakeJob.PipelineIDReturns(1)
   358  						fakeJob.PipelineNameReturns("some-pipeline")
   359  						fakeJob.NameReturns("some-job")
   360  						fakeJob.TagsReturns([]string{"group-1", "group-2"})
   361  						fakeJob.FinishedAndNextBuildReturns(build1, build2, nil)
   362  
   363  						fakePipeline.JobReturns(fakeJob, true, nil)
   364  					})
   365  
   366  					It("fetches the inputs", func() {
   367  						Expect(fakeJob.InputsCallCount()).To(Equal(1))
   368  					})
   369  
   370  					Context("when getting the inputs fails", func() {
   371  						BeforeEach(func() {
   372  							fakeJob.InputsReturns(nil, errors.New("nope"))
   373  						})
   374  
   375  						It("returns 500", func() {
   376  							Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
   377  						})
   378  					})
   379  
   380  					Context("when getting the inputs succeeds", func() {
   381  						BeforeEach(func() {
   382  							fakeJob.InputsReturns([]atc.JobInput{
   383  								{
   384  									Name:     "some-input",
   385  									Resource: "some-input",
   386  								},
   387  								{
   388  									Name:     "some-name",
   389  									Resource: "some-other-input",
   390  									Passed:   []string{"a", "b"},
   391  									Trigger:  true,
   392  								},
   393  							}, nil)
   394  						})
   395  
   396  						It("fetches the outputs", func() {
   397  							Expect(fakeJob.OutputsCallCount()).To(Equal(1))
   398  						})
   399  
   400  						Context("when getting the outputs fails", func() {
   401  							BeforeEach(func() {
   402  								fakeJob.OutputsReturns(nil, errors.New("nope"))
   403  							})
   404  
   405  							It("returns 500", func() {
   406  								Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
   407  							})
   408  						})
   409  
   410  						Context("when getting the outputs succeeds", func() {
   411  							BeforeEach(func() {
   412  								fakeJob.OutputsReturns([]atc.JobOutput{
   413  									{
   414  										Name:     "some-output",
   415  										Resource: "some-output",
   416  									},
   417  									{
   418  										Name:     "some-other-output",
   419  										Resource: "some-other-output",
   420  									},
   421  								}, nil)
   422  							})
   423  
   424  							It("fetches by job", func() {
   425  								Expect(fakeJob.FinishedAndNextBuildCallCount()).To(Equal(1))
   426  							})
   427  
   428  							It("returns 200 OK", func() {
   429  								Expect(response.StatusCode).To(Equal(http.StatusOK))
   430  							})
   431  
   432  							It("returns Content-Type 'application/json'", func() {
   433  								expectedHeaderEntries := map[string]string{
   434  									"Content-Type": "application/json",
   435  								}
   436  								Expect(response).Should(IncludeHeaderEntries(expectedHeaderEntries))
   437  							})
   438  
   439  							It("returns the job's name, if it's paused, and any running and finished builds", func() {
   440  								body, err := ioutil.ReadAll(response.Body)
   441  								Expect(err).NotTo(HaveOccurred())
   442  
   443  								Expect(body).To(MatchJSON(`{
   444  							"id": 1,
   445  							"name": "some-job",
   446  							"pipeline_id": 1,
   447  							"pipeline_name": "some-pipeline",
   448  							"team_name": "some-team",
   449  							"paused": true,
   450  							"first_logged_build_id": 99,
   451  							"next_build": {
   452  								"id": 3,
   453  								"name": "2",
   454  								"job_name": "some-job",
   455  								"status": "started",
   456  								"api_url": "/api/v1/builds/3",
   457  								"pipeline_id": 1,
   458  								"pipeline_name": "some-pipeline",
   459  								"team_name": "some-team"
   460  							},
   461  							"finished_build": {
   462  								"id": 1,
   463  								"name": "1",
   464  								"job_name": "some-job",
   465  								"status": "succeeded",
   466  								"api_url": "/api/v1/builds/1",
   467  								"pipeline_id": 1,
   468  								"pipeline_name": "some-pipeline",
   469  								"team_name": "some-team",
   470  								"start_time": 1,
   471  								"end_time": 100
   472  							},
   473  							"inputs": [
   474  								{
   475  									"name": "some-input",
   476  									"resource": "some-input",
   477  									"trigger": false
   478  								},
   479  								{
   480  									"name": "some-name",
   481  									"resource": "some-other-input",
   482  									"passed": ["a", "b"],
   483  									"trigger": true
   484  								}
   485  							],
   486  							"outputs": [
   487  								{
   488  									"name": "some-output",
   489  									"resource": "some-output"
   490  								},
   491  								{
   492  									"name": "some-other-output",
   493  									"resource": "some-other-output"
   494  								}
   495  							],
   496  							"groups": ["group-1", "group-2"]
   497  						}`))
   498  
   499  							})
   500  
   501  							Context("when there are no running or finished builds", func() {
   502  								BeforeEach(func() {
   503  									fakeJob.FinishedAndNextBuildReturns(nil, nil, nil)
   504  								})
   505  
   506  								It("returns null as their entries", func() {
   507  									var job atc.Job
   508  									err := json.NewDecoder(response.Body).Decode(&job)
   509  									Expect(err).NotTo(HaveOccurred())
   510  
   511  									Expect(job.NextBuild).To(BeNil())
   512  									Expect(job.FinishedBuild).To(BeNil())
   513  								})
   514  							})
   515  
   516  							Context("when getting the job's builds fails", func() {
   517  								BeforeEach(func() {
   518  									fakeJob.FinishedAndNextBuildReturns(nil, nil, errors.New("oh no!"))
   519  								})
   520  
   521  								It("returns 500", func() {
   522  									Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
   523  								})
   524  							})
   525  						})
   526  					})
   527  				})
   528  			})
   529  
   530  			Context("when the job is not found", func() {
   531  				BeforeEach(func() {
   532  					fakePipeline.JobReturns(nil, false, nil)
   533  				})
   534  
   535  				It("returns 404", func() {
   536  					Expect(response.StatusCode).To(Equal(http.StatusNotFound))
   537  				})
   538  			})
   539  		})
   540  	})
   541  
   542  	Describe("GET /api/v1/teams/:team_name/pipelines/:pipeline_name/jobs/:job_name/badge", func() {
   543  		var response *http.Response
   544  
   545  		JustBeforeEach(func() {
   546  			var err error
   547  
   548  			response, err = client.Get(server.URL + "/api/v1/teams/some-team/pipelines/some-pipeline/jobs/some-job/badge")
   549  			Expect(err).NotTo(HaveOccurred())
   550  		})
   551  
   552  		Context("when authenticated and not authorized", func() {
   553  			BeforeEach(func() {
   554  				fakeAccess.IsAuthenticatedReturns(true)
   555  				fakeAccess.IsAuthorizedReturns(false)
   556  			})
   557  
   558  			Context("and the pipeline is private", func() {
   559  				BeforeEach(func() {
   560  					fakePipeline.PublicReturns(false)
   561  				})
   562  
   563  				It("returns 403", func() {
   564  					Expect(response.StatusCode).To(Equal(http.StatusForbidden))
   565  				})
   566  			})
   567  
   568  			Context("and the pipeline is public", func() {
   569  				BeforeEach(func() {
   570  					fakePipeline.PublicReturns(true)
   571  					fakePipeline.JobReturns(fakeJob, true, nil)
   572  				})
   573  
   574  				It("returns 200 OK", func() {
   575  					Expect(response.StatusCode).To(Equal(http.StatusOK))
   576  				})
   577  			})
   578  		})
   579  
   580  		Context("when authorized", func() {
   581  			BeforeEach(func() {
   582  				fakeAccess.IsAuthenticatedReturns(true)
   583  				fakeAccess.IsAuthorizedReturns(true)
   584  
   585  				fakePipeline.JobReturns(fakeJob, true, nil)
   586  				fakeJob.NameReturns("some-job")
   587  			})
   588  
   589  			It("fetches by job", func() {
   590  				Expect(fakeJob.FinishedAndNextBuildCallCount()).To(Equal(1))
   591  			})
   592  
   593  			It("returns 200 OK", func() {
   594  				Expect(response.StatusCode).To(Equal(http.StatusOK))
   595  			})
   596  
   597  			It("returns Content-Type as image/svg+xml and disables caching", func() {
   598  				expectedHeaderEntries := map[string]string{
   599  					"Content-Type":  "image/svg+xml",
   600  					"Cache-Control": "no-cache, no-store, must-revalidate",
   601  					"Expires":       "0",
   602  				}
   603  				Expect(response).Should(IncludeHeaderEntries(expectedHeaderEntries))
   604  			})
   605  
   606  			Context("when generates bagde title", func() {
   607  				It("uses url `title` parameter", func() {
   608  					response, err := client.Get(server.URL + "/api/v1/teams/some-team/pipelines/some-pipeline/jobs/some-job/badge?title=cov")
   609  					Expect(err).NotTo(HaveOccurred())
   610  
   611  					body, err := ioutil.ReadAll(response.Body)
   612  					Expect(err).NotTo(HaveOccurred())
   613  
   614  					Expect(strings.Contains(string(body), `
   615        <text x="18.5" y="15" fill="#010101" fill-opacity=".3">cov</text>
   616        <text x="18.5" y="14">cov</text>
   617  `)).Should(BeTrue())
   618  				})
   619  				It("uses default `build` title if not specified", func() {
   620  					response, err := client.Get(server.URL + "/api/v1/teams/some-team/pipelines/some-pipeline/jobs/some-job/badge")
   621  					Expect(err).NotTo(HaveOccurred())
   622  
   623  					body, err := ioutil.ReadAll(response.Body)
   624  					Expect(err).NotTo(HaveOccurred())
   625  
   626  					Expect(strings.Contains(string(body), `
   627        <text x="18.5" y="15" fill="#010101" fill-opacity=".3">build</text>
   628        <text x="18.5" y="14">build</text>
   629  `)).Should(BeTrue())
   630  				})
   631  				It("uses default `build` title if url `title` parameter is falsy", func() {
   632  					response, err := client.Get(server.URL + "/api/v1/teams/some-team/pipelines/some-pipeline/jobs/some-job/badge?title=")
   633  					Expect(err).NotTo(HaveOccurred())
   634  
   635  					body, err := ioutil.ReadAll(response.Body)
   636  					Expect(err).NotTo(HaveOccurred())
   637  
   638  					Expect(strings.Contains(string(body), `
   639        <text x="18.5" y="15" fill="#010101" fill-opacity=".3">build</text>
   640        <text x="18.5" y="14">build</text>
   641  `)).Should(BeTrue())
   642  				})
   643  				It("html escapes title", func() {
   644  					response, err := client.Get(server.URL + "/api/v1/teams/some-team/pipelines/some-pipeline/jobs/some-job/badge?title=%24cov")
   645  					Expect(err).NotTo(HaveOccurred())
   646  
   647  					body, err := ioutil.ReadAll(response.Body)
   648  					Expect(err).NotTo(HaveOccurred())
   649  
   650  					Expect(strings.Contains(string(body), `
   651        <text x="18.5" y="15" fill="#010101" fill-opacity=".3">$cov</text>
   652        <text x="18.5" y="14">$cov</text>
   653  `)).Should(BeTrue())
   654  				})
   655  			})
   656  
   657  			Context("when the finished build is successful", func() {
   658  				BeforeEach(func() {
   659  					build1 := new(dbfakes.FakeBuild)
   660  					build1.IDReturns(1)
   661  					build1.NameReturns("1")
   662  					build1.JobNameReturns("some-job")
   663  					build1.PipelineNameReturns("some-pipeline")
   664  					build1.StartTimeReturns(time.Unix(1, 0))
   665  					build1.EndTimeReturns(time.Unix(100, 0))
   666  					build1.StatusReturns(db.BuildStatusSucceeded)
   667  
   668  					build2 := new(dbfakes.FakeBuild)
   669  					build2.IDReturns(3)
   670  					build2.NameReturns("2")
   671  					build2.JobNameReturns("some-job")
   672  					build2.PipelineNameReturns("some-pipeline")
   673  					build2.StatusReturns(db.BuildStatusStarted)
   674  
   675  					fakeJob.FinishedAndNextBuildReturns(build1, build2, nil)
   676  				})
   677  
   678  				It("returns 200 OK", func() {
   679  					Expect(response.StatusCode).To(Equal(http.StatusOK))
   680  				})
   681  
   682  				It("returns some SVG showing that the job is successful", func() {
   683  					body, err := ioutil.ReadAll(response.Body)
   684  					Expect(err).NotTo(HaveOccurred())
   685  
   686  					Expect(string(body)).To(Equal(`<?xml version="1.0" encoding="UTF-8"?>
   687  <svg xmlns="http://www.w3.org/2000/svg" width="88" height="20">
   688     <linearGradient id="b" x2="0" y2="100%">
   689        <stop offset="0" stop-color="#bbb" stop-opacity=".1" />
   690        <stop offset="1" stop-opacity=".1" />
   691     </linearGradient>
   692     <mask id="a">
   693        <rect width="88" height="20" rx="3" fill="#fff" />
   694     </mask>
   695     <g mask="url(#a)">
   696        <path fill="#555" d="M0 0h37v20H0z" />
   697        <path fill="#44cc11" d="M37 0h51v20H37z" />
   698        <path fill="url(#b)" d="M0 0h88v20H0z" />
   699     </g>
   700     <g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
   701        <text x="18.5" y="15" fill="#010101" fill-opacity=".3">build</text>
   702        <text x="18.5" y="14">build</text>
   703        <text x="61.5" y="15" fill="#010101" fill-opacity=".3">passing</text>
   704        <text x="61.5" y="14">passing</text>
   705     </g>
   706  </svg>`))
   707  				})
   708  			})
   709  
   710  			Context("when the finished build is failed", func() {
   711  				BeforeEach(func() {
   712  					build1 := new(dbfakes.FakeBuild)
   713  					build1.IDReturns(1)
   714  					build1.NameReturns("1")
   715  					build1.JobNameReturns("some-job")
   716  					build1.PipelineNameReturns("some-pipeline")
   717  					build1.StartTimeReturns(time.Unix(1, 0))
   718  					build1.EndTimeReturns(time.Unix(100, 0))
   719  					build1.StatusReturns(db.BuildStatusFailed)
   720  
   721  					build2 := new(dbfakes.FakeBuild)
   722  					build2.IDReturns(3)
   723  					build2.NameReturns("2")
   724  					build2.JobNameReturns("some-job")
   725  					build2.PipelineNameReturns("some-pipeline")
   726  					build2.StatusReturns(db.BuildStatusStarted)
   727  
   728  					fakeJob.FinishedAndNextBuildReturns(build1, build2, nil)
   729  				})
   730  
   731  				It("returns 200 OK", func() {
   732  					Expect(response.StatusCode).To(Equal(http.StatusOK))
   733  				})
   734  
   735  				It("returns some SVG showing that the job has failed", func() {
   736  					body, err := ioutil.ReadAll(response.Body)
   737  					Expect(err).NotTo(HaveOccurred())
   738  
   739  					Expect(string(body)).To(Equal(`<?xml version="1.0" encoding="UTF-8"?>
   740  <svg xmlns="http://www.w3.org/2000/svg" width="80" height="20">
   741     <linearGradient id="b" x2="0" y2="100%">
   742        <stop offset="0" stop-color="#bbb" stop-opacity=".1" />
   743        <stop offset="1" stop-opacity=".1" />
   744     </linearGradient>
   745     <mask id="a">
   746        <rect width="80" height="20" rx="3" fill="#fff" />
   747     </mask>
   748     <g mask="url(#a)">
   749        <path fill="#555" d="M0 0h37v20H0z" />
   750        <path fill="#e05d44" d="M37 0h43v20H37z" />
   751        <path fill="url(#b)" d="M0 0h80v20H0z" />
   752     </g>
   753     <g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
   754        <text x="18.5" y="15" fill="#010101" fill-opacity=".3">build</text>
   755        <text x="18.5" y="14">build</text>
   756        <text x="57.5" y="15" fill="#010101" fill-opacity=".3">failing</text>
   757        <text x="57.5" y="14">failing</text>
   758     </g>
   759  </svg>`))
   760  				})
   761  			})
   762  
   763  			Context("when the finished build was aborted", func() {
   764  				BeforeEach(func() {
   765  					build1 := new(dbfakes.FakeBuild)
   766  					build1.IDReturns(1)
   767  					build1.NameReturns("1")
   768  					build1.JobNameReturns("some-job")
   769  					build1.PipelineNameReturns("some-pipeline")
   770  					build1.StatusReturns(db.BuildStatusAborted)
   771  
   772  					build2 := new(dbfakes.FakeBuild)
   773  					build2.IDReturns(1)
   774  					build2.NameReturns("1")
   775  					build2.JobNameReturns("some-job")
   776  					build2.PipelineNameReturns("some-pipeline")
   777  					build2.StartTimeReturns(time.Unix(1, 0))
   778  					build2.EndTimeReturns(time.Unix(100, 0))
   779  					build2.StatusReturns(db.BuildStatusAborted)
   780  
   781  					fakeJob.FinishedAndNextBuildReturns(build1, build2, nil)
   782  				})
   783  
   784  				It("returns 200 OK", func() {
   785  					Expect(response.StatusCode).To(Equal(http.StatusOK))
   786  				})
   787  
   788  				It("returns some SVG showing that the job was aborted", func() {
   789  					body, err := ioutil.ReadAll(response.Body)
   790  					Expect(err).NotTo(HaveOccurred())
   791  
   792  					Expect(string(body)).To(Equal(`<?xml version="1.0" encoding="UTF-8"?>
   793  <svg xmlns="http://www.w3.org/2000/svg" width="90" height="20">
   794     <linearGradient id="b" x2="0" y2="100%">
   795        <stop offset="0" stop-color="#bbb" stop-opacity=".1" />
   796        <stop offset="1" stop-opacity=".1" />
   797     </linearGradient>
   798     <mask id="a">
   799        <rect width="90" height="20" rx="3" fill="#fff" />
   800     </mask>
   801     <g mask="url(#a)">
   802        <path fill="#555" d="M0 0h37v20H0z" />
   803        <path fill="#8f4b2d" d="M37 0h53v20H37z" />
   804        <path fill="url(#b)" d="M0 0h90v20H0z" />
   805     </g>
   806     <g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
   807        <text x="18.5" y="15" fill="#010101" fill-opacity=".3">build</text>
   808        <text x="18.5" y="14">build</text>
   809        <text x="62.5" y="15" fill="#010101" fill-opacity=".3">aborted</text>
   810        <text x="62.5" y="14">aborted</text>
   811     </g>
   812  </svg>`))
   813  				})
   814  			})
   815  
   816  			Context("when the finished build errored", func() {
   817  				BeforeEach(func() {
   818  					build1 := new(dbfakes.FakeBuild)
   819  					build1.IDReturns(1)
   820  					build1.NameReturns("1")
   821  					build1.JobNameReturns("some-job")
   822  					build1.PipelineNameReturns("some-pipeline")
   823  					build1.StartTimeReturns(time.Unix(1, 0))
   824  					build1.EndTimeReturns(time.Unix(100, 0))
   825  					build1.StatusReturns(db.BuildStatusErrored)
   826  
   827  					build2 := new(dbfakes.FakeBuild)
   828  					build2.IDReturns(3)
   829  					build2.NameReturns("2")
   830  					build2.JobNameReturns("some-job")
   831  					build2.PipelineNameReturns("some-pipeline")
   832  					build2.StatusReturns(db.BuildStatusStarted)
   833  
   834  					fakeJob.FinishedAndNextBuildReturns(build1, build2, nil)
   835  				})
   836  
   837  				It("returns 200 OK", func() {
   838  					Expect(response.StatusCode).To(Equal(http.StatusOK))
   839  				})
   840  
   841  				It("returns some SVG showing that the job has errored", func() {
   842  					body, err := ioutil.ReadAll(response.Body)
   843  					Expect(err).NotTo(HaveOccurred())
   844  
   845  					Expect(string(body)).To(Equal(`<?xml version="1.0" encoding="UTF-8"?>
   846  <svg xmlns="http://www.w3.org/2000/svg" width="88" height="20">
   847     <linearGradient id="b" x2="0" y2="100%">
   848        <stop offset="0" stop-color="#bbb" stop-opacity=".1" />
   849        <stop offset="1" stop-opacity=".1" />
   850     </linearGradient>
   851     <mask id="a">
   852        <rect width="88" height="20" rx="3" fill="#fff" />
   853     </mask>
   854     <g mask="url(#a)">
   855        <path fill="#555" d="M0 0h37v20H0z" />
   856        <path fill="#fe7d37" d="M37 0h51v20H37z" />
   857        <path fill="url(#b)" d="M0 0h88v20H0z" />
   858     </g>
   859     <g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
   860        <text x="18.5" y="15" fill="#010101" fill-opacity=".3">build</text>
   861        <text x="18.5" y="14">build</text>
   862        <text x="61.5" y="15" fill="#010101" fill-opacity=".3">errored</text>
   863        <text x="61.5" y="14">errored</text>
   864     </g>
   865  </svg>`))
   866  				})
   867  			})
   868  
   869  			Context("when there are no running or finished builds", func() {
   870  				BeforeEach(func() {
   871  					fakeJob.FinishedAndNextBuildReturns(nil, nil, nil)
   872  				})
   873  
   874  				It("returns an unknown badge", func() {
   875  					body, err := ioutil.ReadAll(response.Body)
   876  					Expect(err).NotTo(HaveOccurred())
   877  					Expect(string(body)).To(Equal(`<?xml version="1.0" encoding="UTF-8"?>
   878  <svg xmlns="http://www.w3.org/2000/svg" width="98" height="20">
   879     <linearGradient id="b" x2="0" y2="100%">
   880        <stop offset="0" stop-color="#bbb" stop-opacity=".1" />
   881        <stop offset="1" stop-opacity=".1" />
   882     </linearGradient>
   883     <mask id="a">
   884        <rect width="98" height="20" rx="3" fill="#fff" />
   885     </mask>
   886     <g mask="url(#a)">
   887        <path fill="#555" d="M0 0h37v20H0z" />
   888        <path fill="#9f9f9f" d="M37 0h61v20H37z" />
   889        <path fill="url(#b)" d="M0 0h98v20H0z" />
   890     </g>
   891     <g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
   892        <text x="18.5" y="15" fill="#010101" fill-opacity=".3">build</text>
   893        <text x="18.5" y="14">build</text>
   894        <text x="66.5" y="15" fill="#010101" fill-opacity=".3">unknown</text>
   895        <text x="66.5" y="14">unknown</text>
   896     </g>
   897  </svg>`))
   898  				})
   899  			})
   900  
   901  			Context("when getting the job's builds fails", func() {
   902  				BeforeEach(func() {
   903  					fakeJob.FinishedAndNextBuildReturns(nil, nil, errors.New("oh no!"))
   904  				})
   905  
   906  				It("returns 500", func() {
   907  					Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
   908  				})
   909  			})
   910  
   911  			Context("when the job is not present", func() {
   912  				BeforeEach(func() {
   913  					fakePipeline.JobReturns(nil, false, nil)
   914  				})
   915  
   916  				It("returns 404", func() {
   917  					Expect(response.StatusCode).To(Equal(http.StatusNotFound))
   918  				})
   919  			})
   920  		})
   921  	})
   922  
   923  	Describe("GET /api/v1/teams/:team_name/pipelines/:pipeline_name/jobs", func() {
   924  		var response *http.Response
   925  		var dashboardResponse []atc.JobSummary
   926  
   927  		JustBeforeEach(func() {
   928  			var err error
   929  
   930  			response, err = client.Get(server.URL + "/api/v1/teams/some-team/pipelines/some-pipeline/jobs")
   931  			Expect(err).NotTo(HaveOccurred())
   932  		})
   933  
   934  		Context("when getting the dashboard succeeds", func() {
   935  
   936  			BeforeEach(func() {
   937  
   938  				dashboardResponse = []atc.JobSummary{
   939  					{
   940  						ID:                   1,
   941  						Name:                 "job-1",
   942  						PipelineID:           2,
   943  						PipelineName:         "another-pipeline",
   944  						PipelineInstanceVars: atc.InstanceVars{"branch": "master"},
   945  						TeamName:             "some-team",
   946  						Paused:               true,
   947  						NextBuild: &atc.BuildSummary{
   948  							ID:                   3,
   949  							Name:                 "2",
   950  							JobName:              "job-1",
   951  							PipelineID:           2,
   952  							PipelineName:         "another-pipeline",
   953  							PipelineInstanceVars: atc.InstanceVars{"branch": "master"},
   954  							TeamName:             "some-team",
   955  							Status:               "started",
   956  						},
   957  						FinishedBuild: &atc.BuildSummary{
   958  							ID:                   1,
   959  							Name:                 "1",
   960  							JobName:              "job-1",
   961  							PipelineID:           2,
   962  							PipelineName:         "another-pipeline",
   963  							PipelineInstanceVars: atc.InstanceVars{"branch": "master"},
   964  							TeamName:             "some-team",
   965  							Status:               "succeeded",
   966  							StartTime:            1,
   967  							EndTime:              100,
   968  						},
   969  						TransitionBuild: &atc.BuildSummary{
   970  							ID:                   5,
   971  							Name:                 "five",
   972  							JobName:              "job-1",
   973  							PipelineID:           2,
   974  							PipelineName:         "another-pipeline",
   975  							PipelineInstanceVars: atc.InstanceVars{"branch": "master"},
   976  							TeamName:             "some-team",
   977  							Status:               "failed",
   978  							StartTime:            101,
   979  							EndTime:              200,
   980  						},
   981  						Inputs: []atc.JobInputSummary{
   982  							{
   983  								Name:     "input-1",
   984  								Resource: "input-1",
   985  							},
   986  						},
   987  						Groups: []string{
   988  							"group-1", "group-2",
   989  						},
   990  					},
   991  					{
   992  						ID:                   2,
   993  						Name:                 "job-2",
   994  						PipelineID:           2,
   995  						PipelineName:         "another-pipeline",
   996  						PipelineInstanceVars: atc.InstanceVars{"branch": "master"},
   997  						TeamName:             "some-team",
   998  						Paused:               true,
   999  						NextBuild:            nil,
  1000  						FinishedBuild: &atc.BuildSummary{
  1001  							ID:                   4,
  1002  							Name:                 "1",
  1003  							JobName:              "job-2",
  1004  							PipelineID:           2,
  1005  							PipelineName:         "another-pipeline",
  1006  							PipelineInstanceVars: atc.InstanceVars{"branch": "master"},
  1007  							TeamName:             "some-team",
  1008  							Status:               "succeeded",
  1009  							StartTime:            101,
  1010  							EndTime:              200,
  1011  						},
  1012  						TransitionBuild: nil,
  1013  						Inputs: []atc.JobInputSummary{
  1014  							{
  1015  								Name:     "input-2",
  1016  								Resource: "input-2",
  1017  							},
  1018  						},
  1019  						Groups: []string{
  1020  							"group-2",
  1021  						},
  1022  					},
  1023  					{
  1024  						ID:                   3,
  1025  						Name:                 "job-3",
  1026  						PipelineID:           2,
  1027  						PipelineName:         "another-pipeline",
  1028  						PipelineInstanceVars: atc.InstanceVars{"branch": "master"},
  1029  						TeamName:             "some-team",
  1030  						Paused:               true,
  1031  						NextBuild:            nil,
  1032  						FinishedBuild:        nil,
  1033  						TransitionBuild:      nil,
  1034  						Inputs: []atc.JobInputSummary{
  1035  							{
  1036  								Name:     "input-3",
  1037  								Resource: "input-3",
  1038  							},
  1039  						},
  1040  						Groups: []string{},
  1041  					},
  1042  				}
  1043  				fakePipeline.DashboardReturns(dashboardResponse, nil)
  1044  			})
  1045  
  1046  			Context("when not authorized", func() {
  1047  				BeforeEach(func() {
  1048  					fakeAccess.IsAuthorizedReturns(false)
  1049  				})
  1050  
  1051  				Context("when not authenticated", func() {
  1052  					BeforeEach(func() {
  1053  						fakeAccess.IsAuthenticatedReturns(false)
  1054  					})
  1055  
  1056  					Context("and the pipeline is private", func() {
  1057  						BeforeEach(func() {
  1058  							fakePipeline.PublicReturns(false)
  1059  						})
  1060  
  1061  						It("returns 401", func() {
  1062  							Expect(response.StatusCode).To(Equal(http.StatusUnauthorized))
  1063  						})
  1064  					})
  1065  
  1066  					Context("and the pipeline is public", func() {
  1067  						BeforeEach(func() {
  1068  							fakePipeline.PublicReturns(true)
  1069  						})
  1070  
  1071  						It("returns 200 OK", func() {
  1072  							Expect(response.StatusCode).To(Equal(http.StatusOK))
  1073  						})
  1074  					})
  1075  				})
  1076  			})
  1077  
  1078  			Context("when authorized", func() {
  1079  				BeforeEach(func() {
  1080  					fakeAccess.IsAuthorizedReturns(true)
  1081  					fakeAccess.IsAuthenticatedReturns(true)
  1082  				})
  1083  
  1084  				It("returns 200 OK", func() {
  1085  					Expect(response.StatusCode).To(Equal(http.StatusOK))
  1086  				})
  1087  
  1088  				It("returns Content-Type 'application/json'", func() {
  1089  					expectedHeaderEntries := map[string]string{
  1090  						"Content-Type": "application/json",
  1091  					}
  1092  					Expect(response).Should(IncludeHeaderEntries(expectedHeaderEntries))
  1093  				})
  1094  
  1095  				It("returns each job's name and any running and finished builds", func() {
  1096  					body, err := ioutil.ReadAll(response.Body)
  1097  					Expect(err).NotTo(HaveOccurred())
  1098  
  1099  					Expect(body).To(MatchJSON(`[
  1100  							{
  1101  								"id": 1,
  1102  								"name": "job-1",
  1103  								"pipeline_id": 2,
  1104  								"pipeline_name": "another-pipeline",
  1105  								"pipeline_instance_vars": {
  1106  									"branch": "master"
  1107  								},
  1108  								"team_name": "some-team",
  1109  								"paused": true,
  1110  								"next_build": {
  1111  									"id": 3,
  1112  									"name": "2",
  1113  									"job_name": "job-1",
  1114  									"status": "started",
  1115  									"pipeline_id": 2,
  1116  									"pipeline_name": "another-pipeline",
  1117  									"pipeline_instance_vars": {
  1118  										"branch": "master"
  1119  									},
  1120  									"team_name": "some-team"
  1121  								},
  1122  								"finished_build": {
  1123  									"id": 1,
  1124  									"name": "1",
  1125  									"job_name": "job-1",
  1126  									"status": "succeeded",
  1127  									"pipeline_id": 2,
  1128  									"pipeline_name": "another-pipeline",
  1129  									"pipeline_instance_vars": {
  1130  										"branch": "master"
  1131  									},
  1132  									"team_name": "some-team",
  1133  									"start_time": 1,
  1134  									"end_time": 100
  1135  								},
  1136  								"transition_build": {
  1137  									"id": 5,
  1138  									"name": "five",
  1139  									"job_name": "job-1",
  1140  									"status": "failed",
  1141  									"pipeline_id": 2,
  1142  									"pipeline_name": "another-pipeline",
  1143  									"pipeline_instance_vars": {
  1144  										"branch": "master"
  1145  									},
  1146  									"team_name": "some-team",
  1147  									"start_time": 101,
  1148  									"end_time": 200
  1149  								},
  1150  								"inputs": [{"name": "input-1", "resource": "input-1"}],
  1151  								"groups": ["group-1", "group-2"]
  1152  							},
  1153  							{
  1154  								"id": 2,
  1155  								"name": "job-2",
  1156  								"pipeline_id": 2,
  1157  								"pipeline_name": "another-pipeline",
  1158  								"pipeline_instance_vars": {
  1159  									"branch": "master"
  1160  								},
  1161  								"team_name": "some-team",
  1162  								"paused": true,
  1163  								"finished_build": {
  1164  									"id": 4,
  1165  									"name": "1",
  1166  									"job_name": "job-2",
  1167  									"status": "succeeded",
  1168  									"pipeline_id": 2,
  1169  									"pipeline_name": "another-pipeline",
  1170  									"pipeline_instance_vars": {
  1171  										"branch": "master"
  1172  									},
  1173  									"team_name": "some-team",
  1174  									"start_time": 101,
  1175  									"end_time": 200
  1176  								},
  1177  								"inputs": [{"name": "input-2", "resource": "input-2"}],
  1178  								"groups": ["group-2"]
  1179  							},
  1180  							{
  1181  								"id": 3,
  1182  								"name": "job-3",
  1183  								"pipeline_id": 2,
  1184  								"pipeline_name": "another-pipeline",
  1185  								"pipeline_instance_vars": {
  1186  									"branch": "master"
  1187  								},
  1188  								"team_name": "some-team",
  1189  								"paused": true,
  1190  								"inputs": [{"name": "input-3", "resource": "input-3"}]
  1191  							}
  1192  						]`))
  1193  				})
  1194  
  1195  				Context("when there are no jobs in dashboard", func() {
  1196  					BeforeEach(func() {
  1197  						dashboardResponse = []atc.JobSummary{}
  1198  						fakePipeline.DashboardReturns(dashboardResponse, nil)
  1199  					})
  1200  					It("should return an empty array", func() {
  1201  						body, err := ioutil.ReadAll(response.Body)
  1202  						Expect(err).NotTo(HaveOccurred())
  1203  
  1204  						Expect(body).To(MatchJSON(`[]`))
  1205  					})
  1206  				})
  1207  
  1208  				Context("when getting the dashboard fails", func() {
  1209  					Context("with an unknown error", func() {
  1210  						BeforeEach(func() {
  1211  							fakePipeline.DashboardReturns(nil, errors.New("oh no!"))
  1212  						})
  1213  
  1214  						It("returns 500", func() {
  1215  							Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
  1216  						})
  1217  					})
  1218  				})
  1219  			})
  1220  		})
  1221  	})
  1222  
  1223  	Describe("GET /api/v1/teams/:team_name/pipelines/:pipeline_name/jobs/:job_name/builds", func() {
  1224  		var response *http.Response
  1225  		var queryParams string
  1226  
  1227  		JustBeforeEach(func() {
  1228  			var err error
  1229  
  1230  			fakePipeline.NameReturns("some-pipeline")
  1231  			response, err = client.Get(server.URL + "/api/v1/teams/some-team/pipelines/some-pipeline/jobs/some-job/builds" + queryParams)
  1232  			Expect(err).NotTo(HaveOccurred())
  1233  		})
  1234  
  1235  		Context("when authenticated and not authorized", func() {
  1236  			BeforeEach(func() {
  1237  				fakeAccess.IsAuthorizedReturns(false)
  1238  				fakeAccess.IsAuthenticatedReturns(true)
  1239  			})
  1240  
  1241  			Context("and the pipeline is private", func() {
  1242  				BeforeEach(func() {
  1243  					fakePipeline.PublicReturns(false)
  1244  				})
  1245  
  1246  				It("returns 403", func() {
  1247  					Expect(response.StatusCode).To(Equal(http.StatusForbidden))
  1248  				})
  1249  			})
  1250  
  1251  			Context("and the pipeline is public", func() {
  1252  				BeforeEach(func() {
  1253  					fakeBuild := new(dbfakes.FakeBuild)
  1254  
  1255  					fakePipeline.PublicReturns(true)
  1256  					fakePipeline.JobReturns(fakeJob, true, nil)
  1257  					fakeJob.BuildReturns(fakeBuild, true, nil)
  1258  				})
  1259  
  1260  				It("returns 200 OK", func() {
  1261  					Expect(response.StatusCode).To(Equal(http.StatusOK))
  1262  				})
  1263  			})
  1264  		})
  1265  
  1266  		Context("when authorized", func() {
  1267  			BeforeEach(func() {
  1268  				fakeAccess.IsAuthorizedReturns(true)
  1269  			})
  1270  
  1271  			Context("when getting the job succeeds", func() {
  1272  				BeforeEach(func() {
  1273  					fakeJob.NameReturns("some-job")
  1274  					fakePipeline.JobReturns(fakeJob, true, nil)
  1275  				})
  1276  
  1277  				Context("when no params are passed", func() {
  1278  					It("does not set defaults for since and until", func() {
  1279  						Expect(fakeJob.BuildsCallCount()).To(Equal(1))
  1280  
  1281  						page := fakeJob.BuildsArgsForCall(0)
  1282  						Expect(page).To(Equal(db.Page{
  1283  							Limit: 100,
  1284  						}))
  1285  					})
  1286  				})
  1287  
  1288  				Context("when all the params are passed", func() {
  1289  					BeforeEach(func() {
  1290  						queryParams = "?from=2&to=3&limit=8"
  1291  					})
  1292  
  1293  					It("passes them through", func() {
  1294  						Expect(fakeJob.BuildsCallCount()).To(Equal(1))
  1295  
  1296  						page := fakeJob.BuildsArgsForCall(0)
  1297  						Expect(page).To(Equal(db.Page{
  1298  							From:  db.NewIntPtr(2),
  1299  							To:    db.NewIntPtr(3),
  1300  							Limit: 8,
  1301  						}))
  1302  					})
  1303  				})
  1304  
  1305  				Context("when getting the builds succeeds", func() {
  1306  					var returnedBuilds []db.Build
  1307  
  1308  					BeforeEach(func() {
  1309  						queryParams = "?since=5&limit=2"
  1310  
  1311  						build1 := new(dbfakes.FakeBuild)
  1312  						build1.IDReturns(4)
  1313  						build1.NameReturns("2")
  1314  						build1.JobNameReturns("some-job")
  1315  						build1.PipelineNameReturns("some-pipeline")
  1316  						build1.TeamNameReturns("some-team")
  1317  						build1.StatusReturns(db.BuildStatusStarted)
  1318  						build1.StartTimeReturns(time.Unix(1, 0))
  1319  						build1.EndTimeReturns(time.Unix(100, 0))
  1320  
  1321  						build2 := new(dbfakes.FakeBuild)
  1322  						build2.IDReturns(2)
  1323  						build2.NameReturns("1")
  1324  						build2.JobNameReturns("some-job")
  1325  						build2.PipelineNameReturns("some-pipeline")
  1326  						build2.TeamNameReturns("some-team")
  1327  						build2.StatusReturns(db.BuildStatusSucceeded)
  1328  						build2.StartTimeReturns(time.Unix(101, 0))
  1329  						build2.EndTimeReturns(time.Unix(200, 0))
  1330  
  1331  						build3 := new(dbfakes.FakeBuild)
  1332  						build3.IDReturns(3)
  1333  						build3.NameReturns("1.1")
  1334  						build3.JobNameReturns("some-job")
  1335  						build3.PipelineNameReturns("some-pipeline")
  1336  						build3.TeamNameReturns("some-team")
  1337  						build3.StatusReturns(db.BuildStatusSucceeded)
  1338  						build3.StartTimeReturns(time.Unix(102, 0))
  1339  						build3.EndTimeReturns(time.Unix(300, 0))
  1340  						build3.RerunOfReturns(2)
  1341  						build3.RerunOfNameReturns("1")
  1342  						build3.RerunNumberReturns(3)
  1343  
  1344  						returnedBuilds = []db.Build{build1, build2, build3}
  1345  						fakeJob.BuildsReturns(returnedBuilds, db.Pagination{}, nil)
  1346  					})
  1347  
  1348  					It("returns 200 OK", func() {
  1349  						Expect(response.StatusCode).To(Equal(http.StatusOK))
  1350  					})
  1351  
  1352  					It("returns Content-Type 'application/json'", func() {
  1353  						expectedHeaderEntries := map[string]string{
  1354  							"Content-Type": "application/json",
  1355  						}
  1356  						Expect(response).Should(IncludeHeaderEntries(expectedHeaderEntries))
  1357  					})
  1358  
  1359  					It("returns the builds", func() {
  1360  						body, err := ioutil.ReadAll(response.Body)
  1361  						Expect(err).NotTo(HaveOccurred())
  1362  
  1363  						Expect(body).To(MatchJSON(`[
  1364  					{
  1365  						"id": 4,
  1366  						"name": "2",
  1367  						"job_name": "some-job",
  1368  						"status": "started",
  1369  						"api_url": "/api/v1/builds/4",
  1370  						"pipeline_name":"some-pipeline",
  1371  						"team_name": "some-team",
  1372  						"start_time": 1,
  1373  						"end_time": 100
  1374  					},
  1375  					{
  1376  						"id": 2,
  1377  						"name": "1",
  1378  						"job_name": "some-job",
  1379  						"status": "succeeded",
  1380  						"api_url": "/api/v1/builds/2",
  1381  						"pipeline_name": "some-pipeline",
  1382  						"team_name": "some-team",
  1383  						"start_time": 101,
  1384  						"end_time": 200
  1385  					},
  1386  					{
  1387  						"id": 3,
  1388  						"name": "1.1",
  1389  						"job_name": "some-job",
  1390  						"status": "succeeded",
  1391  						"api_url": "/api/v1/builds/3",
  1392  						"pipeline_name": "some-pipeline",
  1393  						"team_name": "some-team",
  1394  						"start_time": 102,
  1395  						"end_time": 300,
  1396  						"rerun_of": {
  1397  							"id": 2,
  1398  							"name": "1"
  1399  						},
  1400  						"rerun_number": 3
  1401  					}
  1402  				]`))
  1403  					})
  1404  
  1405  					Context("when next/previous pages are available", func() {
  1406  						BeforeEach(func() {
  1407  							fakeJob.BuildsReturns(returnedBuilds, db.Pagination{
  1408  								Newer: &db.Page{From: db.NewIntPtr(4), Limit: 2},
  1409  								Older: &db.Page{To: db.NewIntPtr(2), Limit: 2},
  1410  							}, nil)
  1411  						})
  1412  
  1413  						It("returns Link headers per rfc5988", func() {
  1414  							Expect(response.Header["Link"]).To(ConsistOf([]string{
  1415  								fmt.Sprintf(`<%s/api/v1/teams/some-team/pipelines/some-pipeline/jobs/some-job/builds?from=4&limit=2>; rel="previous"`, externalURL),
  1416  								fmt.Sprintf(`<%s/api/v1/teams/some-team/pipelines/some-pipeline/jobs/some-job/builds?to=2&limit=2>; rel="next"`, externalURL),
  1417  							}))
  1418  						})
  1419  
  1420  						Context("and builds are on instanced pipeline", func() {
  1421  							BeforeEach(func() {
  1422  								fakePipeline.InstanceVarsReturns(atc.InstanceVars{"branch": "master"})
  1423  							})
  1424  
  1425  							It("returns Link headers per rfc5988", func() {
  1426  								link := fmt.Sprintf(`<%s/api/v1/teams/some-team/pipelines/some-pipeline/jobs/some-job/builds?`, externalURL)
  1427  								Expect(response.Header["Link"]).To(ConsistOf([]string{
  1428  									link + `from=4&limit=2&instance_vars=%7B%22branch%22%3A%22master%22%7D>; rel="previous"`,
  1429  									link + `to=2&limit=2&instance_vars=%7B%22branch%22%3A%22master%22%7D>; rel="next"`,
  1430  								}))
  1431  							})
  1432  						})
  1433  					})
  1434  				})
  1435  
  1436  				Context("when getting the build fails", func() {
  1437  					BeforeEach(func() {
  1438  						fakeJob.BuildsReturns(nil, db.Pagination{}, errors.New("oh no!"))
  1439  					})
  1440  
  1441  					It("returns 404 Not Found", func() {
  1442  						Expect(response.StatusCode).To(Equal(http.StatusNotFound))
  1443  					})
  1444  				})
  1445  			})
  1446  
  1447  			Context("when getting the job fails", func() {
  1448  				BeforeEach(func() {
  1449  					fakePipeline.JobReturns(nil, false, errors.New("oh no!"))
  1450  				})
  1451  
  1452  				It("returns 500", func() {
  1453  					Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
  1454  				})
  1455  			})
  1456  
  1457  			Context("when the job is not found", func() {
  1458  				BeforeEach(func() {
  1459  					fakePipeline.JobReturns(nil, false, nil)
  1460  				})
  1461  
  1462  				It("returns 404 Not Found", func() {
  1463  					Expect(response.StatusCode).To(Equal(http.StatusNotFound))
  1464  				})
  1465  			})
  1466  		})
  1467  	})
  1468  
  1469  	Describe("POST /api/v1/teams/:team_name/pipelines/:pipeline_name/jobs/:job_name/builds", func() {
  1470  		var request *http.Request
  1471  		var response *http.Response
  1472  
  1473  		BeforeEach(func() {
  1474  			var err error
  1475  
  1476  			request, err = http.NewRequest("POST", server.URL+"/api/v1/teams/some-team/pipelines/some-pipeline/jobs/some-job/builds", nil)
  1477  			Expect(err).NotTo(HaveOccurred())
  1478  		})
  1479  
  1480  		JustBeforeEach(func() {
  1481  			var err error
  1482  
  1483  			response, err = client.Do(request)
  1484  			Expect(err).NotTo(HaveOccurred())
  1485  		})
  1486  
  1487  		Context("when authorized and authenticated", func() {
  1488  			BeforeEach(func() {
  1489  				fakeAccess.IsAuthorizedReturns(true)
  1490  				fakeAccess.IsAuthenticatedReturns(true)
  1491  			})
  1492  
  1493  			Context("when getting the job fails", func() {
  1494  				BeforeEach(func() {
  1495  					fakePipeline.JobReturns(nil, false, errors.New("errorrr"))
  1496  				})
  1497  
  1498  				It("returns a 500", func() {
  1499  					Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
  1500  				})
  1501  			})
  1502  
  1503  			Context("when the job is not found", func() {
  1504  				BeforeEach(func() {
  1505  					fakePipeline.JobReturns(nil, false, nil)
  1506  				})
  1507  
  1508  				It("returns a 404", func() {
  1509  					Expect(response.StatusCode).To(Equal(http.StatusNotFound))
  1510  				})
  1511  			})
  1512  
  1513  			Context("when getting the job succeeds", func() {
  1514  				BeforeEach(func() {
  1515  					fakeJob.NameReturns("some-job")
  1516  					fakePipeline.JobReturns(fakeJob, true, nil)
  1517  				})
  1518  
  1519  				Context("when manual triggering is disabled", func() {
  1520  					BeforeEach(func() {
  1521  						fakeJob.DisableManualTriggerReturns(true)
  1522  					})
  1523  
  1524  					It("should return 409", func() {
  1525  						Expect(response.StatusCode).To(Equal(http.StatusConflict))
  1526  					})
  1527  
  1528  					It("does not trigger the build", func() {
  1529  						Expect(fakeJob.CreateBuildCallCount()).To(Equal(0))
  1530  					})
  1531  				})
  1532  
  1533  				Context("when manual triggering is enabled", func() {
  1534  					BeforeEach(func() {
  1535  						fakeJob.DisableManualTriggerReturns(false)
  1536  					})
  1537  
  1538  					Context("when triggering the build fails", func() {
  1539  						BeforeEach(func() {
  1540  							fakeJob.CreateBuildReturns(nil, errors.New("nopers"))
  1541  						})
  1542  						It("returns a 500", func() {
  1543  							Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
  1544  						})
  1545  					})
  1546  
  1547  					Context("when triggering the build succeeds", func() {
  1548  						BeforeEach(func() {
  1549  							build := new(dbfakes.FakeBuild)
  1550  							build.IDReturns(42)
  1551  							build.NameReturns("1")
  1552  							build.JobNameReturns("some-job")
  1553  							build.PipelineNameReturns("a-pipeline")
  1554  							build.TeamNameReturns("some-team")
  1555  							build.StatusReturns(db.BuildStatusStarted)
  1556  							build.StartTimeReturns(time.Unix(1, 0))
  1557  							build.EndTimeReturns(time.Unix(100, 0))
  1558  
  1559  							fakeJob.CreateBuildReturns(build, nil)
  1560  						})
  1561  
  1562  						It("triggers the build", func() {
  1563  							Expect(fakeJob.CreateBuildCallCount()).To(Equal(1))
  1564  						})
  1565  
  1566  						Context("when finding the pipeline resources fails", func() {
  1567  							BeforeEach(func() {
  1568  								fakePipeline.ResourcesReturns(nil, errors.New("nope"))
  1569  							})
  1570  
  1571  							It("returns a 500", func() {
  1572  								Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
  1573  							})
  1574  						})
  1575  
  1576  						Context("when finding the pipeline resources succeeds", func() {
  1577  							var fakeResource *dbfakes.FakeResource
  1578  
  1579  							BeforeEach(func() {
  1580  								fakeResource = new(dbfakes.FakeResource)
  1581  								fakeResource.NameReturns("some-input")
  1582  								fakeResource.CurrentPinnedVersionReturns(atc.Version{"some": "version"})
  1583  
  1584  								fakePipeline.ResourcesReturns([]db.Resource{fakeResource}, nil)
  1585  							})
  1586  
  1587  							Context("when finding the pipeline resource types fails", func() {
  1588  								BeforeEach(func() {
  1589  									fakePipeline.ResourceTypesReturns(nil, errors.New("nope"))
  1590  								})
  1591  
  1592  								It("returns a 500", func() {
  1593  									Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
  1594  								})
  1595  							})
  1596  
  1597  							Context("when finding the pipeline resources types succeeds", func() {
  1598  								var fakeResourceType *dbfakes.FakeResourceType
  1599  
  1600  								BeforeEach(func() {
  1601  									fakeResourceType = new(dbfakes.FakeResourceType)
  1602  									fakeResourceType.NameReturns("some-input")
  1603  
  1604  									fakePipeline.ResourceTypesReturns([]db.ResourceType{fakeResourceType}, nil)
  1605  								})
  1606  
  1607  								It("fetches the job inputs", func() {
  1608  									Expect(fakeJob.InputsCallCount()).To(Equal(1))
  1609  								})
  1610  
  1611  								Context("when it fails to fetch the job inputs", func() {
  1612  									BeforeEach(func() {
  1613  										fakeJob.InputsReturns(nil, errors.New("nope"))
  1614  									})
  1615  
  1616  									It("returns a 500", func() {
  1617  										Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
  1618  									})
  1619  								})
  1620  
  1621  								Context("when the job inputs are successfully fetched", func() {
  1622  									BeforeEach(func() {
  1623  										fakeJob.InputsReturns([]atc.JobInput{
  1624  											{
  1625  												Name:     "some-input",
  1626  												Resource: "some-input",
  1627  											},
  1628  										}, nil)
  1629  									})
  1630  
  1631  									It("returns 200 OK", func() {
  1632  										Expect(response.StatusCode).To(Equal(http.StatusOK))
  1633  									})
  1634  
  1635  									It("returns Content-Type 'application/json'", func() {
  1636  										expectedHeaderEntries := map[string]string{
  1637  											"Content-Type": "application/json",
  1638  										}
  1639  										Expect(response).Should(IncludeHeaderEntries(expectedHeaderEntries))
  1640  									})
  1641  
  1642  									It("creates a check for the resource", func() {
  1643  										Expect(dbCheckFactory.TryCreateCheckCallCount()).To(Equal(1))
  1644  									})
  1645  
  1646  									It("runs the check from the current pinned version", func() {
  1647  										_, _, _, fromVersion, _ := dbCheckFactory.TryCreateCheckArgsForCall(0)
  1648  										Expect(fromVersion).To(Equal(atc.Version{"some": "version"}))
  1649  									})
  1650  
  1651  									It("returns the build", func() {
  1652  										body, err := ioutil.ReadAll(response.Body)
  1653  										Expect(err).NotTo(HaveOccurred())
  1654  
  1655  										Expect(body).To(MatchJSON(`{
  1656  							"id": 42,
  1657  							"name": "1",
  1658  							"job_name": "some-job",
  1659  							"status": "started",
  1660  							"api_url": "/api/v1/builds/42",
  1661  							"pipeline_name": "a-pipeline",
  1662  							"team_name": "some-team",
  1663  							"start_time": 1,
  1664  							"end_time": 100
  1665  						}`))
  1666  									})
  1667  								})
  1668  							})
  1669  						})
  1670  					})
  1671  				})
  1672  			})
  1673  		})
  1674  	})
  1675  
  1676  	Describe("GET /api/v1/teams/:team_name/pipelines/:pipeline_name/jobs/:job_name/inputs", func() {
  1677  		var response *http.Response
  1678  
  1679  		JustBeforeEach(func() {
  1680  			var err error
  1681  
  1682  			response, err = client.Get(server.URL + "/api/v1/teams/some-team/pipelines/some-pipeline/jobs/some-job/inputs")
  1683  			Expect(err).NotTo(HaveOccurred())
  1684  		})
  1685  
  1686  		Context("when not authenticated", func() {
  1687  			BeforeEach(func() {
  1688  				fakeAccess.IsAuthenticatedReturns(false)
  1689  			})
  1690  
  1691  			It("returns 401", func() {
  1692  				Expect(response.StatusCode).To(Equal(http.StatusUnauthorized))
  1693  			})
  1694  		})
  1695  
  1696  		Context("when authenticated", func() {
  1697  			BeforeEach(func() {
  1698  				fakeAccess.IsAuthenticatedReturns(true)
  1699  			})
  1700  
  1701  			Context("when not authorized", func() {
  1702  				BeforeEach(func() {
  1703  					fakeAccess.IsAuthorizedReturns(false)
  1704  				})
  1705  
  1706  				It("returns 403", func() {
  1707  					Expect(response.StatusCode).To(Equal(http.StatusForbidden))
  1708  				})
  1709  			})
  1710  
  1711  			Context("when authorized", func() {
  1712  				BeforeEach(func() {
  1713  					fakeAccess.IsAuthorizedReturns(true)
  1714  				})
  1715  
  1716  				Context("when getting the job fails", func() {
  1717  					BeforeEach(func() {
  1718  						fakePipeline.JobReturns(nil, false, errors.New("some-error"))
  1719  					})
  1720  
  1721  					It("returns 500", func() {
  1722  						Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
  1723  					})
  1724  				})
  1725  
  1726  				Context("when the job is not found", func() {
  1727  					BeforeEach(func() {
  1728  						fakePipeline.JobReturns(nil, false, nil)
  1729  					})
  1730  
  1731  					It("returns 404", func() {
  1732  						Expect(response.StatusCode).To(Equal(http.StatusNotFound))
  1733  					})
  1734  				})
  1735  
  1736  				Context("when getting the job succeeds", func() {
  1737  					BeforeEach(func() {
  1738  						fakePipeline.JobReturns(fakeJob, true, nil)
  1739  					})
  1740  
  1741  					Context("when getting the resources fails", func() {
  1742  						BeforeEach(func() {
  1743  							fakePipeline.ResourcesReturns(nil, errors.New("some-error"))
  1744  						})
  1745  
  1746  						It("returns 500", func() {
  1747  							Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
  1748  						})
  1749  					})
  1750  
  1751  					Context("when getting the resources succeeds", func() {
  1752  						BeforeEach(func() {
  1753  							resource1 := new(dbfakes.FakeResource)
  1754  							resource1.IDReturns(1)
  1755  							resource1.NameReturns("some-resource")
  1756  							resource1.TypeReturns("some-type")
  1757  							resource1.SourceReturns(atc.Source{"some": "source"})
  1758  
  1759  							resource2 := new(dbfakes.FakeResource)
  1760  							resource1.IDReturns(2)
  1761  							resource2.NameReturns("some-other-resource")
  1762  							resource2.TypeReturns("some-other-type")
  1763  							resource2.SourceReturns(atc.Source{"some": "other-source"})
  1764  
  1765  							fakePipeline.ResourcesReturns([]db.Resource{resource1, resource2}, nil)
  1766  						})
  1767  
  1768  						Context("when getting the input versions for the job fails", func() {
  1769  							BeforeEach(func() {
  1770  								fakeJob.GetFullNextBuildInputsReturns(nil, false, errors.New("oh no!"))
  1771  							})
  1772  
  1773  							It("returns 500", func() {
  1774  								Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
  1775  							})
  1776  						})
  1777  
  1778  						Context("when the job has no input versions available", func() {
  1779  							BeforeEach(func() {
  1780  								fakeJob.GetFullNextBuildInputsReturns(nil, false, nil)
  1781  							})
  1782  
  1783  							It("returns 404", func() {
  1784  								Expect(response.StatusCode).To(Equal(http.StatusNotFound))
  1785  							})
  1786  						})
  1787  
  1788  						Context("when the job has input versions", func() {
  1789  							BeforeEach(func() {
  1790  								inputs := []db.BuildInput{
  1791  									{
  1792  										Name:       "some-input",
  1793  										Version:    atc.Version{"some": "version"},
  1794  										ResourceID: 1,
  1795  									},
  1796  									{
  1797  										Name:       "some-other-input",
  1798  										Version:    atc.Version{"some": "other-version"},
  1799  										ResourceID: 2,
  1800  									},
  1801  								}
  1802  
  1803  								fakeJob.GetFullNextBuildInputsReturns(inputs, true, nil)
  1804  							})
  1805  
  1806  							It("fetches the job config", func() {
  1807  								Expect(fakeJob.ConfigCallCount()).To(Equal(1))
  1808  							})
  1809  
  1810  							Context("when it fails to fetch the job config", func() {
  1811  								BeforeEach(func() {
  1812  									fakeJob.ConfigReturns(atc.JobConfig{}, errors.New("nope"))
  1813  								})
  1814  
  1815  								It("returns a 500", func() {
  1816  									Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
  1817  								})
  1818  							})
  1819  
  1820  							Context("when the job inputs are successfully fetched", func() {
  1821  								BeforeEach(func() {
  1822  									fakeJob.ConfigReturns(atc.JobConfig{
  1823  										Name: "some-job",
  1824  										PlanSequence: []atc.Step{
  1825  											{
  1826  												Config: &atc.GetStep{
  1827  													Name:     "some-input",
  1828  													Resource: "some-resource",
  1829  													Passed:   []string{"job-a", "job-b"},
  1830  													Params:   atc.Params{"some": "params"},
  1831  												},
  1832  											},
  1833  											{
  1834  												Config: &atc.GetStep{
  1835  													Name:     "some-other-input",
  1836  													Resource: "some-other-resource",
  1837  													Passed:   []string{"job-c", "job-d"},
  1838  													Params:   atc.Params{"some": "other-params"},
  1839  													Tags:     []string{"some-tag"},
  1840  												},
  1841  											},
  1842  										},
  1843  									}, nil)
  1844  								})
  1845  
  1846  								It("returns 200 OK", func() {
  1847  									Expect(response.StatusCode).To(Equal(http.StatusOK))
  1848  								})
  1849  
  1850  								It("returns Content-Type 'application/json'", func() {
  1851  									expectedHeaderEntries := map[string]string{
  1852  										"Content-Type": "application/json",
  1853  									}
  1854  									Expect(response).Should(IncludeHeaderEntries(expectedHeaderEntries))
  1855  								})
  1856  
  1857  								It("returns the inputs", func() {
  1858  									body, err := ioutil.ReadAll(response.Body)
  1859  									Expect(err).NotTo(HaveOccurred())
  1860  
  1861  									Expect(body).To(MatchJSON(`[
  1862  									{
  1863  										"name": "some-input",
  1864  										"resource": "some-resource",
  1865  										"type": "some-type",
  1866  										"source": {"some": "source"},
  1867  										"version": {"some": "version"},
  1868  										"params": {"some": "params"}
  1869  									},
  1870  									{
  1871  										"name": "some-other-input",
  1872  										"resource": "some-other-resource",
  1873  										"type": "some-other-type",
  1874  										"source": {"some": "other-source"},
  1875  										"version": {"some": "other-version"},
  1876  										"params": {"some": "other-params"},
  1877  										"tags": ["some-tag"]
  1878  									}
  1879  								]`))
  1880  								})
  1881  							})
  1882  						})
  1883  					})
  1884  				})
  1885  			})
  1886  		})
  1887  	})
  1888  
  1889  	Describe("GET /api/v1/teams/:team_name/pipelines/:pipeline_name/jobs/:job_name/builds/:build_name", func() {
  1890  		var response *http.Response
  1891  
  1892  		JustBeforeEach(func() {
  1893  			var err error
  1894  
  1895  			response, err = client.Get(server.URL + "/api/v1/teams/some-team/pipelines/some-pipeline/jobs/some-job/builds/some-build")
  1896  			Expect(err).NotTo(HaveOccurred())
  1897  		})
  1898  
  1899  		Context("when authorized", func() {
  1900  			BeforeEach(func() {
  1901  				fakeAccess.IsAuthorizedReturns(true)
  1902  				fakeAccess.IsAuthenticatedReturns(true)
  1903  			})
  1904  
  1905  			Context("when getting the job succeeds", func() {
  1906  				var fakeJob *dbfakes.FakeJob
  1907  
  1908  				BeforeEach(func() {
  1909  					fakeJob = new(dbfakes.FakeJob)
  1910  					fakeJob.NameReturns("some-job")
  1911  					fakePipeline.JobReturns(fakeJob, true, nil)
  1912  				})
  1913  
  1914  				Context("when getting the build succeeds", func() {
  1915  					BeforeEach(func() {
  1916  						dbBuild := new(dbfakes.FakeBuild)
  1917  						dbBuild.IDReturns(1)
  1918  						dbBuild.NameReturns("1")
  1919  						dbBuild.JobNameReturns("some-job")
  1920  						dbBuild.PipelineNameReturns("a-pipeline")
  1921  						dbBuild.TeamNameReturns("some-team")
  1922  						dbBuild.StatusReturns(db.BuildStatusSucceeded)
  1923  						dbBuild.StartTimeReturns(time.Unix(1, 0))
  1924  						dbBuild.EndTimeReturns(time.Unix(100, 0))
  1925  						fakeJob.BuildReturns(dbBuild, true, nil)
  1926  					})
  1927  
  1928  					It("returns 200 OK", func() {
  1929  						Expect(response.StatusCode).To(Equal(http.StatusOK))
  1930  					})
  1931  
  1932  					It("returns Content-Type 'application/json'", func() {
  1933  						expectedHeaderEntries := map[string]string{
  1934  							"Content-Type": "application/json",
  1935  						}
  1936  						Expect(response).Should(IncludeHeaderEntries(expectedHeaderEntries))
  1937  					})
  1938  
  1939  					It("fetches by job and build name", func() {
  1940  						Expect(fakeJob.BuildCallCount()).To(Equal(1))
  1941  
  1942  						buildName := fakeJob.BuildArgsForCall(0)
  1943  						Expect(buildName).To(Equal("some-build"))
  1944  					})
  1945  
  1946  					It("returns the build", func() {
  1947  						body, err := ioutil.ReadAll(response.Body)
  1948  						Expect(err).NotTo(HaveOccurred())
  1949  
  1950  						Expect(body).To(MatchJSON(`{
  1951  					"id": 1,
  1952  					"name": "1",
  1953  					"job_name": "some-job",
  1954  					"status": "succeeded",
  1955  					"api_url": "/api/v1/builds/1",
  1956  					"pipeline_name": "a-pipeline",
  1957  					"team_name": "some-team",
  1958  					"start_time": 1,
  1959  					"end_time": 100
  1960  				}`))
  1961  
  1962  					})
  1963  				})
  1964  
  1965  				Context("when the build is not found", func() {
  1966  					BeforeEach(func() {
  1967  						fakeJob.BuildReturns(nil, false, nil)
  1968  					})
  1969  
  1970  					It("returns Not Found", func() {
  1971  						Expect(response.StatusCode).To(Equal(http.StatusNotFound))
  1972  					})
  1973  				})
  1974  
  1975  				Context("when getting the build fails", func() {
  1976  					BeforeEach(func() {
  1977  						fakeJob.BuildReturns(nil, false, errors.New("oh no!"))
  1978  					})
  1979  
  1980  					It("returns Internal Server Error", func() {
  1981  						Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
  1982  					})
  1983  				})
  1984  			})
  1985  
  1986  			Context("when the job is not found", func() {
  1987  				BeforeEach(func() {
  1988  					fakePipeline.JobReturns(nil, false, nil)
  1989  				})
  1990  
  1991  				It("returns Not Found", func() {
  1992  					Expect(response.StatusCode).To(Equal(http.StatusNotFound))
  1993  				})
  1994  			})
  1995  
  1996  			Context("when getting the build fails", func() {
  1997  				BeforeEach(func() {
  1998  					fakePipeline.JobReturns(nil, false, errors.New("oh no!"))
  1999  				})
  2000  
  2001  				It("returns Internal Server Error", func() {
  2002  					Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
  2003  				})
  2004  			})
  2005  		})
  2006  
  2007  		Context("when not authorized", func() {
  2008  			BeforeEach(func() {
  2009  				fakeAccess.IsAuthorizedReturns(false)
  2010  			})
  2011  
  2012  			Context("and the pipeline is private", func() {
  2013  				BeforeEach(func() {
  2014  					fakePipeline.PublicReturns(false)
  2015  				})
  2016  
  2017  				Context("when not authenticated", func() {
  2018  					BeforeEach(func() {
  2019  						fakeAccess.IsAuthenticatedReturns(false)
  2020  					})
  2021  					It("returns 401", func() {
  2022  						Expect(response.StatusCode).To(Equal(http.StatusUnauthorized))
  2023  					})
  2024  				})
  2025  
  2026  				Context("when authenticated", func() {
  2027  					BeforeEach(func() {
  2028  						fakeAccess.IsAuthenticatedReturns(true)
  2029  					})
  2030  
  2031  					It("returns 403", func() {
  2032  						Expect(response.StatusCode).To(Equal(http.StatusForbidden))
  2033  					})
  2034  				})
  2035  			})
  2036  
  2037  			Context("and the pipeline is public", func() {
  2038  				BeforeEach(func() {
  2039  					fakeBuild := new(dbfakes.FakeBuild)
  2040  					fakePipeline.JobReturns(fakeJob, true, nil)
  2041  					fakeJob.BuildReturns(fakeBuild, true, nil)
  2042  
  2043  					fakePipeline.PublicReturns(true)
  2044  				})
  2045  
  2046  				It("returns 200 OK", func() {
  2047  					Expect(response.StatusCode).To(Equal(http.StatusOK))
  2048  				})
  2049  			})
  2050  		})
  2051  	})
  2052  
  2053  	Describe("POST /api/v1/teams/:team_name/pipelines/:pipeline_name/jobs/:job_name/builds/:build_name", func() {
  2054  		var request *http.Request
  2055  		var response *http.Response
  2056  
  2057  		BeforeEach(func() {
  2058  			var err error
  2059  
  2060  			request, err = http.NewRequest("POST", server.URL+"/api/v1/teams/some-team/pipelines/some-pipeline/jobs/some-job/builds/some-build", nil)
  2061  			Expect(err).NotTo(HaveOccurred())
  2062  		})
  2063  
  2064  		JustBeforeEach(func() {
  2065  			var err error
  2066  
  2067  			response, err = client.Do(request)
  2068  			Expect(err).NotTo(HaveOccurred())
  2069  		})
  2070  
  2071  		Context("when authorized and authenticated", func() {
  2072  			BeforeEach(func() {
  2073  				fakeAccess.IsAuthorizedReturns(true)
  2074  				fakeAccess.IsAuthenticatedReturns(true)
  2075  			})
  2076  
  2077  			Context("when getting the job fails", func() {
  2078  				BeforeEach(func() {
  2079  					fakePipeline.JobReturns(nil, false, errors.New("errorrr"))
  2080  				})
  2081  
  2082  				It("returns a 500", func() {
  2083  					Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
  2084  				})
  2085  			})
  2086  
  2087  			Context("when the job is not found", func() {
  2088  				BeforeEach(func() {
  2089  					fakePipeline.JobReturns(nil, false, nil)
  2090  				})
  2091  
  2092  				It("returns a 404", func() {
  2093  					Expect(response.StatusCode).To(Equal(http.StatusNotFound))
  2094  				})
  2095  			})
  2096  
  2097  			Context("when getting the job succeeds", func() {
  2098  				BeforeEach(func() {
  2099  					fakeJob.NameReturns("some-job")
  2100  					fakePipeline.JobReturns(fakeJob, true, nil)
  2101  				})
  2102  
  2103  				It("tries to get the build to rerun", func() {
  2104  					Expect(fakeJob.BuildCallCount()).To(Equal(1))
  2105  				})
  2106  
  2107  				Context("when getting the build to rerun fails", func() {
  2108  					BeforeEach(func() {
  2109  						fakeJob.BuildReturns(nil, false, errors.New("oops"))
  2110  					})
  2111  
  2112  					It("returns a 500", func() {
  2113  						Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
  2114  					})
  2115  				})
  2116  
  2117  				Context("when the build to rerun is not found", func() {
  2118  					BeforeEach(func() {
  2119  						fakeJob.BuildReturns(nil, false, nil)
  2120  					})
  2121  
  2122  					It("returns a 404", func() {
  2123  						Expect(response.StatusCode).To(Equal(http.StatusNotFound))
  2124  					})
  2125  				})
  2126  
  2127  				Context("when getting the build to rerun succeeds", func() {
  2128  					var fakeBuild *dbfakes.FakeBuild
  2129  					BeforeEach(func() {
  2130  						fakeBuild = new(dbfakes.FakeBuild)
  2131  						fakeBuild.IDReturns(1)
  2132  						fakeBuild.NameReturns("1")
  2133  						fakeBuild.JobNameReturns("some-job")
  2134  						fakeBuild.PipelineNameReturns("a-pipeline")
  2135  						fakeBuild.TeamNameReturns("some-team")
  2136  						fakeBuild.StatusReturns(db.BuildStatusStarted)
  2137  						fakeBuild.StartTimeReturns(time.Unix(1, 0))
  2138  						fakeBuild.EndTimeReturns(time.Unix(100, 0))
  2139  
  2140  						fakeJob.BuildReturns(fakeBuild, true, nil)
  2141  					})
  2142  
  2143  					Context("when the build has no inputs", func() {
  2144  						BeforeEach(func() {
  2145  							fakeBuild.InputsReadyReturns(false)
  2146  						})
  2147  
  2148  						It("returns a 500", func() {
  2149  							Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
  2150  						})
  2151  					})
  2152  
  2153  					Context("when the build is input ready", func() {
  2154  						BeforeEach(func() {
  2155  							fakeBuild.InputsReadyReturns(true)
  2156  						})
  2157  						Context("when creating the rerun build fails", func() {
  2158  							BeforeEach(func() {
  2159  								fakeJob.RerunBuildReturns(nil, errors.New("nopers"))
  2160  							})
  2161  
  2162  							It("returns a 500", func() {
  2163  								Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
  2164  							})
  2165  						})
  2166  
  2167  						Context("when creating the rerun build succeeds", func() {
  2168  							BeforeEach(func() {
  2169  								build := new(dbfakes.FakeBuild)
  2170  								build.IDReturns(2)
  2171  								build.NameReturns("1.1")
  2172  								build.JobNameReturns("some-job")
  2173  								build.PipelineNameReturns("a-pipeline")
  2174  								build.TeamNameReturns("some-team")
  2175  								build.StatusReturns(db.BuildStatusStarted)
  2176  								build.StartTimeReturns(time.Unix(1, 0))
  2177  								build.EndTimeReturns(time.Unix(100, 0))
  2178  
  2179  								fakeJob.RerunBuildReturns(build, nil)
  2180  							})
  2181  
  2182  							It("returns 200 OK", func() {
  2183  								Expect(response.StatusCode).To(Equal(http.StatusOK))
  2184  							})
  2185  
  2186  							It("returns Content-Type 'application/json'", func() {
  2187  								expectedHeaderEntries := map[string]string{
  2188  									"Content-Type": "application/json",
  2189  								}
  2190  								Expect(response).Should(IncludeHeaderEntries(expectedHeaderEntries))
  2191  							})
  2192  
  2193  							It("returns the build", func() {
  2194  								body, err := ioutil.ReadAll(response.Body)
  2195  								Expect(err).NotTo(HaveOccurred())
  2196  
  2197  								Expect(body).To(MatchJSON(`{
  2198  							"id": 2,
  2199  							"name": "1.1",
  2200  							"job_name": "some-job",
  2201  							"status": "started",
  2202  							"api_url": "/api/v1/builds/2",
  2203  							"pipeline_name": "a-pipeline",
  2204  							"team_name": "some-team",
  2205  							"start_time": 1,
  2206  							"end_time": 100
  2207  						}`))
  2208  							})
  2209  						})
  2210  					})
  2211  				})
  2212  			})
  2213  		})
  2214  	})
  2215  
  2216  	Describe("PUT /api/v1/teams/:team_name/pipelines/:pipeline_name/jobs/:job_name/pause", func() {
  2217  		var response *http.Response
  2218  
  2219  		JustBeforeEach(func() {
  2220  			var err error
  2221  
  2222  			request, err := http.NewRequest("PUT", server.URL+"/api/v1/teams/some-team/pipelines/some-pipeline/jobs/job-name/pause", nil)
  2223  			Expect(err).NotTo(HaveOccurred())
  2224  
  2225  			response, err = client.Do(request)
  2226  			Expect(err).NotTo(HaveOccurred())
  2227  		})
  2228  
  2229  		Context("when authenticated", func() {
  2230  			BeforeEach(func() {
  2231  				fakeAccess.IsAuthenticatedReturns(true)
  2232  			})
  2233  			Context("when authorized", func() {
  2234  				BeforeEach(func() {
  2235  					fakeAccess.IsAuthorizedReturns(true)
  2236  
  2237  					fakePipeline.JobReturns(fakeJob, true, nil)
  2238  					fakeJob.PauseReturns(nil)
  2239  				})
  2240  
  2241  				It("finds the job on the pipeline and pauses it", func() {
  2242  					jobName := fakePipeline.JobArgsForCall(0)
  2243  					Expect(jobName).To(Equal("job-name"))
  2244  
  2245  					Expect(fakeJob.PauseCallCount()).To(Equal(1))
  2246  
  2247  					Expect(response.StatusCode).To(Equal(http.StatusOK))
  2248  				})
  2249  
  2250  				Context("when the job is not found", func() {
  2251  					BeforeEach(func() {
  2252  						fakePipeline.JobReturns(nil, false, nil)
  2253  					})
  2254  
  2255  					It("returns a 404", func() {
  2256  						Expect(response.StatusCode).To(Equal(http.StatusNotFound))
  2257  					})
  2258  				})
  2259  
  2260  				Context("when finding the job fails", func() {
  2261  					BeforeEach(func() {
  2262  						fakePipeline.JobReturns(nil, false, errors.New("some-error"))
  2263  					})
  2264  
  2265  					It("returns a 500", func() {
  2266  						Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
  2267  					})
  2268  				})
  2269  
  2270  				Context("when the job fails to be paused", func() {
  2271  					BeforeEach(func() {
  2272  						fakeJob.PauseReturns(errors.New("some-error"))
  2273  					})
  2274  
  2275  					It("returns a 500", func() {
  2276  						Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
  2277  					})
  2278  				})
  2279  			})
  2280  		})
  2281  
  2282  		Context("when not authenticated", func() {
  2283  			BeforeEach(func() {
  2284  				fakeAccess.IsAuthenticatedReturns(false)
  2285  			})
  2286  
  2287  			It("returns Status Unauthorized", func() {
  2288  				Expect(response.StatusCode).To(Equal(http.StatusUnauthorized))
  2289  			})
  2290  		})
  2291  	})
  2292  
  2293  	Describe("PUT /api/v1/teams/:team_name/pipelines/:pipeline_name/jobs/:job_name/unpause", func() {
  2294  		var response *http.Response
  2295  
  2296  		JustBeforeEach(func() {
  2297  			var err error
  2298  
  2299  			request, err := http.NewRequest("PUT", server.URL+"/api/v1/teams/some-team/pipelines/some-pipeline/jobs/job-name/unpause", nil)
  2300  			Expect(err).NotTo(HaveOccurred())
  2301  
  2302  			response, err = client.Do(request)
  2303  			Expect(err).NotTo(HaveOccurred())
  2304  		})
  2305  
  2306  		Context("when authorized", func() {
  2307  			BeforeEach(func() {
  2308  				fakeAccess.IsAuthorizedReturns(true)
  2309  			})
  2310  
  2311  			Context("when authenticated", func() {
  2312  				BeforeEach(func() {
  2313  					fakeAccess.IsAuthenticatedReturns(true)
  2314  
  2315  					fakePipeline.JobReturns(fakeJob, true, nil)
  2316  					fakeJob.UnpauseReturns(nil)
  2317  				})
  2318  
  2319  				It("finds the job on the pipeline and unpauses it", func() {
  2320  					jobName := fakePipeline.JobArgsForCall(0)
  2321  					Expect(jobName).To(Equal("job-name"))
  2322  
  2323  					Expect(fakeJob.UnpauseCallCount()).To(Equal(1))
  2324  
  2325  					Expect(response.StatusCode).To(Equal(http.StatusOK))
  2326  				})
  2327  
  2328  				Context("when the job is not found", func() {
  2329  					BeforeEach(func() {
  2330  						fakePipeline.JobReturns(nil, false, nil)
  2331  					})
  2332  
  2333  					It("returns a 404", func() {
  2334  						Expect(response.StatusCode).To(Equal(http.StatusNotFound))
  2335  					})
  2336  				})
  2337  
  2338  				Context("when finding the job fails", func() {
  2339  					BeforeEach(func() {
  2340  						fakePipeline.JobReturns(nil, false, errors.New("some-error"))
  2341  					})
  2342  
  2343  					It("returns a 500", func() {
  2344  						Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
  2345  					})
  2346  				})
  2347  
  2348  				Context("when the job fails to be unpaused", func() {
  2349  					BeforeEach(func() {
  2350  						fakeJob.UnpauseReturns(errors.New("some-error"))
  2351  					})
  2352  
  2353  					It("returns a 500", func() {
  2354  						Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
  2355  					})
  2356  				})
  2357  			})
  2358  		})
  2359  
  2360  		Context("when not authenticated", func() {
  2361  			BeforeEach(func() {
  2362  				fakeAccess.IsAuthenticatedReturns(false)
  2363  			})
  2364  
  2365  			It("returns Status Unauthorized", func() {
  2366  				Expect(response.StatusCode).To(Equal(http.StatusUnauthorized))
  2367  			})
  2368  		})
  2369  	})
  2370  
  2371  	Describe("DELETE /api/v1/teams/:team_name/pipelines/:pipeline_name/jobs/:job_name/tasks/:step_name/cache", func() {
  2372  		var (
  2373  			request  *http.Request
  2374  			response *http.Response
  2375  		)
  2376  
  2377  		BeforeEach(func() {
  2378  			var err error
  2379  
  2380  			request, err = http.NewRequest("DELETE", server.URL+"/api/v1/teams/some-team/pipelines/some-pipeline/jobs/job-name/tasks/:step_name/cache", nil)
  2381  			Expect(err).NotTo(HaveOccurred())
  2382  		})
  2383  
  2384  		JustBeforeEach(func() {
  2385  			var err error
  2386  
  2387  			response, err = client.Do(request)
  2388  			Expect(err).NotTo(HaveOccurred())
  2389  		})
  2390  
  2391  		Context("when authorized", func() {
  2392  			BeforeEach(func() {
  2393  				fakeAccess.IsAuthorizedReturns(true)
  2394  			})
  2395  
  2396  			Context("when authenticated", func() {
  2397  				BeforeEach(func() {
  2398  					fakeAccess.IsAuthenticatedReturns(true)
  2399  
  2400  					fakePipeline.JobReturns(fakeJob, true, nil)
  2401  					fakeJob.ClearTaskCacheReturns(1, nil)
  2402  
  2403  				})
  2404  
  2405  				Context("when no cachePath is passed", func() {
  2406  					It("it finds the right job", func() {
  2407  						jobName := fakePipeline.JobArgsForCall(0)
  2408  						Expect(jobName).To(Equal("job-name"))
  2409  					})
  2410  
  2411  					It("it clears the db cache entries successfully", func() {
  2412  						Expect(fakeJob.ClearTaskCacheCallCount()).To(Equal(1))
  2413  						_, cachePath := fakeJob.ClearTaskCacheArgsForCall(0)
  2414  						Expect(cachePath).To(Equal(""))
  2415  					})
  2416  
  2417  					It("returns 200 OK", func() {
  2418  						Expect(response.StatusCode).To(Equal(http.StatusOK))
  2419  					})
  2420  
  2421  					It("returns Content-Type 'application/json'", func() {
  2422  						expectedHeaderEntries := map[string]string{
  2423  							"Content-Type": "application/json",
  2424  						}
  2425  						Expect(response).Should(IncludeHeaderEntries(expectedHeaderEntries))
  2426  					})
  2427  
  2428  					It("it returns the number of rows deleted", func() {
  2429  						body, err := ioutil.ReadAll(response.Body)
  2430  						Expect(err).NotTo(HaveOccurred())
  2431  
  2432  						Expect(body).To(MatchJSON(`{"caches_removed": 1}`))
  2433  					})
  2434  
  2435  					Context("but no rows were deleted", func() {
  2436  						BeforeEach(func() {
  2437  							fakeJob.ClearTaskCacheReturns(0, nil)
  2438  						})
  2439  
  2440  						It("it returns that 0 rows were deleted", func() {
  2441  							body, err := ioutil.ReadAll(response.Body)
  2442  							Expect(err).NotTo(HaveOccurred())
  2443  
  2444  							Expect(body).To(MatchJSON(`{"caches_removed": 0}`))
  2445  						})
  2446  
  2447  					})
  2448  				})
  2449  
  2450  				Context("when a cachePath is passed", func() {
  2451  					BeforeEach(func() {
  2452  						query := request.URL.Query()
  2453  						query.Add(atc.ClearTaskCacheQueryPath, "cache-path")
  2454  						request.URL.RawQuery = query.Encode()
  2455  					})
  2456  
  2457  					It("it finds the right job", func() {
  2458  						jobName := fakePipeline.JobArgsForCall(0)
  2459  						Expect(jobName).To(Equal("job-name"))
  2460  					})
  2461  
  2462  					It("it clears the db cache entries successfully", func() {
  2463  						Expect(fakeJob.ClearTaskCacheCallCount()).To(Equal(1))
  2464  						_, cachePath := fakeJob.ClearTaskCacheArgsForCall(0)
  2465  						Expect(cachePath).To(Equal("cache-path"))
  2466  					})
  2467  
  2468  					It("returns 200 OK", func() {
  2469  						Expect(response.StatusCode).To(Equal(http.StatusOK))
  2470  					})
  2471  
  2472  					It("returns Content-Type 'application/json'", func() {
  2473  						expectedHeaderEntries := map[string]string{
  2474  							"Content-Type": "application/json",
  2475  						}
  2476  						Expect(response).Should(IncludeHeaderEntries(expectedHeaderEntries))
  2477  					})
  2478  
  2479  					It("it returns the number of rows deleted", func() {
  2480  						body, err := ioutil.ReadAll(response.Body)
  2481  						Expect(err).NotTo(HaveOccurred())
  2482  
  2483  						Expect(body).To(MatchJSON(`{"caches_removed": 1}`))
  2484  					})
  2485  
  2486  					Context("but no rows corresponding to the cachePath are deleted", func() {
  2487  						BeforeEach(func() {
  2488  							fakeJob.ClearTaskCacheReturns(0, nil)
  2489  						})
  2490  
  2491  						It("it returns that 0 rows were deleted", func() {
  2492  							body, err := ioutil.ReadAll(response.Body)
  2493  							Expect(err).NotTo(HaveOccurred())
  2494  
  2495  							Expect(body).To(MatchJSON(`{"caches_removed": 0}`))
  2496  						})
  2497  					})
  2498  				})
  2499  
  2500  				Context("when the job is not found", func() {
  2501  					BeforeEach(func() {
  2502  						fakePipeline.JobReturns(nil, false, nil)
  2503  					})
  2504  
  2505  					It("returns a 404", func() {
  2506  						Expect(response.StatusCode).To(Equal(http.StatusNotFound))
  2507  					})
  2508  				})
  2509  
  2510  				Context("when finding the job fails", func() {
  2511  					BeforeEach(func() {
  2512  						fakePipeline.JobReturns(nil, false, errors.New("some-error"))
  2513  					})
  2514  
  2515  					It("returns a 500", func() {
  2516  						Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
  2517  					})
  2518  				})
  2519  
  2520  				Context("when there are problems removing the db cache entries", func() {
  2521  					BeforeEach(func() {
  2522  						fakeJob.ClearTaskCacheReturns(-1, errors.New("some-error"))
  2523  					})
  2524  
  2525  					It("returns a 500", func() {
  2526  						Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
  2527  					})
  2528  				})
  2529  			})
  2530  		})
  2531  
  2532  		Context("when not authenticated", func() {
  2533  			BeforeEach(func() {
  2534  				fakeAccess.IsAuthenticatedReturns(false)
  2535  			})
  2536  
  2537  			It("returns Status Unauthorized", func() {
  2538  				Expect(response.StatusCode).To(Equal(http.StatusUnauthorized))
  2539  			})
  2540  		})
  2541  	})
  2542  
  2543  	Describe("PUT /api/v1/teams/:team_name/pipelines/:pipeline_name/jobs/:job_name/schedule", func() {
  2544  		var response *http.Response
  2545  
  2546  		JustBeforeEach(func() {
  2547  			var err error
  2548  
  2549  			request, err := http.NewRequest("PUT", server.URL+"/api/v1/teams/some-team/pipelines/some-pipeline/jobs/job-name/schedule", nil)
  2550  			Expect(err).NotTo(HaveOccurred())
  2551  
  2552  			response, err = client.Do(request)
  2553  			Expect(err).NotTo(HaveOccurred())
  2554  		})
  2555  
  2556  		Context("when authenticated", func() {
  2557  			BeforeEach(func() {
  2558  				fakeAccess.IsAuthenticatedReturns(true)
  2559  			})
  2560  			Context("when authorized", func() {
  2561  				BeforeEach(func() {
  2562  					fakeAccess.IsAuthorizedReturns(true)
  2563  
  2564  					fakePipeline.JobReturns(fakeJob, true, nil)
  2565  					fakeJob.RequestScheduleReturns(nil)
  2566  				})
  2567  
  2568  				It("finds the job on the pipeline and schedules it", func() {
  2569  					jobName := fakePipeline.JobArgsForCall(0)
  2570  					Expect(jobName).To(Equal("job-name"))
  2571  
  2572  					Expect(fakeJob.RequestScheduleCallCount()).To(Equal(1))
  2573  
  2574  					Expect(response.StatusCode).To(Equal(http.StatusOK))
  2575  				})
  2576  
  2577  				Context("when the job is not found", func() {
  2578  					BeforeEach(func() {
  2579  						fakePipeline.JobReturns(nil, false, nil)
  2580  					})
  2581  
  2582  					It("returns a 404", func() {
  2583  						Expect(response.StatusCode).To(Equal(http.StatusNotFound))
  2584  					})
  2585  				})
  2586  
  2587  				Context("when finding the job fails", func() {
  2588  					BeforeEach(func() {
  2589  						fakePipeline.JobReturns(nil, false, errors.New("some-error"))
  2590  					})
  2591  
  2592  					It("returns a 500", func() {
  2593  						Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
  2594  					})
  2595  				})
  2596  
  2597  				Context("when the job fails to be scheduled", func() {
  2598  					BeforeEach(func() {
  2599  						fakeJob.RequestScheduleReturns(errors.New("some-error"))
  2600  					})
  2601  
  2602  					It("returns a 500", func() {
  2603  						Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
  2604  					})
  2605  				})
  2606  			})
  2607  		})
  2608  
  2609  		Context("when not authenticated", func() {
  2610  			BeforeEach(func() {
  2611  				fakeAccess.IsAuthenticatedReturns(false)
  2612  			})
  2613  
  2614  			It("returns Status Unauthorized", func() {
  2615  				Expect(response.StatusCode).To(Equal(http.StatusUnauthorized))
  2616  			})
  2617  		})
  2618  	})
  2619  })
  2620  
  2621  func fakeDBResourceType(t atc.VersionedResourceType) *dbfakes.FakeResourceType {
  2622  	fake := new(dbfakes.FakeResourceType)
  2623  	fake.NameReturns(t.Name)
  2624  	fake.TypeReturns(t.Type)
  2625  	fake.SourceReturns(t.Source)
  2626  	fake.VersionReturns(t.Version)
  2627  	return fake
  2628  }