jobs_controller_spec.rb 31.9 KB
Newer Older
1
# coding: utf-8
S
Shinya Maeda 已提交
2 3
require 'spec_helper'

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

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

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

K
Kamil Trzcinski 已提交
17
  describe 'GET index' do
18 19 20 21 22 23 24 25
    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
26
        expect(response).to have_gitlab_http_status(:ok)
27 28 29 30 31 32 33 34 35 36 37
        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

38
      it 'has only running jobs' do
39
        expect(response).to have_gitlab_http_status(:ok)
40 41 42 43 44 45 46 47 48 49 50
        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

51
      it 'has only finished jobs' do
52
        expect(response).to have_gitlab_http_status(:ok)
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
        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
68
          expect(response).to have_gitlab_http_status(:ok)
69 70 71 72 73
          expect(assigns(:builds).current_page).to eq(last_page)
        end
      end
    end

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

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

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

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

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

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

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

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

        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
125 126
      end

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

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

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

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

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

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

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

160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
      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

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

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

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

210 211 212 213 214 215 216 217 218 219 220
      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
221 222 223 224 225 226 227 228 229 230 231 232 233

      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 已提交
234
        let(:job) { create(:ci_build, :running, environment: environment.name, pipeline: pipeline) }
235 236 237 238 239 240 241 242

        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 已提交
243 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

      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
302 303 304 305 306 307 308 309 310 311

      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 已提交
312
          expect(json_response['stuck']).to be true
313 314 315 316 317 318 319 320 321 322 323 324
        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 已提交
325
          expect(json_response['stuck']).to be true
326 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
        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
353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368

      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
369 370 371 372

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

375 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
    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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

      post_retry
    end

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

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

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

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

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

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

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

627 628 629 630 631
      sign_in(user)

      post_play
    end

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

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

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

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

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

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

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

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

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

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

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

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

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

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

S
Shinya Maeda 已提交
731 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
  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 已提交
765 766 767 768 769
      post :unschedule, params: {
                          namespace_id: project.namespace,
                          project_id: project,
                          id: job.id
                        }
S
Shinya Maeda 已提交
770 771 772
    end
  end

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

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

      post_erase
    end

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

      it 'returns not_found' do
909 910
        response = subject

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

916 917 918 919 920 921 922 923 924 925
    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
926 927
    end
  end
F
Francisco Javier López 已提交
928 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

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