jobs_controller_spec.rb 33.1 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
    context 'number of queries' do
76 77
      render_views

K
Kamil Trzcinski 已提交
78 79
      before do
        Ci::Build::AVAILABLE_STATUSES.each do |status|
80
          create_job(status, status)
K
Kamil Trzcinski 已提交
81
        end
82 83 84

        allow(Appearance).to receive(:current_without_cache)
          .and_return(nil)
K
Kamil Trzcinski 已提交
85 86
      end

87
      it 'verifies number of queries', :request_store do
88
        expect { get_index }.not_to be_n_plus_1_query.with_threshold(3)
K
Kamil Trzcinski 已提交
89 90
      end

91
      def create_job(name, status)
92 93
        user = create(:user)
        pipeline = create(:ci_pipeline, project: project, user: user)
K
Kamil Trzcinski 已提交
94
        create(:ci_build, :tags, :triggered, :artifacts,
95 96
               pipeline: pipeline, name: name, status: status,
               user: user)
K
Kamil Trzcinski 已提交
97 98
      end
    end
99 100 101 102 103 104 105

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

B
blackst0ne 已提交
106
      get :index, params: params.merge(extra_params)
107 108 109
    end
  end

110
  describe 'GET show', :request_store do
111
    let!(:job) { create(:ci_build, :failed, pipeline: pipeline) }
112 113
    let!(:second_job) { create(:ci_build, :failed, pipeline: pipeline) }
    let!(:third_job) { create(:ci_build, :failed) }
114

115
    context 'when requesting HTML' do
116
      context 'when job exists' do
117
        before do
118
          get_show(id: job.id)
119 120
        end

121
        it 'has a job' do
122
          expect(response).to have_gitlab_http_status(:ok)
123
          expect(assigns(:build).id).to eq(job.id)
124
        end
125 126 127 128 129 130 131

        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
132 133
      end

134
      context 'when job does not exist' do
135 136 137 138 139
        before do
          get_show(id: 1234)
        end

        it 'renders not_found' do
140
          expect(response).to have_gitlab_http_status(:not_found)
141
        end
142 143 144
      end
    end

145
    context 'when requesting JSON' do
146 147
      let(:merge_request) { create(:merge_request, source_project: project) }

148
      before do
149 150 151
        project.add_developer(user)
        sign_in(user)

152 153 154
        allow_any_instance_of(Ci::Build)
          .to receive(:merge_request)
          .and_return(merge_request)
155 156
      end

157
      it 'does not serialize builds in exposed stages' do
158 159
        get_show_json

160
        json_response.dig('pipeline', 'details', 'stages').tap do |stages|
161
          expect(stages.flat_map(&:keys))
162
            .to eq %w[name title status path dropdown_path]
163 164 165
        end
      end

166 167
      context 'when job failed' do
        it 'exposes needed information' do
168 169
          get_show_json

170 171 172
          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})
173
          expect(json_response['merge_request']['path']).to match(%r{merge_requests/\d+\z})
174 175
          expect(json_response['new_issue_path']).to include('/issues/new')
        end
176 177
      end

178
      context 'when job is running' do
179 180 181 182
        before do
          get_show_json
        end

183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203
        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

204
      context 'when job has artifacts' do
205 206 207 208
        before do
          get_show_json
        end

209 210
        context 'with not expiry date' do
          let(:job) { create(:ci_build, :success, :artifacts, pipeline: pipeline) }
211

212 213 214 215 216
          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})
217 218
            expect(json_response['artifact']).not_to have_key('expired')
            expect(json_response['artifact']).not_to have_key('expired_at')
219 220
          end
        end
221

222 223 224 225 226 227
        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')
228 229
            expect(json_response['artifact']).not_to have_key('download_path')
            expect(json_response['artifact']).not_to have_key('browse_path')
230 231 232 233
            expect(json_response['artifact']['expired']).to eq(true)
            expect(json_response['artifact']['expire_at']).not_to be_empty
          end
        end
234 235
      end

236 237 238 239
      context 'when job passed with no trace' do
        let(:job) { create(:ci_build, :success, :artifacts, pipeline: pipeline) }

        it 'exposes empty state illustrations' do
240 241
          get_show_json

242 243 244 245 246 247 248
          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
249 250 251 252 253

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

        it 'does not exposes the deployment information' do
254 255
          get_show_json

256 257 258 259 260 261 262 263
          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 已提交
264
        let(:job) { create(:ci_build, :running, environment: environment.name, pipeline: pipeline) }
265

266 267 268 269
        before do
          create(:deployment, :success, environment: environment, project: project)
        end

270
        it 'exposes the deployment information' do
271 272
          get_show_json

