jobs_controller_spec.rb 32.8 KB
Newer Older
1
# coding: utf-8
G
gfyoung 已提交
2
# frozen_string_literal: true
S
Shinya Maeda 已提交
3 4
require 'spec_helper'

S
Shinya Maeda 已提交
5
describe Projects::JobsController, :clean_gitlab_redis_shared_state do
6
  include ApiHelpers
7
  include HttpIOHelpers
S
Shinya Maeda 已提交
8

9
  let(:project) { create(:project, :public) }
10 11
  let(:pipeline) { create(:ci_pipeline, project: project) }
  let(:user) { create(:user) }
S
Shinya Maeda 已提交
12

13
  before do
S
Shinya Maeda 已提交
14
    stub_feature_flags(ci_enable_live_trace: true)
15 16 17
    stub_not_protect_default_branch
  end

K
Kamil Trzcinski 已提交
18
  describe 'GET index' do
19 20 21 22 23 24 25 26
    context 'when scope is pending' do
      before do
        create(:ci_build, :pending, pipeline: pipeline)

        get_index(scope: 'pending')
      end

      it 'has only pending builds' do
27
        expect(response).to have_gitlab_http_status(:ok)
28 29 30 31 32 33 34 35 36 37 38
        expect(assigns(:builds).first.status).to eq('pending')
      end
    end

    context 'when scope is running' do
      before do
        create(:ci_build, :running, pipeline: pipeline)

        get_index(scope: 'running')
      end

39
      it 'has only running jobs' do
40
        expect(response).to have_gitlab_http_status(:ok)
41 42 43 44 45 46 47 48 49 50 51
        expect(assigns(:builds).first.status).to eq('running')
      end
    end

    context 'when scope is finished' do
      before do
        create(:ci_build, :success, pipeline: pipeline)

        get_index(scope: 'finished')
      end

52
      it 'has only finished jobs' do
53
        expect(response).to have_gitlab_http_status(:ok)
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68
        expect(assigns(:builds).first.status).to eq('success')
      end
    end

    context 'when page is specified' do
      let(:last_page) { project.builds.page.total_pages }

      context 'when page number is eligible' do
        before do
          create_list(:ci_build, 2, pipeline: pipeline)

          get_index(page: last_page.to_param)
        end

        it 'redirects to the page' do
69
          expect(response).to have_gitlab_http_status(:ok)
70 71 72 73 74
          expect(assigns(:builds).current_page).to eq(last_page)
        end
      end
    end

K
Kamil Trzcinski 已提交
75 76 77
    context 'number of queries' do
      before do
        Ci::Build::AVAILABLE_STATUSES.each do |status|
78
          create_job(status, status)
K
Kamil Trzcinski 已提交
79 80 81
        end
      end

82
      it 'verifies number of queries', :request_store do
83
        recorded = ActiveRecord::QueryRecorder.new { get_index }
84
        expect(recorded.count).to be_within(5).of(7)
K
Kamil Trzcinski 已提交
85 86
      end

87
      def create_job(name, status)
K
Kamil Trzcinski 已提交
88 89
        pipeline = create(:ci_pipeline, project: project)
        create(:ci_build, :tags, :triggered, :artifacts,
90
               pipeline: pipeline, name: name, status: status)
K
Kamil Trzcinski 已提交
91 92
      end
    end
93 94 95 96 97 98 99

    def get_index(**extra_params)
      params = {
        namespace_id: project.namespace.to_param,
        project_id: project
      }

B
blackst0ne 已提交
100
      get :index, params: params.merge(extra_params)
101 102 103
    end
  end

104
  describe 'GET show', :request_store do
105
    let!(:job) { create(:ci_build, :failed, pipeline: pipeline) }
106 107
    let!(:second_job) { create(:ci_build, :failed, pipeline: pipeline) }
    let!(:third_job) { create(:ci_build, :failed) }
108

109
    context 'when requesting HTML' do
110
      context 'when job exists' do
111
        before do
112
          get_show(id: job.id)
113 114
        end

115
        it 'has a job' do
116
          expect(response).to have_gitlab_http_status(:ok)
117
          expect(assigns(:build).id).to eq(job.id)
118
        end
119 120 121 122 123 124 125

        it 'has the correct build collection' do
          builds = assigns(:builds).map(&:id)

          expect(builds).to include(job.id, second_job.id)
          expect(builds).not_to include(third_job.id)
        end
126 127
      end

128
      context 'when job does not exist' do
129 130 131 132 133
        before do
          get_show(id: 1234)
        end

        it 'renders not_found' do
