builds_spec.rb 14.3 KB
Newer Older
1
2
require 'spec_helper'

3
describe API::Builds, api: true do
4
5
6
  include ApiHelpers

  let(:user) { create(:user) }
Kamil Trzcinski's avatar
Kamil Trzcinski committed
7
  let(:api_user) { user }
8
  let!(:project) { create(:project, :repository, creator: user, public_builds: false) }
9
  let!(:developer) { create(:project_member, :developer, user: user, project: project) }
10
11
  let(:reporter) { create(:project_member, :reporter, project: project) }
  let(:guest) { create(:project_member, :guest, project: project) }
12
  let!(:pipeline) { create(:ci_empty_pipeline, project: project, sha: project.commit.id, ref: project.default_branch) }
13
  let!(:build) { create(:ci_build, pipeline: pipeline) }
14
15

  describe 'GET /projects/:id/builds ' do
Kamil Trzcinski's avatar
Kamil Trzcinski committed
16
17
    let(:query) { '' }

Lin Jen-Shin's avatar
Lin Jen-Shin committed
18
    before do
19
20
      create(:ci_build, :skipped, pipeline: pipeline)

Lin Jen-Shin's avatar
Lin Jen-Shin committed
21
22
      get api("/projects/#{project.id}/builds?#{query}", api_user)
    end
Kamil Trzcinski's avatar
Kamil Trzcinski committed
23

24
    context 'authorized user' do
25
      it 'returns project builds' do
26
        expect(response).to have_http_status(200)
27
        expect(response).to include_pagination_headers
28
29
30
        expect(json_response).to be_an Array
      end

31
32
33
34
35
      it 'returns correct values' do
        expect(json_response).not_to be_empty
        expect(json_response.first['commit']['id']).to eq project.commit.id
      end

36
37
38
39
40
41
42
43
44
      it 'returns pipeline data' do
        json_build = json_response.first
        expect(json_build['pipeline']).not_to be_empty
        expect(json_build['pipeline']['id']).to eq build.pipeline.id
        expect(json_build['pipeline']['ref']).to eq build.pipeline.ref
        expect(json_build['pipeline']['sha']).to eq build.pipeline.sha
        expect(json_build['pipeline']['status']).to eq build.pipeline.status
      end

Kamil Trzcinski's avatar
Kamil Trzcinski committed
45
46
      context 'filter project with one scope element' do
        let(:query) { 'scope=pending' }
47

Kamil Trzcinski's avatar
Kamil Trzcinski committed
48
        it do
49
          expect(response).to have_http_status(200)
Kamil Trzcinski's avatar
Kamil Trzcinski committed
50
51
          expect(json_response).to be_an Array
        end
52
53
      end

54
55
56
57
58
59
60
61
62
63
64
65
      context 'filter project with scope skipped' do
        let(:query) { 'scope=skipped' }
        let(:json_build) { json_response.first }

        it 'return builds with status skipped' do
          expect(response).to have_http_status 200
          expect(json_response).to be_an Array
          expect(json_response.length).to eq 1
          expect(json_build['status']).to eq 'skipped'
        end
      end

Kamil Trzcinski's avatar
Kamil Trzcinski committed
66
67
      context 'filter project with array of scope elements' do
        let(:query) { 'scope[0]=pending&scope[1]=running' }
68

Kamil Trzcinski's avatar
Kamil Trzcinski committed
69
        it do
70
          expect(response).to have_http_status(200)
Kamil Trzcinski's avatar
Kamil Trzcinski committed
71
72
          expect(json_response).to be_an Array
        end
73
      end
Tomasz Maczukin's avatar
Tomasz Maczukin committed
74

Kamil Trzcinski's avatar
Kamil Trzcinski committed
75
76
      context 'respond 400 when scope contains invalid state' do
        let(:query) { 'scope[0]=pending&scope[1]=unknown_status' }
Tomasz Maczukin's avatar
Tomasz Maczukin committed
77

78
        it { expect(response).to have_http_status(400) }
Tomasz Maczukin's avatar
Tomasz Maczukin committed
79
      end
80
81
82
    end

    context 'unauthorized user' do
Kamil Trzcinski's avatar
Kamil Trzcinski committed
83
      let(:api_user) { nil }
84

Valery Sizov's avatar
Valery Sizov committed
85
      it 'does not return project builds' do
86
        expect(response).to have_http_status(401)