273 274
          expect(response).to have_gitlab_http_status(:ok)
          expect(json_response).to match_schema('job/job_details')
275 276 277
          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
278 279
          expect(json_response.dig('deployment_status', 'environment', 'last_deployment'))
            .not_to include('commit')
280 281
        end
      end
S
Steve Azzopardi 已提交
282 283 284 285 286 287 288 289 290 291 292 293

      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
294 295
            get_show_json

S
Steve Azzopardi 已提交
296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313
            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
314 315
            get_show_json

S
Steve Azzopardi 已提交
316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332
            expect(response).to have_gitlab_http_status(:ok)
            expect(response).to match_response_schema('job/job_details')
            expect(json_response['runner']).not_to have_key('edit_path')
          end
        end

        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
333 334
            get_show_json

S
Steve Azzopardi 已提交
335 336 337 338 339 340
            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
341 342 343 344 345 346

      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
347 348
          get_show_json

349 350 351 352
          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 已提交
353
          expect(json_response['stuck']).to be true
354 355 356 357 358 359 360 361
        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
362 363
          get_show_json

364 365 366 367
          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 已提交
368
          expect(json_response['stuck']).to be true
369 370 371 372
        end
      end

      context 'settings_path' do
373 374 375 376
        before do
          get_show_json
        end

377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399
        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
400 401 402

      context 'when no trace is available' do
        it 'has_trace is false' do
403 404
          get_show_json

405 406 407 408 409 410 411 412 413
          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
414 415
          get_show_json

416 417 418 419
          expect(response).to match_response_schema('job/job_details')
          expect(json_response['has_trace']).to be true
        end
      end
420 421

      it 'exposes the stage the job belongs to' do
422 423
        get_show_json

424 425
        expect(json_response['stage']).to eq('test')
      end
426 427
    end

428
    context 'when requesting triggered job JSON' do
429 430 431 432 433 434 435 436 437
      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)

438 439 440
        allow_any_instance_of(Ci::Build)
          .to receive(:merge_request)
          .and_return(merge_request)
441 442 443 444
      end

      context 'with no variables' do
        it 'exposes trigger information' do
445 446
          get_show_json

447 448 449 450 451 452 453
          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

454
      context 'with variables' do
455 456
        before do
          create(:ci_pipeline_variable, pipeline: pipeline, key: :TRIGGER_KEY_1, value: 'TRIGGER_VALUE_1')
J
jhampton 已提交
457 458
        end

459 460 461
        context 'user is a maintainer' do
          before do
            project.add_maintainer(user)
J
jhampton 已提交
462

463
            get_show_json
464
          end
J
jhampton 已提交
465

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

471 472 473 474
          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 已提交
475

476 477
          it 'exposes correct variable properties' do
            first_variable = json_response['trigger']['variables'].first
J
jhampton 已提交
478

479 480 481 482
            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 已提交
483 484
        end

485 486
        context 'user is not a mantainer' do
          before do
487
            get_show_json
488
          end
J
jhampton 已提交
489

490 491 492 493
          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 已提交
494

495 496 497 498 499 500 501 502 503 504 505 506
          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 已提交
507 508
        end
      end
509 510
    end

511
    def get_show_json
512 513
      expect { get_show(id: job.id, format: :json) }
        .not_to change { Gitlab::GitalyClient.get_request_count }
514 515
    end

516 517 518 519 520 521
    def get_show(**extra_params)
      params = {
        namespace_id: project.namespace.to_param,
        project_id: project
      }

B
blackst0ne 已提交
522
      get :show, params: params.merge(extra_params)
523 524 525 526 527 528 529 530
    end
  end

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

531 532 533 534 535 536 537 538 539 540 541
    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

542
    context 'when job has a trace' do
543
      let(:job) { create(:ci_build, :trace_live, pipeline: pipeline) }
544 545

      it 'returns a trace' do
546
        expect(response).to have_gitlab_http_status(:ok)
547 548
        expect(json_response['id']).to eq job.id
        expect(json_response['status']).to eq job.status
F
Fabio Pitino 已提交
549
        expect(json_response['html']).to eq('<span class="">BUILD TRACE</span>')
550 551 552
      end
    end

553 554
    context 'when job has no traces' do
      let(:job) { create(:ci_build, pipeline: pipeline) }
555 556

      it 'returns no traces' do
557
        expect(response).to have_gitlab_http_status(:ok)
558 559
        expect(json_response['id']).to eq job.id
        expect(json_response['status']).to eq job.status
560
        expect(json_response['html']).to be_nil
561 562 563
      end
    end

564
    context 'when job has a trace with ANSI sequence and Unicode' do
