jobs_spec.rb 22.5 KB
Newer Older
1 2
require 'spec_helper'

3
describe API::Jobs do
4 5
  include HttpIOHelpers

6
  set(:project) do
7 8 9
    create(:project, :repository, public_builds: false)
  end

10
  set(:pipeline) do
11 12 13 14 15
    create(:ci_empty_pipeline, project: project,
                               sha: project.commit.id,
                               ref: project.default_branch)
  end

Kamil Trzciński's avatar
Kamil Trzciński committed
16
  let!(:job) { create(:ci_build, :success, pipeline: pipeline) }
17 18

  let(:user) { create(:user) }
Kamil Trzcinski's avatar
Kamil Trzcinski committed
19
  let(:api_user) { user }
20 21 22 23 24 25
  let(:reporter) { create(:project_member, :reporter, project: project).user }
  let(:guest) { create(:project_member, :guest, project: project).user }

  before do
    project.add_developer(user)
  end
26

27 28
  describe 'GET /projects/:id/jobs' do
    let(:query) { Hash.new }
Kamil Trzcinski's avatar
Kamil Trzcinski committed
29

30 31 32 33
    before do |example|
      unless example.metadata[:skip_before_request]
        get api("/projects/#{project.id}/jobs", api_user), query
      end
Lin Jen-Shin's avatar
Lin Jen-Shin committed
34
    end
Kamil Trzcinski's avatar
Kamil Trzcinski committed
35

36
    context 'authorized user' do
37
      it 'returns project jobs' do
38
        expect(response).to have_gitlab_http_status(200)
39
        expect(response).to include_pagination_headers
40 41 42
        expect(json_response).to be_an Array
      end

43 44 45 46 47
      it 'returns correct values' do
        expect(json_response).not_to be_empty
        expect(json_response.first['commit']['id']).to eq project.commit.id
      end

48
      it 'returns pipeline data' do
49
        json_job = json_response.first
50

51 52 53 54 55
        expect(json_job['pipeline']).not_to be_empty
        expect(json_job['pipeline']['id']).to eq job.pipeline.id
        expect(json_job['pipeline']['ref']).to eq job.pipeline.ref
        expect(json_job['pipeline']['sha']).to eq job.pipeline.sha
        expect(json_job['pipeline']['status']).to eq job.pipeline.status
56 57
      end

58
      it 'avoids N+1 queries', :skip_before_request do
59 60 61 62 63
        first_build = create(:ci_build, :artifacts, pipeline: pipeline)
        first_build.runner = create(:ci_runner)
        first_build.user = create(:user)
        first_build.save

64
        control_count = ActiveRecord::QueryRecorder.new { go }.count
65 66 67 68 69 70 71

        second_pipeline = create(:ci_empty_pipeline, project: project, sha: project.commit.id, ref: project.default_branch)
        second_build = create(:ci_build, :artifacts, pipeline: second_pipeline)
        second_build.runner = create(:ci_runner)
        second_build.user = create(:user)
        second_build.save

72 73 74
        expect { go }.not_to exceed_query_limit(control_count)
      end

Kamil Trzcinski's avatar
Kamil Trzcinski committed
75
      context 'filter project with one scope element' do
76
        let(:query) { { 'scope' => 'pending' } }
77

Kamil Trzcinski's avatar
Kamil Trzcinski committed
78
        it do
79
          expect(response).to have_gitlab_http_status(200)
Kamil Trzcinski's avatar
Kamil Trzcinski committed
80 81
          expect(json_response).to be_an Array
        end
82 83
      end

Kamil Trzcinski's avatar
Kamil Trzcinski committed
84
      context 'filter project with array of scope elements' do
85
        let(:query) { { scope: %w(pending running) } }
86

Kamil Trzcinski's avatar
Kamil Trzcinski committed
87
        it do
88
          expect(response).to have_gitlab_http_status(200)
Kamil Trzcinski's avatar
Kamil Trzcinski committed
89 90
          expect(json_response).to be_an Array
        end
91
      end
92

Kamil Trzcinski's avatar
Kamil Trzcinski committed
93
      context 'respond 400 when scope contains invalid state' do
