jobs_controller_spec.rb 33.5 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
    stub_feature_flags(job_log_json: false)
16 17 18
    stub_not_protect_default_branch
  end

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

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

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

K
Kamil Trzcinski 已提交
76
    context 'number of queries' do
77 78
      render_views

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        it 'exposes empty state illustrations' do
241 242
          get_show_json

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

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

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

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

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

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

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

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

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

S
Steve Azzopardi 已提交
317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333
            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
334 335
            get_show_json

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

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

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

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

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

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 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
401 402 403

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

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

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

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

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

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

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

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

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

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

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

464
            get_show_json
465
          end
J
jhampton 已提交
466

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

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

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

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

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

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

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

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

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

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

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

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

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

      it 'returns a trace' do
547
        expect(response).to have_gitlab_http_status(:ok)
548 549
        expect(json_response['id']).to eq job.id
        expect(json_response['status']).to eq job.status
550
        expect(json_response['html']).to eq('<span>BUILD TRACE</span>')
551 552 553
      end
    end

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

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

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

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

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

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

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

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

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

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

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

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

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

      post_retry
    end

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

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

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

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

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

  describe 'POST play' do
M
Matija Čupić 已提交
680 681
    let(:variable_attributes) { [] }

682
    before do
683
      project.add_developer(user)
684 685 686 687

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

688 689 690 691 692
      sign_in(user)

      post_play
    end

693 694
    context 'when job is playable' do
      let(:job) { create(:ci_build, :playable, pipeline: pipeline) }
695

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

      it 'transits to pending' do
702
        expect(job.reload).to be_pending
703
      end
M
Matija Čupić 已提交
704 705 706 707 708 709 710 711

      context 'when job variables are specified' do
        let(:variable_attributes) { [{ key: 'first', secret_value: 'first' }] }

        it 'assigns the job variables' do
          expect(job.reload.job_variables.map(&:key)).to contain_exactly('first')
        end
      end
712 713
    end

714 715
    context 'when job is not playable' do
      let(:job) { create(:ci_build, pipeline: pipeline) }
716 717

      it 'renders unprocessable_entity' do
718
        expect(response).to have_gitlab_http_status(:unprocessable_entity)
719 720 721 722
      end
    end

    def post_play
B
blackst0ne 已提交
723 724 725
      post :play, params: {
                    namespace_id: project.namespace,
                    project_id: project,
M
Matija Čupić 已提交
726 727
                    id: job.id,
                    job_variables_attributes: variable_attributes
B
blackst0ne 已提交
728
                  }
729 730 731 732 733
    end
  end

  describe 'POST cancel' do
    before do
734
      project.add_developer(user)
735 736 737
      sign_in(user)
    end

738
    context 'when continue url is present' do
739
      let(:job) { create(:ci_build, :cancelable, pipeline: pipeline) }
740

741 742 743 744 745 746 747 748 749 750 751 752 753 754 755
      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
756 757
      end

758 759 760 761 762 763
      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
764 765 766
      end
    end

767 768 769 770
    context 'when continue url is not present' do
      before do
        post_cancel
      end
771

772 773
      context 'when job is cancelable' do
        let(:job) { create(:ci_build, :cancelable, pipeline: pipeline) }
774

775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790
        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
791 792 793
      end
    end

794
    def post_cancel(additional_params = {})
B
blackst0ne 已提交
795 796 797
      post :cancel, params: { namespace_id: project.namespace,
                              project_id: project,
                              id: job.id }.merge(additional_params)
798 799 800
    end
  end

S
Shinya Maeda 已提交
801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834
  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 已提交
835 836 837 838 839
      post :unschedule, params: {
                          namespace_id: project.namespace,
                          project_id: project,
                          id: job.id
                        }
S
Shinya Maeda 已提交
840 841 842
    end
  end

843
  describe 'POST erase' do
844
    let(:role) { :maintainer }
S
Shinya Maeda 已提交
845

846
    before do
847
      project.add_role(user, role)
848 849 850 851 852
      sign_in(user)

      post_erase
    end

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