565
      let(:job) { create(:ci_build, :unicode_trace_live, pipeline: pipeline) }
566 567

      it 'returns a trace with Unicode' do
568
        expect(response).to have_gitlab_http_status(:ok)
569 570
        expect(json_response['id']).to eq job.id
        expect(json_response['status']).to eq job.status
571 572 573 574
        expect(json_response['html']).to include("ヾ(´༎ຶД༎ຶ`)ノ")
      end
    end

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

      context 'when there are no network issues' do
        before do
K
Kamil Trzciński 已提交
588
          stub_remote_url_206(url, file_path)
589 590 591 592 593 594 595 596 597 598 599 600 601 602

          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
603
          stub_remote_url_500(url)
604 605 606
        end

        it 'returns a trace' do
607
          expect { get_trace }.to raise_error(Gitlab::HttpIO::FailedToGetChunkError)
608 609 610 611
        end
      end
    end

612
    def get_trace
B
blackst0ne 已提交
613 614 615 616 617
      get :trace, params: {
                    namespace_id: project.namespace,
                    project_id: project,
                    id: job.id
                  },
618 619
                  format: :json
    end
K
Kamil Trzcinski 已提交
620 621
  end

S
Shinya Maeda 已提交
622
  describe 'GET status.json' do
623 624
    let(:job) { create(:ci_build, pipeline: pipeline) }
    let(:status) { job.detailed_status(double('user')) }
S
Shinya Maeda 已提交
625

626
    before do
B
blackst0ne 已提交
627 628 629 630 631
      get :status, params: {
                     namespace_id: project.namespace,
                     project_id: project,
                     id: job.id
                   },
632 633
                   format: :json
    end
S
Shinya Maeda 已提交
634

635
    it 'return a detailed job status in json' do
636
      expect(response).to have_gitlab_http_status(:ok)
637 638 639
      expect(json_response['text']).to eq status.text
      expect(json_response['label']).to eq status.label
      expect(json_response['icon']).to eq status.icon
640
      expect(json_response['favicon']).to match_asset_path "/assets/ci_favicons/#{status.favicon}.png"
S
Shinya Maeda 已提交
641 642
    end
  end
643

644 645
  describe 'POST retry' do
    before do
646
      project.add_developer(user)
647 648 649 650 651
      sign_in(user)

      post_retry
    end

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

655
      it 'redirects to the retried job page' do
656
        expect(response).to have_gitlab_http_status(:found)
657
        expect(response).to redirect_to(namespace_project_job_path(id: Ci::Build.last.id))
658 659 660
      end
    end

661 662
    context 'when job is not retryable' do
      let(:job) { create(:ci_build, pipeline: pipeline) }
663 664

      it 'renders unprocessable_entity' do
665
        expect(response).to have_gitlab_http_status(:unprocessable_entity)
666 667 668 669
      end
    end

    def post_retry
B
blackst0ne 已提交
670 671 672 673 674
      post :retry, params: {
                     namespace_id: project.namespace,
                     project_id: project,
                     id: job.id
                   }
675 676 677 678 679
    end
  end

  describe 'POST play' do
    before do
680
      project.add_developer(user)
681 682 683 684

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

685 686 687 688 689
      sign_in(user)

      post_play
    end

690 691
    context 'when job is playable' do
      let(:job) { create(:ci_build, :playable, pipeline: pipeline) }
692

693
      it 'redirects to the played job page' do
694
        expect(response).to have_gitlab_http_status(:found)
695
        expect(response).to redirect_to(namespace_project_job_path(id: job.id))
696 697 698
      end

      it 'transits to pending' do
699
        expect(job.reload).to be_pending
700 701 702
      end
    end

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

      it 'renders unprocessable_entity' do
707
        expect(response).to have_gitlab_http_status(:unprocessable_entity)
708 709 710 711
      end
    end

    def post_play
B
blackst0ne 已提交
712 713 714 715 716
      post :play, params: {
                    namespace_id: project.namespace,
                    project_id: project,
                    id: job.id
                  }
717 718 719 720 721
    end
  end

  describe 'POST cancel' do
    before do
722
      project.add_developer(user)
723 724 725
      sign_in(user)
    end

726
    context 'when continue url is present' do
727
      let(:job) { create(:ci_build, :cancelable, pipeline: pipeline) }
728

729 730 731 732 733 734 735 736 737 738 739 740 741 742 743
      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
744 745
      end

746 747 748 749 750 751
      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
752 753 754
      end
    end

755 756 757 758
    context 'when continue url is not present' do
      before do
        post_cancel
      end
759

760 761
      context 'when job is cancelable' do
        let(:job) { create(:ci_build, :cancelable, pipeline: pipeline) }
762

763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778
        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
