jobs_controller_spec.rb 31.9 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 104
    end
  end

  describe 'GET show' 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 146 147
        project.add_developer(user)
        sign_in(user)

        allow_any_instance_of(Ci::Build).to receive(:merge_request).and_return(merge_request)

148
        get_show(id: job.id, format: :json)
149 150
      end

151 152 153 154 155
      context 'when job failed' do
        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['raw_path']).to match(%r{jobs/\d+/raw\z})
156
          expect(json_response['merge_request']['path']).to match(%r{merge_requests/\d+\z})
157 158
          expect(json_response['new_issue_path']).to include('/issues/new')
        end
159 160
      end

161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182
      context 'when job is running' do
        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

183 184 185
      context 'when job has artifacts' do
        context 'with not expiry date' do
          let(:job) { create(:ci_build, :success, :artifacts, pipeline: pipeline) }
186

187 188 189 190 191
          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})
192 193
            expect(json_response['artifact']).not_to have_key('expired')
            expect(json_response['artifact']).not_to have_key('expired_at')
194 195
          end
        end
196

197 198 199 200 201 202
        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')
203 204
            expect(json_response['artifact']).not_to have_key('download_path')
            expect(json_response['artifact']).not_to have_key('browse_path')
205 206 207 208
            expect(json_response['artifact']['expired']).to eq(true)
            expect(json_response['artifact']['expire_at']).not_to be_empty
          end
        end
209 210
      end

211 212 213 214 215 216 217 218 219 220 221
      context 'when job passed with no trace' do
        let(:job) { create(:ci_build, :success, :artifacts, pipeline: pipeline) }

        it 'exposes empty state illustrations' do
          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
222 223 224 225 226 227 228 229 230 231 232 233 234

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

        it 'does not exposes the deployment information' do
          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 已提交
235
        let(:job) { create(:ci_build, :running, environment: environment.name, pipeline: pipeline) }
236 237 238 239 240 241 242 243

        it 'exposes the deployment information' do
          expect(response).to have_gitlab_http_status(:ok)
          expect(json_response).to match_schema('job/job_details')
          expect(json_response['deployment_status']["status"]).to eq 'creating'
          expect(json_response['deployment_status']["environment"]).not_to be_nil
        end
      end
S
Steve Azzopardi 已提交
244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302

      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)

            get_show(id: job.id, format: :json)
          end

          it 'user can edit runner' do
            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)

            get_show(id: job.id, format: :json)
          end

          it 'user can not edit runner' do
            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)

            get_show(id: job.id, format: :json)
          end

          it 'user can not edit runner' do
            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
303 304 305 306 307 308 309 310 311 312

      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
          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 已提交
313
          expect(json_response['stuck']).to be true
314 315 316 317 318 319 320 321 322 323 324 325
        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
          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 已提交
326
          expect(json_response['stuck']).to be true
327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353
        end
      end

      context 'settings_path' do
        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
354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369

      context 'when no trace is available' do
        it 'has_trace is false' do
          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
          expect(response).to match_response_schema('job/job_details')
          expect(json_response['has_trace']).to be true
        end
      end
370 371 372 373

      it 'exposes the stage the job belongs to' do
        expect(json_response['stage']).to eq('test')
      end
374 375
    end

376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401
    context 'when requesting JSON job is triggered' do
      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)

        allow_any_instance_of(Ci::Build).to receive(:merge_request).and_return(merge_request)
      end

      context 'with no variables' do
        before do
          get_show(id: job.id, format: :json)
        end

        it 'exposes trigger information' do
          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

402
      context 'with variables' do
403 404
        before do
          create(:ci_pipeline_variable, pipeline: pipeline, key: :TRIGGER_KEY_1, value: 'TRIGGER_VALUE_1')
J
jhampton 已提交
405 406
        end

407 408 409
        context 'user is a maintainer' do
          before do
            project.add_maintainer(user)
J
jhampton 已提交
410

411 412
            get_show(id: job.id, format: :json)
          end
J
jhampton 已提交
413