134
          expect(response).to have_gitlab_http_status(:not_found)
135
        end
136 137 138
      end
    end

139
    context 'when requesting JSON' do
140 141
      let(:merge_request) { create(:merge_request, source_project: project) }

142
      before do
143 144 145
        project.add_developer(user)
        sign_in(user)

146 147 148
        allow_any_instance_of(Ci::Build)
          .to receive(:merge_request)
          .and_return(merge_request)
149 150
      end

151
      it 'does not serialize builds in exposed stages' do
152 153
        get_show_json

154
        json_response.dig('pipeline', 'details', 'stages').tap do |stages|
155 156
          expect(stages.map(&:keys).flatten)
            .to eq %w[name title status path dropdown_path]
157 158 159
        end
      end

160 161
      context 'when job failed' do
        it 'exposes needed information' do
162 163
          get_show_json

164 165 166
          expect(response).to have_gitlab_http_status(:ok)
          expect(response).to match_response_schema('job/job_details')
          expect(json_response['raw_path']).to match(%r{jobs/\d+/raw\z})
167
          expect(json_response['merge_request']['path']).to match(%r{merge_requests/\d+\z})
168 169
          expect(json_response['new_issue_path']).to include('/issues/new')
        end
170 171
      end

172
      context 'when job is running' do
173 174 175 176
        before do
          get_show_json
        end

177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197
        context 'job is cancelable' do
          let(:job) { create(:ci_build, :running, pipeline: pipeline) }

          it 'cancel_path is present with correct redirect' do
            expect(response).to have_gitlab_http_status(:ok)
            expect(response).to match_response_schema('job/job_details')
            expect(json_response['cancel_path']).to include(CGI.escape(json_response['build_path']))
          end
        end

        context 'with web terminal' do
          let(:job) { create(:ci_build, :running, :with_runner_session, pipeline: pipeline) }

          it 'exposes the terminal path' do
            expect(response).to have_gitlab_http_status(:ok)
            expect(response).to match_response_schema('job/job_details')
            expect(json_response['terminal_path']).to match(%r{/terminal})
          end
        end
      end

198
      context 'when job has artifacts' do
199 200 201 202
        before do
          get_show_json
        end

203 204
        context 'with not expiry date' do
          let(:job) { create(:ci_build, :success, :artifacts, pipeline: pipeline) }
205

206 207 208 209 210
          it 'exposes needed information' do
            expect(response).to have_gitlab_http_status(:ok)
            expect(response).to match_response_schema('job/job_details')
            expect(json_response['artifact']['download_path']).to match(%r{artifacts/download})
            expect(json_response['artifact']['browse_path']).to match(%r{artifacts/browse})
211 212
            expect(json_response['artifact']).not_to have_key('expired')
            expect(json_response['artifact']).not_to have_key('expired_at')
213 214
          end
        end
215

216 217 218 219 220 221
        context 'with expiry date' do
          let(:job) { create(:ci_build, :success, :artifacts, :expired, pipeline: pipeline) }

          it 'exposes needed information' do
            expect(response).to have_gitlab_http_status(:ok)
            expect(response).to match_response_schema('job/job_details')
222 223
            expect(json_response['artifact']).not_to have_key('download_path')
            expect(json_response['artifact']).not_to have_key('browse_path')
224 225 226 227
            expect(json_response['artifact']['expired']).to eq(true)
            expect(json_response['artifact']['expire_at']).not_to be_empty
          end
        end
228 229
      end

230 231 232 233
      context 'when job passed with no trace' do
        let(:job) { create(:ci_build, :success, :artifacts, pipeline: pipeline) }

        it 'exposes empty state illustrations' do
234 235
          get_show_json

236 237 238 239 240 241 242
          expect(response).to have_gitlab_http_status(:ok)
          expect(response).to match_response_schema('job/job_details')
          expect(json_response['status']['illustration']).to have_key('image')
          expect(json_response['status']['illustration']).to have_key('size')
          expect(json_response['status']['illustration']).to have_key('title')
        end
      end
243 244 245 246 247

      context 'with no deployment' do
        let(:job) { create(:ci_build, :success, pipeline: pipeline) }

        it 'does not exposes the deployment information' do
248 249
          get_show_json

250 251 252 253 254 255
          expect(response).to have_gitlab_http_status(:ok)
          expect(json_response['deployment_status']).to be_nil
        end
      end

      context 'with deployment' do
256 257 258 259
        before do
          create(:deployment, :success, environment: environment, project: project)
        end