94
        let(:query) { { scope: %w(unknown running) } }
95

96
        it { expect(response).to have_gitlab_http_status(400) }
97
      end
98 99 100
    end

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

103
      it 'does not return project jobs' do
104
        expect(response).to have_gitlab_http_status(401)
105 106
      end
    end
107

108 109 110
    def go
      get api("/projects/#{project.id}/jobs", api_user), query
    end
111 112
  end

113 114 115 116
  describe 'GET /projects/:id/pipelines/:pipeline_id/jobs' do
    let(:query) { Hash.new }

    before do
117
      job
118 119 120 121 122
      get api("/projects/#{project.id}/pipelines/#{pipeline.id}/jobs", api_user), query
    end

    context 'authorized user' do
      it 'returns pipeline jobs' do
123
        expect(response).to have_gitlab_http_status(200)
124 125 126 127 128 129 130 131 132 133
        expect(response).to include_pagination_headers
        expect(json_response).to be_an Array
      end

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

      it 'returns pipeline data' do
134
        json_job = json_response.first
135

136 137 138 139 140
        expect(json_job['pipeline']).not_to be_empty
        expect(json_job['pipeline']['id']).to eq job.pipeline.id
        expect(json_job['pipeline']['ref']).to eq job.pipeline.ref
        expect(json_job['pipeline']['sha']).to eq job.pipeline.sha
        expect(json_job['pipeline']['status']).to eq job.pipeline.status
141 142 143 144 145 146
      end

      context 'filter jobs with one scope element' do
        let(:query) { { 'scope' => 'pending' } }

        it do
147
          expect(response).to have_gitlab_http_status(200)
148 149 150 151 152
          expect(json_response).to be_an Array
        end
      end

      context 'filter jobs with array of scope elements' do
153
        let(:query) { { scope: %w(pending running) } }
154 155

        it do
156
          expect(response).to have_gitlab_http_status(200)
157 158 159 160 161
          expect(json_response).to be_an Array
        end
      end

      context 'respond 400 when scope contains invalid state' do
162
        let(:query) { { scope: %w(unknown running) } }
163

164
        it { expect(response).to have_gitlab_http_status(400) }
165 166 167 168
      end

      context 'jobs in different pipelines' do
        let!(:pipeline2) { create(:ci_empty_pipeline, project: project) }
169
        let!(:job2) { create(:ci_build, pipeline: pipeline2) }
170 171 172 173 174 175 176 177 178 179 180

        it 'excludes jobs from other pipelines' do
          json_response.each { |job| expect(job['pipeline']['id']).to eq(pipeline.id) }
        end
      end
    end

    context 'unauthorized user' do
      let(:api_user) { nil }

      it 'does not return jobs' do
181
        expect(response).to have_gitlab_http_status(401)
182 183 184 185
      end
    end
  end

186
  describe 'GET /projects/:id/jobs/:job_id' do
Lin Jen-Shin's avatar
Lin Jen-Shin committed
187
    before do
188
      get api("/projects/#{project.id}/jobs/#{job.id}", api_user)
Lin Jen-Shin's avatar
Lin Jen-Shin committed
189
    end
Kamil Trzcinski's avatar
Kamil Trzcinski committed
190

191
    context 'authorized user' do
Filipa Lacerda's avatar
Filipa Lacerda committed
192
      it 'returns specific job data' do
193
        expect(response).to have_gitlab_http_status(200)
194 195 196 197 198 199 200 201 202 203 204
        expect(json_response['id']).to eq(job.id)
        expect(json_response['status']).to eq(job.status)
        expect(json_response['stage']).to eq(job.stage)
        expect(json_response['name']).to eq(job.name)
        expect(json_response['ref']).to eq(job.ref)
        expect(json_response['tag']).to eq(job.tag)
        expect(json_response['coverage']).to eq(job.coverage)
        expect(Time.parse(json_response['created_at'])).to be_like_time(job.created_at)
        expect(Time.parse(json_response['started_at'])).to be_like_time(job.started_at)
        expect(Time.parse(json_response['finished_at'])).to be_like_time(job.finished_at)
        expect(json_response['duration']).to eq(job.duration)