414 415 416 417
          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 已提交
418

419 420 421 422
          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 已提交
423

424 425
          it 'exposes correct variable properties' do
            first_variable = json_response['trigger']['variables'].first
J
jhampton 已提交
426

427 428 429 430
            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 已提交
431 432
        end

433 434 435 436
        context 'user is not a mantainer' do
          before do
            get_show(id: job.id, format: :json)
          end
J
jhampton 已提交
437

438 439 440 441
          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 已提交
442

443 444 445 446 447 448 449 450 451 452 453 454
          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 已提交
455 456
        end
      end
457 458
    end

459 460 461 462 463 464
    def get_show(**extra_params)
      params = {
        namespace_id: project.namespace.to_param,
        project_id: project
      }

B
blackst0ne 已提交
465
      get :show, params: params.merge(extra_params)
466 467 468 469 470 471 472 473
    end
  end

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

474 475 476 477 478 479 480 481 482 483 484
    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

485
    context 'when job has a trace' do
486
      let(:job) { create(:ci_build, :trace_live, pipeline: pipeline) }
487 488

      it 'returns a trace' do
489
        expect(response).to have_gitlab_http_status(:ok)
490 491
        expect(json_response['id']).to eq job.id
        expect(json_response['status']).to eq job.status
492 493 494 495
        expect(json_response['html']).to eq('BUILD TRACE')
      end
    end

496 497
    context 'when job has no traces' do
      let(:job) { create(:ci_build, pipeline: pipeline) }
498 499

      it 'returns no traces' do
500
        expect(response).to have_gitlab_http_status(:ok)
501 502
        expect(json_response['id']).to eq job.id
        expect(json_response['status']).to eq job.status
503
        expect(json_response['html']).to be_nil
504 505 506
      end
    end

507
    context 'when job has a trace with ANSI sequence and Unicode' do
508
      let(:job) { create(:ci_build, :unicode_trace_live, pipeline: pipeline) }
509 510

      it 'returns a trace with Unicode' do
511
        expect(response).to have_gitlab_http_status(:ok)
512 513
        expect(json_response['id']).to eq job.id
        expect(json_response['status']).to eq job.status
514 515 516 517
        expect(json_response['html']).to include("ヾ(´༎ຶД༎ຶ`)ノ")
      end
    end

518
    context 'when trace artifact is in ObjectStorage' do
K
Kamil Trzciński 已提交
519 520
      let(:url) { 'http://object-storage/trace' }
      let(:file_path) { expand_fixture_path('trace/sample_trace') }
521 522 523 524
      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 已提交
525 526
        allow_any_instance_of(JobArtifactUploader).to receive(:url) { url }
        allow_any_instance_of(JobArtifactUploader).to receive(:size) { File.size(file_path) }
527 528 529 530
      end

      context 'when there are no network issues' do
        before do
K
Kamil Trzciński 已提交
531
          stub_remote_url_206(url, file_path)
532 533 534 535 536 537 538 539 540 541 542 543 544 545

          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
546
          stub_remote_url_500(url)
547 548 549
        end

        it 'returns a trace' do
550
          expect { get_trace }.to raise_error(Gitlab::HttpIO::FailedToGetChunkError)
551 552 553 554
        end
      end
    end

555
    def get_trace
B
blackst0ne 已提交
556 557 558 559 560
      get :trace, params: {
                    namespace_id: project.namespace,
                    project_id: project,
                    id: job.id
                  },
561 562
                  format: :json
    end
K
Kamil Trzcinski 已提交
563 564
  end

S
Shinya Maeda 已提交
565
  describe 'GET status.json' do
566 567
    let(:job) { create(:ci_build, pipeline: pipeline) }
    let(:status) { job.detailed_status(double('user')) }
S
Shinya Maeda 已提交
568

569
    before do
B
blackst0ne 已提交
570 571 572 573 574
      get :status, params: {
                     namespace_id: project.namespace,
                     project_id: project,
                     id: job.id
                   },
575 576
                   format: :json
    end
S
Shinya Maeda 已提交
577