260 261
        let(:merge_request) { create(:merge_request, source_project: project) }
        let(:environment) { create(:environment, project: project, name: 'staging', state: :available) }
S
Shinya Maeda 已提交
262
        let(:job) { create(:ci_build, :running, environment: environment.name, pipeline: pipeline) }
263 264

        it 'exposes the deployment information' do
265 266
          get_show_json

267 268
          expect(response).to have_gitlab_http_status(:ok)
          expect(json_response).to match_schema('job/job_details')
269 270 271
          expect(json_response.dig('deployment_status', 'status')).to eq 'creating'
          expect(json_response.dig('deployment_status', 'environment')).not_to be_nil
          expect(json_response.dig('deployment_status', 'environment', 'last_deployment')).not_to be_nil
272 273
        end
      end
S
Steve Azzopardi 已提交
274 275 276 277 278 279 280 281 282 283 284 285

      context 'when user can edit runner' do
        context 'that belongs to the project' do
          let(:runner) { create(:ci_runner, :project, projects: [project]) }
          let(:job) { create(:ci_build, :success, pipeline: pipeline, runner: runner) }

          before do
            project.add_maintainer(user)
            sign_in(user)
          end

          it 'user can edit runner' do
286 287
            get_show_json

S
Steve Azzopardi 已提交
288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305
            expect(response).to have_gitlab_http_status(:ok)
            expect(response).to match_response_schema('job/job_details')
            expect(json_response['runner']).to have_key('edit_path')
          end
        end

        context 'that belongs to group' do
          let(:group) { create(:group) }
          let(:runner) { create(:ci_runner, :group, groups: [group]) }
          let(:job) { create(:ci_build, :success, pipeline: pipeline, runner: runner) }
          let(:user) { create(:user, :admin) }

          before do
            project.add_maintainer(user)
            sign_in(user)
          end

          it 'user can not edit runner' do
306 307
            get_show_json

S
Steve Azzopardi 已提交
308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324
            expect(response).to have_gitlab_http_status(:ok)
            expect(response).to match_response_schema('job/job_details')
            expect(json_response['runner']).not_to have_key('edit_path')
          end
        end

        context 'that belongs to instance' do
          let(:runner) { create(:ci_runner, :instance) }
          let(:job) { create(:ci_build, :success, pipeline: pipeline, runner: runner) }
          let(:user) { create(:user, :admin) }

          before do
            project.add_maintainer(user)
            sign_in(user)
          end

          it 'user can not edit runner' do
325 326
            get_show_json

S
Steve Azzopardi 已提交
327 328 329 330 331 332
            expect(response).to have_gitlab_http_status(:ok)
            expect(response).to match_response_schema('job/job_details')
            expect(json_response['runner']).not_to have_key('edit_path')
          end
        end
      end
333 334 335 336 337 338

      context 'when no runners are available' do
        let(:runner) { create(:ci_runner, :instance, active: false) }
        let(:job) { create(:ci_build, :pending, pipeline: pipeline, runner: runner) }

        it 'exposes needed information' do
339 340
          get_show_json

341 342 343 344
          expect(response).to have_gitlab_http_status(:ok)
          expect(response).to match_response_schema('job/job_details')
          expect(json_response['runners']['online']).to be false
          expect(json_response['runners']['available']).to be false
S
Steve Azzopardi 已提交
345
          expect(json_response['stuck']).to be true
346 347 348 349 350 351 352 353
        end
      end

      context 'when no runner is online' do
        let(:runner) { create(:ci_runner, :instance) }
        let(:job) { create(:ci_build, :pending, pipeline: pipeline, runner: runner) }

        it 'exposes needed information' do
354 355
          get_show_json

356 357 358 359
          expect(response).to have_gitlab_http_status(:ok)
          expect(response).to match_response_schema('job/job_details')
          expect(json_response['runners']['online']).to be false
          expect(json_response['runners']['available']).to be true
S
Steve Azzopardi 已提交
360
          expect(json_response['stuck']).to be true
361 362 363 364
        end
      end

      context 'settings_path' do
365 366 367 368
        before do
          get_show_json
        end

369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391
        context 'when user is developer' do
          it 'settings_path is not available' do
            expect(response).to have_gitlab_http_status(:ok)
            expect(response).to match_response_schema('job/job_details')
            expect(json_response['runners']).not_to have_key('settings_path')
          end
        end

        context 'when user is maintainer' do
          let(:user) { create(:user, :admin) }

          before do
            project.add_maintainer(user)
            sign_in(user)
          end

          it 'settings_path is available' do
            expect(response).to have_gitlab_http_status(:ok)
            expect(response).to match_response_schema('job/job_details')
            expect(json_response['runners']['settings_path']).to match(/runners/)
          end
        end
      end