205
      end
206 207

      it 'returns pipeline data' do
208 209 210 211 212 213 214
        json_job = json_response

        expect(json_job['pipeline']).not_to be_empty
        expect(json_job['pipeline']['id']).to eq job.pipeline.id
        expect(json_job['pipeline']['ref']).to eq job.pipeline.ref
        expect(json_job['pipeline']['sha']).to eq job.pipeline.sha
        expect(json_job['pipeline']['status']).to eq job.pipeline.status
215
      end
216 217 218
    end

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

Filipa Lacerda's avatar
Filipa Lacerda committed
221
      it 'does not return specific job data' do
222
        expect(response).to have_gitlab_http_status(401)
223 224 225 226
      end
    end
  end

227 228 229 230 231 232 233 234
  describe 'GET /projects/:id/jobs/:job_id/artifacts/:artifact_path' do
    context 'when job has artifacts' do
      let(:job) { create(:ci_build, :artifacts, pipeline: pipeline) }

      let(:artifact) do
        'other_artifacts_0.1.2/another-subdirectory/banana_sample.gif'
      end

235
      context 'when user is anonymous' do
236 237
        let(:api_user) { nil }

238 239 240 241 242 243 244 245
        context 'when project is public' do
          it 'allows to access artifacts' do
            project.update_column(:visibility_level,
                                  Gitlab::VisibilityLevel::PUBLIC)
            project.update_column(:public_builds, true)

            get_artifact_file(artifact)

246
            expect(response).to have_gitlab_http_status(200)
247 248 249 250 251 252 253 254 255 256 257
          end
        end

        context 'when project is public with builds access disabled' do
          it 'rejects access to artifacts' do
            project.update_column(:visibility_level,
                                  Gitlab::VisibilityLevel::PUBLIC)
            project.update_column(:public_builds, false)

            get_artifact_file(artifact)

258
            expect(response).to have_gitlab_http_status(403)
259 260 261 262 263 264 265 266 267 268
          end
        end

        context 'when project is private' do
          it 'rejects access and hides existence of artifacts' do
            project.update_column(:visibility_level,
                                  Gitlab::VisibilityLevel::PRIVATE)
            project.update_column(:public_builds, true)

            get_artifact_file(artifact)
269

270
            expect(response).to have_gitlab_http_status(404)
271
          end
272 273 274 275 276 277 278 279 280 281 282
        end
      end

      context 'when user is authorized' do
        it 'returns a specific artifact file for a valid path' do
          expect(Gitlab::Workhorse)
            .to receive(:send_artifacts_entry)
            .and_call_original

          get_artifact_file(artifact)

283
          expect(response).to have_gitlab_http_status(200)
284
          expect(response.headers.to_h)
285 286
            .to include('Content-Type' => 'application/json',
                        'Gitlab-Workhorse-Send-Data' => /artifacts-entry/)
287 288 289 290 291 292 293 294
        end
      end
    end

    context 'when job does not have artifacts' do
      it 'does not return job artifact file' do
        get_artifact_file('some/artifact')

295
        expect(response).to have_gitlab_http_status(404)
296 297 298 299 300 301 302 303 304
      end
    end

    def get_artifact_file(artifact_path)
      get api("/projects/#{project.id}/jobs/#{job.id}/" \
              "artifacts/#{artifact_path}", api_user)
    end
  end

305
  describe 'GET /projects/:id/jobs/:job_id/artifacts' do
Micaël Bergeron's avatar
Micaël Bergeron committed
306 307 308 309 310 311 312 313
    shared_examples 'downloads artifact' do
      let(:download_headers) do
        { 'Content-Transfer-Encoding' => 'binary',
          'Content-Disposition' => 'attachment; filename=ci_build_artifacts.zip' }
      end

      it 'returns specific job artifacts' do
        expect(response).to have_gitlab_http_status(200)
314
        expect(response.headers.to_h).to include(download_headers)
Micaël Bergeron's avatar
Micaël Bergeron committed
315 316 317 318
        expect(response.body).to match_file(job.artifacts_file.file.file)
      end
    end

