jobs_controller_spec.rb 15.1 KB
Newer Older
1
# coding: utf-8
Shinya Maeda's avatar
Shinya Maeda committed
2 3
require 'spec_helper'

4
describe Projects::JobsController do
5
  include ApiHelpers
6
  include HttpIOHelpers
Shinya Maeda's avatar
Shinya Maeda committed
7

8
  let(:project) { create(:project, :public) }
9 10
  let(:pipeline) { create(:ci_pipeline, project: project) }
  let(:user) { create(:user) }
Shinya Maeda's avatar
Shinya Maeda committed
11

12 13 14 15
  before do
    stub_not_protect_default_branch
  end

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

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

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

Kamil Trzcinski's avatar
Kamil Trzcinski committed
73 74 75
    context 'number of queries' do
      before do
        Ci::Build::AVAILABLE_STATUSES.each do |status|
76
          create_job(status, status)
Kamil Trzcinski's avatar
Kamil Trzcinski committed
77 78 79
        end
      end

80
      it 'verifies number of queries', :request_store do
81
        recorded = ActiveRecord::QueryRecorder.new { get_index }
82
        expect(recorded.count).to be_within(5).of(7)
Kamil Trzcinski's avatar
Kamil Trzcinski committed
83 84
      end

85
      def create_job(name, status)
Kamil Trzcinski's avatar
Kamil Trzcinski committed
86 87 88 89 90
        pipeline = create(:ci_pipeline, project: project)
        create(:ci_build, :tags, :triggered, :artifacts,
          pipeline: pipeline, name: name, status: status)
      end
    end
91 92 93 94 95 96 97 98 99 100 101 102

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

      get :index, params.merge(extra_params)
    end
  end

  describe 'GET show' do
103
    let!(:job) { create(:ci_build, :failed, pipeline: pipeline) }
104

105
    context 'when requesting HTML' do
106
      context 'when job exists' do
107
        before do
108
          get_show(id: job.id)
109 110
        end

111
        it 'has a job' do
112
          expect(response).to have_gitlab_http_status(:ok)
113
          expect(assigns(:build).id).to eq(job.id)
114
        end
115 116
      end

117
      context 'when job does not exist' do
118 119 120 121 122
        before do
          get_show(id: 1234)
        end

        it 'renders not_found' do
123
          expect(response).to have_gitlab_http_status(:not_found)
124
        end
125 126 127
      end
    end

128 129 130
    context 'when requesting JSON' do
      let(:merge_request) { create(:merge_request, source_project: project) }

131
      before do
132 133 134 135 136
        project.add_developer(user)
        sign_in(user)

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

137
        get_show(id: job.id, format: :json)
138 139
      end

140
      it 'exposes needed information' do
141
        expect(response).to have_gitlab_http_status(:ok)
142 143
        expect(json_response['raw_path']).to match(%r{jobs/\d+/raw\z})
        expect(json_response.dig('merge_request', 'path')).to match(%r{merge_requests/\d+\z})
Kamil Trzcinski's avatar
Kamil Trzcinski committed
144 145
        expect(json_response['new_issue_path'])
          .to include('/issues/new')
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
      end
    end

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

      get :show, params.merge(extra_params)
    end
  end

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

164 165 166 167 168 169 170 171 172 173 174
    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

175
    context 'when job has a trace' do
176
      let(:job) { create(:ci_build, :trace_live, pipeline: pipeline) }
177 178

      it 'returns a trace' do
179
        expect(response).to have_gitlab_http_status(:ok)
180 181
        expect(json_response['id']).to eq job.id
        expect(json_response['status']).to eq job.status
182 183 184 185
        expect(json_response['html']).to eq('BUILD TRACE')
      end
    end

186 187
    context 'when job has no traces' do
      let(:job) { create(:ci_build, pipeline: pipeline) }
188 189

      it 'returns no traces' do
190
        expect(response).to have_gitlab_http_status(:ok)
191 192
        expect(json_response['id']).to eq job.id
        expect(json_response['status']).to eq job.status
193
        expect(json_response['html']).to be_nil
194 195 196
      end
    end

197
    context 'when job has a trace with ANSI sequence and Unicode' do
198
      let(:job) { create(:ci_build, :unicode_trace_live, pipeline: pipeline) }
199 200

      it 'returns a trace with Unicode' do
201
        expect(response).to have_gitlab_http_status(:ok)
202 203
        expect(json_response['id']).to eq job.id
        expect(json_response['status']).to eq job.status
204 205 206 207
        expect(json_response['html']).to include("ヾ(´༎ຶД༎ຶ`)ノ")
      end
    end