392 393 394

      context 'when no trace is available' do
        it 'has_trace is false' do
395 396
          get_show_json

397 398 399 400 401 402 403 404 405
          expect(response).to match_response_schema('job/job_details')
          expect(json_response['has_trace']).to be false
        end
      end

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

        it "has_trace is true" do
406 407
          get_show_json

408 409 410 411
          expect(response).to match_response_schema('job/job_details')
          expect(json_response['has_trace']).to be true
        end
      end
412 413

      it 'exposes the stage the job belongs to' do
414 415
        get_show_json

416 417
        expect(json_response['stage']).to eq('test')
      end
418 419
    end

420
    context 'when requesting triggered job JSON' do
421 422 423 424 425 426 427 428 429
      let!(:merge_request) { create(:merge_request, source_project: project) }
      let(:trigger) { create(:ci_trigger, project: project) }
      let(:trigger_request) { create(:ci_trigger_request, pipeline: pipeline, trigger: trigger) }
      let(:job) { create(:ci_build, pipeline: pipeline, trigger_request: trigger_request) }

      before do
        project.add_developer(user)
        sign_in(user)

430 431 432
        allow_any_instance_of(Ci::Build)
          .to receive(:merge_request)
          .and_return(merge_request)
433 434 435 436
      end

      context 'with no variables' do
        it 'exposes trigger information' do
437 438
          get_show_json

439 440 441 442 443 444 445
          expect(response).to have_gitlab_http_status(:ok)
          expect(response).to match_response_schema('job/job_details')
          expect(json_response['trigger']['short_token']).to eq 'toke'
          expect(json_response['trigger']['variables'].length).to eq 0
        end
      end

446
      context 'with variables' do
447 448
        before do
          create(:ci_pipeline_variable, pipeline: pipeline, key: :TRIGGER_KEY_1, value: 'TRIGGER_VALUE_1')
J
jhampton 已提交
449 450
        end

451 452 453
        context 'user is a maintainer' do
          before do
            project.add_maintainer(user)
J
jhampton 已提交
454

455
            get_show_json
456
          end
J
jhampton 已提交
457

458 459 460 461
          it 'returns a job_detail' do
            expect(response).to have_gitlab_http_status(:ok)
            expect(response).to match_response_schema('job/job_details')
          end
J
jhampton 已提交
462

463 464 465 466
          it 'exposes trigger information and variables' do
            expect(json_response['trigger']['short_token']).to eq 'toke'
            expect(json_response['trigger']['variables'].length).to eq 1
          end
J
jhampton 已提交
467

468 469
          it 'exposes correct variable properties' do
            first_variable = json_response['trigger']['variables'].first
J
jhampton 已提交
470

471 472 473 474
            expect(first_variable['key']).to eq "TRIGGER_KEY_1"
            expect(first_variable['value']).to eq "TRIGGER_VALUE_1"
            expect(first_variable['public']).to eq false
          end
J
jhampton 已提交
475 476
        end

477 478
        context 'user is not a mantainer' do
          before do
479
            get_show_json
480
          end
J
jhampton 已提交
481

482 483 484 485
          it 'returns a job_detail' do
            expect(response).to have_gitlab_http_status(:ok)
            expect(response).to match_response_schema('job/job_details')
          end
J
jhampton 已提交
486

487 488 489 490 491 492 493 494 495 496 497 498
          it 'exposes trigger information and variables' do
            expect(json_response['trigger']['short_token']).to eq 'toke'
            expect(json_response['trigger']['variables'].length).to eq 1
          end

          it 'exposes correct variable properties' do
            first_variable = json_response['trigger']['variables'].first

            expect(first_variable['key']).to eq "TRIGGER_KEY_1"
            expect(first_variable['value']).to be_nil
            expect(first_variable['public']).to eq false
          end
J
jhampton 已提交
499 500
        end
      end
501 502
    end

503
    def get_show_json
504 505
      expect { get_show(id: job.id, format: :json) }
        .not_to change { Gitlab::GitalyClient.get_request_count }
506 507
    end

508 509 510 511 512 513
    def get_show(**extra_params)
      params = {
        namespace_id: project.namespace.to_param,
        project_id: project
      }

B
blackst0ne 已提交
514
      get :show, params: params.merge(extra_params)
515 516 517 518 519 520 521 522
    end
  end

  describe 'GET trace.json' do
    before do
      get_trace
    end