856
      it 'redirects to the erased job page' do
857
        expect(response).to have_gitlab_http_status(:found)
858
        expect(response).to redirect_to(namespace_project_job_path(id: job.id))
859 860 861
      end

      it 'erases artifacts' do
862 863
        expect(job.artifacts_file.present?).to be_falsey
        expect(job.artifacts_metadata.present?).to be_falsey
864 865 866
      end

      it 'erases trace' do
867
        expect(job.trace.exist?).to be_falsey
868 869 870
      end
    end

871 872
    context 'when job is not erasable' do
      let(:job) { create(:ci_build, :erased, pipeline: pipeline) }
873 874

      it 'returns unprocessable_entity' do
875
        expect(response).to have_gitlab_http_status(:unprocessable_entity)
876 877 878
      end
    end

879 880
    context 'when user is developer' do
      let(:role) { :developer }
881
      let(:job) { create(:ci_build, :erasable, :trace_artifact, pipeline: pipeline, user: triggered_by) }
882 883 884 885 886 887 888 889 890 891 892 893 894

      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 已提交
895
          expect(response).not_to have_gitlab_http_status(:found)
896 897 898 899
        end
      end
    end

900
    def post_erase
B
blackst0ne 已提交
901 902 903 904 905
      post :erase, params: {
                     namespace_id: project.namespace,
                     project_id: project,
                     id: job.id
                   }
906 907 908 909
    end
  end

  describe 'GET raw' do
910
    subject do
B
blackst0ne 已提交
911 912 913 914 915
      post :raw, params: {
                   namespace_id: project.namespace,
                   project_id: project,
                   id: job.id
                 }
916 917
    end

918
    context "when job has a trace artifact" do
919 920
      let(:job) { create(:ci_build, :trace_artifact, pipeline: pipeline) }

921 922
      it "sets #{Gitlab::Workhorse::DETECT_HEADER} header" do
        response = subject
923

924 925 926 927
        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"
928 929 930
      end
    end

931
    context "when job has a trace file" do
932
      let(:job) { create(:ci_build, :trace_live, pipeline: pipeline) }
933

934
      it 'sends a trace file' do
935 936
        response = subject

937
        expect(response).to have_gitlab_http_status(:ok)
938
        expect(response.headers["Content-Type"]).to eq("text/plain; charset=utf-8")
939
        expect(response.headers["Content-Disposition"]).to match(/^inline/)
940
        expect(response.body).to eq("BUILD TRACE")
941 942 943
      end
    end

944
    context "when job has a trace in database" do
S
Shinya Maeda 已提交
945 946 947
      let(:job) { create(:ci_build, pipeline: pipeline) }

      before do
948
        job.update_column(:trace, "Sample trace")
S
Shinya Maeda 已提交
949 950
      end

951
      it 'sends a trace file' do
S
Shinya Maeda 已提交
952 953 954
        response = subject

        expect(response).to have_gitlab_http_status(:ok)
955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971
        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 已提交
972 973 974
      end
    end

975 976
    context 'when job does not have a trace file' do
      let(:job) { create(:ci_build, pipeline: pipeline) }
977 978

      it 'returns not_found' do
979 980
        response = subject

S
Shinya Maeda 已提交
981 982
        expect(response).to have_gitlab_http_status(:ok)
        expect(response.body).to eq ''
983 984 985
      end
    end

986 987 988 989 990 991 992 993 994 995
    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
996 997
    end
  end
F
Francisco Javier López 已提交
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 1030 1031 1032 1033 1034 1035 1036 1037 1038 1039 1040 1041

  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 已提交
1042
      get :terminal, params: params.merge(extra_params)
F
Francisco Javier López 已提交
1043 1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061
    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)
1062
            .to receive(:channel_websocket)
F
Francisco Javier López 已提交
1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083 1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095
            .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 已提交
1096
      get :terminal_websocket_authorize, params: params.merge(extra_params)
F
Francisco Javier López 已提交
1097 1098
    end
  end
S
Shinya Maeda 已提交
1099
end