jobs_controller_spec.rb 33.0 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 256 257
          expect(response).to have_gitlab_http_status(:ok)
          expect(json_response['deployment_status']).to be_nil
        end
      end

      context 'with deployment' do
        let(:merge_request) { create(:merge_request, source_project: project) }
        let(:environment) { create(:environment, project: project, name: 'staging', state: :available) }
S
Shinya Maeda 已提交
258
        let(:job) { create(:ci_build, :running, environment: environment.name, pipeline: pipeline) }
259

260 261 262 263
        before do
          create(:deployment, :success, environment: environment, project: project)
        end

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
          expect(json_response.dig('deployment_status', 'environment', 'last_deployment'))
            .not_to include('commit')
274 275
        end
      end
S
Steve Azzopardi 已提交
276 277 278 279 280 281 282 283 284 285 286 287

      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
288 289
            get_show_json

S
Steve Azzopardi 已提交
290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307
            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
308 309
            get_show_json

S
Steve Azzopardi 已提交
310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326
            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
327 328
            get_show_json

S
Steve Azzopardi 已提交
329 330 331 332 333 334
            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
335 336 337 338 339 340

      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
341 342
          get_show_json

343 344 345 346
          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 已提交
347
          expect(json_response['stuck']).to be true
348 349 350 351 352 353 354 355
        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
356 357
          get_show_json

358 359 360 361
          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 已提交
362
          expect(json_response['stuck']).to be true
363 364 365 366
        end
      end

      context 'settings_path' do
367 368 369 370
        before do
          get_show_json
        end

371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393
        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
394 395 396

      context 'when no trace is available' do
        it 'has_trace is false' do
397 398
          get_show_json

399 400 401 402 403 404 405 406 407
          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
408 409
          get_show_json

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

      it 'exposes the stage the job belongs to' do
416 417
        get_show_json

418 419
        expect(json_response['stage']).to eq('test')
      end
420 421
    end

422
    context 'when requesting triggered job JSON' do
423 424 425 426 427 428 429 430 431
      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)

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

      context 'with no variables' do
        it 'exposes trigger information' do
439 440
          get_show_json

441 442 443 444 445 446 447
          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

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

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

457
            get_show_json
458
          end
J
jhampton 已提交
459

460 461 462 463
          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 已提交
464

465 466 467 468
          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 已提交
469

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

473 474 475 476
            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 已提交
477 478
        end

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

484 485 486 487
          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 已提交
488

489 490 491 492 493 494 495 496 497 498 499 500
          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 已提交
501 502
        end
      end
503 504
    end

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

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

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

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

525 526 527 528 529 530 531 532 533 534 535
    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

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

      it 'returns a trace' do
540
        expect(response).to have_gitlab_http_status(:ok)
541 542
        expect(json_response['id']).to eq job.id
        expect(json_response['status']).to eq job.status
F
Fabio Pitino 已提交
543
        expect(json_response['html']).to eq('<span class="">BUILD TRACE</span>')
544 545 546
      end
    end

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

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

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

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

569
    context 'when trace artifact is in ObjectStorage' do
K
Kamil Trzciński 已提交
570 571
      let(:url) { 'http://object-storage/trace' }
      let(:file_path) { expand_fixture_path('trace/sample_trace') }
572 573 574 575
      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 已提交
576 577
        allow_any_instance_of(JobArtifactUploader).to receive(:url) { url }
        allow_any_instance_of(JobArtifactUploader).to receive(:size) { File.size(file_path) }
578 579 580 581
      end

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

          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
597
          stub_remote_url_500(url)
598 599 600
        end

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

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

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

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

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

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

      post_retry
    end

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

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

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

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

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

  describe 'POST play' do
    before do
674
      project.add_developer(user)
675 676 677 678

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

679 680 681 682 683
      sign_in(user)

      post_play
    end

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

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

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

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

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

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

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

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

723 724 725 726 727 728 729 730 731 732 733 734 735 736 737
      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
738 739
      end

740 741 742 743 744 745
      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
746 747 748
      end
    end

749 750 751 752
    context 'when continue url is not present' do
      before do
        post_cancel
      end
753

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

757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772
        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
773 774 775
      end
    end

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

S
Shinya Maeda 已提交
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 815 816
  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 已提交
817 818 819 820 821
      post :unschedule, params: {
                          namespace_id: project.namespace,
                          project_id: project,
                          id: job.id
                        }
S
Shinya Maeda 已提交
822 823 824
    end
  end

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

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

      post_erase
    end

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

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

      it 'erases artifacts' do
844 845
        expect(job.artifacts_file.present?).to be_falsey
        expect(job.artifacts_metadata.present?).to be_falsey
846 847 848
      end

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

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

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

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

      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 已提交
877
          expect(response).not_to have_gitlab_http_status(:found)
878 879 880 881
        end
      end
    end

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

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

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

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

906 907 908 909
        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"
910 911 912
      end
    end

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

916
      it 'sends a trace file' do
917 918
        response = subject

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

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

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

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

        expect(response).to have_gitlab_http_status(:ok)
937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953
        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 已提交
954 955 956
      end
    end

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

      it 'returns not_found' do
961 962
        response = subject

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

968 969 970 971 972 973 974 975 976 977
    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
978 979
    end
  end
F
Francisco Javier López 已提交
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 1022 1023

  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 已提交
1024
      get :terminal, params: params.merge(extra_params)
F
Francisco Javier López 已提交
1025 1026 1027 1028 1029 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043
    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)
1044
            .to receive(:channel_websocket)
F
Francisco Javier López 已提交
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 1076 1077
            .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 已提交
1078
      get :terminal_websocket_authorize, params: params.merge(extra_params)
F
Francisco Javier López 已提交
1079 1080
    end
  end
S
Shinya Maeda 已提交
1081
end