523 524 525 526 527 528 529 530 531 532 533
    context 'when job has a trace artifact' do
      let(:job) { create(:ci_build, :trace_artifact, pipeline: pipeline) }

      it 'returns a trace' do
        expect(response).to have_gitlab_http_status(:ok)
        expect(json_response['id']).to eq job.id
        expect(json_response['status']).to eq job.status
        expect(json_response['html']).to eq(job.trace.html)
      end
    end

534
    context 'when job has a trace' do
535
      let(:job) { create(:ci_build, :trace_live, pipeline: pipeline) }
536 537

      it 'returns a trace' do
538
        expect(response).to have_gitlab_http_status(:ok)
539 540
        expect(json_response['id']).to eq job.id
        expect(json_response['status']).to eq job.status
541 542 543 544
        expect(json_response['html']).to eq('BUILD TRACE')
      end
    end

545 546
    context 'when job has no traces' do
      let(:job) { create(:ci_build, pipeline: pipeline) }
547 548

      it 'returns no traces' do
549
        expect(response).to have_gitlab_http_status(:ok)
550 551
        expect(json_response['id']).to eq job.id
        expect(json_response['status']).to eq job.status
552
        expect(json_response['html']).to be_nil
553 554 555
      end
    end

556
    context 'when job has a trace with ANSI sequence and Unicode' do
557
      let(:job) { create(:ci_build, :unicode_trace_live, pipeline: pipeline) }
558 559

      it 'returns a trace with Unicode' do
560
        expect(response).to have_gitlab_http_status(:ok)
561 562
        expect(json_response['id']).to eq job.id
        expect(json_response['status']).to eq job.status
563 564 565 566
        expect(json_response['html']).to include("ヾ(´༎ຶД༎ຶ`)ノ")
      end
    end

567
    context 'when trace artifact is in ObjectStorage' do
K
Kamil Trzciński 已提交
568 569
      let(:url) { 'http://object-storage/trace' }
      let(:file_path) { expand_fixture_path('trace/sample_trace') }
570 571 572 573
      let!(:job) { create(:ci_build, :success, :trace_artifact, pipeline: pipeline) }

      before do
        allow_any_instance_of(JobArtifactUploader).to receive(:file_storage?) { false }
K
Kamil Trzciński 已提交
574 575
        allow_any_instance_of(JobArtifactUploader).to receive(:url) { url }
        allow_any_instance_of(JobArtifactUploader).to receive(:size) { File.size(file_path) }
576 577 578 579
      end

      context 'when there are no network issues' do
        before do
K
Kamil Trzciński 已提交
580
          stub_remote_url_206(url, file_path)
581 582 583 584 585 586 587 588 589 590 591 592 593 594

          get_trace
        end

        it 'returns a trace' do
          expect(response).to have_gitlab_http_status(:ok)
          expect(json_response['id']).to eq job.id
          expect(json_response['status']).to eq job.status
          expect(json_response['html']).to eq(job.trace.html)
        end
      end

      context 'when there is a network issue' do
        before do
595
          stub_remote_url_500(url)
596 597 598
        end

        it 'returns a trace' do
599
          expect { get_trace }.to raise_error(Gitlab::HttpIO::FailedToGetChunkError)
600 601 602 603
        end
      end
    end

604
    def get_trace
B
blackst0ne 已提交
605 606 607 608 609
      get :trace, params: {
                    namespace_id: project.namespace,
                    project_id: project,
                    id: job.id
                  },
610 611
                  format: :json
    end
K
Kamil Trzcinski 已提交
612 613
  end

S
Shinya Maeda 已提交
614
  describe 'GET status.json' do
615 616
    let(:job) { create(:ci_build, pipeline: pipeline) }
    let(:status) { job.detailed_status(double('user')) }
S
Shinya Maeda 已提交
617

618
    before do
B
blackst0ne 已提交
619 620 621 622 623
      get :status, params: {
                     namespace_id: project.namespace,
                     project_id: project,
                     id: job.id
                   },
624 625
                   format: :json
    end
S
Shinya Maeda 已提交
626

627
    it 'return a detailed job status in json' do
628
      expect(response).to have_gitlab_http_status(:ok)
629 630 631
      expect(json_response['text']).to eq status.text
      expect(json_response['label']).to eq status.label
      expect(json_response['icon']).to eq status.icon
632
      expect(json_response['favicon']).to match_asset_path "/assets/ci_favicons/#{status.favicon}.png"
S
Shinya Maeda 已提交
633 634
    end
  end
635

