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 })