github.com/pf-qiu/concourse/v6@v6.7.3-0.20201207032516-1f455d73275f/atc/api/resources_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/onsi/ginkgo"
    13  	. "github.com/onsi/gomega"
    14  
    15  	"github.com/pf-qiu/concourse/v6/atc"
    16  	"github.com/pf-qiu/concourse/v6/atc/creds"
    17  	"github.com/pf-qiu/concourse/v6/atc/db"
    18  	"github.com/pf-qiu/concourse/v6/atc/db/dbfakes"
    19  	. "github.com/pf-qiu/concourse/v6/atc/testhelpers"
    20  	"github.com/pf-qiu/concourse/v6/vars"
    21  )
    22  
    23  var _ = Describe("Resources API", func() {
    24  	var (
    25  		fakePipeline *dbfakes.FakePipeline
    26  		resource1    *dbfakes.FakeResource
    27  		variables    vars.Variables
    28  	)
    29  
    30  	BeforeEach(func() {
    31  		fakePipeline = new(dbfakes.FakePipeline)
    32  		dbTeamFactory.FindTeamReturns(dbTeam, true, nil)
    33  		dbTeam.PipelineReturns(fakePipeline, true, nil)
    34  	})
    35  
    36  	Describe("GET /api/v1/resources", func() {
    37  		var response *http.Response
    38  
    39  		JustBeforeEach(func() {
    40  			var err error
    41  
    42  			response, err = client.Get(server.URL + "/api/v1/resources")
    43  			Expect(err).NotTo(HaveOccurred())
    44  		})
    45  
    46  		Context("when getting the dashboard resources succeeds", func() {
    47  			BeforeEach(func() {
    48  				resource1 = new(dbfakes.FakeResource)
    49  				resource1.IDReturns(1)
    50  				resource1.PipelineIDReturns(1)
    51  				resource1.PipelineNameReturns("a-pipeline")
    52  				resource1.TeamNameReturns("some-team")
    53  				resource1.NameReturns("resource-1")
    54  				resource1.TypeReturns("type-1")
    55  				resource1.LastCheckEndTimeReturns(time.Unix(1513364881, 0))
    56  
    57  				resource2 := new(dbfakes.FakeResource)
    58  				resource2.IDReturns(2)
    59  				resource2.PipelineIDReturns(1)
    60  				resource2.PipelineNameReturns("a-pipeline")
    61  				resource2.TeamNameReturns("other-team")
    62  				resource2.NameReturns("resource-2")
    63  				resource2.TypeReturns("type-2")
    64  				resource2.BuildSummaryReturns(&atc.BuildSummary{
    65  					ID:                   123,
    66  					Name:                 "123",
    67  					Status:               atc.StatusSucceeded,
    68  					StartTime:            456,
    69  					EndTime:              789,
    70  					TeamName:             "some-team",
    71  					PipelineID:           99,
    72  					PipelineName:         "some-pipeline",
    73  					PipelineInstanceVars: atc.InstanceVars{"foo": 1},
    74  				})
    75  
    76  				resource3 := new(dbfakes.FakeResource)
    77  				resource3.IDReturns(3)
    78  				resource3.TeamNameReturns("some-team")
    79  				resource3.PipelineIDReturns(2)
    80  				resource3.PipelineNameReturns("some-pipeline")
    81  				resource3.PipelineInstanceVarsReturns(atc.InstanceVars{"branch": "master"})
    82  				resource3.TeamNameReturns("another-team")
    83  				resource3.NameReturns("resource-3")
    84  				resource3.TypeReturns("type-3")
    85  
    86  				dbResourceFactory.VisibleResourcesReturns([]db.Resource{
    87  					resource1, resource2, resource3,
    88  				}, nil)
    89  			})
    90  
    91  			It("returns 200 OK", func() {
    92  				Expect(response.StatusCode).To(Equal(http.StatusOK))
    93  			})
    94  
    95  			It("returns Content-Type 'application/json'", func() {
    96  				expectedHeaderEntries := map[string]string{
    97  					"Content-Type": "application/json",
    98  				}
    99  				Expect(response).Should(IncludeHeaderEntries(expectedHeaderEntries))
   100  			})
   101  
   102  			It("returns each resource, including their build", func() {
   103  				body, err := ioutil.ReadAll(response.Body)
   104  				Expect(err).NotTo(HaveOccurred())
   105  
   106  				Expect(body).To(MatchJSON(`[
   107  						{
   108  							"name": "resource-1",
   109  							"pipeline_id": 1,
   110  							"pipeline_name": "a-pipeline",
   111  							"team_name": "some-team",
   112  							"type": "type-1",
   113  							"last_checked": 1513364881
   114  						},
   115  						{
   116  							"name": "resource-2",
   117  							"pipeline_id": 1,
   118  							"pipeline_name": "a-pipeline",
   119  							"team_name": "other-team",
   120  							"type": "type-2",
   121  							"build": {
   122                  "id": 123,
   123  								"name": "123",
   124                  "status": "succeeded",
   125                  "start_time": 456,
   126                  "end_time": 789,
   127                  "team_name": "some-team",
   128                  "pipeline_id": 99,
   129                  "pipeline_name": "some-pipeline",
   130                  "pipeline_instance_vars": {
   131                    "foo": 1
   132                  }
   133  							}
   134  						},
   135  						{
   136  							"name": "resource-3",
   137  							"pipeline_id": 2,
   138  							"pipeline_name": "some-pipeline",
   139  							"pipeline_instance_vars": {
   140  								"branch": "master"
   141  							},
   142  							"team_name": "another-team",
   143  							"type": "type-3"
   144  						}
   145  					]`))
   146  			})
   147  
   148  			Context("when getting the resource config fails", func() {
   149  				BeforeEach(func() {
   150  					dbResourceFactory.VisibleResourcesReturns(nil, errors.New("nope"))
   151  				})
   152  
   153  				It("returns 500", func() {
   154  					Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
   155  				})
   156  			})
   157  
   158  			Context("when there are no visible resources", func() {
   159  				BeforeEach(func() {
   160  					dbResourceFactory.VisibleResourcesReturns(nil, nil)
   161  				})
   162  
   163  				It("returns empty array", func() {
   164  					body, err := ioutil.ReadAll(response.Body)
   165  					Expect(err).NotTo(HaveOccurred())
   166  
   167  					Expect(body).To(MatchJSON(`[]`))
   168  				})
   169  			})
   170  
   171  			Context("when not authenticated", func() {
   172  				It("populates resource factory with no team names", func() {
   173  					Expect(dbResourceFactory.VisibleResourcesCallCount()).To(Equal(1))
   174  					Expect(dbResourceFactory.VisibleResourcesArgsForCall(0)).To(BeEmpty())
   175  				})
   176  			})
   177  
   178  			Context("when authenticated", func() {
   179  				BeforeEach(func() {
   180  					fakeAccess.TeamNamesReturns([]string{"some-team"})
   181  				})
   182  
   183  				It("constructs job factory with provided team names", func() {
   184  					Expect(dbResourceFactory.VisibleResourcesCallCount()).To(Equal(1))
   185  					Expect(dbResourceFactory.VisibleResourcesArgsForCall(0)).To(ContainElement("some-team"))
   186  				})
   187  
   188  				Context("when user has admin privilege", func() {
   189  					BeforeEach(func() {
   190  						fakeAccess.IsAdminReturns(true)
   191  					})
   192  
   193  					It("returns all resources", func() {
   194  						Expect(dbResourceFactory.AllResourcesCallCount()).To(Equal(1))
   195  					})
   196  				})
   197  			})
   198  		})
   199  	})
   200  
   201  	Describe("GET /api/v1/teams/:team_name/pipelines/:pipeline_name/resources", func() {
   202  		var response *http.Response
   203  
   204  		JustBeforeEach(func() {
   205  			var err error
   206  
   207  			response, err = client.Get(server.URL + "/api/v1/teams/a-team/pipelines/a-pipeline/resources")
   208  			Expect(err).NotTo(HaveOccurred())
   209  		})
   210  
   211  		Context("when getting the dashboard resources succeeds", func() {
   212  			BeforeEach(func() {
   213  				resource1 = new(dbfakes.FakeResource)
   214  				resource1.IDReturns(1)
   215  				resource1.TeamNameReturns("a-team")
   216  				resource1.PipelineIDReturns(1)
   217  				resource1.PipelineNameReturns("a-pipeline")
   218  				resource1.NameReturns("resource-1")
   219  				resource1.TypeReturns("type-1")
   220  				resource1.LastCheckEndTimeReturns(time.Unix(1513364881, 0))
   221  
   222  				resource2 := new(dbfakes.FakeResource)
   223  				resource2.IDReturns(2)
   224  				resource2.TeamNameReturns("a-team")
   225  				resource2.PipelineIDReturns(1)
   226  				resource2.PipelineNameReturns("a-pipeline")
   227  				resource2.NameReturns("resource-2")
   228  				resource2.TypeReturns("type-2")
   229  
   230  				resource3 := new(dbfakes.FakeResource)
   231  				resource3.IDReturns(3)
   232  				resource3.TeamNameReturns("a-team")
   233  				resource3.PipelineIDReturns(2)
   234  				resource3.PipelineNameReturns("some-pipeline")
   235  				resource3.PipelineInstanceVarsReturns(atc.InstanceVars{"branch": "master"})
   236  				resource3.NameReturns("resource-3")
   237  				resource3.TypeReturns("type-3")
   238  
   239  				fakePipeline.ResourcesReturns([]db.Resource{
   240  					resource1, resource2, resource3,
   241  				}, nil)
   242  			})
   243  
   244  			Context("when not authenticated and not authorized", func() {
   245  				BeforeEach(func() {
   246  					fakeAccess.IsAuthenticatedReturns(false)
   247  					fakeAccess.IsAuthorizedReturns(false)
   248  				})
   249  
   250  				Context("and the pipeline is private", func() {
   251  					BeforeEach(func() {
   252  						fakePipeline.PublicReturns(false)
   253  					})
   254  
   255  					It("returns 401", func() {
   256  						Expect(response.StatusCode).To(Equal(http.StatusUnauthorized))
   257  					})
   258  				})
   259  
   260  				Context("and the pipeline is public", func() {
   261  					BeforeEach(func() {
   262  						fakePipeline.PublicReturns(true)
   263  					})
   264  
   265  					It("returns 200 OK", func() {
   266  						Expect(response.StatusCode).To(Equal(http.StatusOK))
   267  					})
   268  
   269  					It("returns Content-Type 'application/json'", func() {
   270  						expectedHeaderEntries := map[string]string{
   271  							"Content-Type": "application/json",
   272  						}
   273  						Expect(response).Should(IncludeHeaderEntries(expectedHeaderEntries))
   274  					})
   275  
   276  					It("returns each resource", func() {
   277  						body, err := ioutil.ReadAll(response.Body)
   278  						Expect(err).NotTo(HaveOccurred())
   279  
   280  						Expect(body).To(MatchJSON(`[
   281  					{
   282  						"name": "resource-1",
   283  						"pipeline_id": 1,
   284  						"pipeline_name": "a-pipeline",
   285  						"team_name": "a-team",
   286  						"type": "type-1",
   287  						"last_checked": 1513364881
   288  					},
   289  					{
   290  						"name": "resource-2",
   291  						"pipeline_id": 1,
   292  						"pipeline_name": "a-pipeline",
   293  						"team_name": "a-team",
   294  						"type": "type-2"
   295  					},
   296  					{
   297  						"name": "resource-3",
   298  						"pipeline_id": 2,
   299  						"pipeline_name": "some-pipeline",
   300  						"pipeline_instance_vars": {
   301  							"branch": "master"
   302  						},
   303  						"team_name": "a-team",
   304  						"type": "type-3"
   305  					}
   306  				]`))
   307  					})
   308  				})
   309  			})
   310  
   311  			Context("when authorized", func() {
   312  				BeforeEach(func() {
   313  					fakeAccess.IsAuthenticatedReturns(true)
   314  					fakeAccess.IsAuthorizedReturns(true)
   315  				})
   316  
   317  				It("returns 200 OK", func() {
   318  					Expect(response.StatusCode).To(Equal(http.StatusOK))
   319  				})
   320  
   321  				It("returns Content-Type 'application/json'", func() {
   322  					expectedHeaderEntries := map[string]string{
   323  						"Content-Type": "application/json",
   324  					}
   325  					Expect(response).Should(IncludeHeaderEntries(expectedHeaderEntries))
   326  				})
   327  
   328  				It("returns each resource", func() {
   329  					body, err := ioutil.ReadAll(response.Body)
   330  					Expect(err).NotTo(HaveOccurred())
   331  
   332  					Expect(body).To(MatchJSON(`[
   333  						{
   334  							"name": "resource-1",
   335  							"pipeline_id": 1,
   336  							"pipeline_name": "a-pipeline",
   337  							"team_name": "a-team",
   338  							"type": "type-1",
   339  							"last_checked": 1513364881
   340  						},
   341  						{
   342  							"name": "resource-2",
   343  							"pipeline_id": 1,
   344  							"pipeline_name": "a-pipeline",
   345  							"team_name": "a-team",
   346  							"type": "type-2"
   347  						},
   348  						{
   349  							"name": "resource-3",
   350  							"pipeline_id": 2,
   351  							"pipeline_name": "some-pipeline",
   352  							"pipeline_instance_vars": {
   353  								"branch": "master"
   354  							},
   355  							"team_name": "a-team",
   356  							"type": "type-3"
   357  						}
   358  					]`))
   359  				})
   360  
   361  				Context("when the pipeline has no resources", func() {
   362  					BeforeEach(func() {
   363  						fakePipeline.ResourcesReturns(nil, nil)
   364  					})
   365  
   366  					It("returns an empty list", func() {
   367  						body, err := ioutil.ReadAll(response.Body)
   368  						Expect(err).NotTo(HaveOccurred())
   369  
   370  						Expect(body).To(MatchJSON(`[]`))
   371  					})
   372  				})
   373  
   374  				Context("when getting the resource config fails", func() {
   375  					Context("when the resources are not found", func() {
   376  						BeforeEach(func() {
   377  							fakePipeline.ResourcesReturns(nil, nil)
   378  						})
   379  
   380  						It("returns 200", func() {
   381  							Expect(response.StatusCode).To(Equal(http.StatusOK))
   382  						})
   383  					})
   384  
   385  					Context("with an unknown error", func() {
   386  						BeforeEach(func() {
   387  							fakePipeline.ResourcesReturns(nil, errors.New("oh no!"))
   388  						})
   389  
   390  						It("returns 500", func() {
   391  							Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
   392  						})
   393  					})
   394  				})
   395  			})
   396  		})
   397  	})
   398  
   399  	Describe("PUT /api/v1/teams/:team_name/pipelines/:pipeline_name/resources/:resource_name/unpin", func() {
   400  		var response *http.Response
   401  		var fakeResource *dbfakes.FakeResource
   402  
   403  		JustBeforeEach(func() {
   404  			var err error
   405  
   406  			request, err := http.NewRequest("PUT", server.URL+"/api/v1/teams/a-team/pipelines/a-pipeline/resources/resource-name/unpin", nil)
   407  			Expect(err).NotTo(HaveOccurred())
   408  
   409  			response, err = client.Do(request)
   410  			Expect(err).NotTo(HaveOccurred())
   411  		})
   412  
   413  		Context("when authenticated ", func() {
   414  			BeforeEach(func() {
   415  				fakeAccess.IsAuthenticatedReturns(true)
   416  			})
   417  
   418  			Context("when authorized", func() {
   419  				BeforeEach(func() {
   420  					fakeAccess.IsAuthorizedReturns(true)
   421  				})
   422  
   423  				It("tries to find the resource", func() {
   424  					resourceName := fakePipeline.ResourceArgsForCall(0)
   425  					Expect(resourceName).To(Equal("resource-name"))
   426  				})
   427  
   428  				Context("when finding the resource succeeds", func() {
   429  					BeforeEach(func() {
   430  						fakeResource = new(dbfakes.FakeResource)
   431  						fakeResource.IDReturns(1)
   432  						fakePipeline.ResourceReturns(fakeResource, true, nil)
   433  					})
   434  
   435  					Context("when unpinning the resource version succeeds", func() {
   436  						BeforeEach(func() {
   437  							fakeResource.UnpinVersionReturns(nil)
   438  						})
   439  
   440  						It("returns 200", func() {
   441  							Expect(response.StatusCode).To(Equal(http.StatusOK))
   442  						})
   443  					})
   444  
   445  					Context("when unpinning the resource fails", func() {
   446  						BeforeEach(func() {
   447  							fakeResource.UnpinVersionReturns(errors.New("welp"))
   448  						})
   449  
   450  						It("returns 500", func() {
   451  							Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
   452  						})
   453  					})
   454  				})
   455  
   456  				Context("when it fails to find the resource", func() {
   457  					BeforeEach(func() {
   458  						fakePipeline.ResourceReturns(nil, false, errors.New("welp"))
   459  					})
   460  
   461  					It("returns Internal Server Error", func() {
   462  						Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
   463  					})
   464  				})
   465  
   466  				Context("when the resource is not found", func() {
   467  					BeforeEach(func() {
   468  						fakePipeline.ResourceReturns(nil, false, nil)
   469  					})
   470  
   471  					It("returns not found", func() {
   472  						Expect(response.StatusCode).To(Equal(http.StatusNotFound))
   473  					})
   474  				})
   475  			})
   476  			Context("when not authorized", func() {
   477  				BeforeEach(func() {
   478  					fakeAccess.IsAuthorizedReturns(false)
   479  				})
   480  
   481  				It("returns Forbidden", func() {
   482  					Expect(response.StatusCode).To(Equal(http.StatusForbidden))
   483  				})
   484  			})
   485  		})
   486  		Context("when not authenticated", func() {
   487  			BeforeEach(func() {
   488  				fakeAccess.IsAuthenticatedReturns(false)
   489  			})
   490  
   491  			It("returns Unauthorized", func() {
   492  				Expect(response.StatusCode).To(Equal(http.StatusUnauthorized))
   493  			})
   494  		})
   495  	})
   496  
   497  	Describe("PUT /api/v1/teams/:team_name/pipelines/:pipeline_name/resources/:resource_name/pin_comment", func() {
   498  		var response *http.Response
   499  		var pinCommentRequestBody atc.SetPinCommentRequestBody
   500  		var fakeResource *dbfakes.FakeResource
   501  
   502  		BeforeEach(func() {
   503  			pinCommentRequestBody = atc.SetPinCommentRequestBody{}
   504  		})
   505  
   506  		JustBeforeEach(func() {
   507  			reqPayload, err := json.Marshal(pinCommentRequestBody)
   508  			Expect(err).NotTo(HaveOccurred())
   509  
   510  			request, err := http.NewRequest("PUT", server.URL+"/api/v1/teams/a-team/pipelines/a-pipeline/resources/resource-name/pin_comment", bytes.NewBuffer(reqPayload))
   511  			Expect(err).NotTo(HaveOccurred())
   512  
   513  			response, err = client.Do(request)
   514  			Expect(err).NotTo(HaveOccurred())
   515  		})
   516  
   517  		Context("when not authenticated", func() {
   518  			BeforeEach(func() {
   519  				fakeAccess.IsAuthenticatedReturns(false)
   520  			})
   521  
   522  			It("returns Unauthorized", func() {
   523  				Expect(response.StatusCode).To(Equal(http.StatusUnauthorized))
   524  			})
   525  		})
   526  
   527  		Context("when authenticated ", func() {
   528  			BeforeEach(func() {
   529  				fakeAccess.IsAuthenticatedReturns(true)
   530  			})
   531  
   532  			Context("when authorized", func() {
   533  				BeforeEach(func() {
   534  					fakeAccess.IsAuthorizedReturns(true)
   535  				})
   536  
   537  				It("tries to find the resource", func() {
   538  					resourceName := fakePipeline.ResourceArgsForCall(0)
   539  					Expect(resourceName).To(Equal("resource-name"))
   540  				})
   541  
   542  				Context("when finding the resource succeeds", func() {
   543  					BeforeEach(func() {
   544  						fakeResource = new(dbfakes.FakeResource)
   545  						fakeResource.IDReturns(1)
   546  						fakePipeline.ResourceReturns(fakeResource, true, nil)
   547  						pinCommentRequestBody.PinComment = "I am a pin comment"
   548  					})
   549  
   550  					It("Tries to set the pin comment", func() {
   551  						Expect(fakeResource.SetPinCommentCallCount()).To(Equal(1))
   552  						comment := fakeResource.SetPinCommentArgsForCall(0)
   553  						Expect(comment).To(Equal("I am a pin comment"))
   554  					})
   555  
   556  					Context("when setting the pin comment succeeds", func() {
   557  						BeforeEach(func() {
   558  							fakeResource.SetPinCommentReturns(nil)
   559  						})
   560  
   561  						It("returns 200", func() {
   562  							Expect(response.StatusCode).To(Equal(http.StatusOK))
   563  						})
   564  					})
   565  
   566  					Context("when setting the pin comment fails", func() {
   567  						BeforeEach(func() {
   568  							fakeResource.SetPinCommentReturns(errors.New("welp"))
   569  						})
   570  
   571  						It("returns 500", func() {
   572  							Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
   573  						})
   574  					})
   575  				})
   576  
   577  				Context("when it fails to find the resource", func() {
   578  					BeforeEach(func() {
   579  						fakePipeline.ResourceReturns(nil, false, errors.New("welp"))
   580  					})
   581  
   582  					It("returns Internal Server Error", func() {
   583  						Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
   584  					})
   585  				})
   586  
   587  				Context("when the resource is not found", func() {
   588  					BeforeEach(func() {
   589  						fakePipeline.ResourceReturns(nil, false, nil)
   590  					})
   591  
   592  					It("returns not found", func() {
   593  						Expect(response.StatusCode).To(Equal(http.StatusNotFound))
   594  					})
   595  				})
   596  			})
   597  			Context("when not authorized", func() {
   598  				BeforeEach(func() {
   599  					fakeAccess.IsAuthorizedReturns(false)
   600  				})
   601  
   602  				It("returns Forbidden", func() {
   603  					Expect(response.StatusCode).To(Equal(http.StatusForbidden))
   604  				})
   605  			})
   606  		})
   607  	})
   608  
   609  	Describe("POST /api/v1/teams/:team_name/pipelines/:pipeline_name/resources/:resource_name/check", func() {
   610  		var checkRequestBody atc.CheckRequestBody
   611  		var response *http.Response
   612  
   613  		BeforeEach(func() {
   614  			checkRequestBody = atc.CheckRequestBody{}
   615  		})
   616  
   617  		JustBeforeEach(func() {
   618  			reqPayload, err := json.Marshal(checkRequestBody)
   619  			Expect(err).NotTo(HaveOccurred())
   620  
   621  			request, err := http.NewRequest("POST", server.URL+"/api/v1/teams/a-team/pipelines/a-pipeline/resources/resource-name/check", bytes.NewBuffer(reqPayload))
   622  			Expect(err).NotTo(HaveOccurred())
   623  			request.Header.Set("Content-Type", "application/json")
   624  
   625  			response, err = client.Do(request)
   626  			Expect(err).NotTo(HaveOccurred())
   627  		})
   628  
   629  		Context("when authorized", func() {
   630  			BeforeEach(func() {
   631  				fakeAccess.IsAuthenticatedReturns(true)
   632  				fakeAccess.IsAuthorizedReturns(true)
   633  			})
   634  
   635  			Context("when looking up the resource fails", func() {
   636  				BeforeEach(func() {
   637  					fakePipeline.ResourceReturns(nil, false, errors.New("nope"))
   638  				})
   639  				It("returns 500", func() {
   640  					Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
   641  				})
   642  			})
   643  
   644  			Context("when the resource is not found", func() {
   645  				BeforeEach(func() {
   646  					fakePipeline.ResourceReturns(nil, false, nil)
   647  				})
   648  				It("returns 404", func() {
   649  					Expect(response.StatusCode).To(Equal(http.StatusNotFound))
   650  				})
   651  			})
   652  
   653  			Context("when it finds the resource", func() {
   654  				var fakeResource *dbfakes.FakeResource
   655  
   656  				BeforeEach(func() {
   657  					fakeResource = new(dbfakes.FakeResource)
   658  					fakeResource.IDReturns(1)
   659  					fakePipeline.ResourceReturns(fakeResource, true, nil)
   660  				})
   661  
   662  				Context("when looking up the resource types fails", func() {
   663  					BeforeEach(func() {
   664  						fakePipeline.ResourceTypesReturns(nil, errors.New("nope"))
   665  					})
   666  					It("returns 500", func() {
   667  						Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
   668  					})
   669  				})
   670  
   671  				Context("when looking up the resource types succeeds", func() {
   672  					var fakeResourceTypes db.ResourceTypes
   673  
   674  					BeforeEach(func() {
   675  						fakeResourceTypes = db.ResourceTypes{}
   676  						fakePipeline.ResourceTypesReturns(fakeResourceTypes, nil)
   677  					})
   678  
   679  					It("checks with no version specified", func() {
   680  						Expect(dbCheckFactory.TryCreateCheckCallCount()).To(Equal(1))
   681  						_, actualResource, actualResourceTypes, actualFromVersion, manuallyTriggered := dbCheckFactory.TryCreateCheckArgsForCall(0)
   682  						Expect(actualResource).To(Equal(fakeResource))
   683  						Expect(actualResourceTypes).To(Equal(fakeResourceTypes))
   684  						Expect(actualFromVersion).To(BeNil())
   685  						Expect(manuallyTriggered).To(BeTrue())
   686  					})
   687  
   688  					Context("when checking with a version specified", func() {
   689  						BeforeEach(func() {
   690  							checkRequestBody = atc.CheckRequestBody{
   691  								From: atc.Version{
   692  									"some-version-key": "some-version-value",
   693  								},
   694  							}
   695  						})
   696  
   697  						It("checks with the version specified", func() {
   698  							Expect(dbCheckFactory.TryCreateCheckCallCount()).To(Equal(1))
   699  							_, actualResource, actualResourceTypes, actualFromVersion, manuallyTriggered := dbCheckFactory.TryCreateCheckArgsForCall(0)
   700  							Expect(actualResource).To(Equal(fakeResource))
   701  							Expect(actualResourceTypes).To(Equal(fakeResourceTypes))
   702  							Expect(actualFromVersion).To(Equal(checkRequestBody.From))
   703  							Expect(manuallyTriggered).To(BeTrue())
   704  						})
   705  					})
   706  
   707  					Context("when checking fails", func() {
   708  						BeforeEach(func() {
   709  							dbCheckFactory.TryCreateCheckReturns(nil, false, errors.New("nope"))
   710  						})
   711  
   712  						It("returns 500", func() {
   713  							Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
   714  						})
   715  					})
   716  
   717  					Context("when checking does not create a new check", func() {
   718  						BeforeEach(func() {
   719  							dbCheckFactory.TryCreateCheckReturns(nil, false, nil)
   720  						})
   721  
   722  						It("returns 500", func() {
   723  							Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
   724  						})
   725  					})
   726  
   727  					Context("when checking creates a new check", func() {
   728  						var fakeBuild *dbfakes.FakeBuild
   729  
   730  						BeforeEach(func() {
   731  							fakeBuild = new(dbfakes.FakeBuild)
   732  							fakeBuild.IDReturns(10)
   733  							fakeBuild.NameReturns("some-name")
   734  							fakeBuild.TeamNameReturns("some-team")
   735  							fakeBuild.StatusReturns("started")
   736  							fakeBuild.StartTimeReturns(time.Date(2001, 01, 01, 0, 0, 0, 0, time.UTC))
   737  							fakeBuild.EndTimeReturns(time.Date(2002, 01, 01, 0, 0, 0, 0, time.UTC))
   738  
   739  							dbCheckFactory.TryCreateCheckReturns(fakeBuild, true, nil)
   740  						})
   741  
   742  						It("returns 201", func() {
   743  							Expect(response.StatusCode).To(Equal(http.StatusCreated))
   744  							Expect(ioutil.ReadAll(response.Body)).To(MatchJSON(`{
   745  								"id": 10,
   746  								"name": "some-name",
   747  								"team_name": "some-team",
   748  								"status": "started",
   749  								"api_url": "/api/v1/builds/10",
   750  								"start_time": 978307200,
   751  								"end_time": 1009843200
   752  							}`))
   753  						})
   754  					})
   755  				})
   756  			})
   757  		})
   758  
   759  		Context("when not authenticated", func() {
   760  			BeforeEach(func() {
   761  				fakeAccess.IsAuthenticatedReturns(false)
   762  			})
   763  
   764  			It("returns Unauthorized", func() {
   765  				Expect(response.StatusCode).To(Equal(http.StatusUnauthorized))
   766  			})
   767  		})
   768  	})
   769  
   770  	Describe("GET /api/v1/teams/:team_name/pipelines/:pipeline_name/resource-types", func() {
   771  		var response *http.Response
   772  
   773  		JustBeforeEach(func() {
   774  			var err error
   775  
   776  			response, err = client.Get(server.URL + "/api/v1/teams/a-team/pipelines/a-pipeline/resource-types")
   777  			Expect(err).NotTo(HaveOccurred())
   778  		})
   779  
   780  		Context("when getting the resource types succeeds", func() {
   781  			BeforeEach(func() {
   782  				resourceType1 := new(dbfakes.FakeResourceType)
   783  				resourceType1.IDReturns(1)
   784  				resourceType1.NameReturns("resource-type-1")
   785  				resourceType1.TypeReturns("type-1")
   786  				resourceType1.SourceReturns(map[string]interface{}{"source-key-1": "source-value-1"})
   787  				resourceType1.PrivilegedReturns(false)
   788  				resourceType1.TagsReturns([]string{"tag1"})
   789  				resourceType1.ParamsReturns(map[string]interface{}{"param-key-1": "param-value-1"})
   790  				resourceType1.VersionReturns(map[string]string{
   791  					"version-key-1": "version-value-1",
   792  					"version-key-2": "version-value-2",
   793  				})
   794  
   795  				resourceType2 := new(dbfakes.FakeResourceType)
   796  				resourceType2.IDReturns(2)
   797  				resourceType2.NameReturns("resource-type-2")
   798  				resourceType2.TypeReturns("type-2")
   799  				resourceType2.SourceReturns(map[string]interface{}{"source-key-2": "source-value-2"})
   800  				resourceType2.PrivilegedReturns(true)
   801  				resourceType2.CheckEveryReturns("10ms")
   802  				resourceType2.TagsReturns([]string{"tag1", "tag2"})
   803  				resourceType2.ParamsReturns(map[string]interface{}{"param-key-2": "param-value-2"})
   804  				resourceType2.VersionReturns(map[string]string{
   805  					"version-key-2": "version-value-2",
   806  				})
   807  
   808  				fakePipeline.ResourceTypesReturns(db.ResourceTypes{
   809  					resourceType1, resourceType2,
   810  				}, nil)
   811  			})
   812  
   813  			Context("when not authorized", func() {
   814  				BeforeEach(func() {
   815  					fakeAccess.IsAuthenticatedReturns(false)
   816  					fakeAccess.IsAuthorizedReturns(false)
   817  				})
   818  
   819  				Context("and the pipeline is private", func() {
   820  					BeforeEach(func() {
   821  						fakePipeline.PublicReturns(false)
   822  					})
   823  
   824  					It("returns 401", func() {
   825  						Expect(response.StatusCode).To(Equal(http.StatusUnauthorized))
   826  					})
   827  				})
   828  
   829  				Context("and the pipeline is public", func() {
   830  					BeforeEach(func() {
   831  						fakePipeline.PublicReturns(true)
   832  					})
   833  
   834  					It("returns 200 OK", func() {
   835  						Expect(response.StatusCode).To(Equal(http.StatusOK))
   836  					})
   837  
   838  					It("returns application/json", func() {
   839  						expectedHeaderEntries := map[string]string{
   840  							"Content-Type": "application/json",
   841  						}
   842  						Expect(response).Should(IncludeHeaderEntries(expectedHeaderEntries))
   843  					})
   844  
   845  					It("returns each resource type", func() {
   846  						body, err := ioutil.ReadAll(response.Body)
   847  						Expect(err).NotTo(HaveOccurred())
   848  
   849  						Expect(body).To(MatchJSON(`[
   850  				{
   851  					"name": "resource-type-1",
   852  					"type": "type-1",
   853  					"tags": ["tag1"],
   854  					"params": {"param-key-1": "param-value-1"},
   855  					"source": {"source-key-1": "source-value-1"},
   856  					"version": {
   857  						"version-key-1": "version-value-1",
   858  						"version-key-2": "version-value-2"
   859  					}
   860  				},
   861  				{
   862  					"name": "resource-type-2",
   863  					"type": "type-2",
   864  					"tags": ["tag1", "tag2"],
   865  					"privileged": true,
   866  					"check_every": "10ms",
   867  					"params": {"param-key-2": "param-value-2"},
   868  					"source": {"source-key-2": "source-value-2"},
   869  					"version": {
   870  						"version-key-2": "version-value-2"
   871  					}
   872  				}
   873  			]`))
   874  					})
   875  				})
   876  			})
   877  
   878  			Context("when authorized", func() {
   879  				BeforeEach(func() {
   880  					fakeAccess.IsAuthenticatedReturns(true)
   881  					fakeAccess.IsAuthorizedReturns(true)
   882  				})
   883  
   884  				It("returns 200 OK", func() {
   885  					Expect(response.StatusCode).To(Equal(http.StatusOK))
   886  				})
   887  
   888  				It("returns application/json", func() {
   889  					expectedHeaderEntries := map[string]string{
   890  						"Content-Type": "application/json",
   891  					}
   892  					Expect(response).Should(IncludeHeaderEntries(expectedHeaderEntries))
   893  				})
   894  
   895  				It("returns each resource type", func() {
   896  					body, err := ioutil.ReadAll(response.Body)
   897  					Expect(err).NotTo(HaveOccurred())
   898  
   899  					Expect(body).To(MatchJSON(`[
   900  			{
   901  				"name": "resource-type-1",
   902  				"type": "type-1",
   903  				"tags": ["tag1"],
   904  				"params": {"param-key-1": "param-value-1"},
   905  				"source": {"source-key-1": "source-value-1"},
   906  				"version": {
   907  					"version-key-1": "version-value-1",
   908  					"version-key-2": "version-value-2"
   909  				}
   910  			},
   911  			{
   912  				"name": "resource-type-2",
   913  				"type": "type-2",
   914  				"tags": ["tag1", "tag2"],
   915  				"privileged": true,
   916  				"check_every": "10ms",
   917  				"params": {"param-key-2": "param-value-2"},
   918  				"source": {"source-key-2": "source-value-2"},
   919  				"version": {
   920  					"version-key-2": "version-value-2"
   921  				}
   922  			}
   923  		]`))
   924  				})
   925  
   926  				Context("when getting the resource type fails", func() {
   927  					Context("when the resource type are not found", func() {
   928  						BeforeEach(func() {
   929  							fakePipeline.ResourceTypesReturns(nil, nil)
   930  						})
   931  
   932  						It("returns 200", func() {
   933  							Expect(response.StatusCode).To(Equal(http.StatusOK))
   934  						})
   935  
   936  						It("returns application/json", func() {
   937  							expectedHeaderEntries := map[string]string{
   938  								"Content-Type": "application/json",
   939  							}
   940  							Expect(response).Should(IncludeHeaderEntries(expectedHeaderEntries))
   941  						})
   942  					})
   943  
   944  					Context("with an unknown error", func() {
   945  						BeforeEach(func() {
   946  							fakePipeline.ResourceTypesReturns(nil, errors.New("oh no!"))
   947  						})
   948  
   949  						It("returns 500", func() {
   950  							Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
   951  						})
   952  					})
   953  				})
   954  			})
   955  		})
   956  	})
   957  
   958  	Describe("GET /api/v1/teams/:team_name/pipelines/:pipeline_name/resources/:resource_name", func() {
   959  		var response *http.Response
   960  		var resourceName string
   961  		BeforeEach(func() {
   962  			resourceName = "some-resource"
   963  		})
   964  
   965  		JustBeforeEach(func() {
   966  			var err error
   967  
   968  			request, err := http.NewRequest("GET", fmt.Sprintf("%s/api/v1/teams/a-team/pipelines/a-pipeline/resources/%s", server.URL, resourceName), nil)
   969  			Expect(err).NotTo(HaveOccurred())
   970  
   971  			response, err = client.Do(request)
   972  			Expect(err).NotTo(HaveOccurred())
   973  		})
   974  
   975  		Context("when not authenticated and not authorized", func() {
   976  			BeforeEach(func() {
   977  				fakeAccess.IsAuthenticatedReturns(false)
   978  				fakeAccess.IsAuthorizedReturns(false)
   979  			})
   980  
   981  			Context("and the pipeline is private", func() {
   982  				BeforeEach(func() {
   983  					fakePipeline.PublicReturns(false)
   984  				})
   985  
   986  				It("returns 401", func() {
   987  					Expect(response.StatusCode).To(Equal(http.StatusUnauthorized))
   988  				})
   989  			})
   990  
   991  			Context("and the pipeline is public", func() {
   992  				BeforeEach(func() {
   993  					fakePipeline.PublicReturns(true)
   994  					resourceName = "resource-1"
   995  
   996  					resource1 := new(dbfakes.FakeResource)
   997  					resource1.TeamNameReturns("a-team")
   998  					resource1.PipelineIDReturns(1)
   999  					resource1.PipelineNameReturns("a-pipeline")
  1000  					resource1.NameReturns("resource-1")
  1001  					resource1.TypeReturns("type-1")
  1002  					resource1.LastCheckEndTimeReturns(time.Unix(1513364881, 0))
  1003  					resource1.BuildSummaryReturns(&atc.BuildSummary{
  1004  						ID:                   123,
  1005  						Name:                 "123",
  1006  						Status:               atc.StatusSucceeded,
  1007  						StartTime:            456,
  1008  						EndTime:              789,
  1009  						TeamName:             "some-team",
  1010  						PipelineID:           99,
  1011  						PipelineName:         "some-pipeline",
  1012  						PipelineInstanceVars: atc.InstanceVars{"foo": 1},
  1013  					})
  1014  
  1015  					fakePipeline.ResourceReturns(resource1, true, nil)
  1016  				})
  1017  
  1018  				It("returns 200 OK", func() {
  1019  					Expect(response.StatusCode).To(Equal(http.StatusOK))
  1020  				})
  1021  
  1022  				It("returns Content-Type 'application/json'", func() {
  1023  					expectedHeaderEntries := map[string]string{
  1024  						"Content-Type": "application/json",
  1025  					}
  1026  					Expect(response).Should(IncludeHeaderEntries(expectedHeaderEntries))
  1027  				})
  1028  
  1029  				It("returns the resource json", func() {
  1030  					body, err := ioutil.ReadAll(response.Body)
  1031  					Expect(err).NotTo(HaveOccurred())
  1032  
  1033  					Expect(body).To(MatchJSON(`
  1034  					{
  1035  						"name": "resource-1",
  1036  						"pipeline_id": 1,
  1037  						"pipeline_name": "a-pipeline",
  1038  						"team_name": "a-team",
  1039  						"type": "type-1",
  1040  						"last_checked": 1513364881,
  1041  						"build": {
  1042  							"id": 123,
  1043  							"name": "123",
  1044  							"status": "succeeded",
  1045  							"start_time": 456,
  1046  							"end_time": 789,
  1047  							"team_name": "some-team",
  1048  							"pipeline_id": 99,
  1049  							"pipeline_name": "some-pipeline",
  1050  							"pipeline_instance_vars": {
  1051  								"foo": 1
  1052  							}
  1053  						}
  1054  					}`))
  1055  				})
  1056  			})
  1057  		})
  1058  
  1059  		Context("when authorized", func() {
  1060  			BeforeEach(func() {
  1061  				fakeAccess.IsAuthenticatedReturns(true)
  1062  				fakeAccess.IsAuthorizedReturns(true)
  1063  			})
  1064  
  1065  			It("looks it up in the database", func() {
  1066  				Expect(fakePipeline.ResourceCallCount()).To(Equal(1))
  1067  				Expect(fakePipeline.ResourceArgsForCall(0)).To(Equal("some-resource"))
  1068  			})
  1069  
  1070  			Context("when the resource cannot be found in the database", func() {
  1071  				BeforeEach(func() {
  1072  					resourceName = "resource-in-config-but-not-db"
  1073  				})
  1074  
  1075  				It("returns a 404", func() {
  1076  					Expect(response.StatusCode).To(Equal(http.StatusNotFound))
  1077  				})
  1078  			})
  1079  
  1080  			Context("when the call to the db returns an error", func() {
  1081  				BeforeEach(func() {
  1082  					fakePipeline.ResourceReturns(nil, false, errors.New("Oh no!"))
  1083  				})
  1084  
  1085  				It("returns a 500 error", func() {
  1086  					Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
  1087  				})
  1088  			})
  1089  
  1090  			Context("when the call to get a resource succeeds", func() {
  1091  				Context("when the resource version is pinned via pipeline config", func() {
  1092  					BeforeEach(func() {
  1093  						resource1 := new(dbfakes.FakeResource)
  1094  						resource1.TeamNameReturns("a-team")
  1095  						resource1.PipelineIDReturns(1)
  1096  						resource1.PipelineNameReturns("a-pipeline")
  1097  						resource1.NameReturns("resource-1")
  1098  						resource1.TypeReturns("type-1")
  1099  						resource1.LastCheckEndTimeReturns(time.Unix(1513364881, 0))
  1100  						resource1.ConfigPinnedVersionReturns(atc.Version{"version": "v1"})
  1101  
  1102  						fakePipeline.ResourceReturns(resource1, true, nil)
  1103  					})
  1104  
  1105  					It("returns 200 ok", func() {
  1106  						Expect(response.StatusCode).To(Equal(http.StatusOK))
  1107  					})
  1108  
  1109  					It("returns Content-Type 'application/json'", func() {
  1110  						expectedHeaderEntries := map[string]string{
  1111  							"Content-Type": "application/json",
  1112  						}
  1113  						Expect(response).Should(IncludeHeaderEntries(expectedHeaderEntries))
  1114  					})
  1115  
  1116  					It("returns the resource json", func() {
  1117  						body, err := ioutil.ReadAll(response.Body)
  1118  						Expect(err).NotTo(HaveOccurred())
  1119  
  1120  						Expect(body).To(MatchJSON(`
  1121  							{
  1122  								"name": "resource-1",
  1123  								"pipeline_id": 1,
  1124  								"pipeline_name": "a-pipeline",
  1125  								"team_name": "a-team",
  1126  								"type": "type-1",
  1127  								"last_checked": 1513364881,
  1128  								"pinned_version": {"version": "v1"},
  1129  								"pinned_in_config": true
  1130  							}`))
  1131  					})
  1132  				})
  1133  
  1134  				Context("when the resource version is pinned via the API", func() {
  1135  					BeforeEach(func() {
  1136  						resource1 := new(dbfakes.FakeResource)
  1137  						resource1.TeamNameReturns("a-team")
  1138  						resource1.PipelineIDReturns(1)
  1139  						resource1.PipelineNameReturns("a-pipeline")
  1140  						resource1.NameReturns("resource-1")
  1141  						resource1.TypeReturns("type-1")
  1142  						resource1.LastCheckEndTimeReturns(time.Unix(1513364881, 0))
  1143  						resource1.APIPinnedVersionReturns(atc.Version{"version": "v1"})
  1144  
  1145  						fakePipeline.ResourceReturns(resource1, true, nil)
  1146  					})
  1147  
  1148  					It("returns 200 ok", func() {
  1149  						Expect(response.StatusCode).To(Equal(http.StatusOK))
  1150  					})
  1151  
  1152  					It("returns Content-Type 'application/json'", func() {
  1153  						expectedHeaderEntries := map[string]string{
  1154  							"Content-Type": "application/json",
  1155  						}
  1156  						Expect(response).Should(IncludeHeaderEntries(expectedHeaderEntries))
  1157  					})
  1158  
  1159  					It("returns the resource json describing the pinned version", func() {
  1160  						body, err := ioutil.ReadAll(response.Body)
  1161  						Expect(err).NotTo(HaveOccurred())
  1162  
  1163  						Expect(body).To(MatchJSON(`
  1164  							{
  1165  								"name": "resource-1",
  1166  								"pipeline_id": 1,
  1167  								"pipeline_name": "a-pipeline",
  1168  								"team_name": "a-team",
  1169  								"type": "type-1",
  1170  								"last_checked": 1513364881,
  1171  								"pinned_version": {"version": "v1"}
  1172  							}`))
  1173  					})
  1174  				})
  1175  
  1176  				Context("when the resource has a pin comment", func() {
  1177  					BeforeEach(func() {
  1178  						resource1 := new(dbfakes.FakeResource)
  1179  						resource1.TeamNameReturns("a-team")
  1180  						resource1.PipelineIDReturns(1)
  1181  						resource1.PipelineNameReturns("a-pipeline")
  1182  						resource1.NameReturns("resource-1")
  1183  						resource1.TypeReturns("type-1")
  1184  						resource1.LastCheckEndTimeReturns(time.Unix(1513364881, 0))
  1185  						resource1.APIPinnedVersionReturns(atc.Version{"version": "v1"})
  1186  						resource1.PinCommentReturns("a pin comment")
  1187  						fakePipeline.ResourceReturns(resource1, true, nil)
  1188  					})
  1189  
  1190  					It("returns the pin comment in the response json", func() {
  1191  						body, err := ioutil.ReadAll(response.Body)
  1192  						Expect(err).NotTo(HaveOccurred())
  1193  
  1194  						Expect(body).To(MatchJSON(`
  1195  							{
  1196  								"name": "resource-1",
  1197  								"pipeline_id": 1,
  1198  								"pipeline_name": "a-pipeline",
  1199  								"team_name": "a-team",
  1200  								"type": "type-1",
  1201  								"last_checked": 1513364881,
  1202  								"pinned_version": {"version": "v1"},
  1203  								"pin_comment": "a pin comment"
  1204  							}`))
  1205  					})
  1206  				})
  1207  			})
  1208  		})
  1209  
  1210  		Context("when authenticated but not authorized", func() {
  1211  			BeforeEach(func() {
  1212  				fakeAccess.IsAuthenticatedReturns(true)
  1213  				fakeAccess.IsAuthorizedReturns(false)
  1214  			})
  1215  
  1216  			Context("and the pipeline is private", func() {
  1217  				BeforeEach(func() {
  1218  					fakePipeline.PublicReturns(false)
  1219  				})
  1220  
  1221  				It("returns 403", func() {
  1222  					Expect(response.StatusCode).To(Equal(http.StatusForbidden))
  1223  				})
  1224  			})
  1225  
  1226  			Context("and the pipeline is public", func() {
  1227  				BeforeEach(func() {
  1228  					fakePipeline.PublicReturns(true)
  1229  					resourceName = "resource-1"
  1230  
  1231  					resource1 := new(dbfakes.FakeResource)
  1232  					resource1.TeamNameReturns("a-team")
  1233  					resource1.PipelineIDReturns(1)
  1234  					resource1.PipelineNameReturns("a-pipeline")
  1235  					resource1.NameReturns("resource-1")
  1236  					resource1.TypeReturns("type-1")
  1237  					resource1.LastCheckEndTimeReturns(time.Unix(1513364881, 0))
  1238  
  1239  					fakePipeline.ResourceReturns(resource1, true, nil)
  1240  				})
  1241  
  1242  				It("returns 200 OK", func() {
  1243  					Expect(response.StatusCode).To(Equal(http.StatusOK))
  1244  				})
  1245  
  1246  				It("returns Content-Type 'application/json'", func() {
  1247  					expectedHeaderEntries := map[string]string{
  1248  						"Content-Type": "application/json",
  1249  					}
  1250  					Expect(response).Should(IncludeHeaderEntries(expectedHeaderEntries))
  1251  				})
  1252  
  1253  				It("returns the resource json", func() {
  1254  					body, err := ioutil.ReadAll(response.Body)
  1255  					Expect(err).NotTo(HaveOccurred())
  1256  
  1257  					Expect(body).To(MatchJSON(`
  1258  					{
  1259  						"name": "resource-1",
  1260  						"pipeline_id": 1,
  1261  						"pipeline_name": "a-pipeline",
  1262  						"team_name": "a-team",
  1263  						"type": "type-1",
  1264  						"last_checked": 1513364881
  1265  					}`))
  1266  				})
  1267  			})
  1268  		})
  1269  	})
  1270  
  1271  	Describe("POST /api/v1/teams/:team_name/pipelines/:pipeline_name/resource-types/:resource_type_name/check", func() {
  1272  		var checkRequestBody atc.CheckRequestBody
  1273  		var response *http.Response
  1274  
  1275  		BeforeEach(func() {
  1276  			checkRequestBody = atc.CheckRequestBody{}
  1277  		})
  1278  
  1279  		JustBeforeEach(func() {
  1280  			reqPayload, err := json.Marshal(checkRequestBody)
  1281  			Expect(err).NotTo(HaveOccurred())
  1282  
  1283  			request, err := http.NewRequest("POST", server.URL+"/api/v1/teams/a-team/pipelines/a-pipeline/resource-types/resource-type-name/check", bytes.NewBuffer(reqPayload))
  1284  			Expect(err).NotTo(HaveOccurred())
  1285  			request.Header.Set("Content-Type", "application/json")
  1286  
  1287  			response, err = client.Do(request)
  1288  			Expect(err).NotTo(HaveOccurred())
  1289  		})
  1290  
  1291  		Context("when not authenticated", func() {
  1292  			BeforeEach(func() {
  1293  				fakeAccess.IsAuthenticatedReturns(false)
  1294  			})
  1295  
  1296  			It("returns Unauthorized", func() {
  1297  				Expect(response.StatusCode).To(Equal(http.StatusUnauthorized))
  1298  			})
  1299  		})
  1300  
  1301  		Context("when not authorized", func() {
  1302  			BeforeEach(func() {
  1303  				fakeAccess.IsAuthenticatedReturns(true)
  1304  				fakeAccess.IsAuthorizedReturns(false)
  1305  			})
  1306  
  1307  			It("returns Forbidden", func() {
  1308  				Expect(response.StatusCode).To(Equal(http.StatusForbidden))
  1309  			})
  1310  		})
  1311  
  1312  		Context("when authenticated and authorized", func() {
  1313  
  1314  			BeforeEach(func() {
  1315  				fakeAccess.IsAuthenticatedReturns(true)
  1316  				fakeAccess.IsAuthorizedReturns(true)
  1317  			})
  1318  
  1319  			Context("when looking up the resource type fails", func() {
  1320  				BeforeEach(func() {
  1321  					fakePipeline.ResourceTypeReturns(nil, false, errors.New("nope"))
  1322  				})
  1323  				It("returns 500", func() {
  1324  					Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
  1325  				})
  1326  			})
  1327  
  1328  			Context("when the resource type is not found", func() {
  1329  				BeforeEach(func() {
  1330  					fakePipeline.ResourceTypeReturns(nil, false, nil)
  1331  				})
  1332  				It("returns 404", func() {
  1333  					Expect(response.StatusCode).To(Equal(http.StatusNotFound))
  1334  				})
  1335  			})
  1336  
  1337  			Context("when it finds the resource type", func() {
  1338  				var fakeResourceType *dbfakes.FakeResourceType
  1339  
  1340  				BeforeEach(func() {
  1341  					fakeResourceType = new(dbfakes.FakeResourceType)
  1342  					fakeResourceType.IDReturns(1)
  1343  					fakePipeline.ResourceTypeReturns(fakeResourceType, true, nil)
  1344  				})
  1345  
  1346  				Context("when looking up the resource types fails", func() {
  1347  					BeforeEach(func() {
  1348  						fakePipeline.ResourceTypesReturns(nil, errors.New("nope"))
  1349  					})
  1350  
  1351  					It("returns 500", func() {
  1352  						Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
  1353  					})
  1354  				})
  1355  
  1356  				Context("when looking up the resource types succeeds", func() {
  1357  					var fakeResourceTypes db.ResourceTypes
  1358  
  1359  					BeforeEach(func() {
  1360  						fakeResourceTypes = db.ResourceTypes{}
  1361  						fakePipeline.ResourceTypesReturns(fakeResourceTypes, nil)
  1362  					})
  1363  
  1364  					It("checks with no version specified", func() {
  1365  						Expect(dbCheckFactory.TryCreateCheckCallCount()).To(Equal(1))
  1366  						_, actualResourceType, actualResourceTypes, actualFromVersion, manuallyTriggered := dbCheckFactory.TryCreateCheckArgsForCall(0)
  1367  						Expect(actualResourceType).To(Equal(fakeResourceType))
  1368  						Expect(actualResourceTypes).To(Equal(fakeResourceTypes))
  1369  						Expect(actualFromVersion).To(BeNil())
  1370  						Expect(manuallyTriggered).To(BeTrue())
  1371  					})
  1372  
  1373  					Context("when checking with a version specified", func() {
  1374  						BeforeEach(func() {
  1375  							checkRequestBody = atc.CheckRequestBody{
  1376  								From: atc.Version{
  1377  									"some-version-key": "some-version-value",
  1378  								},
  1379  							}
  1380  						})
  1381  
  1382  						It("checks with no version specified", func() {
  1383  							Expect(dbCheckFactory.TryCreateCheckCallCount()).To(Equal(1))
  1384  							_, actualResourceType, actualResourceTypes, actualFromVersion, manuallyTriggered := dbCheckFactory.TryCreateCheckArgsForCall(0)
  1385  							Expect(actualResourceType).To(Equal(fakeResourceType))
  1386  							Expect(actualResourceTypes).To(Equal(fakeResourceTypes))
  1387  							Expect(actualFromVersion).To(Equal(checkRequestBody.From))
  1388  							Expect(manuallyTriggered).To(BeTrue())
  1389  						})
  1390  					})
  1391  
  1392  					Context("when checking fails", func() {
  1393  						BeforeEach(func() {
  1394  							dbCheckFactory.TryCreateCheckReturns(nil, false, errors.New("nope"))
  1395  						})
  1396  
  1397  						It("returns 500", func() {
  1398  							Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
  1399  						})
  1400  					})
  1401  
  1402  					Context("when checking does not create a new check", func() {
  1403  						BeforeEach(func() {
  1404  							dbCheckFactory.TryCreateCheckReturns(nil, false, nil)
  1405  						})
  1406  
  1407  						It("returns 500", func() {
  1408  							Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
  1409  						})
  1410  					})
  1411  
  1412  					Context("when checking creates a new check", func() {
  1413  						var fakeBuild *dbfakes.FakeBuild
  1414  
  1415  						BeforeEach(func() {
  1416  							fakeBuild = new(dbfakes.FakeBuild)
  1417  							fakeBuild.IDReturns(10)
  1418  							fakeBuild.NameReturns("some-name")
  1419  							fakeBuild.TeamNameReturns("some-team")
  1420  							fakeBuild.StatusReturns("started")
  1421  							fakeBuild.StartTimeReturns(time.Date(2001, 01, 01, 0, 0, 0, 0, time.UTC))
  1422  							fakeBuild.EndTimeReturns(time.Date(2002, 01, 01, 0, 0, 0, 0, time.UTC))
  1423  
  1424  							dbCheckFactory.TryCreateCheckReturns(fakeBuild, true, nil)
  1425  						})
  1426  
  1427  						It("returns 201", func() {
  1428  							Expect(response.StatusCode).To(Equal(http.StatusCreated))
  1429  							Expect(ioutil.ReadAll(response.Body)).To(MatchJSON(`{
  1430                   "id": 10,
  1431  								 "name": "some-name",
  1432  								 "team_name": "some-team",
  1433  								 "status": "started",
  1434  								 "api_url": "/api/v1/builds/10",
  1435  								 "start_time": 978307200,
  1436  								 "end_time": 1009843200
  1437  							}`))
  1438  						})
  1439  					})
  1440  
  1441  				})
  1442  			})
  1443  		})
  1444  	})
  1445  
  1446  	Describe("POST /api/v1/teams/:team_name/pipelines/:pipeline_name/resources/:resource_name/check/webhook", func() {
  1447  		var (
  1448  			checkRequestBody atc.CheckRequestBody
  1449  			response         *http.Response
  1450  			fakeResource     *dbfakes.FakeResource
  1451  		)
  1452  
  1453  		BeforeEach(func() {
  1454  			checkRequestBody = atc.CheckRequestBody{}
  1455  
  1456  			fakeResource = new(dbfakes.FakeResource)
  1457  			fakeResource.NameReturns("resource-name")
  1458  			fakeResource.IDReturns(10)
  1459  		})
  1460  
  1461  		JustBeforeEach(func() {
  1462  			reqPayload, err := json.Marshal(checkRequestBody)
  1463  			Expect(err).NotTo(HaveOccurred())
  1464  
  1465  			request, err := http.NewRequest("POST", server.URL+"/api/v1/teams/a-team/pipelines/a-pipeline/resources/resource-name/check/webhook?webhook_token=fake-token", bytes.NewBuffer(reqPayload))
  1466  			Expect(err).NotTo(HaveOccurred())
  1467  			request.Header.Set("Content-Type", "application/json")
  1468  
  1469  			response, err = client.Do(request)
  1470  			Expect(err).NotTo(HaveOccurred())
  1471  		})
  1472  
  1473  		Context("when authorized", func() {
  1474  			BeforeEach(func() {
  1475  				variables = vars.StaticVariables{
  1476  					"webhook-token": "fake-token",
  1477  				}
  1478  				token, err := creds.NewString(variables, "((webhook-token))").Evaluate()
  1479  				Expect(err).NotTo(HaveOccurred())
  1480  				fakeResource.WebhookTokenReturns(token)
  1481  				fakePipeline.ResourceReturns(fakeResource, true, nil)
  1482  				fakeResource.ResourceConfigIDReturns(1)
  1483  				fakeResource.ResourceConfigScopeIDReturns(2)
  1484  			})
  1485  
  1486  			It("injects the proper pipelineDB", func() {
  1487  				Expect(dbTeam.PipelineCallCount()).To(Equal(1))
  1488  				resourceName := fakePipeline.ResourceArgsForCall(0)
  1489  				Expect(resourceName).To(Equal("resource-name"))
  1490  			})
  1491  
  1492  			It("tries to find the resource", func() {
  1493  				Expect(fakePipeline.ResourceCallCount()).To(Equal(1))
  1494  				Expect(fakePipeline.ResourceArgsForCall(0)).To(Equal("resource-name"))
  1495  			})
  1496  
  1497  			Context("when finding the resource succeeds", func() {
  1498  				BeforeEach(func() {
  1499  					fakePipeline.ResourceReturns(fakeResource, true, nil)
  1500  				})
  1501  
  1502  				Context("when finding the resource types fails", func() {
  1503  					BeforeEach(func() {
  1504  						fakePipeline.ResourceTypesReturns(nil, errors.New("oops"))
  1505  					})
  1506  
  1507  					It("returns 500", func() {
  1508  						Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
  1509  					})
  1510  				})
  1511  
  1512  				Context("when finding the resource types succeeds", func() {
  1513  					var fakeResourceTypes db.ResourceTypes
  1514  
  1515  					BeforeEach(func() {
  1516  						fakeResourceTypes = db.ResourceTypes{}
  1517  						fakePipeline.ResourceTypesReturns(fakeResourceTypes, nil)
  1518  					})
  1519  
  1520  					It("checks with a nil version", func() {
  1521  						Expect(dbCheckFactory.TryCreateCheckCallCount()).To(Equal(1))
  1522  						_, actualResource, actualResourceTypes, actualFromVersion, manuallyTriggered := dbCheckFactory.TryCreateCheckArgsForCall(0)
  1523  						Expect(actualResource).To(Equal(fakeResource))
  1524  						Expect(actualResourceTypes).To(Equal(fakeResourceTypes))
  1525  						Expect(actualFromVersion).To(BeNil())
  1526  						Expect(manuallyTriggered).To(BeTrue())
  1527  					})
  1528  
  1529  					Context("when checking fails", func() {
  1530  						BeforeEach(func() {
  1531  							dbCheckFactory.TryCreateCheckReturns(nil, false, errors.New("nope"))
  1532  						})
  1533  
  1534  						It("returns 500", func() {
  1535  							Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
  1536  						})
  1537  					})
  1538  
  1539  					Context("when checking does not create a new check", func() {
  1540  						BeforeEach(func() {
  1541  							dbCheckFactory.TryCreateCheckReturns(nil, false, nil)
  1542  						})
  1543  
  1544  						It("returns 500", func() {
  1545  							Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
  1546  						})
  1547  					})
  1548  
  1549  					Context("when checking creates a new check", func() {
  1550  						var fakeBuild *dbfakes.FakeBuild
  1551  
  1552  						BeforeEach(func() {
  1553  							fakeBuild = new(dbfakes.FakeBuild)
  1554  							fakeBuild.IDReturns(10)
  1555  							fakeBuild.NameReturns("some-name")
  1556  							fakeBuild.TeamNameReturns("some-team")
  1557  							fakeBuild.StatusReturns("started")
  1558  							fakeBuild.StartTimeReturns(time.Date(2001, 01, 01, 0, 0, 0, 0, time.UTC))
  1559  							fakeBuild.EndTimeReturns(time.Date(2002, 01, 01, 0, 0, 0, 0, time.UTC))
  1560  
  1561  							dbCheckFactory.TryCreateCheckReturns(fakeBuild, true, nil)
  1562  						})
  1563  
  1564  						It("returns 201", func() {
  1565  							Expect(response.StatusCode).To(Equal(http.StatusCreated))
  1566  							Expect(ioutil.ReadAll(response.Body)).To(MatchJSON(`{
  1567                   "id": 10,
  1568  								 "name": "some-name",
  1569  								 "team_name": "some-team",
  1570  								 "status": "started",
  1571  								 "api_url": "/api/v1/builds/10",
  1572  								 "start_time": 978307200,
  1573  								 "end_time": 1009843200
  1574  							}`))
  1575  						})
  1576  					})
  1577  				})
  1578  			})
  1579  
  1580  			Context("when finding the resource fails", func() {
  1581  				BeforeEach(func() {
  1582  					fakePipeline.ResourceReturns(nil, false, errors.New("oops"))
  1583  				})
  1584  
  1585  				It("returns 500", func() {
  1586  					Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
  1587  				})
  1588  			})
  1589  
  1590  			Context("when the resource is not found", func() {
  1591  				BeforeEach(func() {
  1592  					fakePipeline.ResourceReturns(nil, false, nil)
  1593  				})
  1594  
  1595  				It("returns 404", func() {
  1596  					Expect(response.StatusCode).To(Equal(http.StatusNotFound))
  1597  				})
  1598  			})
  1599  		})
  1600  
  1601  		Context("when unauthorized", func() {
  1602  			BeforeEach(func() {
  1603  				fakeResource.WebhookTokenReturns("wrong-token")
  1604  				fakePipeline.ResourceReturns(fakeResource, true, nil)
  1605  			})
  1606  			It("returns 401", func() {
  1607  				Expect(response.StatusCode).To(Equal(http.StatusUnauthorized))
  1608  			})
  1609  		})
  1610  	})
  1611  })