636 637
  describe 'POST retry' do
    before do
638
      project.add_developer(user)
639 640 641 642 643
      sign_in(user)

      post_retry
    end

644 645
    context 'when job is retryable' do
      let(:job) { create(:ci_build, :retryable, pipeline: pipeline) }
646

647
      it 'redirects to the retried job page' do
648
        expect(response).to have_gitlab_http_status(:found)
649
        expect(response).to redirect_to(namespace_project_job_path(id: Ci::Build.last.id))
650 651 652
      end
    end

653 654
    context 'when job is not retryable' do
      let(:job) { create(:ci_build, pipeline: pipeline) }
655 656

      it 'renders unprocessable_entity' do
657
        expect(response).to have_gitlab_http_status(:unprocessable_entity)
658 659 660 661
      end
    end

    def post_retry
B
blackst0ne 已提交
662 663 664 665 666
      post :retry, params: {
                     namespace_id: project.namespace,
                     project_id: project,
                     id: job.id
                   }
667 668 669 670 671
    end
  end

  describe 'POST play' do
    before do
672
      project.add_developer(user)
673 674 675 676

      create(:protected_branch, :developers_can_merge,
             name: 'master', project: project)

677 678 679 680 681
      sign_in(user)

      post_play
    end

682 683
    context 'when job is playable' do
      let(:job) { create(:ci_build, :playable, pipeline: pipeline) }
684

685
      it 'redirects to the played job page' do
686
        expect(response).to have_gitlab_http_status(:found)
687
        expect(response).to redirect_to(namespace_project_job_path(id: job.id))
688 689 690
      end

      it 'transits to pending' do
691
        expect(job.reload).to be_pending
692 693 694
      end
    end

695 696
    context 'when job is not playable' do
      let(:job) { create(:ci_build, pipeline: pipeline) }
697 698

      it 'renders unprocessable_entity' do
699
        expect(response).to have_gitlab_http_status(:unprocessable_entity)
700 701 702 703
      end
    end

    def post_play
B
blackst0ne 已提交
704 705 706 707 708
      post :play, params: {
                    namespace_id: project.namespace,
                    project_id: project,
                    id: job.id
                  }
709 710 711 712 713
    end
  end

  describe 'POST cancel' do
    before do
714
      project.add_developer(user)
715 716 717
      sign_in(user)
    end

718
    context 'when continue url is present' do
719
      let(:job) { create(:ci_build, :cancelable, pipeline: pipeline) }
720

721 722 723 724 725 726 727 728 729 730 731 732 733 734 735
      context 'when continue to is a safe url' do
        let(:url) { '/test' }

        before do
          post_cancel(continue: { to: url })
        end

        it 'redirects to the continue url' do
          expect(response).to have_gitlab_http_status(:found)
          expect(response).to redirect_to(url)
        end

        it 'transits to canceled' do
          expect(job.reload).to be_canceled
        end
736 737
      end

738 739 740 741 742 743
      context 'when continue to is not a safe url' do
        let(:url) { 'http://example.com' }

        it 'raises an error' do
          expect { cancel_with_redirect(url) }.to raise_error
        end
744 745 746
      end
    end

747 748 749 750
    context 'when continue url is not present' do
      before do
        post_cancel
      end
751

752 753
      context 'when job is cancelable' do
        let(:job) { create(:ci_build, :cancelable, pipeline: pipeline) }
754

755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770
        it 'redirects to the builds page' do
          expect(response).to have_gitlab_http_status(:found)
          expect(response).to redirect_to(builds_namespace_project_pipeline_path(id: pipeline.id))
        end

        it 'transits to canceled' do
          expect(job.reload).to be_canceled
        end
      end

      context 'when job is not cancelable' do
        let(:job) { create(:ci_build, :canceled, pipeline: pipeline) }

        it 'returns unprocessable_entity' do
          expect(response).to have_gitlab_http_status(:unprocessable_entity)
        end
771 772 773
      end
    end

774
    def post_cancel(additional_params = {})
B
blackst0ne 已提交
775 776 777
      post :cancel, params: { namespace_id: project.namespace,
                              project_id: project,
                              id: job.id }.merge(additional_params)
778 779 780
    end
  end