319 320 321 322 323 324 325 326 327 328 329
    context 'normal authentication' do
      context 'job with artifacts' do
        context 'when artifacts are stored locally' do
          let(:job) { create(:ci_build, :artifacts, pipeline: pipeline) }

          before do
            get api("/projects/#{project.id}/jobs/#{job.id}/artifacts", api_user)
          end

          context 'authorized user' do
            it_behaves_like 'downloads artifact'
330 331
          end

Micaël Bergeron's avatar
Micaël Bergeron committed
332 333 334 335 336 337
          context 'unauthorized user' do
            let(:api_user) { nil }

            it 'does not return specific job artifacts' do
              expect(response).to have_gitlab_http_status(404)
            end
338
          end
Kamil Trzcinski's avatar
Kamil Trzcinski committed
339
        end
340

341
        context 'when artifacts are stored remotely' do
342 343
          let(:proxy_download) { false }

Micaël Bergeron's avatar
Micaël Bergeron committed
344
          before do
345
            stub_artifacts_object_storage(proxy_download: proxy_download)
Micaël Bergeron's avatar
Micaël Bergeron committed
346 347
          end

348 349 350 351 352 353 354 355
          let(:job) { create(:ci_build, pipeline: pipeline) }
          let!(:artifact) { create(:ci_job_artifact, :archive, :remote_store, job: job) }

          before do
            job.reload

            get api("/projects/#{project.id}/jobs/#{job.id}/artifacts", api_user)
          end
356

357 358 359 360 361 362 363 364 365 366 367 368 369 370
          context 'when proxy download is enabled' do
            let(:proxy_download) { true }

            it 'responds with the workhorse send-url' do
              expect(response.headers[Gitlab::Workhorse::SEND_DATA_HEADER]).to start_with("send-url:")
            end
          end

          context 'when proxy download is disabled' do
            it 'returns location redirect' do
              expect(response).to have_gitlab_http_status(302)
            end
          end

Micaël Bergeron's avatar
Micaël Bergeron committed
371 372 373 374 375 376 377 378 379 380 381 382
          context 'authorized user' do
            it 'returns the file remote URL' do
              expect(response).to redirect_to(artifact.file.url)
            end
          end

          context 'unauthorized user' do
            let(:api_user) { nil }

            it 'does not return specific job artifacts' do
              expect(response).to have_gitlab_http_status(404)
            end
383
          end
Kamil Trzcinski's avatar
Kamil Trzcinski committed
384
        end
385 386 387 388

        it 'does not return job artifacts if not uploaded' do
          get api("/projects/#{project.id}/jobs/#{job.id}/artifacts", api_user)

Micaël Bergeron's avatar
Micaël Bergeron committed
389
          expect(response).to have_gitlab_http_status(:not_found)
390 391 392
        end
      end
    end
393 394
  end

395
  describe 'GET /projects/:id/artifacts/:ref_name/download?job=name' do
396
    let(:api_user) { reporter }
397
    let(:job) { create(:ci_build, :artifacts, pipeline: pipeline, user: api_user) }
398 399

    before do
Micaël Bergeron's avatar
Micaël Bergeron committed
400
      stub_artifacts_object_storage
401
      job.success
402
    end
403

404 405
    def get_for_ref(ref = pipeline.ref, job_name = job.name)
      get api("/projects/#{project.id}/jobs/artifacts/#{ref}/download", api_user), job: job_name
406 407
    end

408
    context 'when not logged in' do
409
      let(:api_user) { nil }
410 411

      before do
412
        get_for_ref
413 414
      end

415 416
      it 'does not find a resource in a private project' do
        expect(project).to be_private
417
        expect(response).to have_gitlab_http_status(404)
418 419 420
      end
    end

421
    context 'when logging as guest' do
422
      let(:api_user) { guest }
423 424

      before do
425
        get_for_ref
426 427 428
      end

      it 'gives 403' do
429
        expect(response).to have_gitlab_http_status(403)
430 431 432
      end
    end