87
88
89
90
      end
    end
  end

91
  describe 'GET /projects/:id/repository/commits/:sha/builds' do
92
93
94
95
    context 'when commit does not exist in repository' do
      before do
        get api("/projects/#{project.id}/repository/commits/1a271fd1/builds", api_user)
      end
96

97
98
99
      it 'responds with 404' do
        expect(response).to have_http_status(404)
      end
Kamil Trzcinski's avatar
Kamil Trzcinski committed
100
101
    end

102
103
    context 'when commit exists in repository' do
      context 'when user is authorized' do
Filipa Lacerda's avatar
Filipa Lacerda committed
104
        context 'when pipeline has jobs' do
105
          before do
Lin Jen-Shin's avatar
Lin Jen-Shin committed
106
            create(:ci_pipeline, project: project, sha: project.commit.id)
107
            create(:ci_build, pipeline: pipeline)
Lin Jen-Shin's avatar
Lin Jen-Shin committed
108
            create(:ci_build)
109
110
111
112

            get api("/projects/#{project.id}/repository/commits/#{project.commit.id}/builds", api_user)
          end

Filipa Lacerda's avatar
Filipa Lacerda committed
113
          it 'returns project jobs for specific commit' do
114
            expect(response).to have_http_status(200)
115
            expect(response).to include_pagination_headers
116
117
118
            expect(json_response).to be_an Array
            expect(json_response.size).to eq 2
          end
119
120
121
122
123
124
125
126
127

          it 'returns pipeline data' do
            json_build = json_response.first
            expect(json_build['pipeline']).not_to be_empty
            expect(json_build['pipeline']['id']).to eq build.pipeline.id
            expect(json_build['pipeline']['ref']).to eq build.pipeline.ref
            expect(json_build['pipeline']['sha']).to eq build.pipeline.sha
            expect(json_build['pipeline']['status']).to eq build.pipeline.status
          end
128
129
        end

Filipa Lacerda's avatar
Filipa Lacerda committed
130
        context 'when pipeline has no jobs' do
131
132
133
134
135
136
137
138
139
140
141
          before do
            branch_head = project.commit('feature').id
            get api("/projects/#{project.id}/repository/commits/#{branch_head}/builds", api_user)
          end

          it 'returns an empty array' do
            expect(response).to have_http_status(200)
            expect(json_response).to be_an Array
            expect(json_response).to be_empty
          end
        end
142
143
      end

144
145
      context 'when user is not authorized' do
        before do
Lin Jen-Shin's avatar
Lin Jen-Shin committed
146
147
          create(:ci_pipeline, project: project, sha: project.commit.id)
          create(:ci_build, pipeline: pipeline)
148

149
150
151
          get api("/projects/#{project.id}/repository/commits/#{project.commit.id}/builds", nil)
        end

Filipa Lacerda's avatar
Filipa Lacerda committed
152
        it 'does not return project jobs' do
153
154
155
          expect(response).to have_http_status(401)
          expect(json_response.except('message')).to be_empty
        end
156
157
158
      end
    end
  end
159

160
  describe 'GET /projects/:id/builds/:build_id' do
Lin Jen-Shin's avatar
Lin Jen-Shin committed
161
162
163
    before do
      get api("/projects/#{project.id}/builds/#{build.id}", api_user)
    end
Kamil Trzcinski's avatar
Kamil Trzcinski committed
164

165
    context 'authorized user' do
Filipa Lacerda's avatar
Filipa Lacerda committed
166
      it 'returns specific job data' do
167
        expect(response).to have_http_status(200)
168
169
        expect(json_response['name']).to eq('test')
      end
170
171
172
173
174
175
176
177
178

      it 'returns pipeline data' do
        json_build = json_response
        expect(json_build['pipeline']).not_to be_empty
        expect(json_build['pipeline']['id']).to eq build.pipeline.id
        expect(json_build['pipeline']['ref']).to eq build.pipeline.ref
        expect(json_build['pipeline']['sha']).to eq build.pipeline.sha
        expect(json_build['pipeline']['status']).to eq build.pipeline.status
      end
179
180
181
    end

    context 'unauthorized user' do
Kamil Trzcinski's avatar
Kamil Trzcinski committed
182
      let(:api_user) { nil }
183

Filipa Lacerda's avatar
Filipa Lacerda committed
184
      it 'does not return specific job data' do