578
    it 'return a detailed job status in json' do
579
      expect(response).to have_gitlab_http_status(:ok)
580 581 582
      expect(json_response['text']).to eq status.text
      expect(json_response['label']).to eq status.label
      expect(json_response['icon']).to eq status.icon
583
      expect(json_response['favicon']).to match_asset_path "/assets/ci_favicons/#{status.favicon}.png"
S
Shinya Maeda 已提交
584 585
    end
  end
586

587 588
  describe 'POST retry' do
    before do
589
      project.add_developer(user)
590 591 592 593 594
      sign_in(user)

      post_retry
    end

595 596
    context 'when job is retryable' do
      let(:job) { create(:ci_build, :retryable, pipeline: pipeline) }
597

598
      it 'redirects to the retried job page' do
599
        expect(response).to have_gitlab_http_status(:found)
600
        expect(response).to redirect_to(namespace_project_job_path(id: Ci::Build.last.id))
601 602 603
      end
    end

604 605
    context 'when job is not retryable' do
      let(:job) { create(:ci_build, pipeline: pipeline) }
606 607

      it 'renders unprocessable_entity' do
608
        expect(response).to have_gitlab_http_status(:unprocessable_entity)
609 610 611 612
      end
    end

    def post_retry
B
blackst0ne 已提交
613 614 615 616 617
      post :retry, params: {
                     namespace_id: project.namespace,
                     project_id: project,
                     id: job.id
                   }
618 619 620 621 622
    end
  end

  describe 'POST play' do
    before do
623
      project.add_developer(user)
624 625 626 627

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

628 629 630 631 632
      sign_in(user)

      post_play
    end

633 634
    context 'when job is playable' do
      let(:job) { create(:ci_build, :playable, pipeline: pipeline) }
635

636
      it 'redirects to the played job page' do
637
        expect(response).to have_gitlab_http_status(:found)
638
        expect(response).to redirect_to(namespace_project_job_path(id: job.id))
639 640 641
      end

      it 'transits to pending' do
642
        expect(job.reload).to be_pending
643 644 645
      end
    end

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

      it 'renders unprocessable_entity' do
650
        expect(response).to have_gitlab_http_status(:unprocessable_entity)
651 652 653 654
      end
    end

    def post_play
B
blackst0ne 已提交
655 656 657 658 659
      post :play, params: {
                    namespace_id: project.namespace,
                    project_id: project,
                    id: job.id
                  }
660 661 662 663 664
    end
  end

  describe 'POST cancel' do
    before do
665
      project.add_developer(user)
666 667 668
      sign_in(user)
    end

669
    context 'when continue url is present' do
670
      let(:job) { create(:ci_build, :cancelable, pipeline: pipeline) }
671

672 673 674 675 676 677 678 679 680 681 682 683 684 685 686
      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
687 688
      end

689 690 691 692 693 694
      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
695 696 697
      end
    end

698 699 700 701
    context 'when continue url is not present' do
      before do
        post_cancel
      end
702

703 704
      context 'when job is cancelable' do
        let(:job) { create(:ci_build, :cancelable, pipeline: pipeline) }
705

706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721
        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
722 723 724
      end
    end

725
    def post_cancel(additional_params = {})
B
blackst0ne 已提交
726 727 728
      post :cancel, params: { namespace_id: project.namespace,
                              project_id: project,
                              id: job.id }.merge(additional_params)
729 730 731
    end
  end

S
Shinya Maeda 已提交
732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765
  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 已提交
766 767 768 769 770
      post :unschedule, params: {
                          namespace_id: project.namespace,
                          project_id: project,
                          id: job.id
                        }
S
Shinya Maeda 已提交
771 772 773
    end
  end

774
  describe 'POST erase' do
775
    let(:role) { :maintainer }
S
Shinya Maeda 已提交
776

777
    before do
778
      project.add_role(user, role)
779 780 781 782 783
      sign_in(user)

      post_erase
    end

784
    context 'when job is erasable' do
