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

     1  package api_test
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"io/ioutil"
     7  	"net/http"
     8  	"time"
     9  
    10  	"github.com/pf-qiu/concourse/v6/atc"
    11  	"github.com/pf-qiu/concourse/v6/atc/db"
    12  	"github.com/pf-qiu/concourse/v6/atc/db/dbfakes"
    13  	. "github.com/pf-qiu/concourse/v6/atc/testhelpers"
    14  	. "github.com/onsi/ginkgo"
    15  	. "github.com/onsi/gomega"
    16  )
    17  
    18  var _ = Describe("Versions API", func() {
    19  	var fakePipeline *dbfakes.FakePipeline
    20  
    21  	BeforeEach(func() {
    22  		fakePipeline = new(dbfakes.FakePipeline)
    23  		dbTeamFactory.FindTeamReturns(dbTeam, true, nil)
    24  		dbTeam.PipelineReturns(fakePipeline, true, nil)
    25  	})
    26  
    27  	Describe("GET /api/v1/teams/:team_name/pipelines/:pipeline_name/resources/:resource_name/versions", func() {
    28  		var response *http.Response
    29  		var queryParams string
    30  		var fakeResource *dbfakes.FakeResource
    31  
    32  		BeforeEach(func() {
    33  			queryParams = ""
    34  			fakeResource = new(dbfakes.FakeResource)
    35  		})
    36  
    37  		JustBeforeEach(func() {
    38  			var err error
    39  
    40  			request, err := http.NewRequest("GET", server.URL+"/api/v1/teams/a-team/pipelines/a-pipeline/resources/some-resource/versions"+queryParams, nil)
    41  			Expect(err).NotTo(HaveOccurred())
    42  
    43  			response, err = client.Do(request)
    44  			Expect(err).NotTo(HaveOccurred())
    45  		})
    46  
    47  		Context("when not authorized", func() {
    48  			BeforeEach(func() {
    49  				fakeAccess.IsAuthorizedReturns(false)
    50  			})
    51  
    52  			Context("and the pipeline is private", func() {
    53  				BeforeEach(func() {
    54  					fakePipeline.PublicReturns(false)
    55  				})
    56  
    57  				Context("user is not authenticated", func() {
    58  					BeforeEach(func() {
    59  						fakeAccess.IsAuthenticatedReturns(false)
    60  					})
    61  
    62  					It("returns 401", func() {
    63  						Expect(response.StatusCode).To(Equal(http.StatusUnauthorized))
    64  					})
    65  				})
    66  
    67  				Context("user is authenticated", func() {
    68  					BeforeEach(func() {
    69  						fakeAccess.IsAuthenticatedReturns(true)
    70  					})
    71  
    72  					It("returns 403", func() {
    73  						Expect(response.StatusCode).To(Equal(http.StatusForbidden))
    74  					})
    75  				})
    76  			})
    77  
    78  			Context("and the pipeline is public", func() {
    79  				BeforeEach(func() {
    80  					fakePipeline.PublicReturns(true)
    81  					fakePipeline.ResourceReturns(fakeResource, true, nil)
    82  
    83  					returnedVersions := []atc.ResourceVersion{
    84  						{
    85  							ID:      4,
    86  							Enabled: true,
    87  							Version: atc.Version{
    88  								"some": "version",
    89  							},
    90  							Metadata: []atc.MetadataField{
    91  								{
    92  									Name:  "some",
    93  									Value: "metadata",
    94  								},
    95  							},
    96  						},
    97  						{
    98  							ID:      2,
    99  							Enabled: false,
   100  							Version: atc.Version{
   101  								"some": "version",
   102  							},
   103  							Metadata: []atc.MetadataField{
   104  								{
   105  									Name:  "some",
   106  									Value: "metadata",
   107  								},
   108  							},
   109  						},
   110  					}
   111  
   112  					fakeResource.VersionsReturns(returnedVersions, db.Pagination{}, true, nil)
   113  				})
   114  
   115  				It("returns 200 OK", func() {
   116  					Expect(response.StatusCode).To(Equal(http.StatusOK))
   117  				})
   118  
   119  				It("returns content type application/json", func() {
   120  					expectedHeaderEntries := map[string]string{
   121  						"Content-Type": "application/json",
   122  					}
   123  					Expect(response).Should(IncludeHeaderEntries(expectedHeaderEntries))
   124  				})
   125  
   126  				Context("when resource is public", func() {
   127  					BeforeEach(func() {
   128  						fakeResource.PublicReturns(true)
   129  					})
   130  
   131  					It("returns the json", func() {
   132  						body, err := ioutil.ReadAll(response.Body)
   133  						Expect(err).NotTo(HaveOccurred())
   134  
   135  						Expect(body).To(MatchJSON(`[
   136  					{
   137  						"id": 4,
   138  						"enabled": true,
   139  						"version": {"some":"version"},
   140  						"metadata": [
   141  							{
   142  								"name":"some",
   143  								"value":"metadata"
   144  							}
   145  						]
   146  					},
   147  					{
   148  						"id":2,
   149  						"enabled": false,
   150  						"version": {"some":"version"},
   151  						"metadata": [
   152  							{
   153  								"name":"some",
   154  								"value":"metadata"
   155  							}
   156  						]
   157  					}
   158  				]`))
   159  					})
   160  				})
   161  
   162  				Context("when resource is not public", func() {
   163  					Context("when the user is not authenticated", func() {
   164  						It("returns the json without version metadata", func() {
   165  							body, err := ioutil.ReadAll(response.Body)
   166  							Expect(err).NotTo(HaveOccurred())
   167  
   168  							Expect(body).To(MatchJSON(`[
   169  								{
   170  									"id": 4,
   171  									"enabled": true,
   172  									"version": {"some":"version"}
   173  								},
   174  								{
   175  									"id":2,
   176  									"enabled": false,
   177  									"version": {"some":"version"}
   178  								}
   179  							]`))
   180  						})
   181  					})
   182  
   183  					Context("when the user is authenticated", func() {
   184  						BeforeEach(func() {
   185  							fakeAccess.IsAuthenticatedReturns(true)
   186  						})
   187  
   188  						It("returns the json without version metadata", func() {
   189  							body, err := ioutil.ReadAll(response.Body)
   190  							Expect(err).NotTo(HaveOccurred())
   191  
   192  							Expect(body).To(MatchJSON(`[
   193  								{
   194  									"id": 4,
   195  									"enabled": true,
   196  									"version": {"some":"version"}
   197  								},
   198  								{
   199  									"id":2,
   200  									"enabled": false,
   201  									"version": {"some":"version"}
   202  								}
   203  							]`))
   204  						})
   205  					})
   206  				})
   207  			})
   208  		})
   209  
   210  		Context("when authorized", func() {
   211  			BeforeEach(func() {
   212  				fakeAccess.IsAuthenticatedReturns(true)
   213  				fakeAccess.IsAuthorizedReturns(true)
   214  			})
   215  
   216  			It("finds the resource", func() {
   217  				Expect(fakePipeline.ResourceCallCount()).To(Equal(1))
   218  				Expect(fakePipeline.ResourceArgsForCall(0)).To(Equal("some-resource"))
   219  			})
   220  
   221  			Context("when finding the resource succeeds", func() {
   222  				BeforeEach(func() {
   223  					fakePipeline.ResourceReturns(fakeResource, true, nil)
   224  				})
   225  
   226  				Context("when no params are passed", func() {
   227  					It("does not set defaults for since and until", func() {
   228  						Expect(fakeResource.VersionsCallCount()).To(Equal(1))
   229  
   230  						page, versionFilter := fakeResource.VersionsArgsForCall(0)
   231  						Expect(page).To(Equal(db.Page{
   232  							Limit: 100,
   233  						}))
   234  						Expect(versionFilter).To(Equal(atc.Version{}))
   235  					})
   236  				})
   237  
   238  				Context("when all the params are passed", func() {
   239  					BeforeEach(func() {
   240  						queryParams = "?from=5&to=7&limit=8&filter=ref:foo&filter=some-ref:blah"
   241  					})
   242  
   243  					It("passes them through", func() {
   244  						Expect(fakeResource.VersionsCallCount()).To(Equal(1))
   245  
   246  						page, versionFilter := fakeResource.VersionsArgsForCall(0)
   247  						Expect(page).To(Equal(db.Page{
   248  							From:  db.NewIntPtr(5),
   249  							To:    db.NewIntPtr(7),
   250  							Limit: 8,
   251  						}))
   252  						Expect(versionFilter).To(Equal(atc.Version{
   253  							"ref":      "foo",
   254  							"some-ref": "blah",
   255  						}))
   256  					})
   257  				})
   258  
   259  				Context("when params includes version filter has special char", func() {
   260  					Context("space char", func() {
   261  						BeforeEach(func() {
   262  							queryParams = "?filter=some%20ref:some%20value"
   263  						})
   264  
   265  						It("passes them through", func() {
   266  							Expect(fakeResource.VersionsCallCount()).To(Equal(1))
   267  
   268  							_, versionFilter := fakeResource.VersionsArgsForCall(0)
   269  							Expect(versionFilter).To(Equal(atc.Version{
   270  								"some ref": "some value",
   271  							}))
   272  						})
   273  					})
   274  
   275  					Context("% char", func() {
   276  						BeforeEach(func() {
   277  							queryParams = "?filter=ref:some%25value"
   278  						})
   279  
   280  						It("passes them through", func() {
   281  							Expect(fakeResource.VersionsCallCount()).To(Equal(1))
   282  
   283  							_, versionFilter := fakeResource.VersionsArgsForCall(0)
   284  							Expect(versionFilter).To(Equal(atc.Version{
   285  								"ref": "some%value",
   286  							}))
   287  						})
   288  					})
   289  
   290  					Context(": char", func() {
   291  						BeforeEach(func() {
   292  							queryParams = "?filter=key%3Awith%3Acolon:abcdef"
   293  						})
   294  
   295  						It("passes them through by splitting on first colon", func() {
   296  							Expect(fakeResource.VersionsCallCount()).To(Equal(1))
   297  
   298  							_, versionFilter := fakeResource.VersionsArgsForCall(0)
   299  							Expect(versionFilter).To(Equal(atc.Version{
   300  								"key": "with:colon:abcdef",
   301  							}))
   302  						})
   303  					})
   304  
   305  					Context("if there is no : ", func() {
   306  						BeforeEach(func() {
   307  							queryParams = "?filter=abcdef"
   308  						})
   309  
   310  						It("set no filter when fetching versions", func() {
   311  							Expect(fakeResource.VersionsCallCount()).To(Equal(1))
   312  
   313  							_, versionFilter := fakeResource.VersionsArgsForCall(0)
   314  							Expect(versionFilter).To(BeEmpty())
   315  						})
   316  					})
   317  				})
   318  
   319  				Context("when getting the versions succeeds", func() {
   320  					var returnedVersions []atc.ResourceVersion
   321  
   322  					BeforeEach(func() {
   323  						queryParams = "?since=5&limit=2"
   324  						returnedVersions = []atc.ResourceVersion{
   325  							{
   326  								ID:      4,
   327  								Enabled: true,
   328  								Version: atc.Version{
   329  									"some": "version",
   330  									"ref":  "foo",
   331  								},
   332  								Metadata: []atc.MetadataField{
   333  									{
   334  										Name:  "some",
   335  										Value: "metadata",
   336  									},
   337  								},
   338  							},
   339  							{
   340  								ID:      2,
   341  								Enabled: false,
   342  								Version: atc.Version{
   343  									"some": "version",
   344  									"ref":  "blah",
   345  								},
   346  								Metadata: []atc.MetadataField{
   347  									{
   348  										Name:  "some",
   349  										Value: "metadata",
   350  									},
   351  								},
   352  							},
   353  						}
   354  
   355  						fakeResource.VersionsReturns(returnedVersions, db.Pagination{}, true, nil)
   356  					})
   357  
   358  					It("returns 200 OK", func() {
   359  						Expect(response.StatusCode).To(Equal(http.StatusOK))
   360  					})
   361  
   362  					It("returns content type application/json", func() {
   363  						expectedHeaderEntries := map[string]string{
   364  							"Content-Type": "application/json",
   365  						}
   366  						Expect(response).Should(IncludeHeaderEntries(expectedHeaderEntries))
   367  					})
   368  
   369  					It("returns the json", func() {
   370  						body, err := ioutil.ReadAll(response.Body)
   371  						Expect(err).NotTo(HaveOccurred())
   372  
   373  						Expect(body).To(MatchJSON(`[
   374  					{
   375  						"id": 4,
   376  						"enabled": true,
   377  						"version": {"some":"version", "ref":"foo"},
   378  						"metadata": [
   379  							{
   380  								"name":"some",
   381  								"value":"metadata"
   382  							}
   383  						]
   384  					},
   385  					{
   386  						"id":2,
   387  						"enabled": false,
   388  						"version": {"some":"version", "ref":"blah"},
   389  						"metadata": [
   390  							{
   391  								"name":"some",
   392  								"value":"metadata"
   393  							}
   394  						]
   395  					}
   396  				]`))
   397  					})
   398  
   399  					Context("when next/previous pages are available", func() {
   400  						BeforeEach(func() {
   401  							fakePipeline.NameReturns("some-pipeline")
   402  							fakeResource.VersionsReturns(returnedVersions, db.Pagination{
   403  								Newer: &db.Page{From: db.NewIntPtr(4), Limit: 2},
   404  								Older: &db.Page{To: db.NewIntPtr(2), Limit: 2},
   405  							}, true, nil)
   406  						})
   407  
   408  						It("returns Link headers per rfc5988", func() {
   409  							Expect(response.Header["Link"]).To(ConsistOf([]string{
   410  								fmt.Sprintf(`<%s/api/v1/teams/a-team/pipelines/some-pipeline/resources/some-resource/versions?from=4&limit=2>; rel="previous"`, externalURL),
   411  								fmt.Sprintf(`<%s/api/v1/teams/a-team/pipelines/some-pipeline/resources/some-resource/versions?to=2&limit=2>; rel="next"`, externalURL),
   412  							}))
   413  						})
   414  
   415  						Context("and resource is on an instanced pipeline", func() {
   416  							BeforeEach(func() {
   417  								fakePipeline.InstanceVarsReturns(atc.InstanceVars{"branch": "master"})
   418  							})
   419  
   420  							It("returns Link headers per rfc5988", func() {
   421  								link := fmt.Sprintf(`<%s/api/v1/teams/a-team/pipelines/some-pipeline/resources/some-resource/versions?`, externalURL)
   422  								Expect(response.Header["Link"]).To(ConsistOf([]string{
   423  									link + `to=2&limit=2&instance_vars=%7B%22branch%22%3A%22master%22%7D>; rel="next"`,
   424  									link + `from=4&limit=2&instance_vars=%7B%22branch%22%3A%22master%22%7D>; rel="previous"`,
   425  								}))
   426  							})
   427  						})
   428  					})
   429  				})
   430  
   431  				Context("when the versions can't be found", func() {
   432  					BeforeEach(func() {
   433  						fakeResource.VersionsReturns(nil, db.Pagination{}, false, nil)
   434  					})
   435  
   436  					It("returns 404 not found", func() {
   437  						Expect(response.StatusCode).To(Equal(http.StatusNotFound))
   438  					})
   439  				})
   440  
   441  				Context("when getting the versions fails", func() {
   442  					BeforeEach(func() {
   443  						fakeResource.VersionsReturns(nil, db.Pagination{}, false, errors.New("oh no!"))
   444  					})
   445  
   446  					It("returns 500 Internal Server Error", func() {
   447  						Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
   448  					})
   449  				})
   450  			})
   451  
   452  			Context("when finding the resource fails", func() {
   453  				BeforeEach(func() {
   454  					fakePipeline.ResourceReturns(nil, false, errors.New("oh no!"))
   455  				})
   456  
   457  				It("returns 500 Internal Server Error", func() {
   458  					Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
   459  				})
   460  			})
   461  
   462  			Context("when the resource is not found", func() {
   463  				BeforeEach(func() {
   464  					fakePipeline.ResourceReturns(nil, false, nil)
   465  				})
   466  
   467  				It("returns 404 not found", func() {
   468  					Expect(response.StatusCode).To(Equal(http.StatusNotFound))
   469  				})
   470  			})
   471  		})
   472  	})
   473  
   474  	Describe("PUT /api/v1/teams/:team_name/pipelines/:pipeline_name/resources/:resource_name/versions/:resource_version_id/enable", func() {
   475  		var response *http.Response
   476  		var fakeResource *dbfakes.FakeResource
   477  
   478  		JustBeforeEach(func() {
   479  			var err error
   480  
   481  			request, err := http.NewRequest("PUT", server.URL+"/api/v1/teams/a-team/pipelines/a-pipeline/resources/resource-name/versions/42/enable", nil)
   482  			Expect(err).NotTo(HaveOccurred())
   483  
   484  			response, err = client.Do(request)
   485  			Expect(err).NotTo(HaveOccurred())
   486  		})
   487  
   488  		Context("when authenticated", func() {
   489  			BeforeEach(func() {
   490  				fakeAccess.IsAuthenticatedReturns(true)
   491  			})
   492  
   493  			Context("when authorized", func() {
   494  				BeforeEach(func() {
   495  					fakeAccess.IsAuthorizedReturns(true)
   496  				})
   497  
   498  				It("tries to find the resource", func() {
   499  					resourceName := fakePipeline.ResourceArgsForCall(0)
   500  					Expect(resourceName).To(Equal("resource-name"))
   501  				})
   502  
   503  				Context("when finding the resource succeeds", func() {
   504  					BeforeEach(func() {
   505  						fakeResource = new(dbfakes.FakeResource)
   506  						fakeResource.IDReturns(1)
   507  						fakePipeline.ResourceReturns(fakeResource, true, nil)
   508  					})
   509  
   510  					It("tries to enable the right resource config version", func() {
   511  						resourceConfigVersionID := fakeResource.EnableVersionArgsForCall(0)
   512  						Expect(resourceConfigVersionID).To(Equal(42))
   513  					})
   514  
   515  					Context("when enabling the resource succeeds", func() {
   516  						BeforeEach(func() {
   517  							fakeResource.EnableVersionReturns(nil)
   518  						})
   519  
   520  						It("returns 200", func() {
   521  							Expect(response.StatusCode).To(Equal(http.StatusOK))
   522  						})
   523  					})
   524  
   525  					Context("when enabling the resource fails", func() {
   526  						BeforeEach(func() {
   527  							fakeResource.EnableVersionReturns(errors.New("welp"))
   528  						})
   529  
   530  						It("returns 500", func() {
   531  							Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
   532  						})
   533  					})
   534  				})
   535  
   536  				Context("when it fails to find the resource", func() {
   537  					BeforeEach(func() {
   538  						fakePipeline.ResourceReturns(nil, false, errors.New("welp"))
   539  					})
   540  
   541  					It("returns Internal Server Error", func() {
   542  						Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
   543  					})
   544  				})
   545  
   546  				Context("when the resource is not found", func() {
   547  					BeforeEach(func() {
   548  						fakePipeline.ResourceReturns(nil, false, nil)
   549  					})
   550  
   551  					It("returns not found", func() {
   552  						Expect(response.StatusCode).To(Equal(http.StatusNotFound))
   553  					})
   554  				})
   555  			})
   556  
   557  			Context("when not authorized", func() {
   558  				BeforeEach(func() {
   559  					fakeAccess.IsAuthorizedReturns(false)
   560  				})
   561  				It("returns Forbidden", func() {
   562  					Expect(response.StatusCode).To(Equal(http.StatusForbidden))
   563  				})
   564  			})
   565  		})
   566  
   567  		Context("when not authenticated", func() {
   568  			BeforeEach(func() {
   569  				fakeAccess.IsAuthenticatedReturns(false)
   570  			})
   571  
   572  			It("returns Unauthorized", func() {
   573  				Expect(response.StatusCode).To(Equal(http.StatusUnauthorized))
   574  			})
   575  		})
   576  	})
   577  
   578  	Describe("PUT /api/v1/teams/:team_name/pipelines/:pipeline_name/resources/:resource_name/versions/:resource_version_id/disable", func() {
   579  		var response *http.Response
   580  		var fakeResource *dbfakes.FakeResource
   581  
   582  		JustBeforeEach(func() {
   583  			var err error
   584  
   585  			request, err := http.NewRequest("PUT", server.URL+"/api/v1/teams/a-team/pipelines/a-pipeline/resources/resource-name/versions/42/disable", nil)
   586  			Expect(err).NotTo(HaveOccurred())
   587  
   588  			response, err = client.Do(request)
   589  			Expect(err).NotTo(HaveOccurred())
   590  		})
   591  
   592  		Context("when authenticated ", func() {
   593  			BeforeEach(func() {
   594  				fakeAccess.IsAuthenticatedReturns(true)
   595  			})
   596  
   597  			Context("when authorized", func() {
   598  				BeforeEach(func() {
   599  					fakeAccess.IsAuthorizedReturns(true)
   600  				})
   601  
   602  				It("tries to find the resource", func() {
   603  					resourceName := fakePipeline.ResourceArgsForCall(0)
   604  					Expect(resourceName).To(Equal("resource-name"))
   605  				})
   606  
   607  				Context("when finding the resource succeeds", func() {
   608  					BeforeEach(func() {
   609  						fakeResource = new(dbfakes.FakeResource)
   610  						fakeResource.IDReturns(1)
   611  						fakePipeline.ResourceReturns(fakeResource, true, nil)
   612  					})
   613  
   614  					It("tries to disable the right resource config version", func() {
   615  						resourceConfigVersionID := fakeResource.DisableVersionArgsForCall(0)
   616  						Expect(resourceConfigVersionID).To(Equal(42))
   617  					})
   618  
   619  					Context("when disabling the resource version succeeds", func() {
   620  						BeforeEach(func() {
   621  							fakeResource.DisableVersionReturns(nil)
   622  						})
   623  
   624  						It("returns 200", func() {
   625  							Expect(response.StatusCode).To(Equal(http.StatusOK))
   626  						})
   627  					})
   628  
   629  					Context("when disabling the resource fails", func() {
   630  						BeforeEach(func() {
   631  							fakeResource.DisableVersionReturns(errors.New("welp"))
   632  						})
   633  
   634  						It("returns 500", func() {
   635  							Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
   636  						})
   637  					})
   638  				})
   639  
   640  				Context("when it fails to find the resource", func() {
   641  					BeforeEach(func() {
   642  						fakePipeline.ResourceReturns(nil, false, errors.New("welp"))
   643  					})
   644  
   645  					It("returns Internal Server Error", func() {
   646  						Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
   647  					})
   648  				})
   649  
   650  				Context("when the resource is not found", func() {
   651  					BeforeEach(func() {
   652  						fakePipeline.ResourceReturns(nil, false, nil)
   653  					})
   654  
   655  					It("returns not found", func() {
   656  						Expect(response.StatusCode).To(Equal(http.StatusNotFound))
   657  					})
   658  				})
   659  			})
   660  			Context("when not authorized", func() {
   661  				BeforeEach(func() {
   662  					fakeAccess.IsAuthorizedReturns(false)
   663  				})
   664  
   665  				It("returns Forbidden", func() {
   666  					Expect(response.StatusCode).To(Equal(http.StatusForbidden))
   667  				})
   668  			})
   669  		})
   670  		Context("when not authenticated", func() {
   671  			BeforeEach(func() {
   672  				fakeAccess.IsAuthenticatedReturns(false)
   673  			})
   674  
   675  			It("returns Unauthorized", func() {
   676  				Expect(response.StatusCode).To(Equal(http.StatusUnauthorized))
   677  			})
   678  		})
   679  	})
   680  
   681  	Describe("PUT /api/v1/teams/:team_name/pipelines/:pipeline_name/resources/:resource_name/versions/:resource_version_id/pin", func() {
   682  		var response *http.Response
   683  		var fakeResource *dbfakes.FakeResource
   684  
   685  		JustBeforeEach(func() {
   686  			var err error
   687  
   688  			request, err := http.NewRequest("PUT", server.URL+"/api/v1/teams/a-team/pipelines/a-pipeline/resources/resource-name/versions/42/pin", nil)
   689  			Expect(err).NotTo(HaveOccurred())
   690  
   691  			response, err = client.Do(request)
   692  			Expect(err).NotTo(HaveOccurred())
   693  		})
   694  
   695  		Context("when authenticated", func() {
   696  			BeforeEach(func() {
   697  				fakeAccess.IsAuthenticatedReturns(true)
   698  			})
   699  
   700  			Context("when authorized", func() {
   701  				BeforeEach(func() {
   702  					fakeAccess.IsAuthorizedReturns(true)
   703  				})
   704  
   705  				It("tries to find the resource", func() {
   706  					resourceName := fakePipeline.ResourceArgsForCall(0)
   707  					Expect(resourceName).To(Equal("resource-name"))
   708  				})
   709  
   710  				Context("when finding the resource succeeds", func() {
   711  					BeforeEach(func() {
   712  						fakeResource = new(dbfakes.FakeResource)
   713  						fakeResource.IDReturns(1)
   714  						fakePipeline.ResourceReturns(fakeResource, true, nil)
   715  					})
   716  
   717  					It("tries to pin the right resource config version", func() {
   718  						resourceConfigVersionID := fakeResource.PinVersionArgsForCall(0)
   719  						Expect(resourceConfigVersionID).To(Equal(42))
   720  					})
   721  
   722  					Context("when pinning the resource succeeds", func() {
   723  						BeforeEach(func() {
   724  							fakeResource.PinVersionReturns(true, nil)
   725  						})
   726  
   727  						It("returns 200", func() {
   728  							Expect(response.StatusCode).To(Equal(http.StatusOK))
   729  						})
   730  					})
   731  
   732  					Context("when pinning the resource fails by resource not exist", func() {
   733  						BeforeEach(func() {
   734  							fakeResource.PinVersionReturns(false, nil)
   735  						})
   736  
   737  						It("returns 404", func() {
   738  							Expect(response.StatusCode).To(Equal(http.StatusNotFound))
   739  						})
   740  					})
   741  
   742  					Context("when pinning the resource fails by error", func() {
   743  						BeforeEach(func() {
   744  							fakeResource.PinVersionReturns(false, errors.New("welp"))
   745  						})
   746  
   747  						It("returns 500", func() {
   748  							Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
   749  						})
   750  					})
   751  				})
   752  
   753  				Context("when it fails to find the resource", func() {
   754  					BeforeEach(func() {
   755  						fakePipeline.ResourceReturns(nil, false, errors.New("welp"))
   756  					})
   757  
   758  					It("returns Internal Server Error", func() {
   759  						Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
   760  					})
   761  				})
   762  
   763  				Context("when the resource is not found", func() {
   764  					BeforeEach(func() {
   765  						fakePipeline.ResourceReturns(nil, false, nil)
   766  					})
   767  
   768  					It("returns not found", func() {
   769  						Expect(response.StatusCode).To(Equal(http.StatusNotFound))
   770  					})
   771  				})
   772  			})
   773  
   774  			Context("when not authorized", func() {
   775  				BeforeEach(func() {
   776  					fakeAccess.IsAuthorizedReturns(false)
   777  				})
   778  				It("returns Forbidden", func() {
   779  					Expect(response.StatusCode).To(Equal(http.StatusForbidden))
   780  				})
   781  			})
   782  		})
   783  
   784  		Context("when not authenticated", func() {
   785  			BeforeEach(func() {
   786  				fakeAccess.IsAuthenticatedReturns(false)
   787  			})
   788  
   789  			It("returns Unauthorized", func() {
   790  				Expect(response.StatusCode).To(Equal(http.StatusUnauthorized))
   791  			})
   792  		})
   793  	})
   794  
   795  	Describe("GET /api/v1/teams/:team_name/pipelines/:pipeline_name/resources/:resource_name/versions/:resource_version_id/input_to", func() {
   796  		var response *http.Response
   797  		var stringVersionID string
   798  		var fakeResource *dbfakes.FakeResource
   799  
   800  		JustBeforeEach(func() {
   801  			var err error
   802  
   803  			request, err := http.NewRequest("GET", server.URL+"/api/v1/teams/a-team/pipelines/a-pipeline/resources/some-resource/versions/"+stringVersionID+"/input_to", nil)
   804  			Expect(err).NotTo(HaveOccurred())
   805  
   806  			response, err = client.Do(request)
   807  			Expect(err).NotTo(HaveOccurred())
   808  		})
   809  
   810  		BeforeEach(func() {
   811  			fakeResource = new(dbfakes.FakeResource)
   812  			fakeResource.IDReturns(1)
   813  			stringVersionID = "123"
   814  		})
   815  
   816  		Context("when not authorized", func() {
   817  			BeforeEach(func() {
   818  				fakeAccess.IsAuthorizedReturns(false)
   819  			})
   820  
   821  			Context("and the pipeline is private", func() {
   822  				BeforeEach(func() {
   823  					fakePipeline.PublicReturns(false)
   824  				})
   825  
   826  				Context("when authenticated", func() {
   827  					BeforeEach(func() {
   828  						fakeAccess.IsAuthenticatedReturns(true)
   829  					})
   830  
   831  					It("returns 403", func() {
   832  						Expect(response.StatusCode).To(Equal(http.StatusForbidden))
   833  					})
   834  				})
   835  
   836  				Context("when not authenticated", func() {
   837  					BeforeEach(func() {
   838  						fakeAccess.IsAuthenticatedReturns(false)
   839  					})
   840  
   841  					It("returns 401", func() {
   842  						Expect(response.StatusCode).To(Equal(http.StatusUnauthorized))
   843  					})
   844  				})
   845  			})
   846  
   847  			Context("and the pipeline is public", func() {
   848  				BeforeEach(func() {
   849  					fakePipeline.PublicReturns(true)
   850  					fakePipeline.ResourceReturns(fakeResource, true, nil)
   851  				})
   852  
   853  				It("returns 200 OK", func() {
   854  					Expect(response.StatusCode).To(Equal(http.StatusOK))
   855  				})
   856  			})
   857  		})
   858  
   859  		Context("when authorized", func() {
   860  			BeforeEach(func() {
   861  				fakeAccess.IsAuthenticatedReturns(true)
   862  				fakeAccess.IsAuthorizedReturns(true)
   863  			})
   864  
   865  			Context("when not finding the resource", func() {
   866  				BeforeEach(func() {
   867  					fakePipeline.ResourceReturns(nil, false, nil)
   868  				})
   869  
   870  				It("returns 404", func() {
   871  					Expect(response.StatusCode).To(Equal(http.StatusNotFound))
   872  				})
   873  			})
   874  
   875  			Context("when failing to retrieve the resource", func() {
   876  				BeforeEach(func() {
   877  					fakePipeline.ResourceReturns(nil, false, errors.New("banana"))
   878  				})
   879  
   880  				It("returns 500", func() {
   881  					Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
   882  				})
   883  			})
   884  
   885  			It("looks for the resource", func() {
   886  				Expect(fakePipeline.ResourceCallCount()).To(Equal(1))
   887  				Expect(fakePipeline.ResourceArgsForCall(0)).To(Equal("some-resource"))
   888  			})
   889  
   890  			Context("when resource retrieval succeeds", func() {
   891  				BeforeEach(func() {
   892  					fakePipeline.ResourceReturns(fakeResource, true, nil)
   893  				})
   894  
   895  				It("looks up the given version ID", func() {
   896  					Expect(fakePipeline.GetBuildsWithVersionAsInputCallCount()).To(Equal(1))
   897  					resourceID, versionID := fakePipeline.GetBuildsWithVersionAsInputArgsForCall(0)
   898  					Expect(resourceID).To(Equal(1))
   899  					Expect(versionID).To(Equal(123))
   900  				})
   901  
   902  				Context("when getting the builds succeeds", func() {
   903  					BeforeEach(func() {
   904  						build1 := new(dbfakes.FakeBuild)
   905  						build1.IDReturns(1024)
   906  						build1.NameReturns("5")
   907  						build1.JobNameReturns("some-job")
   908  						build1.PipelineNameReturns("a-pipeline")
   909  						build1.TeamNameReturns("a-team")
   910  						build1.StatusReturns(db.BuildStatusSucceeded)
   911  						build1.StartTimeReturns(time.Unix(1, 0))
   912  						build1.EndTimeReturns(time.Unix(100, 0))
   913  
   914  						build2 := new(dbfakes.FakeBuild)
   915  						build2.IDReturns(1025)
   916  						build2.NameReturns("6")
   917  						build2.JobNameReturns("some-job")
   918  						build2.PipelineNameReturns("a-pipeline")
   919  						build2.TeamNameReturns("a-team")
   920  						build2.StatusReturns(db.BuildStatusSucceeded)
   921  						build2.StartTimeReturns(time.Unix(200, 0))
   922  						build2.EndTimeReturns(time.Unix(300, 0))
   923  
   924  						fakePipeline.GetBuildsWithVersionAsInputReturns([]db.Build{build1, build2}, nil)
   925  					})
   926  
   927  					It("returns 200 OK", func() {
   928  						Expect(response.StatusCode).To(Equal(http.StatusOK))
   929  					})
   930  
   931  					It("returns content type application/json", func() {
   932  						expectedHeaderEntries := map[string]string{
   933  							"Content-Type": "application/json",
   934  						}
   935  						Expect(response).Should(IncludeHeaderEntries(expectedHeaderEntries))
   936  					})
   937  
   938  					It("returns the json", func() {
   939  						body, err := ioutil.ReadAll(response.Body)
   940  						Expect(err).NotTo(HaveOccurred())
   941  
   942  						Expect(body).To(MatchJSON(`[
   943  					{
   944  						"id": 1024,
   945  						"team_name": "a-team",
   946  						"name": "5",
   947  						"status": "succeeded",
   948  						"job_name": "some-job",
   949  						"api_url": "/api/v1/builds/1024",
   950  						"pipeline_name": "a-pipeline",
   951  						"start_time": 1,
   952  						"end_time": 100
   953  					},
   954  					{
   955  						"id": 1025,
   956  						"name": "6",
   957  						"team_name": "a-team",
   958  						"status": "succeeded",
   959  						"job_name": "some-job",
   960  						"api_url": "/api/v1/builds/1025",
   961  						"pipeline_name": "a-pipeline",
   962  						"start_time": 200,
   963  						"end_time": 300
   964  					}
   965  				]`))
   966  					})
   967  				})
   968  
   969  				Context("when the version ID is invalid", func() {
   970  					BeforeEach(func() {
   971  						stringVersionID = "hello"
   972  					})
   973  
   974  					It("returns an empty list", func() {
   975  						body, err := ioutil.ReadAll(response.Body)
   976  						Expect(err).NotTo(HaveOccurred())
   977  
   978  						Expect(body).To(MatchJSON(`[]`))
   979  					})
   980  				})
   981  
   982  				Context("when the call to get builds returns an error", func() {
   983  					BeforeEach(func() {
   984  						fakePipeline.GetBuildsWithVersionAsInputReturns(nil, errors.New("NOPE"))
   985  					})
   986  
   987  					It("returns a 500 internal server error", func() {
   988  						Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
   989  					})
   990  				})
   991  			})
   992  		})
   993  	})
   994  
   995  	Describe("GET /api/v1/teams/:team_name/pipelines/:pipeline_name/resources/:resource_name/versions/:resource_version_id/output_of", func() {
   996  		var response *http.Response
   997  		var stringVersionID string
   998  		var fakeResource *dbfakes.FakeResource
   999  
  1000  		JustBeforeEach(func() {
  1001  			var err error
  1002  
  1003  			request, err := http.NewRequest("GET", server.URL+"/api/v1/teams/a-team/pipelines/a-pipeline/resources/some-resource/versions/"+stringVersionID+"/output_of", nil)
  1004  			Expect(err).NotTo(HaveOccurred())
  1005  
  1006  			response, err = client.Do(request)
  1007  			Expect(err).NotTo(HaveOccurred())
  1008  		})
  1009  
  1010  		BeforeEach(func() {
  1011  			stringVersionID = "123"
  1012  			fakeResource = new(dbfakes.FakeResource)
  1013  			fakeResource.IDReturns(1)
  1014  		})
  1015  
  1016  		Context("when not authorized", func() {
  1017  			BeforeEach(func() {
  1018  				fakeAccess.IsAuthorizedReturns(false)
  1019  			})
  1020  
  1021  			Context("and the pipeline is private", func() {
  1022  				BeforeEach(func() {
  1023  					fakePipeline.PublicReturns(false)
  1024  				})
  1025  
  1026  				Context("when authenticated", func() {
  1027  					BeforeEach(func() {
  1028  						fakeAccess.IsAuthenticatedReturns(true)
  1029  					})
  1030  
  1031  					It("returns 403", func() {
  1032  						Expect(response.StatusCode).To(Equal(http.StatusForbidden))
  1033  					})
  1034  				})
  1035  
  1036  				Context("when not authenticated", func() {
  1037  					BeforeEach(func() {
  1038  						fakeAccess.IsAuthenticatedReturns(false)
  1039  					})
  1040  
  1041  					It("returns 401", func() {
  1042  						Expect(response.StatusCode).To(Equal(http.StatusUnauthorized))
  1043  					})
  1044  				})
  1045  			})
  1046  
  1047  			Context("and the pipeline is public", func() {
  1048  				BeforeEach(func() {
  1049  					fakePipeline.PublicReturns(true)
  1050  					fakePipeline.ResourceReturns(fakeResource, true, nil)
  1051  				})
  1052  
  1053  				It("returns 200 OK", func() {
  1054  					Expect(response.StatusCode).To(Equal(http.StatusOK))
  1055  				})
  1056  			})
  1057  		})
  1058  
  1059  		Context("when authorized", func() {
  1060  			BeforeEach(func() {
  1061  				fakeAccess.IsAuthenticatedReturns(true)
  1062  				fakeAccess.IsAuthorizedReturns(true)
  1063  			})
  1064  
  1065  			Context("when not finding the resource", func() {
  1066  				BeforeEach(func() {
  1067  					fakePipeline.ResourceReturns(nil, false, nil)
  1068  				})
  1069  
  1070  				It("returns 404", func() {
  1071  					Expect(response.StatusCode).To(Equal(http.StatusNotFound))
  1072  				})
  1073  			})
  1074  
  1075  			Context("when failing to retrieve the resource", func() {
  1076  				BeforeEach(func() {
  1077  					fakePipeline.ResourceReturns(nil, false, errors.New("banana"))
  1078  				})
  1079  
  1080  				It("returns 500", func() {
  1081  					Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
  1082  				})
  1083  			})
  1084  
  1085  			It("looks for the resource", func() {
  1086  				Expect(fakePipeline.ResourceCallCount()).To(Equal(1))
  1087  				Expect(fakePipeline.ResourceArgsForCall(0)).To(Equal("some-resource"))
  1088  			})
  1089  
  1090  			Context("when resource retrieval succeeds", func() {
  1091  				BeforeEach(func() {
  1092  					fakePipeline.ResourceReturns(fakeResource, true, nil)
  1093  				})
  1094  
  1095  				It("looks up the given version ID", func() {
  1096  					Expect(fakePipeline.GetBuildsWithVersionAsOutputCallCount()).To(Equal(1))
  1097  					resourceID, versionID := fakePipeline.GetBuildsWithVersionAsOutputArgsForCall(0)
  1098  					Expect(resourceID).To(Equal(1))
  1099  					Expect(versionID).To(Equal(123))
  1100  				})
  1101  
  1102  				Context("when getting the builds succeeds", func() {
  1103  					BeforeEach(func() {
  1104  						build1 := new(dbfakes.FakeBuild)
  1105  						build1.IDReturns(1024)
  1106  						build1.NameReturns("5")
  1107  						build1.JobNameReturns("some-job")
  1108  						build1.PipelineNameReturns("a-pipeline")
  1109  						build1.TeamNameReturns("a-team")
  1110  						build1.StatusReturns(db.BuildStatusSucceeded)
  1111  						build1.StartTimeReturns(time.Unix(1, 0))
  1112  						build1.EndTimeReturns(time.Unix(100, 0))
  1113  
  1114  						build2 := new(dbfakes.FakeBuild)
  1115  						build2.IDReturns(1025)
  1116  						build2.NameReturns("6")
  1117  						build2.JobNameReturns("some-job")
  1118  						build2.PipelineNameReturns("a-pipeline")
  1119  						build2.TeamNameReturns("a-team")
  1120  						build2.StatusReturns(db.BuildStatusSucceeded)
  1121  						build2.StartTimeReturns(time.Unix(200, 0))
  1122  						build2.EndTimeReturns(time.Unix(300, 0))
  1123  
  1124  						fakePipeline.GetBuildsWithVersionAsOutputReturns([]db.Build{build1, build2}, nil)
  1125  					})
  1126  
  1127  					It("returns 200 OK", func() {
  1128  						Expect(response.StatusCode).To(Equal(http.StatusOK))
  1129  					})
  1130  
  1131  					It("returns content type application/json", func() {
  1132  						expectedHeaderEntries := map[string]string{
  1133  							"Content-Type": "application/json",
  1134  						}
  1135  						Expect(response).Should(IncludeHeaderEntries(expectedHeaderEntries))
  1136  					})
  1137  
  1138  					It("returns the json", func() {
  1139  						body, err := ioutil.ReadAll(response.Body)
  1140  						Expect(err).NotTo(HaveOccurred())
  1141  
  1142  						Expect(body).To(MatchJSON(`[
  1143  					{
  1144  						"id": 1024,
  1145  						"name": "5",
  1146  						"status": "succeeded",
  1147  						"job_name": "some-job",
  1148  						"api_url": "/api/v1/builds/1024",
  1149  						"pipeline_name": "a-pipeline",
  1150  						"team_name": "a-team",
  1151  						"start_time": 1,
  1152  						"end_time": 100
  1153  					},
  1154  					{
  1155  						"id": 1025,
  1156  						"name": "6",
  1157  						"status": "succeeded",
  1158  						"job_name": "some-job",
  1159  						"api_url": "/api/v1/builds/1025",
  1160  						"pipeline_name": "a-pipeline",
  1161  						"team_name": "a-team",
  1162  						"start_time": 200,
  1163  						"end_time": 300
  1164  					}
  1165  				]`))
  1166  					})
  1167  				})
  1168  
  1169  				Context("when the version ID is invalid", func() {
  1170  					BeforeEach(func() {
  1171  						stringVersionID = "hello"
  1172  					})
  1173  
  1174  					It("returns an empty list", func() {
  1175  						body, err := ioutil.ReadAll(response.Body)
  1176  						Expect(err).NotTo(HaveOccurred())
  1177  
  1178  						Expect(body).To(MatchJSON(`[]`))
  1179  					})
  1180  				})
  1181  
  1182  				Context("when the call to get builds returns an error", func() {
  1183  					BeforeEach(func() {
  1184  						fakePipeline.GetBuildsWithVersionAsOutputReturns(nil, errors.New("NOPE"))
  1185  					})
  1186  
  1187  					It("returns a 500 internal server error", func() {
  1188  						Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
  1189  					})
  1190  				})
  1191  			})
  1192  		})
  1193  	})
  1194  })