185
        expect(response).to have_http_status(401)
186
187
188
189
      end
    end
  end

190
  describe 'GET /projects/:id/builds/:build_id/artifacts' do
Lin Jen-Shin's avatar
Lin Jen-Shin committed
191
192
193
    before do
      get api("/projects/#{project.id}/builds/#{build.id}/artifacts", api_user)
    end
194

Filipa Lacerda's avatar
Filipa Lacerda committed
195
    context 'job with artifacts' do
196
      let(:build) { create(:ci_build, :artifacts, pipeline: pipeline) }
197

Kamil Trzcinski's avatar
Kamil Trzcinski committed
198
199
      context 'authorized user' do
        let(:download_headers) do
200
201
          { 'Content-Transfer-Encoding' => 'binary',
            'Content-Disposition' => 'attachment; filename=ci_build_artifacts.zip' }
Kamil Trzcinski's avatar
Kamil Trzcinski committed
202
        end
203

Filipa Lacerda's avatar
Filipa Lacerda committed
204
        it 'returns specific job artifacts' do
205
          expect(response).to have_http_status(200)
Kamil Trzcinski's avatar
Kamil Trzcinski committed
206
          expect(response.headers).to include(download_headers)
207
          expect(response.body).to match_file(build.artifacts_file.file.file)
Kamil Trzcinski's avatar
Kamil Trzcinski committed
208
        end
209
210
      end

Kamil Trzcinski's avatar
Kamil Trzcinski committed
211
212
      context 'unauthorized user' do
        let(:api_user) { nil }
213

Filipa Lacerda's avatar
Filipa Lacerda committed
214
        it 'does not return specific job artifacts' do
215
          expect(response).to have_http_status(401)
Kamil Trzcinski's avatar
Kamil Trzcinski committed
216
        end
217
218
      end
    end
Kamil Trzcinski's avatar
Kamil Trzcinski committed
219

Filipa Lacerda's avatar
Filipa Lacerda committed
220
    it 'does not return job artifacts if not uploaded' do
221
      expect(response).to have_http_status(404)
Kamil Trzcinski's avatar
Kamil Trzcinski committed
222
    end
223
224
  end

225
  describe 'GET /projects/:id/artifacts/:ref_name/download?job=name' do
226
    let(:api_user) { reporter.user }
227
228
229
230
231
    let(:build) { create(:ci_build, :artifacts, pipeline: pipeline) }

    before do
      build.success
    end
232

233
    def path_for_ref(ref = pipeline.ref, job = build.name)
234
      api("/projects/#{project.id}/builds/artifacts/#{ref}/download?job=#{job}", api_user)
235
236
    end

237
    context 'when not logged in' do
238
      let(:api_user) { nil }
239
240

      before do
241
        get path_for_ref
242
243
      end

244
      it 'gives 401' do
245
246
247
248
        expect(response).to have_http_status(401)
      end
    end

249
    context 'when logging as guest' do
250
      let(:api_user) { guest.user }
251
252
253
254
255
256
257
258
259
260

      before do
        get path_for_ref
      end

      it 'gives 403' do
        expect(response).to have_http_status(403)
      end
    end

Filipa Lacerda's avatar
Filipa Lacerda committed
261
    context 'non-existing job' do
Lin Jen-Shin's avatar
Lin Jen-Shin committed
262
263
      shared_examples 'not found' do
        it { expect(response).to have_http_status(:not_found) }
264
265
      end

266
267
      context 'has no such ref' do
        before do
268
          get path_for_ref('TAIL', build.name)
269
270
        end

Lin Jen-Shin's avatar
Lin Jen-Shin committed
271
        it_behaves_like 'not found'
272
273
      end

Filipa Lacerda's avatar
Filipa Lacerda committed
274
      context 'has no such job' do
275
        before do
276
          get path_for_ref(pipeline.ref, 'NOBUILD')
277
278
        end

Lin Jen-Shin's avatar
Lin Jen-Shin committed
279
        it_behaves_like 'not found'
280
      end
281
282
    end

Filipa Lacerda's avatar
Filipa Lacerda committed
283
    context 'find proper job' do
284
      shared_examples 'a valid file' do
Lin Jen-Shin's avatar
Lin Jen-Shin committed
285
        let(:download_headers) do
286
287
288
          { 'Content-Transfer-Encoding' => 'binary',
            'Content-Disposition' =>
              "attachment; filename=#{build.artifacts_file.filename}" }