S
Shinya Maeda 已提交
781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814
  describe 'POST unschedule' do
    before do
      project.add_developer(user)

      create(:protected_branch, :developers_can_merge,
             name: 'master', project: project)

      sign_in(user)

      post_unschedule
    end

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

      it 'redirects to the unscheduled job page' do
        expect(response).to have_gitlab_http_status(:found)
        expect(response).to redirect_to(namespace_project_job_path(id: job.id))
      end

      it 'transits to manual' do
        expect(job.reload).to be_manual
      end
    end

    context 'when job is not scheduled' do
      let(:job) { create(:ci_build, pipeline: pipeline) }

      it 'renders unprocessable_entity' do
        expect(response).to have_gitlab_http_status(:unprocessable_entity)
      end
    end

    def post_unschedule
B
blackst0ne 已提交
815 816 817 818 819
      post :unschedule, params: {
                          namespace_id: project.namespace,
                          project_id: project,
                          id: job.id
                        }
S
Shinya Maeda 已提交
820 821 822
    end
  end

823
  describe 'POST erase' do
824
    let(:role) { :maintainer }
S
Shinya Maeda 已提交
825

826
    before do
827
      project.add_role(user, role)
828 829 830 831 832
      sign_in(user)

      post_erase
    end

833
    context 'when job is erasable' do
834
      let(:job) { create(:ci_build, :erasable, :trace_artifact, pipeline: pipeline) }
835

836
      it 'redirects to the erased job page' do
837
        expect(response).to have_gitlab_http_status(:found)
838
        expect(response).to redirect_to(namespace_project_job_path(id: job.id))
839 840 841
      end

      it 'erases artifacts' do
842 843
        expect(job.artifacts_file.exists?).to be_falsey
        expect(job.artifacts_metadata.exists?).to be_falsey
844 845 846
      end

      it 'erases trace' do
847
        expect(job.trace.exist?).to be_falsey
848 849 850
      end
    end

851 852
    context 'when job is not erasable' do
      let(:job) { create(:ci_build, :erased, pipeline: pipeline) }
853 854

      it 'returns unprocessable_entity' do
855
        expect(response).to have_gitlab_http_status(:unprocessable_entity)
856 857 858
      end
    end

859 860
    context 'when user is developer' do
      let(:role) { :developer }
861
      let(:job) { create(:ci_build, :erasable, :trace_artifact, pipeline: pipeline, user: triggered_by) }
862 863 864 865 866 867 868 869 870 871 872 873 874

      context 'when triggered by same user' do
        let(:triggered_by) { user }

        it 'has successful status' do
          expect(response).to have_gitlab_http_status(:found)
        end
      end

      context 'when triggered by different user' do
        let(:triggered_by) { create(:user) }

        it 'does not have successful status' do
S
Shinya Maeda 已提交
875
          expect(response).not_to have_gitlab_http_status(:found)
876 877 878 879
        end
      end
    end

880
    def post_erase
B
blackst0ne 已提交
881 882 883 884 885
      post :erase, params: {
                     namespace_id: project.namespace,
                     project_id: project,
                     id: job.id
                   }
886 887 888 889
    end
  end

  describe 'GET raw' do
890
    subject do
B
blackst0ne 已提交
891 892 893 894 895
      post :raw, params: {
                   namespace_id: project.namespace,
                   project_id: project,
                   id: job.id
                 }
896 897
    end

898
    context "when job has a trace artifact" do
899 900
      let(:job) { create(:ci_build, :trace_artifact, pipeline: pipeline) }

901 902
      it "sets #{Gitlab::Workhorse::DETECT_HEADER} header" do
        response = subject
903

904 905 906 907
        expect(response).to have_gitlab_http_status(:ok)
        expect(response.headers["Content-Type"]).to eq("text/plain; charset=utf-8")
        expect(response.body).to eq(job.job_artifacts_trace.open.read)
        expect(response.header[Gitlab::Workhorse::DETECT_HEADER]).to eq "true"
908 909 910
      end
    end

911
    context "when job has a trace file" do
912
      let(:job) { create(:ci_build, :trace_live, pipeline: pipeline) }
913

914
      it 'sends a trace file' do
915 916
        response = subject

917
        expect(response).to have_gitlab_http_status(:ok)
918
        expect(response.headers["Content-Type"]).to eq("text/plain; charset=utf-8")
919
        expect(response.headers["Content-Disposition"]).to match(/^inline/)
920
        expect(response.body).to eq("BUILD TRACE")
921 922 923
      end
    end

924
    context "when job has a trace in database" do
S
Shinya Maeda 已提交
925 926 927
      let(:job) { create(:ci_build, pipeline: pipeline) }

      before do
928
        job.update_column(:trace, "Sample trace")
S
Shinya Maeda 已提交
929 930
      end

931
      it 'sends a trace file' do
S
Shinya Maeda 已提交
932 933 934
        response = subject

        expect(response).to have_gitlab_http_status(:ok)
