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

     1  package api_test
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"errors"
     7  	"fmt"
     8  	"io/ioutil"
     9  	"net/http"
    10  	"time"
    11  
    12  	"github.com/pf-qiu/concourse/v6/atc"
    13  	"github.com/pf-qiu/concourse/v6/atc/db"
    14  	"github.com/pf-qiu/concourse/v6/atc/db/dbfakes"
    15  	. "github.com/pf-qiu/concourse/v6/atc/testhelpers"
    16  	. "github.com/onsi/ginkgo"
    17  	. "github.com/onsi/gomega"
    18  )
    19  
    20  var _ = Describe("Pipelines API", func() {
    21  	var (
    22  		dbPipeline *dbfakes.FakePipeline
    23  		fakeTeam   *dbfakes.FakeTeam
    24  
    25  		publicPipeline                 *dbfakes.FakePipeline
    26  		anotherPublicPipeline          *dbfakes.FakePipeline
    27  		privatePipeline                *dbfakes.FakePipeline
    28  		privatePipelineFromAnotherTeam *dbfakes.FakePipeline
    29  	)
    30  	BeforeEach(func() {
    31  		dbPipeline = new(dbfakes.FakePipeline)
    32  		fakeTeam = new(dbfakes.FakeTeam)
    33  		publicPipeline = new(dbfakes.FakePipeline)
    34  
    35  		publicPipeline.IDReturns(1)
    36  		publicPipeline.PausedReturns(true)
    37  		publicPipeline.PublicReturns(true)
    38  		publicPipeline.TeamNameReturns("main")
    39  		publicPipeline.NameReturns("public-pipeline")
    40  		publicPipeline.GroupsReturns(atc.GroupConfigs{
    41  			{
    42  				Name:      "group2",
    43  				Jobs:      []string{"job3", "job4"},
    44  				Resources: []string{"resource3", "resource4"},
    45  			},
    46  		})
    47  		publicPipeline.DisplayReturns(&atc.DisplayConfig{
    48  			BackgroundImage: "background.jpg",
    49  		})
    50  		publicPipeline.LastUpdatedReturns(time.Unix(1, 0))
    51  
    52  		anotherPublicPipeline = new(dbfakes.FakePipeline)
    53  		anotherPublicPipeline.IDReturns(2)
    54  		anotherPublicPipeline.PausedReturns(true)
    55  		anotherPublicPipeline.PublicReturns(true)
    56  		anotherPublicPipeline.TeamNameReturns("another")
    57  		anotherPublicPipeline.NameReturns("another-pipeline")
    58  		anotherPublicPipeline.LastUpdatedReturns(time.Unix(1, 0))
    59  
    60  		privatePipeline = new(dbfakes.FakePipeline)
    61  		privatePipeline.IDReturns(3)
    62  		privatePipeline.PausedReturns(false)
    63  		privatePipeline.PublicReturns(false)
    64  		privatePipeline.ArchivedReturns(true)
    65  		privatePipeline.TeamNameReturns("main")
    66  		privatePipeline.NameReturns("private-pipeline")
    67  		privatePipeline.GroupsReturns(atc.GroupConfigs{
    68  			{
    69  				Name:      "group1",
    70  				Jobs:      []string{"job1", "job2"},
    71  				Resources: []string{"resource1", "resource2"},
    72  			},
    73  		})
    74  		privatePipeline.LastUpdatedReturns(time.Unix(1, 0))
    75  
    76  		privatePipelineFromAnotherTeam = new(dbfakes.FakePipeline)
    77  		privatePipelineFromAnotherTeam.IDReturns(3)
    78  		privatePipelineFromAnotherTeam.PausedReturns(false)
    79  		privatePipelineFromAnotherTeam.PublicReturns(false)
    80  		privatePipelineFromAnotherTeam.TeamNameReturns("main")
    81  		privatePipelineFromAnotherTeam.NameReturns("private-pipeline")
    82  		privatePipelineFromAnotherTeam.LastUpdatedReturns(time.Unix(1, 0))
    83  
    84  		fakeTeam.PipelinesReturns([]db.Pipeline{
    85  			privatePipeline,
    86  			publicPipeline,
    87  		}, nil)
    88  
    89  		fakeTeam.PublicPipelinesReturns([]db.Pipeline{publicPipeline}, nil)
    90  
    91  		dbPipelineFactory.VisiblePipelinesReturns([]db.Pipeline{publicPipeline, anotherPublicPipeline}, nil)
    92  	})
    93  
    94  	Describe("GET /api/v1/pipelines", func() {
    95  		var response *http.Response
    96  
    97  		JustBeforeEach(func() {
    98  			req, err := http.NewRequest("GET", server.URL+"/api/v1/pipelines", nil)
    99  			Expect(err).NotTo(HaveOccurred())
   100  
   101  			req.Header.Set("Content-Type", "application/json")
   102  
   103  			response, err = client.Do(req)
   104  			Expect(err).NotTo(HaveOccurred())
   105  		})
   106  
   107  		It("returns 200 OK", func() {
   108  			Expect(response.StatusCode).To(Equal(http.StatusOK))
   109  		})
   110  
   111  		It("returns application/json", func() {
   112  			expectedHeaderEntries := map[string]string{
   113  				"Content-Type": "application/json",
   114  			}
   115  			Expect(response).Should(IncludeHeaderEntries(expectedHeaderEntries))
   116  		})
   117  
   118  		It("returns a JSON array of pipeline objects", func() {
   119  			body, err := ioutil.ReadAll(response.Body)
   120  			Expect(err).NotTo(HaveOccurred())
   121  			Expect(body).To(MatchJSON(`[
   122  				{
   123  					"id": 1,
   124  					"name": "public-pipeline",
   125  					"paused": true,
   126  					"public": true,
   127  					"archived": false,
   128  					"team_name": "main",
   129  					"last_updated": 1,
   130  					"groups": [
   131  						{
   132  							"name": "group2",
   133  							"jobs": ["job3", "job4"],
   134  							"resources": ["resource3", "resource4"]
   135  						}
   136  					],
   137  					"display": {
   138  						"background_image": "background.jpg"
   139  					}
   140  				},
   141  				{
   142  					"id": 2,
   143  					"name": "another-pipeline",
   144  					"paused": true,
   145  					"public": true,
   146  					"archived": false,
   147  					"team_name": "another",
   148  					"last_updated": 1
   149  				}
   150  			]`))
   151  		})
   152  
   153  		Context("when team is set in user context", func() {
   154  			BeforeEach(func() {
   155  				fakeAccess.TeamNamesReturns([]string{"some-team"})
   156  			})
   157  
   158  			It("constructs pipeline factory with provided team names", func() {
   159  				Expect(dbPipelineFactory.VisiblePipelinesCallCount()).To(Equal(1))
   160  				Expect(dbPipelineFactory.VisiblePipelinesArgsForCall(0)).To(ContainElement("some-team"))
   161  			})
   162  		})
   163  
   164  		Context("when not authenticated", func() {
   165  			It("returns only public pipelines", func() {
   166  				body, err := ioutil.ReadAll(response.Body)
   167  				Expect(err).NotTo(HaveOccurred())
   168  
   169  				var pipelines []map[string]interface{}
   170  				err = json.Unmarshal(body, &pipelines)
   171  				Expect(pipelines).To(ConsistOf(
   172  					HaveKeyWithValue("id", BeNumerically("==", publicPipeline.ID())),
   173  					HaveKeyWithValue("id", BeNumerically("==", anotherPublicPipeline.ID())),
   174  				))
   175  			})
   176  
   177  			It("populates pipeline factory with no team names", func() {
   178  				Expect(dbPipelineFactory.VisiblePipelinesCallCount()).To(Equal(1))
   179  				Expect(dbPipelineFactory.VisiblePipelinesArgsForCall(0)).To(BeEmpty())
   180  			})
   181  		})
   182  
   183  		Context("when authenticated", func() {
   184  			BeforeEach(func() {
   185  				fakeAccess.TeamNamesReturns([]string{"main"})
   186  				dbPipelineFactory.VisiblePipelinesReturns([]db.Pipeline{privatePipeline, publicPipeline, anotherPublicPipeline}, nil)
   187  			})
   188  
   189  			It("returns all pipelines of the team + all public pipelines", func() {
   190  				body, err := ioutil.ReadAll(response.Body)
   191  				Expect(err).NotTo(HaveOccurred())
   192  
   193  				Expect(dbPipelineFactory.VisiblePipelinesCallCount()).To(Equal(1))
   194  
   195  				var pipelines []map[string]interface{}
   196  				err = json.Unmarshal(body, &pipelines)
   197  				Expect(pipelines).To(ConsistOf(
   198  					HaveKeyWithValue("id", BeNumerically("==", publicPipeline.ID())),
   199  					HaveKeyWithValue("id", BeNumerically("==", privatePipeline.ID())),
   200  					HaveKeyWithValue("id", BeNumerically("==", anotherPublicPipeline.ID())),
   201  				))
   202  			})
   203  
   204  			Context("user has the Admin privilege", func() {
   205  				BeforeEach(func() {
   206  					fakeAccess.IsAdminReturns(true)
   207  					dbPipelineFactory.AllPipelinesReturns(
   208  						[]db.Pipeline{publicPipeline, privatePipeline, anotherPublicPipeline, privatePipelineFromAnotherTeam},
   209  						nil)
   210  				})
   211  
   212  				It("user can see all private and public pipelines from all teams", func() {
   213  					Expect(dbPipelineFactory.AllPipelinesCallCount()).To(Equal(1),
   214  						"Expected AllPipelines() to be called once")
   215  
   216  					body, err := ioutil.ReadAll(response.Body)
   217  					Expect(err).NotTo(HaveOccurred())
   218  
   219  					var pipelinesResponse []atc.Pipeline
   220  					err = json.Unmarshal(body, &pipelinesResponse)
   221  					Expect(err).NotTo(HaveOccurred())
   222  					Expect(len(pipelinesResponse)).To(Equal(4))
   223  				})
   224  			})
   225  
   226  			Context("when the call to get active pipelines fails", func() {
   227  				BeforeEach(func() {
   228  					dbPipelineFactory.VisiblePipelinesReturns(nil, errors.New("disaster"))
   229  				})
   230  
   231  				It("returns 500 internal server error", func() {
   232  					Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
   233  				})
   234  			})
   235  		})
   236  	})
   237  
   238  	Describe("GET /api/v1/teams/:team_name/pipelines", func() {
   239  		var response *http.Response
   240  
   241  		JustBeforeEach(func() {
   242  			req, err := http.NewRequest("GET", server.URL+"/api/v1/teams/main/pipelines", nil)
   243  			Expect(err).NotTo(HaveOccurred())
   244  
   245  			req.Header.Set("Content-Type", "application/json")
   246  
   247  			response, err = client.Do(req)
   248  			Expect(err).NotTo(HaveOccurred())
   249  		})
   250  
   251  		Context("when authenticated as requested team", func() {
   252  			BeforeEach(func() {
   253  				fakeAccess.IsAuthorizedReturns(true)
   254  				dbTeamFactory.FindTeamReturns(fakeTeam, true, nil)
   255  			})
   256  
   257  			It("returns 200 OK", func() {
   258  				Expect(response.StatusCode).To(Equal(http.StatusOK))
   259  			})
   260  
   261  			It("returns application/json", func() {
   262  				expectedHeaderEntries := map[string]string{
   263  					"Content-Type": "application/json",
   264  				}
   265  				Expect(response).Should(IncludeHeaderEntries(expectedHeaderEntries))
   266  			})
   267  
   268  			It("constructs team with provided team name", func() {
   269  				Expect(dbTeamFactory.FindTeamCallCount()).To(Equal(1))
   270  				Expect(dbTeamFactory.FindTeamArgsForCall(0)).To(Equal("main"))
   271  			})
   272  
   273  			It("returns a JSON array of pipeline objects", func() {
   274  				body, err := ioutil.ReadAll(response.Body)
   275  				Expect(err).NotTo(HaveOccurred())
   276  				Expect(body).To(MatchJSON(`[
   277  					{
   278  						"id": 3,
   279  						"name": "private-pipeline",
   280  						"paused": false,
   281  						"public": false,
   282  						"archived": true,
   283  						"team_name": "main",
   284  						"last_updated": 1,
   285  						"groups": [
   286  							{
   287  								"name": "group1",
   288  								"jobs": ["job1", "job2"],
   289  								"resources": ["resource1", "resource2"]
   290  							}
   291  						]
   292  					},
   293  					{
   294  						"id": 1,
   295  						"name": "public-pipeline",
   296  						"paused": true,
   297  						"public": true,
   298  						"archived": false,
   299  						"team_name": "main",
   300  						"last_updated": 1,
   301  						"groups": [
   302  							{
   303  								"name": "group2",
   304  								"jobs": ["job3", "job4"],
   305  								"resources": ["resource3", "resource4"]
   306  							}
   307  						],
   308  						"display": {
   309  							"background_image": "background.jpg"
   310  						}
   311  					}
   312  				]`))
   313  			})
   314  
   315  			It("returns all team's pipelines", func() {
   316  				body, err := ioutil.ReadAll(response.Body)
   317  				Expect(err).NotTo(HaveOccurred())
   318  				var pipelines []map[string]interface{}
   319  				json.Unmarshal(body, &pipelines)
   320  
   321  				Expect(pipelines).To(ConsistOf(
   322  					HaveKeyWithValue("id", BeNumerically("==", publicPipeline.ID())),
   323  					HaveKeyWithValue("id", BeNumerically("==", privatePipeline.ID())),
   324  				))
   325  			})
   326  
   327  			Context("when the call to get active pipelines fails", func() {
   328  				BeforeEach(func() {
   329  					fakeTeam.PipelinesReturns(nil, errors.New("disaster"))
   330  				})
   331  
   332  				It("returns 500 internal server error", func() {
   333  					Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
   334  				})
   335  			})
   336  		})
   337  
   338  		Context("when authenticated as another team", func() {
   339  			BeforeEach(func() {
   340  				fakeAccess.IsAuthenticatedReturns(false)
   341  				dbTeamFactory.FindTeamReturns(fakeTeam, true, nil)
   342  			})
   343  
   344  			It("returns only team's public pipelines", func() {
   345  				body, err := ioutil.ReadAll(response.Body)
   346  				Expect(err).NotTo(HaveOccurred())
   347  				var pipelines []map[string]interface{}
   348  				json.Unmarshal(body, &pipelines)
   349  
   350  				Expect(pipelines).To(ConsistOf(
   351  					HaveKeyWithValue("id", BeNumerically("==", publicPipeline.ID())),
   352  				))
   353  			})
   354  		})
   355  
   356  		Context("when not authenticated", func() {
   357  			BeforeEach(func() {
   358  				fakeAccess.IsAuthenticatedReturns(false)
   359  				dbTeamFactory.FindTeamReturns(fakeTeam, true, nil)
   360  			})
   361  
   362  			It("returns only team's public pipelines", func() {
   363  				body, err := ioutil.ReadAll(response.Body)
   364  				Expect(err).NotTo(HaveOccurred())
   365  				var pipelines []map[string]interface{}
   366  				json.Unmarshal(body, &pipelines)
   367  
   368  				Expect(pipelines).To(ConsistOf(
   369  					HaveKeyWithValue("id", BeNumerically("==", publicPipeline.ID())),
   370  				))
   371  			})
   372  		})
   373  	})
   374  
   375  	Describe("GET /api/v1/teams/:team_name/pipelines/:pipeline_name", func() {
   376  		var response *http.Response
   377  		var fakePipeline *dbfakes.FakePipeline
   378  
   379  		BeforeEach(func() {
   380  			fakePipeline = new(dbfakes.FakePipeline)
   381  			fakePipeline.IDReturns(4)
   382  			fakePipeline.NameReturns("some-specific-pipeline")
   383  			fakePipeline.PausedReturns(false)
   384  			fakePipeline.PublicReturns(true)
   385  			fakePipeline.TeamNameReturns("a-team")
   386  			fakePipeline.GroupsReturns(atc.GroupConfigs{
   387  				{
   388  					Name:      "group1",
   389  					Jobs:      []string{"job1", "job2"},
   390  					Resources: []string{"resource1", "resource2"},
   391  				},
   392  				{
   393  					Name:      "group2",
   394  					Jobs:      []string{"job3", "job4"},
   395  					Resources: []string{"resource3", "resource4"},
   396  				},
   397  			})
   398  			fakePipeline.DisplayReturns(&atc.DisplayConfig{
   399  				BackgroundImage: "background.jpg",
   400  			})
   401  			fakePipeline.LastUpdatedReturns(time.Unix(1, 0))
   402  		})
   403  
   404  		JustBeforeEach(func() {
   405  			req, err := http.NewRequest("GET", server.URL+"/api/v1/teams/a-team/pipelines/some-specific-pipeline", nil)
   406  			Expect(err).NotTo(HaveOccurred())
   407  
   408  			req.Header.Set("Content-Type", "application/json")
   409  
   410  			response, err = client.Do(req)
   411  			Expect(err).NotTo(HaveOccurred())
   412  		})
   413  
   414  		Context("when not authenticated", func() {
   415  			BeforeEach(func() {
   416  				fakeAccess.IsAuthenticatedReturns(false)
   417  			})
   418  
   419  			It("returns 401", func() {
   420  				Expect(response.StatusCode).To(Equal(http.StatusUnauthorized))
   421  			})
   422  		})
   423  
   424  		Context("when authenticated as requested team", func() {
   425  			BeforeEach(func() {
   426  				fakeAccess.IsAuthenticatedReturns(true)
   427  				dbTeamFactory.FindTeamReturns(fakeTeam, true, nil)
   428  				fakeTeam.PipelineReturns(fakePipeline, true, nil)
   429  			})
   430  
   431  			It("returns 200 ok", func() {
   432  				Expect(response.StatusCode).To(Equal(http.StatusOK))
   433  			})
   434  
   435  			It("returns application/json", func() {
   436  				expectedHeaderEntries := map[string]string{
   437  					"Content-Type": "application/json",
   438  				}
   439  				Expect(response).Should(IncludeHeaderEntries(expectedHeaderEntries))
   440  			})
   441  
   442  			It("returns a pipeline JSON", func() {
   443  				body, err := ioutil.ReadAll(response.Body)
   444  				Expect(err).NotTo(HaveOccurred())
   445  
   446  				Expect(body).To(MatchJSON(`
   447  					{
   448  						"id": 4,
   449  						"name": "some-specific-pipeline",
   450  						"paused": false,
   451  						"public": true,
   452  						"archived": false,
   453  						"team_name": "a-team",
   454  						"last_updated": 1,
   455  						"groups": [
   456  							{
   457  								"name": "group1",
   458  								"jobs": ["job1", "job2"],
   459  								"resources": ["resource1", "resource2"]
   460  							},
   461  							{
   462  								"name": "group2",
   463  								"jobs": ["job3", "job4"],
   464  								"resources": ["resource3", "resource4"]
   465  							}
   466  						],
   467  						"display": {
   468  							"background_image": "background.jpg"
   469  						}
   470  					}`))
   471  			})
   472  		})
   473  
   474  		Context("when authenticated as another team", func() {
   475  			BeforeEach(func() {
   476  				fakeAccess.IsAuthenticatedReturns(true)
   477  				fakeAccess.IsAuthorizedReturns(false)
   478  
   479  				fakeTeam.PipelineReturns(fakePipeline, true, nil)
   480  				dbTeamFactory.FindTeamReturns(fakeTeam, true, nil)
   481  			})
   482  
   483  			Context("and the pipeline is private", func() {
   484  				BeforeEach(func() {
   485  					fakePipeline.PublicReturns(false)
   486  				})
   487  
   488  				It("returns 403", func() {
   489  					Expect(response.StatusCode).To(Equal(http.StatusForbidden))
   490  				})
   491  			})
   492  
   493  			Context("and the pipeline is public", func() {
   494  				BeforeEach(func() {
   495  					fakeTeam.PipelineReturns(fakePipeline, true, nil)
   496  					fakePipeline.PublicReturns(true)
   497  				})
   498  
   499  				It("returns 200 OK", func() {
   500  					Expect(response.StatusCode).To(Equal(http.StatusOK))
   501  				})
   502  			})
   503  		})
   504  
   505  		Context("when not authenticated at all", func() {
   506  			BeforeEach(func() {
   507  				fakeAccess.IsAuthenticatedReturns(false)
   508  				dbTeam.PipelineReturns(fakePipeline, true, nil)
   509  			})
   510  
   511  			Context("and the pipeline is private", func() {
   512  				BeforeEach(func() {
   513  					fakePipeline.PublicReturns(false)
   514  				})
   515  
   516  				It("returns 401", func() {
   517  					Expect(response.StatusCode).To(Equal(http.StatusUnauthorized))
   518  				})
   519  			})
   520  
   521  			Context("and the pipeline is public", func() {
   522  				BeforeEach(func() {
   523  					fakePipeline.PublicReturns(true)
   524  				})
   525  
   526  				It("returns 200 OK", func() {
   527  					Expect(response.StatusCode).To(Equal(http.StatusOK))
   528  				})
   529  			})
   530  		})
   531  	})
   532  
   533  	Describe("GET /api/v1/teams/:team_name/pipelines/:pipeline_name/badge", func() {
   534  		var response *http.Response
   535  		var jobWithNoBuilds, jobWithSucceededBuild, jobWithAbortedBuild, jobWithErroredBuild, jobWithFailedBuild *dbfakes.FakeJob
   536  
   537  		BeforeEach(func() {
   538  			dbTeamFactory.FindTeamReturns(fakeTeam, true, nil)
   539  			dbPipeline.NameReturns("some-pipeline")
   540  			fakeTeam.PipelineReturns(dbPipeline, true, nil)
   541  
   542  			jobWithNoBuilds = new(dbfakes.FakeJob)
   543  			jobWithSucceededBuild = new(dbfakes.FakeJob)
   544  			jobWithAbortedBuild = new(dbfakes.FakeJob)
   545  			jobWithErroredBuild = new(dbfakes.FakeJob)
   546  			jobWithFailedBuild = new(dbfakes.FakeJob)
   547  
   548  			succeededBuild := new(dbfakes.FakeBuild)
   549  			succeededBuild.StatusReturns(db.BuildStatusSucceeded)
   550  			jobWithSucceededBuild.FinishedAndNextBuildReturns(succeededBuild, nil, nil)
   551  
   552  			abortedBuild := new(dbfakes.FakeBuild)
   553  			abortedBuild.StatusReturns(db.BuildStatusAborted)
   554  			jobWithAbortedBuild.FinishedAndNextBuildReturns(abortedBuild, nil, nil)
   555  
   556  			erroredBuild := new(dbfakes.FakeBuild)
   557  			erroredBuild.StatusReturns(db.BuildStatusErrored)
   558  			jobWithErroredBuild.FinishedAndNextBuildReturns(erroredBuild, nil, nil)
   559  
   560  			failedBuild := new(dbfakes.FakeBuild)
   561  			failedBuild.StatusReturns(db.BuildStatusFailed)
   562  			jobWithFailedBuild.FinishedAndNextBuildReturns(failedBuild, nil, nil)
   563  		})
   564  
   565  		JustBeforeEach(func() {
   566  			var err error
   567  
   568  			response, err = client.Get(server.URL + "/api/v1/teams/some-team/pipelines/some-pipeline/badge")
   569  			Expect(err).NotTo(HaveOccurred())
   570  		})
   571  
   572  		Context("when not authorized", func() {
   573  			BeforeEach(func() {
   574  				fakeAccess.IsAuthorizedReturns(false)
   575  			})
   576  
   577  			Context("and the pipeline is private", func() {
   578  				BeforeEach(func() {
   579  					dbPipeline.PublicReturns(false)
   580  				})
   581  
   582  				Context("when user is authenticated", func() {
   583  					BeforeEach(func() {
   584  						fakeAccess.IsAuthenticatedReturns(true)
   585  					})
   586  					It("returns 403", func() {
   587  						Expect(response.StatusCode).To(Equal(http.StatusForbidden))
   588  					})
   589  				})
   590  
   591  				Context("when user is not authenticated", func() {
   592  					BeforeEach(func() {
   593  						fakeAccess.IsAuthenticatedReturns(false)
   594  					})
   595  
   596  					It("returns 401", func() {
   597  						Expect(response.StatusCode).To(Equal(http.StatusUnauthorized))
   598  					})
   599  				})
   600  			})
   601  
   602  			Context("and the pipeline is public", func() {
   603  				BeforeEach(func() {
   604  					dbPipeline.PublicReturns(true)
   605  				})
   606  
   607  				It("returns 200 OK", func() {
   608  					Expect(response.StatusCode).To(Equal(http.StatusOK))
   609  				})
   610  			})
   611  		})
   612  
   613  		Context("when authorized", func() {
   614  			BeforeEach(func() {
   615  				fakeAccess.IsAuthenticatedReturns(true)
   616  				fakeAccess.IsAuthorizedReturns(true)
   617  			})
   618  
   619  			It("returns 200 OK", func() {
   620  				Expect(response.StatusCode).To(Equal(http.StatusOK))
   621  			})
   622  
   623  			It("returns Content-Type as image/svg+xml and disables caching", func() {
   624  				expectedHeaderEntries := map[string]string{
   625  					"Content-Type":  "image/svg+xml",
   626  					"Cache-Control": "no-cache, no-store, must-revalidate",
   627  					"Expires":       "0",
   628  				}
   629  				Expect(response).Should(IncludeHeaderEntries(expectedHeaderEntries))
   630  			})
   631  
   632  			Context("when the pipeline has no finished builds", func() {
   633  				BeforeEach(func() {
   634  					dbPipeline.JobsReturns([]db.Job{jobWithNoBuilds}, nil)
   635  				})
   636  
   637  				It("returns an unknown badge", func() {
   638  					body, err := ioutil.ReadAll(response.Body)
   639  					Expect(err).NotTo(HaveOccurred())
   640  
   641  					Expect(string(body)).To(Equal(`<?xml version="1.0" encoding="UTF-8"?>
   642  <svg xmlns="http://www.w3.org/2000/svg" width="98" height="20">
   643     <linearGradient id="b" x2="0" y2="100%">
   644        <stop offset="0" stop-color="#bbb" stop-opacity=".1" />
   645        <stop offset="1" stop-opacity=".1" />
   646     </linearGradient>
   647     <mask id="a">
   648        <rect width="98" height="20" rx="3" fill="#fff" />
   649     </mask>
   650     <g mask="url(#a)">
   651        <path fill="#555" d="M0 0h37v20H0z" />
   652        <path fill="#9f9f9f" d="M37 0h61v20H37z" />
   653        <path fill="url(#b)" d="M0 0h98v20H0z" />
   654     </g>
   655     <g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
   656        <text x="18.5" y="15" fill="#010101" fill-opacity=".3">build</text>
   657        <text x="18.5" y="14">build</text>
   658        <text x="66.5" y="15" fill="#010101" fill-opacity=".3">unknown</text>
   659        <text x="66.5" y="14">unknown</text>
   660     </g>
   661  </svg>`))
   662  				})
   663  			})
   664  
   665  			Context("when the pipeline has a successful build", func() {
   666  				BeforeEach(func() {
   667  					dbPipeline.JobsReturns([]db.Job{jobWithNoBuilds, jobWithSucceededBuild}, nil)
   668  				})
   669  
   670  				It("returns a successful badge", func() {
   671  					body, err := ioutil.ReadAll(response.Body)
   672  					Expect(err).NotTo(HaveOccurred())
   673  
   674  					Expect(string(body)).To(Equal(`<?xml version="1.0" encoding="UTF-8"?>
   675  <svg xmlns="http://www.w3.org/2000/svg" width="88" height="20">
   676     <linearGradient id="b" x2="0" y2="100%">
   677        <stop offset="0" stop-color="#bbb" stop-opacity=".1" />
   678        <stop offset="1" stop-opacity=".1" />
   679     </linearGradient>
   680     <mask id="a">
   681        <rect width="88" height="20" rx="3" fill="#fff" />
   682     </mask>
   683     <g mask="url(#a)">
   684        <path fill="#555" d="M0 0h37v20H0z" />
   685        <path fill="#44cc11" d="M37 0h51v20H37z" />
   686        <path fill="url(#b)" d="M0 0h88v20H0z" />
   687     </g>
   688     <g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
   689        <text x="18.5" y="15" fill="#010101" fill-opacity=".3">build</text>
   690        <text x="18.5" y="14">build</text>
   691        <text x="61.5" y="15" fill="#010101" fill-opacity=".3">passing</text>
   692        <text x="61.5" y="14">passing</text>
   693     </g>
   694  </svg>`))
   695  				})
   696  			})
   697  
   698  			Context("when the pipeline has an aborted build", func() {
   699  				BeforeEach(func() {
   700  					dbPipeline.JobsReturns([]db.Job{jobWithNoBuilds, jobWithSucceededBuild, jobWithAbortedBuild}, nil)
   701  				})
   702  
   703  				It("returns an aborted badge", func() {
   704  					body, err := ioutil.ReadAll(response.Body)
   705  					Expect(err).NotTo(HaveOccurred())
   706  
   707  					Expect(string(body)).To(Equal(`<?xml version="1.0" encoding="UTF-8"?>
   708  <svg xmlns="http://www.w3.org/2000/svg" width="90" height="20">
   709     <linearGradient id="b" x2="0" y2="100%">
   710        <stop offset="0" stop-color="#bbb" stop-opacity=".1" />
   711        <stop offset="1" stop-opacity=".1" />
   712     </linearGradient>
   713     <mask id="a">
   714        <rect width="90" height="20" rx="3" fill="#fff" />
   715     </mask>
   716     <g mask="url(#a)">
   717        <path fill="#555" d="M0 0h37v20H0z" />
   718        <path fill="#8f4b2d" d="M37 0h53v20H37z" />
   719        <path fill="url(#b)" d="M0 0h90v20H0z" />
   720     </g>
   721     <g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
   722        <text x="18.5" y="15" fill="#010101" fill-opacity=".3">build</text>
   723        <text x="18.5" y="14">build</text>
   724        <text x="62.5" y="15" fill="#010101" fill-opacity=".3">aborted</text>
   725        <text x="62.5" y="14">aborted</text>
   726     </g>
   727  </svg>`))
   728  				})
   729  			})
   730  
   731  			Context("when the pipeline has an errored build", func() {
   732  				BeforeEach(func() {
   733  					dbPipeline.JobsReturns([]db.Job{jobWithNoBuilds, jobWithSucceededBuild, jobWithAbortedBuild, jobWithErroredBuild}, nil)
   734  				})
   735  
   736  				It("returns an errored badge", func() {
   737  					body, err := ioutil.ReadAll(response.Body)
   738  					Expect(err).NotTo(HaveOccurred())
   739  
   740  					Expect(string(body)).To(Equal(`<?xml version="1.0" encoding="UTF-8"?>
   741  <svg xmlns="http://www.w3.org/2000/svg" width="88" height="20">
   742     <linearGradient id="b" x2="0" y2="100%">
   743        <stop offset="0" stop-color="#bbb" stop-opacity=".1" />
   744        <stop offset="1" stop-opacity=".1" />
   745     </linearGradient>
   746     <mask id="a">
   747        <rect width="88" height="20" rx="3" fill="#fff" />
   748     </mask>
   749     <g mask="url(#a)">
   750        <path fill="#555" d="M0 0h37v20H0z" />
   751        <path fill="#fe7d37" d="M37 0h51v20H37z" />
   752        <path fill="url(#b)" d="M0 0h88v20H0z" />
   753     </g>
   754     <g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
   755        <text x="18.5" y="15" fill="#010101" fill-opacity=".3">build</text>
   756        <text x="18.5" y="14">build</text>
   757        <text x="61.5" y="15" fill="#010101" fill-opacity=".3">errored</text>
   758        <text x="61.5" y="14">errored</text>
   759     </g>
   760  </svg>`))
   761  				})
   762  			})
   763  
   764  			Context("when the pipeline has a failed build", func() {
   765  				BeforeEach(func() {
   766  					dbPipeline.JobsReturns([]db.Job{jobWithNoBuilds, jobWithSucceededBuild, jobWithAbortedBuild, jobWithErroredBuild, jobWithFailedBuild}, nil)
   767  				})
   768  
   769  				It("returns a failed badge", func() {
   770  					body, err := ioutil.ReadAll(response.Body)
   771  					Expect(err).NotTo(HaveOccurred())
   772  
   773  					Expect(string(body)).To(Equal(`<?xml version="1.0" encoding="UTF-8"?>
   774  <svg xmlns="http://www.w3.org/2000/svg" width="80" height="20">
   775     <linearGradient id="b" x2="0" y2="100%">
   776        <stop offset="0" stop-color="#bbb" stop-opacity=".1" />
   777        <stop offset="1" stop-opacity=".1" />
   778     </linearGradient>
   779     <mask id="a">
   780        <rect width="80" height="20" rx="3" fill="#fff" />
   781     </mask>
   782     <g mask="url(#a)">
   783        <path fill="#555" d="M0 0h37v20H0z" />
   784        <path fill="#e05d44" d="M37 0h43v20H37z" />
   785        <path fill="url(#b)" d="M0 0h80v20H0z" />
   786     </g>
   787     <g fill="#fff" text-anchor="middle" font-family="DejaVu Sans,Verdana,Geneva,sans-serif" font-size="11">
   788        <text x="18.5" y="15" fill="#010101" fill-opacity=".3">build</text>
   789        <text x="18.5" y="14">build</text>
   790        <text x="57.5" y="15" fill="#010101" fill-opacity=".3">failing</text>
   791        <text x="57.5" y="14">failing</text>
   792     </g>
   793  </svg>`))
   794  				})
   795  			})
   796  		})
   797  	})
   798  
   799  	Describe("DELETE /api/v1/teams/:team_name/pipelines/:pipeline_name", func() {
   800  		var response *http.Response
   801  
   802  		JustBeforeEach(func() {
   803  			pipelineName := "a-pipeline-name"
   804  			req, err := http.NewRequest("DELETE", server.URL+"/api/v1/teams/a-team/pipelines/"+pipelineName, nil)
   805  			Expect(err).NotTo(HaveOccurred())
   806  
   807  			req.Header.Set("Content-Type", "application/json")
   808  
   809  			response, err = client.Do(req)
   810  			Expect(err).NotTo(HaveOccurred())
   811  		})
   812  
   813  		Context("when authenticated", func() {
   814  			BeforeEach(func() {
   815  				fakeAccess.IsAuthenticatedReturns(true)
   816  			})
   817  
   818  			Context("when requester belongs to the team", func() {
   819  				BeforeEach(func() {
   820  					fakeAccess.IsAuthorizedReturns(true)
   821  
   822  					dbTeamFactory.FindTeamReturns(fakeTeam, true, nil)
   823  					dbPipeline.NameReturns("a-pipeline-name")
   824  					fakeTeam.PipelineReturns(dbPipeline, true, nil)
   825  				})
   826  
   827  				It("returns 204 No Content", func() {
   828  					Expect(response.StatusCode).To(Equal(http.StatusNoContent))
   829  				})
   830  
   831  				It("constructs team with provided team name", func() {
   832  					Expect(dbTeamFactory.FindTeamCallCount()).To(Equal(1))
   833  					Expect(dbTeamFactory.FindTeamArgsForCall(0)).To(Equal("a-team"))
   834  				})
   835  
   836  				It("injects the proper pipelineDB", func() {
   837  					pipelineRef := fakeTeam.PipelineArgsForCall(0)
   838  					Expect(pipelineRef).To(Equal(atc.PipelineRef{Name: "a-pipeline-name"}))
   839  				})
   840  
   841  				It("deletes the named pipeline from the database", func() {
   842  					Expect(dbPipeline.DestroyCallCount()).To(Equal(1))
   843  				})
   844  
   845  				Context("when an error occurs destroying the pipeline", func() {
   846  					BeforeEach(func() {
   847  						fakeTeam.PipelineReturns(dbPipeline, true, nil)
   848  						err := errors.New("disaster!")
   849  						dbPipeline.DestroyReturns(err)
   850  					})
   851  
   852  					It("returns a 500 Internal Server Error", func() {
   853  						Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
   854  					})
   855  				})
   856  			})
   857  
   858  			Context("when requester does not belong to the team", func() {
   859  				BeforeEach(func() {
   860  					fakeAccess.IsAuthorizedReturns(false)
   861  				})
   862  
   863  				It("returns 403", func() {
   864  					Expect(response.StatusCode).To(Equal(http.StatusForbidden))
   865  				})
   866  			})
   867  		})
   868  
   869  		Context("when the user is not logged in", func() {
   870  			BeforeEach(func() {
   871  				fakeAccess.IsAuthenticatedReturns(false)
   872  			})
   873  
   874  			It("returns 401 Unauthorized", func() {
   875  				Expect(response.StatusCode).To(Equal(http.StatusUnauthorized))
   876  			})
   877  		})
   878  	})
   879  
   880  	Describe("PUT /api/v1/teams/:team_name/pipelines/:pipeline_name/pause", func() {
   881  		var response *http.Response
   882  
   883  		JustBeforeEach(func() {
   884  			var err error
   885  
   886  			request, err := http.NewRequest("PUT", server.URL+"/api/v1/teams/a-team/pipelines/a-pipeline/pause", nil)
   887  			Expect(err).NotTo(HaveOccurred())
   888  
   889  			response, err = client.Do(request)
   890  			Expect(err).NotTo(HaveOccurred())
   891  		})
   892  
   893  		Context("when authenticated", func() {
   894  			BeforeEach(func() {
   895  				fakeAccess.IsAuthenticatedReturns(true)
   896  			})
   897  
   898  			Context("when requester belongs to the team", func() {
   899  				BeforeEach(func() {
   900  					fakeAccess.IsAuthorizedReturns(true)
   901  					dbTeamFactory.FindTeamReturns(fakeTeam, true, nil)
   902  				})
   903  
   904  				It("constructs team with provided team name", func() {
   905  					Expect(dbTeamFactory.FindTeamCallCount()).To(Equal(1))
   906  					Expect(dbTeamFactory.FindTeamArgsForCall(0)).To(Equal("a-team"))
   907  				})
   908  
   909  				It("injects the proper pipelineDB", func() {
   910  					pipelineRef := fakeTeam.PipelineArgsForCall(0)
   911  					Expect(pipelineRef).To(Equal(atc.PipelineRef{Name: "a-pipeline"}))
   912  				})
   913  
   914  				Context("when pausing the pipeline succeeds", func() {
   915  					BeforeEach(func() {
   916  						fakeTeam.PipelineReturns(dbPipeline, true, nil)
   917  						dbPipeline.PauseReturns(nil)
   918  					})
   919  
   920  					It("returns 200", func() {
   921  						Expect(response.StatusCode).To(Equal(http.StatusOK))
   922  					})
   923  				})
   924  
   925  				Context("when pausing the pipeline fails", func() {
   926  					BeforeEach(func() {
   927  						fakeTeam.PipelineReturns(dbPipeline, true, nil)
   928  						dbPipeline.PauseReturns(errors.New("welp"))
   929  					})
   930  
   931  					It("returns 500", func() {
   932  						Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
   933  					})
   934  				})
   935  			})
   936  
   937  			Context("when requester does not belong to the team", func() {
   938  				BeforeEach(func() {
   939  					fakeAccess.IsAuthorizedReturns(false)
   940  				})
   941  
   942  				It("returns 403", func() {
   943  					Expect(response.StatusCode).To(Equal(http.StatusForbidden))
   944  				})
   945  			})
   946  		})
   947  
   948  		Context("when not authenticated", func() {
   949  			BeforeEach(func() {
   950  				fakeAccess.IsAuthenticatedReturns(false)
   951  			})
   952  
   953  			It("returns 401", func() {
   954  				Expect(response.StatusCode).To(Equal(http.StatusUnauthorized))
   955  			})
   956  		})
   957  	})
   958  
   959  	Describe("PUT /api/v1/teams/:team_name/pipelines/:pipeline_name/archive", func() {
   960  		var response *http.Response
   961  
   962  		BeforeEach(func() {
   963  			fakeAccess.IsAuthenticatedReturns(true)
   964  			fakeAccess.IsAuthorizedReturns(true)
   965  			dbTeamFactory.FindTeamReturns(fakeTeam, true, nil)
   966  			fakeTeam.PipelineReturns(dbPipeline, true, nil)
   967  		})
   968  
   969  		JustBeforeEach(func() {
   970  			request, _ := http.NewRequest("PUT", server.URL+"/api/v1/teams/a-team/pipelines/a-pipeline/archive", nil)
   971  			var err error
   972  			response, err = client.Do(request)
   973  			Expect(err).NotTo(HaveOccurred())
   974  		})
   975  
   976  		It("returns 200", func() {
   977  			Expect(response.StatusCode).To(Equal(http.StatusOK))
   978  		})
   979  
   980  		It("archives the pipeline", func() {
   981  			Expect(dbPipeline.ArchiveCallCount()).To(Equal(1), "Archive() called the wrong number of times")
   982  		})
   983  
   984  		Context("when archiving the pipeline fails due to the DB", func() {
   985  			BeforeEach(func() {
   986  				dbPipeline.ArchiveReturns(errors.New("pq: a db error"))
   987  			})
   988  
   989  			It("gives a server error", func() {
   990  				Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
   991  			})
   992  		})
   993  
   994  		Context("when not authenticated", func() {
   995  			BeforeEach(func() {
   996  				fakeAccess.IsAuthenticatedReturns(false)
   997  			})
   998  
   999  			It("returns 401", func() {
  1000  				Expect(response.StatusCode).To(Equal(http.StatusUnauthorized))
  1001  			})
  1002  		})
  1003  	})
  1004  
  1005  	Describe("PUT /api/v1/teams/:team_name/pipelines/:pipeline_name/unpause", func() {
  1006  		var response *http.Response
  1007  
  1008  		JustBeforeEach(func() {
  1009  			var err error
  1010  
  1011  			request, err := http.NewRequest("PUT", server.URL+"/api/v1/teams/a-team/pipelines/a-pipeline/unpause", nil)
  1012  			Expect(err).NotTo(HaveOccurred())
  1013  
  1014  			response, err = client.Do(request)
  1015  			Expect(err).NotTo(HaveOccurred())
  1016  		})
  1017  
  1018  		Context("when authenticated", func() {
  1019  			BeforeEach(func() {
  1020  				fakeAccess.IsAuthenticatedReturns(true)
  1021  			})
  1022  			Context("when requester belongs to the team", func() {
  1023  				BeforeEach(func() {
  1024  					fakeAccess.IsAuthorizedReturns(true)
  1025  
  1026  					dbTeamFactory.FindTeamReturns(fakeTeam, true, nil)
  1027  					fakeTeam.PipelineReturns(dbPipeline, true, nil)
  1028  				})
  1029  
  1030  				It("constructs team with provided team name", func() {
  1031  					Expect(dbTeamFactory.FindTeamCallCount()).To(Equal(1))
  1032  					Expect(dbTeamFactory.FindTeamArgsForCall(0)).To(Equal("a-team"))
  1033  				})
  1034  
  1035  				It("injects the proper pipelineDB", func() {
  1036  					pipelineRef := fakeTeam.PipelineArgsForCall(0)
  1037  					Expect(pipelineRef).To(Equal(atc.PipelineRef{Name: "a-pipeline"}))
  1038  				})
  1039  
  1040  				Context("when unpausing the pipeline succeeds", func() {
  1041  					BeforeEach(func() {
  1042  						fakeTeam.PipelineReturns(dbPipeline, true, nil)
  1043  						dbPipeline.UnpauseReturns(nil)
  1044  					})
  1045  
  1046  					It("returns 200", func() {
  1047  						Expect(response.StatusCode).To(Equal(http.StatusOK))
  1048  					})
  1049  
  1050  					It("notifies the resource scanner", func() {
  1051  						Expect(dbTeamFactory.NotifyResourceScannerCallCount()).To(Equal(1))
  1052  					})
  1053  				})
  1054  
  1055  				Context("when unpausing the pipeline fails for an unknown reason", func() {
  1056  					BeforeEach(func() {
  1057  						fakeTeam.PipelineReturns(dbPipeline, true, nil)
  1058  						dbPipeline.UnpauseReturns(errors.New("welp"))
  1059  					})
  1060  
  1061  					It("returns 500", func() {
  1062  						Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
  1063  					})
  1064  				})
  1065  			})
  1066  
  1067  			Context("when requester does not belong to the team", func() {
  1068  				BeforeEach(func() {
  1069  					fakeAccess.IsAuthorizedReturns(false)
  1070  				})
  1071  
  1072  				It("returns 403", func() {
  1073  					Expect(response.StatusCode).To(Equal(http.StatusForbidden))
  1074  				})
  1075  			})
  1076  		})
  1077  
  1078  		Context("when not authenticated", func() {
  1079  			BeforeEach(func() {
  1080  				fakeAccess.IsAuthenticatedReturns(false)
  1081  			})
  1082  
  1083  			It("returns 401 Unauthorized", func() {
  1084  				Expect(response.StatusCode).To(Equal(http.StatusUnauthorized))
  1085  			})
  1086  		})
  1087  	})
  1088  
  1089  	Describe("PUT /api/v1/teams/:team_name/pipelines/:pipeline_name/expose", func() {
  1090  		var response *http.Response
  1091  
  1092  		JustBeforeEach(func() {
  1093  			var err error
  1094  
  1095  			request, err := http.NewRequest("PUT", server.URL+"/api/v1/teams/a-team/pipelines/a-pipeline/expose", nil)
  1096  			Expect(err).NotTo(HaveOccurred())
  1097  
  1098  			response, err = client.Do(request)
  1099  			Expect(err).NotTo(HaveOccurred())
  1100  		})
  1101  
  1102  		Context("when authenticated", func() {
  1103  			BeforeEach(func() {
  1104  				fakeAccess.IsAuthenticatedReturns(true)
  1105  			})
  1106  
  1107  			Context("when requester belongs to the team", func() {
  1108  				BeforeEach(func() {
  1109  					fakeAccess.IsAuthorizedReturns(true)
  1110  					dbTeamFactory.FindTeamReturns(fakeTeam, true, nil)
  1111  				})
  1112  
  1113  				It("constructs team with provided team name", func() {
  1114  					Expect(dbTeamFactory.FindTeamCallCount()).To(Equal(1))
  1115  					Expect(dbTeamFactory.FindTeamArgsForCall(0)).To(Equal("a-team"))
  1116  				})
  1117  
  1118  				It("injects the proper pipelineDB", func() {
  1119  					Expect(fakeTeam.PipelineCallCount()).To(Equal(1))
  1120  					pipelineRef := fakeTeam.PipelineArgsForCall(0)
  1121  					Expect(pipelineRef).To(Equal(atc.PipelineRef{Name: "a-pipeline"}))
  1122  				})
  1123  
  1124  				Context("when exposing the pipeline succeeds", func() {
  1125  					BeforeEach(func() {
  1126  						fakeTeam.PipelineReturns(dbPipeline, true, nil)
  1127  						dbPipeline.ExposeReturns(nil)
  1128  					})
  1129  
  1130  					It("returns 200", func() {
  1131  						Expect(response.StatusCode).To(Equal(http.StatusOK))
  1132  					})
  1133  				})
  1134  
  1135  				Context("when exposing the pipeline fails", func() {
  1136  					BeforeEach(func() {
  1137  						fakeTeam.PipelineReturns(dbPipeline, true, nil)
  1138  						dbPipeline.ExposeReturns(errors.New("welp"))
  1139  					})
  1140  
  1141  					It("returns 500", func() {
  1142  						Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
  1143  					})
  1144  				})
  1145  			})
  1146  
  1147  			Context("when requester does not belong to the team", func() {
  1148  				BeforeEach(func() {
  1149  					fakeAccess.IsAuthorizedReturns(false)
  1150  				})
  1151  
  1152  				It("returns 403", func() {
  1153  					Expect(response.StatusCode).To(Equal(http.StatusForbidden))
  1154  				})
  1155  			})
  1156  		})
  1157  
  1158  		Context("when not authenticated", func() {
  1159  			BeforeEach(func() {
  1160  				fakeAccess.IsAuthenticatedReturns(false)
  1161  			})
  1162  
  1163  			It("returns 401 Unauthorized", func() {
  1164  				Expect(response.StatusCode).To(Equal(http.StatusUnauthorized))
  1165  			})
  1166  		})
  1167  	})
  1168  
  1169  	Describe("PUT /api/v1/teams/:team_name/pipelines/:pipeline_name/hide", func() {
  1170  		var response *http.Response
  1171  
  1172  		JustBeforeEach(func() {
  1173  			var err error
  1174  
  1175  			request, err := http.NewRequest("PUT", server.URL+"/api/v1/teams/a-team/pipelines/a-pipeline/hide", nil)
  1176  			Expect(err).NotTo(HaveOccurred())
  1177  
  1178  			response, err = client.Do(request)
  1179  			Expect(err).NotTo(HaveOccurred())
  1180  		})
  1181  
  1182  		Context("when authenticated", func() {
  1183  			BeforeEach(func() {
  1184  				fakeAccess.IsAuthenticatedReturns(true)
  1185  			})
  1186  			Context("when requester belongs to the team", func() {
  1187  				BeforeEach(func() {
  1188  					fakeAccess.IsAuthorizedReturns(true)
  1189  					dbTeamFactory.FindTeamReturns(fakeTeam, true, nil)
  1190  				})
  1191  
  1192  				It("constructs team with provided team name", func() {
  1193  					Expect(dbTeamFactory.FindTeamCallCount()).To(Equal(1))
  1194  					Expect(dbTeamFactory.FindTeamArgsForCall(0)).To(Equal("a-team"))
  1195  				})
  1196  
  1197  				It("injects the proper pipeline", func() {
  1198  					pipelineRef := fakeTeam.PipelineArgsForCall(0)
  1199  					Expect(pipelineRef).To(Equal(atc.PipelineRef{Name: "a-pipeline"}))
  1200  				})
  1201  
  1202  				Context("when hiding the pipeline succeeds", func() {
  1203  					BeforeEach(func() {
  1204  						fakeTeam.PipelineReturns(dbPipeline, true, nil)
  1205  						dbPipeline.HideReturns(nil)
  1206  					})
  1207  
  1208  					It("returns 200", func() {
  1209  						Expect(response.StatusCode).To(Equal(http.StatusOK))
  1210  					})
  1211  				})
  1212  
  1213  				Context("when hiding the pipeline fails", func() {
  1214  					BeforeEach(func() {
  1215  						fakeTeam.PipelineReturns(dbPipeline, true, nil)
  1216  						dbPipeline.HideReturns(errors.New("welp"))
  1217  					})
  1218  
  1219  					It("returns 500", func() {
  1220  						Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
  1221  					})
  1222  				})
  1223  			})
  1224  
  1225  			Context("when requester does not belong to the team", func() {
  1226  				BeforeEach(func() {
  1227  					fakeAccess.IsAuthorizedReturns(false)
  1228  				})
  1229  
  1230  				It("returns 403 Forbidden", func() {
  1231  					Expect(response.StatusCode).To(Equal(http.StatusForbidden))
  1232  				})
  1233  			})
  1234  		})
  1235  
  1236  		Context("when not authorized", func() {
  1237  			BeforeEach(func() {
  1238  				fakeAccess.IsAuthenticatedReturns(false)
  1239  			})
  1240  
  1241  			It("returns 401 Unauthorized", func() {
  1242  				Expect(response.StatusCode).To(Equal(http.StatusUnauthorized))
  1243  			})
  1244  		})
  1245  	})
  1246  
  1247  	Describe("PUT /api/v1/teams/:team_name/pipelines/ordering", func() {
  1248  		var response *http.Response
  1249  		var requestBody atc.OrderPipelinesRequest
  1250  
  1251  		BeforeEach(func() {
  1252  			requestBody = atc.OrderPipelinesRequest{
  1253  				{Name: "a-pipeline"},
  1254  				{Name: "another-pipeline"},
  1255  				{Name: "yet-another-pipeline"},
  1256  				{Name: "one-final-pipeline"},
  1257  				{Name: "just-kidding"},
  1258  			}
  1259  		})
  1260  
  1261  		JustBeforeEach(func() {
  1262  			requestPayload, err := json.Marshal(requestBody)
  1263  			Expect(err).NotTo(HaveOccurred())
  1264  
  1265  			request, err := http.NewRequest("PUT", server.URL+"/api/v1/teams/a-team/pipelines/ordering", bytes.NewBuffer(requestPayload))
  1266  			Expect(err).NotTo(HaveOccurred())
  1267  
  1268  			response, err = client.Do(request)
  1269  			Expect(err).NotTo(HaveOccurred())
  1270  		})
  1271  
  1272  		Context("when authenticated", func() {
  1273  			BeforeEach(func() {
  1274  				fakeAccess.IsAuthenticatedReturns(true)
  1275  			})
  1276  
  1277  			Context("when requester belongs to the team", func() {
  1278  				BeforeEach(func() {
  1279  					fakeAccess.IsAuthorizedReturns(true)
  1280  					dbTeamFactory.FindTeamReturns(fakeTeam, true, nil)
  1281  				})
  1282  
  1283  				It("constructs team with provided team name", func() {
  1284  					Expect(dbTeamFactory.FindTeamCallCount()).To(Equal(1))
  1285  					Expect(dbTeamFactory.FindTeamArgsForCall(0)).To(Equal("a-team"))
  1286  				})
  1287  
  1288  				Context("when ordering the pipelines succeeds", func() {
  1289  					BeforeEach(func() {
  1290  						fakeTeam.OrderPipelinesReturns(nil)
  1291  					})
  1292  
  1293  					It("orders the pipelines", func() {
  1294  						Expect(fakeTeam.OrderPipelinesCallCount()).To(Equal(1))
  1295  						pipelineRefs := fakeTeam.OrderPipelinesArgsForCall(0)
  1296  						Expect(pipelineRefs).To(Equal([]atc.PipelineRef{
  1297  							{Name: "a-pipeline"},
  1298  							{Name: "another-pipeline"},
  1299  							{Name: "yet-another-pipeline"},
  1300  							{Name: "one-final-pipeline"},
  1301  							{Name: "just-kidding"},
  1302  						}))
  1303  					})
  1304  
  1305  					It("returns 200", func() {
  1306  						Expect(response.StatusCode).To(Equal(http.StatusOK))
  1307  					})
  1308  				})
  1309  
  1310  				Context("when ordering the pipelines fails", func() {
  1311  					BeforeEach(func() {
  1312  						fakeTeam.OrderPipelinesReturns(errors.New("welp"))
  1313  					})
  1314  
  1315  					It("returns 500", func() {
  1316  						Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
  1317  					})
  1318  				})
  1319  
  1320  				Context("when ordering pipeline instances", func() {
  1321  					BeforeEach(func() {
  1322  						requestBody = atc.OrderPipelinesRequest{
  1323  							{Name: "some-pipeline", InstanceVars: atc.InstanceVars{"branch": "master"}},
  1324  							{Name: "some-pipeline", InstanceVars: atc.InstanceVars{"branch": "feature/bar"}},
  1325  							{Name: "some-pipeline", InstanceVars: atc.InstanceVars{"branch": "feature/foo"}},
  1326  							{Name: "some-pipeline", InstanceVars: atc.InstanceVars{"tag": "0.0.0"}},
  1327  						}
  1328  					})
  1329  
  1330  					It("orders the pipelines", func() {
  1331  						Expect(fakeTeam.OrderPipelinesCallCount()).To(Equal(1))
  1332  						pipelineRefs := fakeTeam.OrderPipelinesArgsForCall(0)
  1333  						Expect(pipelineRefs).To(Equal([]atc.PipelineRef{
  1334  							{Name: "some-pipeline", InstanceVars: atc.InstanceVars{"branch": "master"}},
  1335  							{Name: "some-pipeline", InstanceVars: atc.InstanceVars{"branch": "feature/bar"}},
  1336  							{Name: "some-pipeline", InstanceVars: atc.InstanceVars{"branch": "feature/foo"}},
  1337  							{Name: "some-pipeline", InstanceVars: atc.InstanceVars{"tag": "0.0.0"}},
  1338  						}))
  1339  					})
  1340  
  1341  					It("returns 200", func() {
  1342  						Expect(response.StatusCode).To(Equal(http.StatusOK))
  1343  					})
  1344  				})
  1345  
  1346  			})
  1347  
  1348  			Context("when requester does not belong to the team", func() {
  1349  				BeforeEach(func() {
  1350  					fakeAccess.IsAuthorizedReturns(false)
  1351  				})
  1352  
  1353  				It("returns 403", func() {
  1354  					Expect(response.StatusCode).To(Equal(http.StatusForbidden))
  1355  				})
  1356  			})
  1357  		})
  1358  
  1359  		Context("when not authenticated", func() {
  1360  			BeforeEach(func() {
  1361  				fakeAccess.IsAuthenticatedReturns(false)
  1362  			})
  1363  
  1364  			It("returns 401 Unauthorized", func() {
  1365  				Expect(response.StatusCode).To(Equal(http.StatusUnauthorized))
  1366  			})
  1367  		})
  1368  	})
  1369  
  1370  	Describe("GET /api/v1/teams/:team_name/pipelines/:pipeline_name/versions-db", func() {
  1371  		var response *http.Response
  1372  
  1373  		JustBeforeEach(func() {
  1374  			var err error
  1375  
  1376  			request, err := http.NewRequest("GET", server.URL+"/api/v1/teams/a-team/pipelines/a-pipeline/versions-db", nil)
  1377  			Expect(err).NotTo(HaveOccurred())
  1378  
  1379  			response, err = client.Do(request)
  1380  			Expect(err).NotTo(HaveOccurred())
  1381  		})
  1382  
  1383  		Context("when authenticated", func() {
  1384  			BeforeEach(func() {
  1385  				fakeAccess.IsAuthenticatedReturns(true)
  1386  				fakeAccess.IsAuthorizedReturns(true)
  1387  				dbTeamFactory.FindTeamReturns(fakeTeam, true, nil)
  1388  				fakeTeam.PipelineReturns(dbPipeline, true, nil)
  1389  			})
  1390  
  1391  			Context("when getting the debug versions db works", func() {
  1392  				BeforeEach(func() {
  1393  					scopeID := 789
  1394  
  1395  					dbPipeline.LoadDebugVersionsDBReturns(
  1396  						&atc.DebugVersionsDB{
  1397  							ResourceVersions: []atc.DebugResourceVersion{
  1398  								{
  1399  									VersionID:  73,
  1400  									ResourceID: 127,
  1401  									CheckOrder: 123,
  1402  									ScopeID:    111,
  1403  								},
  1404  							},
  1405  							BuildOutputs: []atc.DebugBuildOutput{
  1406  								{
  1407  									DebugResourceVersion: atc.DebugResourceVersion{
  1408  										VersionID:  73,
  1409  										ResourceID: 127,
  1410  										CheckOrder: 123,
  1411  										ScopeID:    111,
  1412  									},
  1413  									BuildID: 66,
  1414  									JobID:   13,
  1415  								},
  1416  							},
  1417  							BuildInputs: []atc.DebugBuildInput{
  1418  								{
  1419  									DebugResourceVersion: atc.DebugResourceVersion{
  1420  										VersionID:  66,
  1421  										ResourceID: 77,
  1422  										CheckOrder: 88,
  1423  										ScopeID:    222,
  1424  									},
  1425  									BuildID:   66,
  1426  									JobID:     13,
  1427  									InputName: "some-input-name",
  1428  								},
  1429  							},
  1430  							BuildReruns: []atc.DebugBuildRerun{
  1431  								{
  1432  									JobID:   13,
  1433  									BuildID: 111,
  1434  									RerunOf: 222,
  1435  								},
  1436  							},
  1437  							Jobs: []atc.DebugJob{
  1438  								{
  1439  									ID:   13,
  1440  									Name: "bad-luck-job",
  1441  								},
  1442  							},
  1443  							Resources: []atc.DebugResource{
  1444  								{
  1445  									ID:      127,
  1446  									Name:    "resource-127",
  1447  									ScopeID: nil,
  1448  								},
  1449  								{
  1450  									ID:      128,
  1451  									Name:    "resource-128",
  1452  									ScopeID: &scopeID,
  1453  								},
  1454  							},
  1455  						},
  1456  						nil,
  1457  					)
  1458  				})
  1459  
  1460  				It("constructs teamDB with provided team name", func() {
  1461  					Expect(dbTeamFactory.FindTeamCallCount()).To(Equal(1))
  1462  					Expect(dbTeamFactory.FindTeamArgsForCall(0)).To(Equal("a-team"))
  1463  				})
  1464  
  1465  				It("returns 200", func() {
  1466  					Expect(response.StatusCode).To(Equal(http.StatusOK))
  1467  				})
  1468  
  1469  				It("returns application/json", func() {
  1470  					expectedHeaderEntries := map[string]string{
  1471  						"Content-Type": "application/json",
  1472  					}
  1473  					Expect(response).Should(IncludeHeaderEntries(expectedHeaderEntries))
  1474  				})
  1475  
  1476  				It("returns a json representation of all the versions in the pipeline", func() {
  1477  					body, err := ioutil.ReadAll(response.Body)
  1478  					Expect(err).NotTo(HaveOccurred())
  1479  
  1480  					Expect(body).To(MatchJSON(`{
  1481  				"ResourceVersions": [
  1482  					{
  1483  						"VersionID": 73,
  1484  						"ResourceID": 127,
  1485  						"CheckOrder": 123,
  1486  						"ScopeID": 111
  1487  			    }
  1488  				],
  1489  				"BuildOutputs": [
  1490  					{
  1491  						"VersionID": 73,
  1492  						"ResourceID": 127,
  1493  						"BuildID": 66,
  1494  						"JobID": 13,
  1495  						"CheckOrder": 123,
  1496  						"ScopeID": 111
  1497  					}
  1498  				],
  1499  				"BuildInputs": [
  1500  					{
  1501  						"VersionID": 66,
  1502  						"ResourceID": 77,
  1503  						"BuildID": 66,
  1504  						"JobID": 13,
  1505  						"CheckOrder": 88,
  1506  						"ScopeID": 222,
  1507  						"InputName": "some-input-name"
  1508  					}
  1509  				],
  1510  				"BuildReruns": [
  1511  					{
  1512  						"JobID": 13,
  1513  						"BuildID": 111,
  1514  						"RerunOf": 222
  1515  					}
  1516  				],
  1517  				"Jobs": [
  1518  					{
  1519  						"ID": 13,
  1520  						"Name": "bad-luck-job"
  1521  					}
  1522  				],
  1523  				"Resources": [
  1524  					{
  1525  						"ID": 127,
  1526  						"Name": "resource-127",
  1527  						"ScopeID": null
  1528  					},
  1529  					{
  1530  						"ID": 128,
  1531  						"Name": "resource-128",
  1532  						"ScopeID": 789
  1533  					}
  1534  				]
  1535  				}`))
  1536  				})
  1537  			})
  1538  
  1539  			Context("when getting the debug versions db fails", func() {
  1540  				BeforeEach(func() {
  1541  					dbPipeline.LoadDebugVersionsDBReturns(nil, errors.New("nope"))
  1542  				})
  1543  
  1544  				It("constructs teamDB with provided team name", func() {
  1545  					Expect(dbTeamFactory.FindTeamCallCount()).To(Equal(1))
  1546  					Expect(dbTeamFactory.FindTeamArgsForCall(0)).To(Equal("a-team"))
  1547  				})
  1548  
  1549  				It("returns 500", func() {
  1550  					Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
  1551  				})
  1552  
  1553  				It("does not return application/json", func() {
  1554  					expectedHeaderEntries := map[string]string{
  1555  						"Content-Type": "",
  1556  					}
  1557  					Expect(response).ShouldNot(IncludeHeaderEntries(expectedHeaderEntries))
  1558  				})
  1559  			})
  1560  		})
  1561  
  1562  		Context("when not authenticated", func() {
  1563  			BeforeEach(func() {
  1564  				fakeAccess.IsAuthenticatedReturns(false)
  1565  			})
  1566  
  1567  			It("returns 401 Unauthorized", func() {
  1568  				Expect(response.StatusCode).To(Equal(http.StatusUnauthorized))
  1569  			})
  1570  		})
  1571  	})
  1572  
  1573  	Describe("PUT /api/v1/teams/:team_name/pipelines/:pipeline_name/rename", func() {
  1574  		var response *http.Response
  1575  		var requestBody string
  1576  
  1577  		BeforeEach(func() {
  1578  			requestBody = `{"name":"some-new-name"}`
  1579  		})
  1580  
  1581  		JustBeforeEach(func() {
  1582  			var err error
  1583  
  1584  			request, err := http.NewRequest("PUT", server.URL+"/api/v1/teams/a-team/pipelines/a-pipeline/rename", bytes.NewBufferString(requestBody))
  1585  			Expect(err).NotTo(HaveOccurred())
  1586  
  1587  			response, err = client.Do(request)
  1588  			Expect(err).NotTo(HaveOccurred())
  1589  		})
  1590  
  1591  		Context("when authenticated", func() {
  1592  			BeforeEach(func() {
  1593  				fakeAccess.IsAuthenticatedReturns(true)
  1594  			})
  1595  			Context("when requester belongs to the team", func() {
  1596  				BeforeEach(func() {
  1597  					fakeAccess.IsAuthorizedReturns(true)
  1598  
  1599  					dbTeamFactory.FindTeamReturns(fakeTeam, true, nil)
  1600  					fakeTeam.PipelineReturns(dbPipeline, true, nil)
  1601  				})
  1602  
  1603  				It("constructs teamDB with provided team name", func() {
  1604  					Expect(dbTeamFactory.FindTeamCallCount()).To(Equal(1))
  1605  					Expect(dbTeamFactory.FindTeamArgsForCall(0)).To(Equal("a-team"))
  1606  				})
  1607  
  1608  				It("injects the proper pipeline", func() {
  1609  					pipelineRef := fakeTeam.PipelineArgsForCall(0)
  1610  					Expect(pipelineRef).To(Equal(atc.PipelineRef{Name: "a-pipeline"}))
  1611  				})
  1612  
  1613  				It("returns 200", func() {
  1614  					Expect(response.StatusCode).To(Equal(http.StatusOK))
  1615  				})
  1616  
  1617  				It("renames the pipeline to the name provided", func() {
  1618  					Expect(dbPipeline.RenameCallCount()).To(Equal(1))
  1619  					Expect(dbPipeline.RenameArgsForCall(0)).To(Equal("some-new-name"))
  1620  				})
  1621  
  1622  				Context("when a warning occurs", func() {
  1623  
  1624  					BeforeEach(func() {
  1625  						requestBody = `{"name":"_some-new-name"}`
  1626  					})
  1627  
  1628  					It("returns a warning in the response body", func() {
  1629  						Expect(ioutil.ReadAll(response.Body)).To(MatchJSON(`
  1630  							{
  1631  								"warnings": [
  1632  									{
  1633  										"type": "invalid_identifier",
  1634  										"message": "pipeline: '_some-new-name' is not a valid identifier: must start with a lowercase letter"
  1635  									}
  1636  								]
  1637  							}`))
  1638  					})
  1639  				})
  1640  
  1641  				Context("when an error occurs on update", func() {
  1642  					BeforeEach(func() {
  1643  						fakeTeam.PipelineReturns(dbPipeline, true, nil)
  1644  						dbPipeline.RenameReturns(errors.New("whoops"))
  1645  					})
  1646  
  1647  					It("returns a 500 internal server error", func() {
  1648  						Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
  1649  					})
  1650  				})
  1651  			})
  1652  
  1653  			Context("when requester does not belong to the team", func() {
  1654  				BeforeEach(func() {
  1655  					fakeAccess.IsAuthorizedReturns(false)
  1656  				})
  1657  
  1658  				It("returns 403 Forbidden", func() {
  1659  					Expect(response.StatusCode).To(Equal(http.StatusForbidden))
  1660  				})
  1661  			})
  1662  		})
  1663  
  1664  		Context("when not authenticated", func() {
  1665  			BeforeEach(func() {
  1666  				fakeAccess.IsAuthenticatedReturns(false)
  1667  			})
  1668  
  1669  			It("returns 401 Unauthorized", func() {
  1670  				Expect(response.StatusCode).To(Equal(http.StatusUnauthorized))
  1671  			})
  1672  		})
  1673  	})
  1674  
  1675  	Describe("GET /api/v1/teams/:team_name/pipelines/:pipeline_name/builds", func() {
  1676  		var response *http.Response
  1677  		var queryParams string
  1678  
  1679  		JustBeforeEach(func() {
  1680  			var err error
  1681  
  1682  			fakePipeline.NameReturns("some-pipeline")
  1683  			response, err = client.Get(server.URL + "/api/v1/teams/some-team/pipelines/some-pipeline/builds" + queryParams)
  1684  			Expect(err).NotTo(HaveOccurred())
  1685  		})
  1686  
  1687  		Context("when not authenticated", func() {
  1688  			BeforeEach(func() {
  1689  				fakeAccess.IsAuthenticatedReturns(false)
  1690  			})
  1691  
  1692  			Context("and the pipeline is private", func() {
  1693  				BeforeEach(func() {
  1694  					fakePipeline.PublicReturns(false)
  1695  				})
  1696  
  1697  				It("returns 401", func() {
  1698  					Expect(response.StatusCode).To(Equal(http.StatusUnauthorized))
  1699  				})
  1700  			})
  1701  
  1702  			Context("and the pipeline is public", func() {
  1703  				BeforeEach(func() {
  1704  					fakePipeline.PublicReturns(true)
  1705  				})
  1706  
  1707  				It("returns 200 OK", func() {
  1708  					Expect(response.StatusCode).To(Equal(http.StatusOK))
  1709  				})
  1710  			})
  1711  		})
  1712  
  1713  		Context("when authorized", func() {
  1714  			BeforeEach(func() {
  1715  				fakeAccess.IsAuthenticatedReturns(true)
  1716  				fakeAccess.IsAuthorizedReturns(true)
  1717  			})
  1718  
  1719  			Context("when no params are passed", func() {
  1720  				It("does not set defaults for since and until", func() {
  1721  					Expect(fakePipeline.BuildsCallCount()).To(Equal(1))
  1722  
  1723  					page := fakePipeline.BuildsArgsForCall(0)
  1724  					Expect(page).To(Equal(db.Page{
  1725  						Limit: 100,
  1726  					}))
  1727  				})
  1728  			})
  1729  
  1730  			Context("when all the params are passed", func() {
  1731  				BeforeEach(func() {
  1732  					queryParams = "?from=2&to=3&limit=8"
  1733  				})
  1734  
  1735  				It("passes them through", func() {
  1736  					Expect(fakePipeline.BuildsCallCount()).To(Equal(1))
  1737  
  1738  					page := fakePipeline.BuildsArgsForCall(0)
  1739  					Expect(page).To(Equal(db.Page{
  1740  						From:  db.NewIntPtr(2),
  1741  						To:    db.NewIntPtr(3),
  1742  						Limit: 8,
  1743  					}))
  1744  				})
  1745  			})
  1746  
  1747  			Context("when getting the builds succeeds", func() {
  1748  				var returnedBuilds []db.Build
  1749  
  1750  				BeforeEach(func() {
  1751  					queryParams = "?since=5&limit=2"
  1752  
  1753  					build1 := new(dbfakes.FakeBuild)
  1754  					build1.IDReturns(4)
  1755  					build1.NameReturns("2")
  1756  					build1.JobNameReturns("some-job")
  1757  					build1.PipelineNameReturns("some-pipeline")
  1758  					build1.TeamNameReturns("some-team")
  1759  					build1.StatusReturns(db.BuildStatusStarted)
  1760  					build1.StartTimeReturns(time.Unix(1, 0))
  1761  					build1.EndTimeReturns(time.Unix(100, 0))
  1762  
  1763  					build2 := new(dbfakes.FakeBuild)
  1764  					build2.IDReturns(2)
  1765  					build2.NameReturns("1")
  1766  					build2.JobNameReturns("some-job")
  1767  					build2.PipelineNameReturns("some-pipeline")
  1768  					build2.TeamNameReturns("some-team")
  1769  					build2.StatusReturns(db.BuildStatusSucceeded)
  1770  					build2.StartTimeReturns(time.Unix(101, 0))
  1771  					build2.EndTimeReturns(time.Unix(200, 0))
  1772  
  1773  					returnedBuilds = []db.Build{build1, build2}
  1774  					fakePipeline.BuildsReturns(returnedBuilds, db.Pagination{}, nil)
  1775  				})
  1776  
  1777  				It("returns 200 OK", func() {
  1778  					Expect(response.StatusCode).To(Equal(http.StatusOK))
  1779  				})
  1780  
  1781  				It("returns Content-Type 'application/json'", func() {
  1782  					expectedHeaderEntries := map[string]string{
  1783  						"Content-Type": "application/json",
  1784  					}
  1785  					Expect(response).Should(IncludeHeaderEntries(expectedHeaderEntries))
  1786  				})
  1787  
  1788  				It("returns the builds", func() {
  1789  					body, err := ioutil.ReadAll(response.Body)
  1790  					Expect(err).NotTo(HaveOccurred())
  1791  
  1792  					Expect(body).To(MatchJSON(`[
  1793  					{
  1794  						"id": 4,
  1795  						"name": "2",
  1796  						"job_name": "some-job",
  1797  						"status": "started",
  1798  						"api_url": "/api/v1/builds/4",
  1799  						"pipeline_name":"some-pipeline",
  1800  						"team_name": "some-team",
  1801  						"start_time": 1,
  1802  						"end_time": 100
  1803  					},
  1804  					{
  1805  						"id": 2,
  1806  						"name": "1",
  1807  						"job_name": "some-job",
  1808  						"status": "succeeded",
  1809  						"api_url": "/api/v1/builds/2",
  1810  						"pipeline_name": "some-pipeline",
  1811  						"team_name": "some-team",
  1812  						"start_time": 101,
  1813  						"end_time": 200
  1814  					}
  1815  				]`))
  1816  				})
  1817  
  1818  				Context("when next/previous pages are available", func() {
  1819  					BeforeEach(func() {
  1820  						fakePipeline.BuildsReturns(returnedBuilds, db.Pagination{
  1821  							Newer: &db.Page{From: db.NewIntPtr(4), Limit: 2},
  1822  							Older: &db.Page{To: db.NewIntPtr(2), Limit: 2},
  1823  						}, nil)
  1824  					})
  1825  
  1826  					It("returns Link headers per rfc5988", func() {
  1827  						Expect(response.Header["Link"]).To(ConsistOf([]string{
  1828  							fmt.Sprintf(`<%s/api/v1/teams/some-team/pipelines/some-pipeline/builds?from=4&limit=2>; rel="previous"`, externalURL),
  1829  							fmt.Sprintf(`<%s/api/v1/teams/some-team/pipelines/some-pipeline/builds?to=2&limit=2>; rel="next"`, externalURL),
  1830  						}))
  1831  					})
  1832  
  1833  					Context("and pipeline is instanced", func() {
  1834  						BeforeEach(func() {
  1835  							fakePipeline.InstanceVarsReturns(atc.InstanceVars{"branch": "master"})
  1836  						})
  1837  
  1838  						It("returns Link headers per rfc5988", func() {
  1839  							link := fmt.Sprintf(`<%s/api/v1/teams/some-team/pipelines/some-pipeline/builds?`, externalURL)
  1840  							Expect(response.Header["Link"]).To(ConsistOf([]string{
  1841  								link + `to=2&limit=2&instance_vars=%7B%22branch%22%3A%22master%22%7D>; rel="next"`,
  1842  								link + `from=4&limit=2&instance_vars=%7B%22branch%22%3A%22master%22%7D>; rel="previous"`,
  1843  							}))
  1844  						})
  1845  					})
  1846  				})
  1847  			})
  1848  
  1849  			Context("when getting the build fails", func() {
  1850  				BeforeEach(func() {
  1851  					fakePipeline.BuildsReturns(nil, db.Pagination{}, errors.New("oh no!"))
  1852  				})
  1853  
  1854  				It("returns 404 Not Found", func() {
  1855  					Expect(response.StatusCode).To(Equal(http.StatusNotFound))
  1856  				})
  1857  			})
  1858  		})
  1859  	})
  1860  
  1861  	Describe("POST /api/v1/teams/:team_name/pipelines/:pipeline_name/builds", func() {
  1862  		var plan atc.Plan
  1863  		var response *http.Response
  1864  
  1865  		BeforeEach(func() {
  1866  			plan = atc.Plan{
  1867  				Task: &atc.TaskPlan{
  1868  					Config: &atc.TaskConfig{
  1869  						Run: atc.TaskRunConfig{
  1870  							Path: "ls",
  1871  						},
  1872  					},
  1873  				},
  1874  			}
  1875  		})
  1876  
  1877  		JustBeforeEach(func() {
  1878  			reqPayload, err := json.Marshal(plan)
  1879  			Expect(err).NotTo(HaveOccurred())
  1880  
  1881  			req, err := http.NewRequest("POST", server.URL+"/api/v1/teams/a-team/pipelines/a-pipeline/builds", bytes.NewBuffer(reqPayload))
  1882  			Expect(err).NotTo(HaveOccurred())
  1883  
  1884  			req.Header.Set("Content-Type", "application/json")
  1885  
  1886  			response, err = client.Do(req)
  1887  			Expect(err).NotTo(HaveOccurred())
  1888  		})
  1889  
  1890  		Context("when not authenticated", func() {
  1891  			BeforeEach(func() {
  1892  				fakeAccess.IsAuthenticatedReturns(false)
  1893  			})
  1894  
  1895  			It("returns 401", func() {
  1896  				Expect(response.StatusCode).To(Equal(http.StatusUnauthorized))
  1897  			})
  1898  
  1899  			It("does not trigger a build", func() {
  1900  				Expect(dbPipeline.CreateOneOffBuildCallCount()).To(BeZero())
  1901  			})
  1902  		})
  1903  
  1904  		Context("when authenticated", func() {
  1905  			BeforeEach(func() {
  1906  				fakeAccess.IsAuthenticatedReturns(true)
  1907  			})
  1908  
  1909  			Context("when not authorized", func() {
  1910  				BeforeEach(func() {
  1911  					fakeAccess.IsAuthorizedReturns(false)
  1912  				})
  1913  
  1914  				It("returns 403", func() {
  1915  					Expect(response.StatusCode).To(Equal(http.StatusForbidden))
  1916  				})
  1917  			})
  1918  
  1919  			Context("when authorized", func() {
  1920  				BeforeEach(func() {
  1921  					fakeAccess.IsAuthorizedReturns(true)
  1922  					dbTeamFactory.FindTeamReturns(fakeTeam, true, nil)
  1923  					fakeTeam.PipelineReturns(dbPipeline, true, nil)
  1924  				})
  1925  
  1926  				Context("when creating a started build fails", func() {
  1927  					BeforeEach(func() {
  1928  						dbPipeline.CreateStartedBuildReturns(nil, errors.New("oh no!"))
  1929  					})
  1930  
  1931  					It("returns 500 Internal Server Error", func() {
  1932  						Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
  1933  					})
  1934  				})
  1935  
  1936  				Context("when creating a started build succeeds", func() {
  1937  					var fakeBuild *dbfakes.FakeBuild
  1938  
  1939  					BeforeEach(func() {
  1940  						fakeBuild = new(dbfakes.FakeBuild)
  1941  						fakeBuild.IDReturns(42)
  1942  						fakeBuild.NameReturns("1")
  1943  						fakeBuild.TeamNameReturns("some-team")
  1944  						fakeBuild.StatusReturns("started")
  1945  						fakeBuild.StartTimeReturns(time.Unix(1, 0))
  1946  						fakeBuild.EndTimeReturns(time.Unix(100, 0))
  1947  						fakeBuild.ReapTimeReturns(time.Unix(200, 0))
  1948  
  1949  						dbPipeline.CreateStartedBuildReturns(fakeBuild, nil)
  1950  					})
  1951  
  1952  					It("returns 201 Created", func() {
  1953  						Expect(response.StatusCode).To(Equal(http.StatusCreated))
  1954  					})
  1955  
  1956  					It("returns Content-Type 'application/json'", func() {
  1957  						expectedHeaderEntries := map[string]string{
  1958  							"Content-Type": "application/json",
  1959  						}
  1960  						Expect(response).Should(IncludeHeaderEntries(expectedHeaderEntries))
  1961  					})
  1962  
  1963  					It("creates a started build", func() {
  1964  						Expect(dbPipeline.CreateStartedBuildCallCount()).To(Equal(1))
  1965  						Expect(dbPipeline.CreateStartedBuildArgsForCall(0)).To(Equal(plan))
  1966  					})
  1967  
  1968  					It("returns the created build", func() {
  1969  						body, err := ioutil.ReadAll(response.Body)
  1970  						Expect(err).NotTo(HaveOccurred())
  1971  
  1972  						Expect(body).To(MatchJSON(`{
  1973  								"id": 42,
  1974  								"name": "1",
  1975  								"team_name": "some-team",
  1976  								"status": "started",
  1977  								"api_url": "/api/v1/builds/42",
  1978  								"start_time": 1,
  1979  								"end_time": 100,
  1980  								"reap_time": 200
  1981  						}`))
  1982  					})
  1983  				})
  1984  			})
  1985  		})
  1986  	})
  1987  })