Filipa Lacerda's avatar
Filipa Lacerda committed
433
    context 'non-existing job' do
434
      shared_examples 'not found' do
435
        it { expect(response).to have_gitlab_http_status(:not_found) }
436 437
      end

438 439
      context 'has no such ref' do
        before do
440
          get_for_ref('TAIL')
441 442
        end

443
        it_behaves_like 'not found'
444 445
      end

Filipa Lacerda's avatar
Filipa Lacerda committed
446
      context 'has no such job' do
447
        before do
448
          get_for_ref(pipeline.ref, 'NOBUILD')
449 450
        end

451
        it_behaves_like 'not found'
452
      end
453 454
    end

Filipa Lacerda's avatar
Filipa Lacerda committed
455
    context 'find proper job' do
456
      shared_examples 'a valid file' do
457 458 459 460 461 462 463
        context 'when artifacts are stored locally' do
          let(:download_headers) do
            { 'Content-Transfer-Encoding' => 'binary',
              'Content-Disposition' =>
                "attachment; filename=#{job.artifacts_file.filename}" }
          end

Micaël Bergeron's avatar
Micaël Bergeron committed
464
          it { expect(response).to have_http_status(:ok) }
465
          it { expect(response.headers.to_h).to include(download_headers) }
466
        end
467

468
        context 'when artifacts are stored remotely' do
469 470 471 472 473 474 475 476
          let(:job) { create(:ci_build, pipeline: pipeline, user: api_user) }
          let!(:artifact) { create(:ci_job_artifact, :archive, :remote_store, job: job) }

          before do
            job.reload

            get api("/projects/#{project.id}/jobs/#{job.id}/artifacts", api_user)
          end
477 478

          it 'returns location redirect' do
Micaël Bergeron's avatar
Micaël Bergeron committed
479
            expect(response).to have_http_status(:found)
480 481
          end
        end
482 483 484 485
      end

      context 'with regular branch' do
        before do
Kamil Trzcinski's avatar
Kamil Trzcinski committed
486
          pipeline.reload
487 488 489
          pipeline.update(ref: 'master',
                          sha: project.commit('master').sha)

490
          get_for_ref('master')
491 492
        end

493
        it_behaves_like 'a valid file'
494 495
      end

496 497
      context 'with branch name containing slash' do
        before do
Kamil Trzcinski's avatar
Kamil Trzcinski committed
498
          pipeline.reload
499 500 501 502 503
          pipeline.update(ref: 'improve/awesome',
                          sha: project.commit('improve/awesome').sha)
        end

        before do
504
          get_for_ref('improve/awesome')
505 506
        end

507
        it_behaves_like 'a valid file'
508
      end
509 510 511
    end
  end

512
  describe 'GET /projects/:id/jobs/:job_id/trace' do
Lin Jen-Shin's avatar
Lin Jen-Shin committed
513
    before do
514
      get api("/projects/#{project.id}/jobs/#{job.id}/trace", api_user)
Lin Jen-Shin's avatar
Lin Jen-Shin committed
515
    end
Kamil Trzcinski's avatar
Kamil Trzcinski committed
516

517
    context 'authorized user' do
518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549
      context 'when trace is in ObjectStorage' do
        let!(:job) { create(:ci_build, :trace_artifact, pipeline: pipeline) }

        before do
          stub_remote_trace_206
          allow_any_instance_of(JobArtifactUploader).to receive(:file_storage?) { false }
          allow_any_instance_of(JobArtifactUploader).to receive(:url) { remote_trace_url }
          allow_any_instance_of(JobArtifactUploader).to receive(:size) { remote_trace_size }
        end

        it 'returns specific job trace' do
          expect(response).to have_gitlab_http_status(200)
          expect(response.body).to eq(job.trace.raw)
        end
      end

      context 'when trace is artifact' do
        let(:job) { create(:ci_build, :trace_artifact, pipeline: pipeline) }

        it 'returns specific job trace' do
          expect(response).to have_gitlab_http_status(200)
          expect(response.body).to eq(job.trace.raw)
        end
      end

      context 'when trace is file' do
        let(:job) { create(:ci_build, :trace_live, pipeline: pipeline) }

        it 'returns specific job trace' do
          expect(response).to have_gitlab_http_status(200)
          expect(response.body).to eq(job.trace.raw)
        end