935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951
        expect(response.headers['Content-Type']).to eq('text/plain; charset=utf-8')
        expect(response.headers['Content-Disposition']).to match(/^inline/)
        expect(response.body).to eq('Sample trace')
      end

      context 'when trace format is not text/plain' do
        before do
          job.update_column(:trace, '<html></html>')
        end

        it 'sets content disposition to attachment' do
          response = subject

          expect(response).to have_gitlab_http_status(:ok)
          expect(response.headers['Content-Type']).to eq('text/plain; charset=utf-8')
          expect(response.headers['Content-Disposition']).to match(/^attachment/)
        end
S
Shinya Maeda 已提交
952 953 954
      end
    end

955 956
    context 'when job does not have a trace file' do
      let(:job) { create(:ci_build, pipeline: pipeline) }
957 958

      it 'returns not_found' do
959 960
        response = subject

S
Shinya Maeda 已提交
961 962
        expect(response).to have_gitlab_http_status(:ok)
        expect(response.body).to eq ''
963 964 965
      end
    end

966 967 968 969 970 971 972 973 974 975
    context 'when the trace artifact is in ObjectStorage' do
      let!(:job) { create(:ci_build, :trace_artifact, pipeline: pipeline) }

      before do
        allow_any_instance_of(JobArtifactUploader).to receive(:file_storage?) { false }
      end

      it 'redirect to the trace file url' do
        expect(subject).to redirect_to(job.job_artifacts_trace.file.url)
      end
976 977
    end
  end
F
Francisco Javier López 已提交
978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021

  describe 'GET #terminal' do
    before do
      project.add_developer(user)
      sign_in(user)
    end

    context 'when job exists' do
      context 'and it has a terminal' do
        let!(:job) { create(:ci_build, :running, :with_runner_session, pipeline: pipeline) }

        it 'has a job' do
          get_terminal(id: job.id)

          expect(response).to have_gitlab_http_status(:ok)
          expect(assigns(:build).id).to eq(job.id)
        end
      end

      context 'and does not have a terminal' do
        let!(:job) { create(:ci_build, :running, pipeline: pipeline) }

        it 'returns not_found' do
          get_terminal(id: job.id)

          expect(response).to have_gitlab_http_status(:not_found)
        end
      end
    end

    context 'when job does not exist' do
      it 'renders not_found' do
        get_terminal(id: 1234)

        expect(response).to have_gitlab_http_status(:not_found)
      end
    end

    def get_terminal(**extra_params)
      params = {
        namespace_id: project.namespace.to_param,
        project_id: project
      }

B
blackst0ne 已提交
1022
      get :terminal, params: params.merge(extra_params)
F
Francisco Javier López 已提交
1023 1024 1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041
    end
  end

  describe 'GET #terminal_websocket_authorize' do
    let!(:job) { create(:ci_build, :running, :with_runner_session, pipeline: pipeline) }

    before do
      project.add_developer(user)
      sign_in(user)
    end

    context 'with valid workhorse signature' do
      before do
        allow(Gitlab::Workhorse).to receive(:verify_api_request!).and_return(nil)
      end

      context 'and valid id' do
        it 'returns the terminal for the job' do
          expect(Gitlab::Workhorse)
1042
            .to receive(:channel_websocket)
F
Francisco Javier López 已提交
1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075
            .and_return(workhorse: :response)

          get_terminal_websocket(id: job.id)

          expect(response).to have_gitlab_http_status(200)
          expect(response.headers["Content-Type"]).to eq(Gitlab::Workhorse::INTERNAL_API_CONTENT_TYPE)
          expect(response.body).to eq('{"workhorse":"response"}')
        end
      end

      context 'and invalid id' do
        it 'returns 404' do
          get_terminal_websocket(id: 1234)

          expect(response).to have_gitlab_http_status(404)
        end
      end
    end

    context 'with invalid workhorse signature' do
      it 'aborts with an exception' do
        allow(Gitlab::Workhorse).to receive(:verify_api_request!).and_raise(JWT::DecodeError)

        expect { get_terminal_websocket(id: job.id) }.to raise_error(JWT::DecodeError)
      end
    end

    def get_terminal_websocket(**extra_params)
      params = {
        namespace_id: project.namespace.to_param,
        project_id: project
      }

B
blackst0ne 已提交
1076
      get :terminal_websocket_authorize, params: params.merge(extra_params)
F
Francisco Javier López 已提交
1077 1078
    end
  end
S
Shinya Maeda 已提交
1079
end