Lin Jen-Shin's avatar
Lin Jen-Shin committed
289
        end
290

291
292
        it { expect(response).to have_http_status(200) }
        it { expect(response.headers).to include(download_headers) }
293
294
295
296
      end

      context 'with regular branch' do
        before do
Kamil Trzcinski's avatar
Kamil Trzcinski committed
297
          pipeline.reload
298
299
300
          pipeline.update(ref: 'master',
                          sha: project.commit('master').sha)

301
          get path_for_ref('master')
302
303
        end

304
        it_behaves_like 'a valid file'
305
306
      end

307
308
      context 'with branch name containing slash' do
        before do
Kamil Trzcinski's avatar
Kamil Trzcinski committed
309
          pipeline.reload
310
311
312
313
314
          pipeline.update(ref: 'improve/awesome',
                          sha: project.commit('improve/awesome').sha)
        end

        before do
315
          get path_for_ref('improve/awesome')
316
317
        end

318
        it_behaves_like 'a valid file'
319
      end
320
321
322
    end
  end

323
  describe 'GET /projects/:id/builds/:build_id/trace' do
324
    let(:build) { create(:ci_build, :trace, pipeline: pipeline) }
325

Lin Jen-Shin's avatar
Lin Jen-Shin committed
326
327
328
    before do
      get api("/projects/#{project.id}/builds/#{build.id}/trace", api_user)
    end
Kamil Trzcinski's avatar
Kamil Trzcinski committed
329

330
    context 'authorized user' do
Filipa Lacerda's avatar
Filipa Lacerda committed
331
      it 'returns specific job trace' do
332
        expect(response).to have_http_status(200)
Kamil Trzcinski's avatar
Kamil Trzcinski committed
333
        expect(response.body).to eq(build.trace)
334
335
336
337
      end
    end

    context 'unauthorized user' do
Kamil Trzcinski's avatar
Kamil Trzcinski committed
338
      let(:api_user) { nil }
339

Filipa Lacerda's avatar
Filipa Lacerda committed
340
      it 'does not return specific job trace' do
341
        expect(response).to have_http_status(401)
342
343
344
      end
    end
  end
345

346
  describe 'POST /projects/:id/builds/:build_id/cancel' do
Lin Jen-Shin's avatar
Lin Jen-Shin committed
347
348
349
    before do
      post api("/projects/#{project.id}/builds/#{build.id}/cancel", api_user)
    end
Kamil Trzcinski's avatar
Kamil Trzcinski committed
350

351
    context 'authorized user' do
352
      context 'user with :update_build persmission' do
Filipa Lacerda's avatar
Filipa Lacerda committed
353
        it 'cancels running or pending job' do
354
          expect(response).to have_http_status(201)
355
356
357
358
          expect(project.builds.first.status).to eq('canceled')
        end
      end

359
      context 'user without :update_build permission' do
360
        let(:api_user) { reporter.user }
361

Filipa Lacerda's avatar
Filipa Lacerda committed
362
        it 'does not cancel job' do
363
          expect(response).to have_http_status(403)
364
365
366
367
368
        end
      end
    end

    context 'unauthorized user' do
Kamil Trzcinski's avatar
Kamil Trzcinski committed
369
      let(:api_user) { nil }
370

Filipa Lacerda's avatar
Filipa Lacerda committed
371
      it 'does not cancel job' do
372
        expect(response).to have_http_status(401)
373
374
375
376
      end
    end
  end

377
  describe 'POST /projects/:id/builds/:build_id/retry' do
378
    let(:build) { create(:ci_build, :canceled, pipeline: pipeline) }
Kamil Trzcinski's avatar
Kamil Trzcinski committed
379

Lin Jen-Shin's avatar
Lin Jen-Shin committed
380
381
382
    before do
      post api("/projects/#{project.id}/builds/#{build.id}/retry", api_user)
    end
Kamil Trzcinski's avatar
Kamil Trzcinski committed
383

384
    context 'authorized user' do
Kamil Trzcinski's avatar
Kamil Trzcinski committed
385
      context 'user with :update_build permission' do
Filipa Lacerda's avatar
Filipa Lacerda committed
386
        it 'retries non-running job' do
387
          expect(response).to have_http_status(201)
388
389
390
391
392
          expect(project.builds.first.status).to eq('canceled')
          expect(json_response['status']).to eq('pending')
        end
      end