785
      let(:job) { create(:ci_build, :erasable, :trace_artifact, pipeline: pipeline) }
786

787
      it 'redirects to the erased job page' do
788
        expect(response).to have_gitlab_http_status(:found)
789
        expect(response).to redirect_to(namespace_project_job_path(id: job.id))
790 791 792
      end

      it 'erases artifacts' do
793 794
        expect(job.artifacts_file.exists?).to be_falsey
        expect(job.artifacts_metadata.exists?).to be_falsey
795 796 797
      end

      it 'erases trace' do
798
        expect(job.trace.exist?).to be_falsey
799 800 801
      end
    end

802 803
    context 'when job is not erasable' do
      let(:job) { create(:ci_build, :erased, pipeline: pipeline) }
804 805

      it 'returns unprocessable_entity' do
806
        expect(response).to have_gitlab_http_status(:unprocessable_entity)
807 808 809
      end
    end

810 811
    context 'when user is developer' do
      let(:role) { :developer }
812
      let(:job) { create(:ci_build, :erasable, :trace_artifact, pipeline: pipeline, user: triggered_by) }
813 814 815 816 817 818 819 820 821 822 823 824 825

      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 已提交
826
          expect(response).not_to have_gitlab_http_status(:found)
827 828 829 830
        end
      end
    end

831
    def post_erase
B
blackst0ne 已提交
832 833 834 835 836
      post :erase, params: {
                     namespace_id: project.namespace,
                     project_id: project,
                     id: job.id
                   }
837 838 839 840
    end
  end

  describe 'GET raw' do
841
    subject do
B
blackst0ne 已提交
842 843 844 845 846
      post :raw, params: {
                   namespace_id: project.namespace,
                   project_id: project,
                   id: job.id
                 }
847 848
    end

849
    context "when job has a trace artifact" do
850 851
      let(:job) { create(:ci_build, :trace_artifact, pipeline: pipeline) }

852 853
      it "sets #{Gitlab::Workhorse::DETECT_HEADER} header" do
        response = subject
854

855 856 857 858
        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"
859 860 861
      end
    end

862
    context "when job has a trace file" do
863
      let(:job) { create(:ci_build, :trace_live, pipeline: pipeline) }
864

865
      it 'sends a trace file' do
866 867
        response = subject

868
        expect(response).to have_gitlab_http_status(:ok)
869
        expect(response.headers["Content-Type"]).to eq("text/plain; charset=utf-8")
870
        expect(response.headers["Content-Disposition"]).to match(/^inline/)
871
        expect(response.body).to eq("BUILD TRACE")
872 873 874
      end
    end

875
    context "when job has a trace in database" do
S
Shinya Maeda 已提交
876 877 878
      let(:job) { create(:ci_build, pipeline: pipeline) }

      before do
879
        job.update_column(:trace, "Sample trace")
S
Shinya Maeda 已提交
880 881
      end

882
      it 'sends a trace file' do
S
Shinya Maeda 已提交
883 884 885
        response = subject

        expect(response).to have_gitlab_http_status(:ok)
886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902
        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 已提交
903 904 905
      end
    end

906 907
    context 'when job does not have a trace file' do
      let(:job) { create(:ci_build, pipeline: pipeline) }
908 909

      it 'returns not_found' do
910 911
        response = subject

S
Shinya Maeda 已提交
912 913
        expect(response).to have_gitlab_http_status(:ok)
        expect(response.body).to eq ''
914 915 916
      end
    end

917 918 919 920 921 922 923 924 925 926
    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
927 928
    end
  end
F
Francisco Javier López 已提交
929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972

  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 已提交
973
      get :terminal, params: params.merge(extra_params)
F
Francisco Javier López 已提交
974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992
    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)
993
            .to receive(:channel_websocket)
F
Francisco Javier López 已提交
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 1024 1025 1026
            .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 已提交
1027
      get :terminal_websocket_authorize, params: params.merge(extra_params)
F
Francisco Javier López 已提交
1028 1029
    end
  end
S
Shinya Maeda 已提交
1030
end