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

     1  package api_test
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"errors"
     7  	"io"
     8  	"io/ioutil"
     9  	"net/http"
    10  	"net/url"
    11  	"time"
    12  
    13  	"github.com/concourse/baggageclaim"
    14  	"github.com/pf-qiu/concourse/v6/atc/db"
    15  	"github.com/pf-qiu/concourse/v6/atc/db/dbfakes"
    16  	. "github.com/pf-qiu/concourse/v6/atc/testhelpers"
    17  	"github.com/pf-qiu/concourse/v6/atc/worker"
    18  	"github.com/pf-qiu/concourse/v6/atc/worker/workerfakes"
    19  	. "github.com/onsi/ginkgo"
    20  	. "github.com/onsi/gomega"
    21  )
    22  
    23  var _ = Describe("ArtifactRepository API", func() {
    24  
    25  	Describe("POST /api/v1/teams/:team_name/artifacts", func() {
    26  		var request *http.Request
    27  		var response *http.Response
    28  
    29  		BeforeEach(func() {
    30  			fakeAccess.IsAuthenticatedReturns(true)
    31  		})
    32  
    33  		JustBeforeEach(func() {
    34  			var err error
    35  			request, err = http.NewRequest("POST", server.URL+"/api/v1/teams/some-team/artifacts", bytes.NewBuffer([]byte("some-data")))
    36  			Expect(err).NotTo(HaveOccurred())
    37  
    38  			request.Header.Set("Content-Type", "application/json")
    39  
    40  			q := url.Values{}
    41  			q.Add("platform", "some-platform")
    42  			request.URL.RawQuery = q.Encode()
    43  
    44  			response, err = client.Do(request)
    45  			Expect(err).NotTo(HaveOccurred())
    46  		})
    47  
    48  		Context("when not authenticated", func() {
    49  			BeforeEach(func() {
    50  				fakeAccess.IsAuthenticatedReturns(false)
    51  			})
    52  
    53  			It("returns 401 Unauthorized", func() {
    54  				Expect(response.StatusCode).To(Equal(http.StatusUnauthorized))
    55  			})
    56  		})
    57  
    58  		Context("when not authorized", func() {
    59  			BeforeEach(func() {
    60  				fakeAccess.IsAuthorizedReturns(false)
    61  			})
    62  
    63  			It("returns 403 Forbidden", func() {
    64  				Expect(response.StatusCode).To(Equal(http.StatusForbidden))
    65  			})
    66  		})
    67  
    68  		Context("when authorized", func() {
    69  			BeforeEach(func() {
    70  				fakeAccess.IsAuthorizedReturns(true)
    71  			})
    72  
    73  			Context("when creating a volume fails", func() {
    74  				BeforeEach(func() {
    75  					fakeWorkerClient.CreateVolumeReturns(nil, errors.New("nope"))
    76  				})
    77  
    78  				It("returns 500 InternalServerError", func() {
    79  					Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
    80  				})
    81  			})
    82  
    83  			Context("when creating a volume succeeds", func() {
    84  				var fakeVolume *workerfakes.FakeVolume
    85  
    86  				BeforeEach(func() {
    87  					fakeVolume = new(workerfakes.FakeVolume)
    88  					fakeVolume.InitializeArtifactReturns(nil, errors.New("nope"))
    89  
    90  					fakeWorkerClient.CreateVolumeReturns(fakeVolume, nil)
    91  				})
    92  
    93  				It("creates the volume using the worker client", func() {
    94  					Expect(fakeWorkerClient.CreateVolumeCallCount()).To(Equal(1))
    95  
    96  					_, volumeSpec, workerSpec, volumeType := fakeWorkerClient.CreateVolumeArgsForCall(0)
    97  					Expect(volumeSpec.Strategy).To(Equal(baggageclaim.EmptyStrategy{}))
    98  					Expect(workerSpec).To(Equal(worker.WorkerSpec{
    99  						TeamID:   734,
   100  						Platform: "some-platform",
   101  					}))
   102  					Expect(volumeType).To(Equal(db.VolumeTypeArtifact))
   103  				})
   104  
   105  				Context("when associating a volume with an artifact fails", func() {
   106  					BeforeEach(func() {
   107  						fakeVolume.InitializeArtifactReturns(nil, errors.New("nope"))
   108  					})
   109  
   110  					It("returns 500 InternalServerError", func() {
   111  						Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
   112  					})
   113  				})
   114  
   115  				Context("when associating a volume with an artifact succeeds", func() {
   116  					var fakeWorkerArtifact *dbfakes.FakeWorkerArtifact
   117  
   118  					BeforeEach(func() {
   119  						fakeWorkerArtifact = new(dbfakes.FakeWorkerArtifact)
   120  						fakeWorkerArtifact.IDReturns(0)
   121  						fakeWorkerArtifact.CreatedAtReturns(time.Unix(42, 0))
   122  
   123  						fakeVolume.InitializeArtifactReturns(fakeWorkerArtifact, nil)
   124  					})
   125  
   126  					It("invokes the initialization of an artifact on a volume", func() {
   127  						Expect(fakeVolume.InitializeArtifactCallCount()).To(Equal(1))
   128  
   129  						name, buildID := fakeVolume.InitializeArtifactArgsForCall(0)
   130  						Expect(name).To(Equal(""))
   131  						Expect(buildID).To(Equal(0))
   132  					})
   133  
   134  					Context("when streaming in data to a volume fails", func() {
   135  						BeforeEach(func() {
   136  							fakeVolume.StreamInReturns(errors.New("nope"))
   137  						})
   138  
   139  						It("returns 500 InternalServerError", func() {
   140  							Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
   141  						})
   142  					})
   143  
   144  					Context("when streaming in data to a volume succeeds", func() {
   145  
   146  						BeforeEach(func() {
   147  							fakeVolume.StreamInReturns(nil)
   148  
   149  							fakeVolume.StreamInStub = func(ctx context.Context, path string, encoding baggageclaim.Encoding, body io.Reader) error {
   150  								Expect(path).To(Equal("/"))
   151  								Expect(encoding).To(Equal(baggageclaim.GzipEncoding))
   152  
   153  								contents, err := ioutil.ReadAll(body)
   154  								Expect(err).ToNot(HaveOccurred())
   155  
   156  								Expect(contents).To(Equal([]byte("some-data")))
   157  								return nil
   158  							}
   159  						})
   160  
   161  						It("streams in the user contents to the new volume", func() {
   162  							Expect(fakeVolume.StreamInCallCount()).To(Equal(1))
   163  						})
   164  
   165  						Context("when the request succeeds", func() {
   166  
   167  							It("returns 201 Created", func() {
   168  								Expect(response.StatusCode).To(Equal(http.StatusCreated))
   169  							})
   170  
   171  							It("returns Content-Type 'application/json'", func() {
   172  								expectedHeaderEntries := map[string]string{
   173  									"Content-Type": "application/json",
   174  								}
   175  								Expect(response).Should(IncludeHeaderEntries(expectedHeaderEntries))
   176  							})
   177  
   178  							It("returns the artifact record", func() {
   179  								body, err := ioutil.ReadAll(response.Body)
   180  								Expect(err).NotTo(HaveOccurred())
   181  
   182  								Expect(body).To(MatchJSON(`{
   183  									"id": 0,
   184  									"name": "",
   185  									"build_id": 0,
   186  									"created_at": 42
   187  								}`))
   188  							})
   189  						})
   190  					})
   191  				})
   192  			})
   193  		})
   194  	})
   195  
   196  	Describe("GET /api/v1/teams/:team_name/artifacts/:artifact_id", func() {
   197  		var response *http.Response
   198  
   199  		BeforeEach(func() {
   200  			fakeAccess.IsAuthenticatedReturns(true)
   201  		})
   202  
   203  		JustBeforeEach(func() {
   204  			var err error
   205  			response, err = http.Get(server.URL + "/api/v1/teams/some-team/artifacts/18")
   206  			Expect(err).NotTo(HaveOccurred())
   207  		})
   208  
   209  		Context("when not authenticated", func() {
   210  			BeforeEach(func() {
   211  				fakeAccess.IsAuthenticatedReturns(false)
   212  			})
   213  
   214  			It("returns 401 Unauthorized", func() {
   215  				Expect(response.StatusCode).To(Equal(http.StatusUnauthorized))
   216  			})
   217  		})
   218  
   219  		Context("when not authorized", func() {
   220  			BeforeEach(func() {
   221  				fakeAccess.IsAuthorizedReturns(false)
   222  			})
   223  
   224  			It("returns 403 Forbidden", func() {
   225  				Expect(response.StatusCode).To(Equal(http.StatusForbidden))
   226  			})
   227  		})
   228  
   229  		Context("when authorized", func() {
   230  			BeforeEach(func() {
   231  				fakeAccess.IsAuthorizedReturns(true)
   232  			})
   233  
   234  			It("uses the artifactID to fetch the db volume record", func() {
   235  				Expect(dbTeam.FindVolumeForWorkerArtifactCallCount()).To(Equal(1))
   236  
   237  				artifactID := dbTeam.FindVolumeForWorkerArtifactArgsForCall(0)
   238  				Expect(artifactID).To(Equal(18))
   239  			})
   240  
   241  			Context("when retrieving db artifact volume fails", func() {
   242  				BeforeEach(func() {
   243  					dbTeam.FindVolumeForWorkerArtifactReturns(nil, false, errors.New("nope"))
   244  				})
   245  
   246  				It("errors", func() {
   247  					Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
   248  				})
   249  			})
   250  
   251  			Context("when the db artifact volume is not found", func() {
   252  				BeforeEach(func() {
   253  					dbTeam.FindVolumeForWorkerArtifactReturns(nil, false, nil)
   254  				})
   255  
   256  				It("returns 404", func() {
   257  					Expect(response.StatusCode).To(Equal(http.StatusNotFound))
   258  				})
   259  			})
   260  
   261  			Context("when the db artifact volume is found", func() {
   262  				var fakeVolume *dbfakes.FakeCreatedVolume
   263  
   264  				BeforeEach(func() {
   265  					fakeVolume = new(dbfakes.FakeCreatedVolume)
   266  					fakeVolume.HandleReturns("some-handle")
   267  
   268  					dbTeam.FindVolumeForWorkerArtifactReturns(fakeVolume, true, nil)
   269  				})
   270  
   271  				It("uses the volume handle to lookup the worker volume", func() {
   272  					Expect(fakeWorkerClient.FindVolumeCallCount()).To(Equal(1))
   273  
   274  					_, teamID, handle := fakeWorkerClient.FindVolumeArgsForCall(0)
   275  					Expect(handle).To(Equal("some-handle"))
   276  					Expect(teamID).To(Equal(734))
   277  				})
   278  
   279  				Context("when the worker client errors", func() {
   280  					BeforeEach(func() {
   281  						fakeWorkerClient.FindVolumeReturns(nil, false, errors.New("nope"))
   282  					})
   283  
   284  					It("returns 500", func() {
   285  						Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
   286  					})
   287  				})
   288  
   289  				Context("when the worker client can't find the volume", func() {
   290  					BeforeEach(func() {
   291  						fakeWorkerClient.FindVolumeReturns(nil, false, nil)
   292  					})
   293  
   294  					It("returns 404", func() {
   295  						Expect(response.StatusCode).To(Equal(http.StatusNotFound))
   296  					})
   297  				})
   298  
   299  				Context("when the worker client finds the volume", func() {
   300  					var fakeWorkerVolume *workerfakes.FakeVolume
   301  
   302  					BeforeEach(func() {
   303  						reader := ioutil.NopCloser(bytes.NewReader([]byte("")))
   304  
   305  						fakeWorkerVolume = new(workerfakes.FakeVolume)
   306  						fakeWorkerVolume.StreamOutReturns(reader, nil)
   307  
   308  						fakeWorkerClient.FindVolumeReturns(fakeWorkerVolume, true, nil)
   309  					})
   310  
   311  					It("streams out the contents of the volume from the root path", func() {
   312  						Expect(fakeWorkerVolume.StreamOutCallCount()).To(Equal(1))
   313  
   314  						_, path, encoding := fakeWorkerVolume.StreamOutArgsForCall(0)
   315  						Expect(path).To(Equal("/"))
   316  						Expect(encoding).To(Equal(baggageclaim.GzipEncoding))
   317  					})
   318  
   319  					Context("when streaming volume contents fails", func() {
   320  						BeforeEach(func() {
   321  							fakeWorkerVolume.StreamOutReturns(nil, errors.New("nope"))
   322  						})
   323  
   324  						It("returns 500", func() {
   325  							Expect(response.StatusCode).To(Equal(http.StatusInternalServerError))
   326  						})
   327  					})
   328  
   329  					Context("when streaming volume contents succeeds", func() {
   330  						BeforeEach(func() {
   331  							reader := ioutil.NopCloser(bytes.NewReader([]byte("some-content")))
   332  							fakeWorkerVolume.StreamOutReturns(reader, nil)
   333  						})
   334  
   335  						It("returns 200", func() {
   336  							Expect(response.StatusCode).To(Equal(http.StatusOK))
   337  						})
   338  
   339  						It("returns Content-Type 'application/octet-stream'", func() {
   340  							expectedHeaderEntries := map[string]string{
   341  								"Content-Type": "application/octet-stream",
   342  							}
   343  							Expect(response).Should(IncludeHeaderEntries(expectedHeaderEntries))
   344  						})
   345  
   346  						It("returns the contents of the volume", func() {
   347  							Expect(ioutil.ReadAll(response.Body)).To(Equal([]byte("some-content")))
   348  						})
   349  					})
   350  				})
   351  			})
   352  		})
   353  	})
   354  })