550 551 552 553
      end
    end

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

Filipa Lacerda's avatar
Filipa Lacerda committed
556
      it 'does not return specific job trace' do
557
        expect(response).to have_gitlab_http_status(401)
558 559 560
      end
    end
  end
561

562
  describe 'POST /projects/:id/jobs/:job_id/cancel' do
Lin Jen-Shin's avatar
Lin Jen-Shin committed
563
    before do
564
      post api("/projects/#{project.id}/jobs/#{job.id}/cancel", api_user)
Lin Jen-Shin's avatar
Lin Jen-Shin committed
565
    end
Kamil Trzcinski's avatar
Kamil Trzcinski committed
566

567
    context 'authorized user' do
568
      context 'user with :update_build persmission' do
Filipa Lacerda's avatar
Filipa Lacerda committed
569
        it 'cancels running or pending job' do
570
          expect(response).to have_gitlab_http_status(201)
Kamil Trzciński's avatar
Kamil Trzciński committed
571
          expect(project.builds.first.status).to eq('success')
572 573 574
        end
      end

575
      context 'user without :update_build permission' do
576
        let(:api_user) { reporter }
577

Filipa Lacerda's avatar
Filipa Lacerda committed
578
        it 'does not cancel job' do
579
          expect(response).to have_gitlab_http_status(403)
580 581 582 583 584
        end
      end
    end

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

Filipa Lacerda's avatar
Filipa Lacerda committed
587
      it 'does not cancel job' do
588
        expect(response).to have_gitlab_http_status(401)
589 590 591 592
      end
    end
  end

593
  describe 'POST /projects/:id/jobs/:job_id/retry' do
594
    let(:job) { create(:ci_build, :canceled, pipeline: pipeline) }
Kamil Trzcinski's avatar
Kamil Trzcinski committed
595

Lin Jen-Shin's avatar
Lin Jen-Shin committed
596
    before do
597
      post api("/projects/#{project.id}/jobs/#{job.id}/retry", api_user)
Lin Jen-Shin's avatar
Lin Jen-Shin committed
598
    end
Kamil Trzcinski's avatar
Kamil Trzcinski committed
599

600
    context 'authorized user' do
Kamil Trzcinski's avatar
Kamil Trzcinski committed
601
      context 'user with :update_build permission' do
Filipa Lacerda's avatar
Filipa Lacerda committed
602
        it 'retries non-running job' do
603
          expect(response).to have_gitlab_http_status(201)
604 605 606 607 608
          expect(project.builds.first.status).to eq('canceled')
          expect(json_response['status']).to eq('pending')
        end
      end

609
      context 'user without :update_build permission' do
610
        let(:api_user) { reporter }
611

Filipa Lacerda's avatar
Filipa Lacerda committed
612
        it 'does not retry job' do
613
          expect(response).to have_gitlab_http_status(403)
614 615 616 617 618
        end
      end
    end

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

Filipa Lacerda's avatar
Filipa Lacerda committed
621
      it 'does not retry job' do
622
        expect(response).to have_gitlab_http_status(401)
623 624 625
      end
    end
  end
626

627
  describe 'POST /projects/:id/jobs/:job_id/erase' do
Shinya Maeda's avatar
Shinya Maeda committed
628
    let(:role) { :master }
Shinya Maeda's avatar
Shinya Maeda committed
629

630
    before do
631
      project.add_role(user, role)
632

633
      post api("/projects/#{project.id}/jobs/#{job.id}/erase", user)
634 635
    end

Filipa Lacerda's avatar
Filipa Lacerda committed
636
    context 'job is erasable' do
637
      let(:job) { create(:ci_build, :trace_artifact, :artifacts, :success, project: project, pipeline: pipeline) }
638

Filipa Lacerda's avatar
Filipa Lacerda committed
639
      it 'erases job content' do
640
        expect(response).to have_gitlab_http_status(201)
641
        expect(job.trace.exist?).to be_falsy