208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242
    context 'when trace artifact is in ObjectStorage' do
      let!(:job) { create(:ci_build, :success, :trace_artifact, pipeline: pipeline) }

      before do
        allow_any_instance_of(JobArtifactUploader).to receive(:file_storage?) { false }
        allow_any_instance_of(JobArtifactUploader).to receive(:url) { remote_trace_url }
        allow_any_instance_of(JobArtifactUploader).to receive(:size) { remote_trace_size }
      end

      context 'when there are no network issues' do
        before do
          stub_remote_trace_206

          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
          stub_remote_trace_500
        end

        it 'returns a trace' do
          expect { get_trace }.to raise_error(Gitlab::Ci::Trace::HttpIO::FailedToGetChunkError)
        end
      end
    end

243 244 245
    def get_trace
      get :trace, namespace_id: project.namespace,
                  project_id: project,
246
                  id: job.id,
247 248
                  format: :json
    end
Kamil Trzcinski's avatar
Kamil Trzcinski committed
249 250
  end

Shinya Maeda's avatar
Shinya Maeda committed
251
  describe 'GET status.json' do
252 253
    let(:job) { create(:ci_build, pipeline: pipeline) }
    let(:status) { job.detailed_status(double('user')) }
Shinya Maeda's avatar
Shinya Maeda committed
254

255 256 257
    before do
      get :status, namespace_id: project.namespace,
                   project_id: project,
258
                   id: job.id,
259 260
                   format: :json
    end
Shinya Maeda's avatar
Shinya Maeda committed
261

262
    it 'return a detailed job status in json' do
263
      expect(response).to have_gitlab_http_status(:ok)
264 265 266
      expect(json_response['text']).to eq status.text
      expect(json_response['label']).to eq status.label
      expect(json_response['icon']).to eq status.icon
267
      expect(json_response['favicon']).to match_asset_path "/assets/ci_favicons/#{status.favicon}.ico"
Shinya Maeda's avatar
Shinya Maeda committed
268 269
    end
  end
270

271 272
  describe 'POST retry' do
    before do
273
      project.add_developer(user)
274 275 276 277 278
      sign_in(user)

      post_retry
    end

279 280
    context 'when job is retryable' do
      let(:job) { create(:ci_build, :retryable, pipeline: pipeline) }
281

282
      it 'redirects to the retried job page' do
283
        expect(response).to have_gitlab_http_status(:found)
284
        expect(response).to redirect_to(namespace_project_job_path(id: Ci::Build.last.id))
285 286 287
      end
    end

288 289
    context 'when job is not retryable' do
      let(:job) { create(:ci_build, pipeline: pipeline) }
290 291

      it 'renders unprocessable_entity' do
292
        expect(response).to have_gitlab_http_status(:unprocessable_entity)
293 294 295 296 297 298
      end
    end

    def post_retry
      post :retry, namespace_id: project.namespace,
                   project_id: project,
299
                   id: job.id
300 301 302 303 304
    end
  end

  describe 'POST play' do
    before do
305
      project.add_developer(user)
306 307 308 309

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

310 311 312 313 314
      sign_in(user)

      post_play
    end

315 316
    context 'when job is playable' do
      let(:job) { create(:ci_build, :playable, pipeline: pipeline) }
317

318
      it 'redirects to the played job page' do
319
        expect(response).to have_gitlab_http_status(:found)
320
        expect(response).to redirect_to(namespace_project_job_path(id: job.id))
321 322 323
      end

      it 'transits to pending' do
324
        expect(job.reload).to be_pending
325 326 327
      end
    end

328 329
    context 'when job is not playable' do
      let(:job) { create(:ci_build, pipeline: pipeline) }
330 331

      it 'renders unprocessable_entity' do
332
        expect(response).to have_gitlab_http_status(:unprocessable_entity)
333 334 335 336 337 338
      end
    end

    def post_play
      post :play, namespace_id: project.namespace,
                  project_id: project,
339
                  id: job.id
340 341 342 343 344
    end
  end

  describe 'POST cancel' do
    before do
345
      project.add_developer(user)
346 347 348 349 350
      sign_in(user)

      post_cancel
    end

351 352
    context 'when job is cancelable' do
      let(:job) { create(:ci_build, :cancelable, pipeline: pipeline) }
353

354
      it 'redirects to the canceled job page' do
355
        expect(response).to have_gitlab_http_status(:found)
356
        expect(response).to redirect_to(namespace_project_job_path(id: job.id))
357 358 359
      end

      it 'transits to canceled' do
360
        expect(job.reload).to be_canceled
361 362 363
      end
    end

364 365
    context 'when job is not cancelable' do
      let(:job) { create(:ci_build, :canceled, pipeline: pipeline) }
366 367

      it 'returns unprocessable_entity' do
368
        expect(response).to have_gitlab_http_status(:unprocessable_entity)
369 370 371 372 373 374
      end
    end

    def post_cancel
      post :cancel, namespace_id: project.namespace,
                    project_id: project,
375
                    id: job.id
376 377 378 379 380
    end
  end

  describe 'POST cancel_all' do
    before do
381
      project.add_developer(user)
382 383 384
      sign_in(user)
    end