393
      context 'user without :update_build permission' do
394
        let(:api_user) { reporter.user }
395

Filipa Lacerda's avatar
Filipa Lacerda committed
396
        it 'does not retry job' do
397
          expect(response).to have_http_status(403)
398
399
400
401
402
        end
      end
    end

    context 'unauthorized user' do
Kamil Trzcinski's avatar
Kamil Trzcinski committed
403
      let(:api_user) { nil }
404

Filipa Lacerda's avatar
Filipa Lacerda committed
405
      it 'does not retry job' do
406
        expect(response).to have_http_status(401)
407
408
409
      end
    end
  end
410

411
  describe 'POST /projects/:id/builds/:build_id/erase' do
412
    before do
413
      post api("/projects/#{project.id}/builds/#{build.id}/erase", user)
414
415
    end

Filipa Lacerda's avatar
Filipa Lacerda committed
416
    context 'job is erasable' do
417
      let(:build) { create(:ci_build, :trace, :artifacts, :success, project: project, pipeline: pipeline) }
418

Filipa Lacerda's avatar
Filipa Lacerda committed
419
      it 'erases job content' do
420
        expect(response.status).to eq 201
421
422
423
424
        expect(build.trace).to be_empty
        expect(build.artifacts_file.exists?).to be_falsy
        expect(build.artifacts_metadata.exists?).to be_falsy
      end
425

Filipa Lacerda's avatar
Filipa Lacerda committed
426
      it 'updates job' do
427
428
429
        expect(build.reload.erased_at).to be_truthy
        expect(build.reload.erased_by).to eq user
      end
430
431
    end

Filipa Lacerda's avatar
Filipa Lacerda committed
432
    context 'job is not erasable' do
433
      let(:build) { create(:ci_build, :trace, project: project, pipeline: pipeline) }
434

435
      it 'responds with forbidden' do
436
437
438
439
        expect(response.status).to eq 403
      end
    end
  end
440
441
442
443
444
445
446
447
448
449
450
451

  describe 'POST /projects/:id/builds/:build_id/artifacts/keep' do
    before do
      post api("/projects/#{project.id}/builds/#{build.id}/artifacts/keep", user)
    end

    context 'artifacts did not expire' do
      let(:build) do
        create(:ci_build, :trace, :artifacts, :success,
               project: project, pipeline: pipeline, artifacts_expire_at: Time.now + 7.days)
      end

452
      it 'keeps artifacts' do
453
        expect(response.status).to eq 200
454
        expect(build.reload.artifacts_expire_at).to be_nil
455
456
457
458
459
460
      end
    end

    context 'no artifacts' do
      let(:build) { create(:ci_build, project: project, pipeline: pipeline) }

461
      it 'responds with not found' do
462
463
464
465
        expect(response.status).to eq 404
      end
    end
  end
Z.J. van de Weg's avatar
Z.J. van de Weg committed
466
467
468
469
470
471

  describe 'POST /projects/:id/builds/:build_id/play' do
    before do
      post api("/projects/#{project.id}/builds/#{build.id}/play", user)
    end

Filipa Lacerda's avatar
Filipa Lacerda committed
472
    context 'on an playable job' do
Z.J. van de Weg's avatar
Z.J. van de Weg committed
473
474
      let(:build) { create(:ci_build, :manual, project: project, pipeline: pipeline) }

Filipa Lacerda's avatar
Filipa Lacerda committed
475
      it 'plays the job' do
Z.J. van de Weg's avatar
Z.J. van de Weg committed
476
477
        expect(response).to have_http_status 200
        expect(json_response['user']['id']).to eq(user.id)
Z.J. van de Weg's avatar
Z.J. van de Weg committed
478
        expect(json_response['id']).to eq(build.id)
Z.J. van de Weg's avatar
Z.J. van de Weg committed
479
480
481
      end
    end

Filipa Lacerda's avatar
Filipa Lacerda committed
482
    context 'on a non-playable job' do
Z.J. van de Weg's avatar
Z.J. van de Weg committed
483
484
      it 'returns a status code 400, Bad Request' do
        expect(response).to have_http_status 400
Filipa Lacerda's avatar
Filipa Lacerda committed
485
        expect(response.body).to match("Unplayable Job")
Z.J. van de Weg's avatar
Z.J. van de Weg committed
486
487
488
      end
    end
  end
489
end