642 643
        expect(job.artifacts_file.exists?).to be_falsy
        expect(job.artifacts_metadata.exists?).to be_falsy
644
      end
645

Filipa Lacerda's avatar
Filipa Lacerda committed
646
      it 'updates job' do
647 648 649 650
        job.reload

        expect(job.erased_at).to be_truthy
        expect(job.erased_by).to eq(user)
651
      end
652 653
    end

Filipa Lacerda's avatar
Filipa Lacerda committed
654
    context 'job is not erasable' do
655
      let(:job) { create(:ci_build, :trace_live, project: project, pipeline: pipeline) }
656

657
      it 'responds with forbidden' do
658
        expect(response).to have_gitlab_http_status(403)
659 660
      end
    end
Shinya Maeda's avatar
Shinya Maeda committed
661

Shinya Maeda's avatar
Shinya Maeda committed
662 663
    context 'when a developer erases a build' do
      let(:role) { :developer }
664
      let(:job) { create(:ci_build, :trace_artifact, :artifacts, :success, project: project, pipeline: pipeline, user: owner) }
Shinya Maeda's avatar
Shinya Maeda committed
665 666 667 668 669 670 671 672 673 674 675

      context 'when the build was created by the developer' do
        let(:owner) { user }

        it { expect(response).to have_gitlab_http_status(201) }
      end

      context 'when the build was created by the other' do
        let(:owner) { create(:user) }

        it { expect(response).to have_gitlab_http_status(403) }
676 677 678
      end
    end
  end
679

680
  describe 'POST /projects/:id/jobs/:job_id/artifacts/keep' do
681
    before do
682
      post api("/projects/#{project.id}/jobs/#{job.id}/artifacts/keep", user)
683 684 685
    end

    context 'artifacts did not expire' do
686
      let(:job) do
687
        create(:ci_build, :trace_artifact, :artifacts, :success,
688 689 690
               project: project, pipeline: pipeline, artifacts_expire_at: Time.now + 7.days)
      end

691
      it 'keeps artifacts' do
692
        expect(response).to have_gitlab_http_status(200)
693
        expect(job.reload.artifacts_expire_at).to be_nil
694 695 696 697
      end
    end

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

700
      it 'responds with not found' do
701
        expect(response).to have_gitlab_http_status(404)
702 703 704
      end
    end
  end
705

706
  describe 'POST /projects/:id/jobs/:job_id/play' do
707
    before do
708
      post api("/projects/#{project.id}/jobs/#{job.id}/play", api_user)
709 710
    end

Filipa Lacerda's avatar
Filipa Lacerda committed
711
    context 'on an playable job' do
712
      let(:job) { create(:ci_build, :manual, project: project, pipeline: pipeline) }
713

714 715
      context 'when user is authorized to trigger a manual action' do
        it 'plays the job' do
716
          expect(response).to have_gitlab_http_status(200)
717
          expect(json_response['user']['id']).to eq(user.id)
718 719
          expect(json_response['id']).to eq(job.id)
          expect(job.reload).to be_pending
720 721 722 723 724 725 726 727
        end
      end

      context 'when user is not authorized to trigger a manual action' do
        context 'when user does not have access to the project' do
          let(:api_user) { create(:user) }

          it 'does not trigger a manual action' do
728
            expect(job.reload).to be_manual
729
            expect(response).to have_gitlab_http_status(404)
730 731 732 733 734 735 736
          end
        end

        context 'when user is not allowed to trigger the manual action' do
          let(:api_user) { reporter }

          it 'does not trigger a manual action' do
737
            expect(job.reload).to be_manual
738
            expect(response).to have_gitlab_http_status(403)
739 740
          end
        end
741 742 743
      end
    end

Filipa Lacerda's avatar
Filipa Lacerda committed
744
    context 'on a non-playable job' do
745
      it 'returns a status code 400, Bad Request' do
746
        expect(response).to have_gitlab_http_status 400
Filipa Lacerda's avatar
Filipa Lacerda committed
747
        expect(response.body).to match("Unplayable Job")
748 749 750
      end
    end
  end
751
end