779 780 781
      end
    end

782
    def post_cancel(additional_params = {})
B
blackst0ne 已提交
783 784 785
      post :cancel, params: { namespace_id: project.namespace,
                              project_id: project,
                              id: job.id }.merge(additional_params)
786 787 788
    end
  end

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

831
  describe 'POST erase' do
832
    let(:role) { :maintainer }
S
Shinya Maeda 已提交
833

834
    before do
835
      project.add_role(user, role)
836 837 838 839 840
      sign_in(user)

      post_erase
    end

841
    context 'when job is erasable' do
842
      let(:job) { create(:ci_build, :erasable, :trace_artifact, pipeline: pipeline) }
843

844
      it 'redirects to the erased job page' do
845
        expect(response).to have_gitlab_http_status(:found)
846
        expect(response).to redirect_to(namespace_project_job_path(id: job.id))
847 848 849
      end

      it 'erases artifacts' do
850 851
        expect(job.artifacts_file.present?).to be_falsey
        expect(job.artifacts_metadata.present?).to be_falsey
852 853 854
      end

      it 'erases trace' do
855
        expect(job.trace.exist?).to be_falsey
856 857 858
      end
    end

859 860
    context 'when job is not erasable' do
      let(:job) { create(:ci_build, :erased, pipeline: pipeline) }
861 862

      it 'returns unprocessable_entity' do
863
        expect(response).to have_gitlab_http_status(:unprocessable_entity)
864 865 866
      end
    end

867 868
    context 'when user is developer' do
      let(:role) { :developer }
869
      let(:job) { create(:ci_build, :erasable, :trace_artifact, pipeline: pipeline, user: triggered_by) }
870 871 872 873 874 875 876 877 878 879 880 881 882

      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 已提交
883
          expect(response).not_to have_gitlab_http_status(:found)
884 885 886 887
        end
      end
    end

888
    def post_erase
B
blackst0ne 已提交
889 890 891 892 893
      post :erase, params: {
                     namespace_id: project.namespace,
                     project_id: project,
                     id: job.id
                   }
894 895 896 897
    end
  end

  describe 'GET raw' do
898
    subject do
B
blackst0ne 已提交
899 900 901 902 903
      post :raw, params: {
                   namespace_id: project.namespace,
                   project_id: project,
                   id: job.id
                 }
904 905
    end

906
    context "when job has a trace artifact" do
907 908
      let(:job) { create(:ci_build, :trace_artifact, pipeline: pipeline) }

909 910
      it "sets #{Gitlab::Workhorse::DETECT_HEADER} header" do
        response = subject
911

912 913 914 915
        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"
916 917 918
      end
    end

919
    context "when job has a trace file" do
920
      let(:job) { create(:ci_build, :trace_live, pipeline: pipeline) }
921

922
      it 'sends a trace file' do
923 924
        response = subject

925
        expect(response).to have_gitlab_http_status(:ok)
926
        expect(response.headers["Content-Type"]).to eq("text/plain; charset=utf-8")
927
        expect(response.headers["Content-Disposition"]).to match(/^inline/)
928
        expect(response.body).to eq("BUILD TRACE")
929 930 931
      end
    end

932
    context "when job has a trace in database" do
S
Shinya Maeda 已提交
933 934 935
      let(:job) { create(:ci_build, pipeline: pipeline) }

      before do
936
        job.update_column(:trace, "Sample trace")
S
Shinya Maeda 已提交
937 938
      end

939
      it 'sends a trace file' do
S
Shinya Maeda 已提交
940 941 942
        response = subject

        expect(response).to have_gitlab_http_status(:ok)
943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959
        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 已提交
960 961 962
      end
    end

963 964
    context 'when job does not have a trace file' do
      let(:job) { create(:ci_build, pipeline: pipeline) }
965 966

      it 'returns not_found' do
967 968
        response = subject

S
Shinya Maeda 已提交
969 970
        expect(response).to have_gitlab_http_status(:ok)
        expect(response.body).to eq ''
971 972 973
      end
    end

974 975 976 977 978 979 980 981 982 983
    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
984 985
    end
  end
F
Francisco Javier López 已提交
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 1024 1025 1026 1027 1028 1029

  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 已提交
1030
      get :terminal, params: params.merge(extra_params)
F
Francisco Javier López 已提交
1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048 1049
    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)
1050
            .to receive(:channel_websocket)
F
Francisco Javier López 已提交
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 1078 1079 1080 1081 1082 1083
            .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 已提交
1084
      get :terminal_websocket_authorize, params: params.merge(extra_params)
F
Francisco Javier López 已提交
1085 1086
    end
  end
S
Shinya Maeda 已提交
1087
end