385
    context 'when jobs are cancelable' do
386 387 388 389 390 391 392
      before do
        create_list(:ci_build, 2, :cancelable, pipeline: pipeline)

        post_cancel_all
      end

      it 'redirects to a index page' do
393
        expect(response).to have_gitlab_http_status(:found)
394
        expect(response).to redirect_to(namespace_project_jobs_path)
395 396 397 398 399 400 401
      end

      it 'transits to canceled' do
        expect(Ci::Build.all).to all(be_canceled)
      end
    end

402
    context 'when jobs are not cancelable' do
403 404 405 406 407 408 409
      before do
        create_list(:ci_build, 2, :canceled, pipeline: pipeline)

        post_cancel_all
      end

      it 'redirects to a index page' do
410
        expect(response).to have_gitlab_http_status(:found)
411
        expect(response).to redirect_to(namespace_project_jobs_path)
412 413 414 415 416 417 418 419 420 421
      end
    end

    def post_cancel_all
      post :cancel_all, namespace_id: project.namespace,
                        project_id: project
    end
  end

  describe 'POST erase' do
Shinya Maeda's avatar
Shinya Maeda committed
422 423
    let(:role) { :master }

424
    before do
425
      project.add_role(user, role)
426 427 428 429 430
      sign_in(user)

      post_erase
    end

431
    context 'when job is erasable' do
432
      let(:job) { create(:ci_build, :erasable, :trace_artifact, pipeline: pipeline) }
433

434
      it 'redirects to the erased job page' do
435
        expect(response).to have_gitlab_http_status(:found)
436
        expect(response).to redirect_to(namespace_project_job_path(id: job.id))
437 438 439
      end

      it 'erases artifacts' do
440 441
        expect(job.artifacts_file.exists?).to be_falsey
        expect(job.artifacts_metadata.exists?).to be_falsey
442 443 444
      end

      it 'erases trace' do
445
        expect(job.trace.exist?).to be_falsey
446 447 448
      end
    end

449 450
    context 'when job is not erasable' do
      let(:job) { create(:ci_build, :erased, pipeline: pipeline) }
451 452

      it 'returns unprocessable_entity' do
453
        expect(response).to have_gitlab_http_status(:unprocessable_entity)
454 455 456
      end
    end

457 458
    context 'when user is developer' do
      let(:role) { :developer }
459
      let(:job) { create(:ci_build, :erasable, :trace_artifact, pipeline: pipeline, user: triggered_by) }
460 461 462 463 464 465 466 467 468 469 470 471 472

      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
Shinya Maeda's avatar
Shinya Maeda committed
473
          expect(response).not_to have_gitlab_http_status(:found)
474 475 476 477
        end
      end
    end

478 479 480
    def post_erase
      post :erase, namespace_id: project.namespace,
                   project_id: project,
481
                   id: job.id
482 483 484 485
    end
  end

  describe 'GET raw' do
486 487 488 489
    subject do
      post :raw, namespace_id: project.namespace,
                 project_id: project,
                 id: job.id
490 491
    end

492 493 494 495
    context 'when job has a trace artifact' do
      let(:job) { create(:ci_build, :trace_artifact, pipeline: pipeline) }

      it 'returns a trace' do
496 497
        response = subject

498 499 500 501 502 503
        expect(response).to have_gitlab_http_status(:ok)
        expect(response.content_type).to eq 'text/plain; charset=utf-8'
        expect(response.body).to eq job.job_artifacts_trace.open.read
      end
    end

504
    context 'when job has a trace file' do
505
      let(:job) { create(:ci_build, :trace_live, pipeline: pipeline) }
506 507

      it 'send a trace file' do
508 509
        response = subject

510
        expect(response).to have_gitlab_http_status(:ok)
511 512 513 514 515
        expect(response.content_type).to eq 'text/plain; charset=utf-8'
        expect(response.body).to eq 'BUILD TRACE'
      end
    end

Shinya Maeda's avatar
Shinya Maeda committed
516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531
    context 'when job has a trace in database' do
      let(:job) { create(:ci_build, pipeline: pipeline) }

      before do
        job.update_column(:trace, 'Sample trace')
      end

      it 'send a trace file' do
        response = subject

        expect(response).to have_gitlab_http_status(:ok)
        expect(response.content_type).to eq 'text/plain; charset=utf-8'
        expect(response.body).to eq 'Sample trace'
      end
    end

532 533
    context 'when job does not have a trace file' do
      let(:job) { create(:ci_build, pipeline: pipeline) }
534 535

      it 'returns not_found' do
536 537
        response = subject

Shinya Maeda's avatar
Shinya Maeda committed
538 539
        expect(response).to have_gitlab_http_status(:ok)
        expect(response.body).to eq ''
540 541 542
      end
    end

543 544 545 546 547 548 549 550 551 552
    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
553 554
    end
  end
Shinya Maeda's avatar
Shinya Maeda committed
555
end