BigW Consortium Gitlab

build_spec.rb 27.8 KB
Newer Older
1 2
require 'spec_helper'

Douwe Maan committed
3
describe Ci::Build, models: true do
4
  let(:project) { create(:project) }
5 6 7

  let(:pipeline) do
    create(:ci_pipeline, project: project,
8
                         sha: project.commit.id,
9
                         ref: project.default_branch,
10
                         status: 'success')
11 12
  end

13
  let(:build) { create(:ci_build, pipeline: pipeline) }
14

Kamil Trzcinski committed
15
  it { is_expected.to validate_presence_of :ref }
16

Dmitriy Zaporozhets committed
17
  it { is_expected.to respond_to :trace_html }
18

Kamil Trzcinski committed
19
  describe '#first_pending' do
20 21
    let!(:first) { create(:ci_build, pipeline: pipeline, status: 'pending', created_at: Date.yesterday) }
    let!(:second) { create(:ci_build, pipeline: pipeline, status: 'pending') }
22
    subject { Ci::Build.first_pending }
23

Dmitriy Zaporozhets committed
24 25
    it { is_expected.to be_a(Ci::Build) }
    it('returns with the first pending build') { is_expected.to eq(first) }
26 27
  end

Kamil Trzcinski committed
28
  describe '#create_from' do
29 30 31 32
    before do
      build.status = 'success'
      build.save
    end
33
    let(:create_from_build) { Ci::Build.create_from build }
34

35
    it 'exists a pending task' do
36
      expect(Ci::Build.pending.count(:all)).to eq 0
37
      create_from_build
38
      expect(Ci::Build.pending.count(:all)).to be > 0
39 40 41
    end
  end

42 43
  describe '#failed_but_allowed?' do
    subject { build.failed_but_allowed? }
44

45
    context 'when build is not allowed to fail' do
46 47 48
      before do
        build.allow_failure = false
      end
49 50

      context 'and build.status is success' do
51 52 53
        before do
          build.status = 'success'
        end
54

Dmitriy Zaporozhets committed
55
        it { is_expected.to be_falsey }
56 57 58
      end

      context 'and build.status is failed' do
59 60 61
        before do
          build.status = 'failed'
        end
62

Dmitriy Zaporozhets committed
63
        it { is_expected.to be_falsey }
64 65 66
      end
    end

67
    context 'when build is allowed to fail' do
68 69 70
      before do
        build.allow_failure = true
      end
71 72

      context 'and build.status is success' do
73 74 75
        before do
          build.status = 'success'
        end
76

Dmitriy Zaporozhets committed
77
        it { is_expected.to be_falsey }
78 79 80
      end

      context 'and build.status is failed' do
81 82 83
        before do
          build.status = 'failed'
        end
84

Dmitriy Zaporozhets committed
85
        it { is_expected.to be_truthy }
86 87 88 89
      end
    end
  end

Kamil Trzcinski committed
90
  describe '#trace' do
91
    it { expect(build.trace).to be_nil }
92

93
    context 'when build.trace contains text' do
94
      let(:text) { 'example output' }
95 96 97
      before do
        build.trace = text
      end
98

99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
      it { expect(build.trace).to eq(text) }
    end

    context 'when build.trace hides runners token' do
      let(:token) { 'my_secret_token' }

      before do
        build.update(trace: token)
        build.project.update(runners_token: token)
      end

      it { expect(build.trace).not_to include(token) }
      it { expect(build.raw_trace).to include(token) }
    end

    context 'when build.trace hides build token' do
      let(:token) { 'my_secret_token' }

      before do
        build.update(trace: token)
        build.update(token: token)
      end

      it { expect(build.trace).not_to include(token) }
      it { expect(build.raw_trace).to include(token) }
    end
  end

  describe '#raw_trace' do
    subject { build.raw_trace }

    context 'when build.trace hides runners token' do
      let(:token) { 'my_secret_token' }

      before do
        build.project.update(runners_token: token)
        build.update(trace: token)
      end

      it { is_expected.not_to include(token) }
    end

    context 'when build.trace hides build token' do
      let(:token) { 'my_secret_token' }

      before do
        build.update(token: token)
        build.update(trace: token)
      end

      it { is_expected.not_to include(token) }
    end
  end

  context '#append_trace' do
    subject { build.trace_html }

    context 'when build.trace hides runners token' do
      let(:token) { 'my_secret_token' }

      before do
        build.project.update(runners_token: token)
        build.append_trace(token, 0)
      end

      it { is_expected.not_to include(token) }
165
    end
166

167
    context 'when build.trace hides build token' do
168 169 170
      let(:token) { 'my_secret_token' }

      before do
171 172
        build.update(token: token)
        build.append_trace(token, 0)
173 174
      end

175
      it { is_expected.not_to include(token) }
176
    end
177 178
  end

179 180 181 182
  # TODO: build timeout
  # describe :timeout do
  #   subject { build.timeout }
  #
183
  #   it { is_expected.to eq(pipeline.project.timeout) }
184
  # end
185

Kamil Trzcinski committed
186
  describe '#options' do
Valery Sizov committed
187
    let(:options) do
188
      {
Valery Sizov committed
189 190
        image: "ruby:2.1",
        services: [
191 192 193
          "postgres"
        ]
      }
Valery Sizov committed
194
    end
195 196

    subject { build.options }
Dmitriy Zaporozhets committed
197
    it { is_expected.to eq(options) }
198 199
  end

200 201 202 203 204 205
  # TODO: allow_git_fetch
  # describe :allow_git_fetch do
  #   subject { build.allow_git_fetch }
  #
  #   it { is_expected.to eq(project.allow_git_fetch) }
  # end
206

Kamil Trzcinski committed
207
  describe '#project' do
208 209
    subject { build.project }

210
    it { is_expected.to eq(pipeline.project) }
211 212
  end

Kamil Trzcinski committed
213
  describe '#project_id' do
214 215
    subject { build.project_id }

216
    it { is_expected.to eq(pipeline.project_id) }
217 218
  end

Kamil Trzcinski committed
219
  describe '#project_name' do
220 221
    subject { build.project_name }

Dmitriy Zaporozhets committed
222
    it { is_expected.to eq(project.name) }
223 224
  end

Kamil Trzcinski committed
225
  describe '#extract_coverage' do
226 227 228
    context 'valid content & regex' do
      subject { build.extract_coverage('Coverage 1033 / 1051 LOC (98.29%) covered', '\(\d+.\d+\%\) covered') }

Dmitriy Zaporozhets committed
229
      it { is_expected.to eq(98.29) }
230 231 232 233 234
    end

    context 'valid content & bad regex' do
      subject { build.extract_coverage('Coverage 1033 / 1051 LOC (98.29%) covered', 'very covered') }

Dmitriy Zaporozhets committed
235
      it { is_expected.to be_nil }
236 237 238 239 240
    end

    context 'no coverage content & regex' do
      subject { build.extract_coverage('No coverage for today :sad:', '\(\d+.\d+\%\) covered') }

Dmitriy Zaporozhets committed
241
      it { is_expected.to be_nil }
242 243 244 245 246
    end

    context 'multiple results in content & regex' do
      subject { build.extract_coverage(' (98.39%) covered. (98.29%) covered', '\(\d+.\d+\%\) covered') }

Dmitriy Zaporozhets committed
247
      it { is_expected.to eq(98.29) }
248
    end
249 250 251 252 253 254

    context 'using a regex capture' do
      subject { build.extract_coverage('TOTAL      9926   3489    65%', 'TOTAL\s+\d+\s+\d+\s+(\d{1,3}\%)') }

      it { is_expected.to eq(65) }
    end
255 256
  end

Kamil Trzcinski committed
257
  describe '#variables' do
258
    let(:container_registry_enabled) { false }
259 260
    let(:predefined_variables) do
      [
261 262 263 264 265 266 267 268 269
        { key: 'CI', value: 'true', public: true },
        { key: 'GITLAB_CI', value: 'true', public: true },
        { key: 'CI_BUILD_ID', value: build.id.to_s, public: true },
        { key: 'CI_BUILD_TOKEN', value: build.token, public: false },
        { key: 'CI_BUILD_REF', value: build.sha, public: true },
        { key: 'CI_BUILD_BEFORE_SHA', value: build.before_sha, public: true },
        { key: 'CI_BUILD_REF_NAME', value: 'master', public: true },
        { key: 'CI_BUILD_NAME', value: 'test', public: true },
        { key: 'CI_BUILD_STAGE', value: 'test', public: true },
270 271 272
        { key: 'CI_SERVER_NAME', value: 'GitLab', public: true },
        { key: 'CI_SERVER_VERSION', value: Gitlab::VERSION, public: true },
        { key: 'CI_SERVER_REVISION', value: Gitlab::REVISION, public: true },
273 274 275 276 277
        { key: 'CI_PROJECT_ID', value: project.id.to_s, public: true },
        { key: 'CI_PROJECT_NAME', value: project.path, public: true },
        { key: 'CI_PROJECT_PATH', value: project.path_with_namespace, public: true },
        { key: 'CI_PROJECT_NAMESPACE', value: project.namespace.path, public: true },
        { key: 'CI_PROJECT_URL', value: project.web_url, public: true },
278
        { key: 'CI_PIPELINE_ID', value: pipeline.id.to_s, public: true }
279 280
      ]
    end
281

282 283 284 285
    before do
      stub_container_registry_config(enabled: container_registry_enabled, host_port: 'registry.example.com')
    end

286
    subject { build.variables }
287

288
    context 'returns variables' do
289
      before do
290
        build.yaml_variables = []
291
      end
292

293 294
      it { is_expected.to eq(predefined_variables) }
    end
295

296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323
    context 'when build has user' do
      let(:user) { create(:user, username: 'starter') }
      let(:user_variables) do
        [
          { key: 'GITLAB_USER_ID',    value: user.id.to_s, public: true },
          { key: 'GITLAB_USER_EMAIL', value: user.email,   public: true }
        ]
      end

      before do
        build.update_attributes(user: user)
      end

      it { user_variables.each { |v| is_expected.to include(v) } }
    end

    context 'when build started manually' do
      before do
        build.update_attributes(when: :manual)
      end

      let(:manual_variable) do
        { key: 'CI_BUILD_MANUAL', value: 'true', public: true }
      end

      it { is_expected.to include(manual_variable) }
    end

324 325 326
    context 'when build is for tag' do
      let(:tag_variable) do
        { key: 'CI_BUILD_TAG', value: 'master', public: true }
327 328
      end

329 330
      before do
        build.update_attributes(tag: true)
331
      end
332

333 334
      it { is_expected.to include(tag_variable) }
    end
335

336 337 338
    context 'when secure variable is defined' do
      let(:secure_variable) do
        { key: 'SECRET_KEY', value: 'secret_value', public: false }
Valery Sizov committed
339
      end
340

341
      before do
342
        build.project.variables << Ci::Variable.new(key: 'SECRET_KEY', value: 'secret_value')
343
      end
344

345 346
      it { is_expected.to include(secure_variable) }
    end
347

348 349 350 351
    context 'when build is for triggers' do
      let(:trigger) { create(:ci_trigger, project: project) }
      let(:trigger_request) { create(:ci_trigger_request_with_variables, pipeline: pipeline, trigger: trigger) }
      let(:user_trigger_variable) do
Katarzyna Kobierska committed
352
        { key: :TRIGGER_KEY_1, value: 'TRIGGER_VALUE_1', public: false }
353 354 355 356
      end
      let(:predefined_trigger_variable) do
        { key: 'CI_BUILD_TRIGGERED', value: 'true', public: true }
      end
357

358 359
      before do
        build.trigger_request = trigger_request
360
      end
361

362 363
      it { is_expected.to include(user_trigger_variable) }
      it { is_expected.to include(predefined_trigger_variable) }
364
    end
365

366
    context 'when yaml_variables are undefined' do
367 368
      before do
        build.yaml_variables = nil
369
      end
370

371 372 373
      context 'use from gitlab-ci.yml' do
        before do
          stub_ci_pipeline_yaml_file(config)
Valery Sizov committed
374
        end
375

376
        context 'when config is not found' do
377 378 379
          let(:config) { nil }

          it { is_expected.to eq(predefined_variables) }
380 381
        end

382
        context 'when config does not have a questioned job' do
383 384
          let(:config) do
            YAML.dump({
385 386 387 388
              test_other: {
                script: 'Hello World'
              }
            })
389
          end
390

391 392
          it { is_expected.to eq(predefined_variables) }
        end
393

394
        context 'when config has variables' do
395 396
          let(:config) do
            YAML.dump({
397 398 399 400 401 402 403
              test: {
                script: 'Hello World',
                variables: {
                  KEY: 'value'
                }
              }
            })
Valery Sizov committed
404
          end
405 406
          let(:variables) do
            [{ key: :KEY, value: 'value', public: true }]
407
          end
408

409 410 411 412
          it { is_expected.to eq(predefined_variables + variables) }
        end
      end
    end
413

414 415 416
    context 'when container registry is enabled' do
      let(:container_registry_enabled) { true }
      let(:ci_registry) do
Kamil Trzcinski committed
417
        { key: 'CI_REGISTRY',  value: 'registry.example.com',  public: true }
418 419
      end
      let(:ci_registry_image) do
Kamil Trzcinski committed
420
        { key: 'CI_REGISTRY_IMAGE',  value: project.container_registry_repository_url, public: true }
421 422 423 424 425 426 427 428 429 430
      end

      context 'and is disabled for project' do
        before do
          project.update(container_registry_enabled: false)
        end

        it { is_expected.to include(ci_registry) }
        it { is_expected.not_to include(ci_registry_image) }
      end
431

432 433 434
      context 'and is enabled for project' do
        before do
          project.update(container_registry_enabled: true)
435
        end
436 437 438

        it { is_expected.to include(ci_registry) }
        it { is_expected.to include(ci_registry_image) }
439 440
      end
    end
441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456

    context 'when runner is assigned to build' do
      let(:runner) { create(:ci_runner, description: 'description', tag_list: ['docker', 'linux']) }

      before do
        build.update(runner: runner)
      end

      it { is_expected.to include({ key: 'CI_RUNNER_ID', value: runner.id.to_s, public: true }) }
      it { is_expected.to include({ key: 'CI_RUNNER_DESCRIPTION', value: 'description', public: true }) }
      it { is_expected.to include({ key: 'CI_RUNNER_TAGS', value: 'docker, linux', public: true }) }
    end

    context 'returns variables in valid order' do
      before do
        allow(build).to receive(:predefined_variables) { ['predefined'] }
457 458 459 460
        allow(project).to receive(:predefined_variables) { ['project'] }
        allow(pipeline).to receive(:predefined_variables) { ['pipeline'] }
        allow(build).to receive(:yaml_variables) { ['yaml'] }
        allow(project).to receive(:secret_variables) { ['secret'] }
461
      end
462

463
      it { is_expected.to eq(%w[predefined project pipeline yaml secret]) }
464 465
    end
  end
Kamil Trzcinski committed
466

467 468 469 470 471 472 473 474
  describe '#has_tags?' do
    context 'when build has tags' do
      subject { create(:ci_build, tag_list: ['tag']) }
      it { is_expected.to have_tags }
    end

    context 'when build does not have tags' do
      subject { create(:ci_build, tag_list: []) }
475
      it { is_expected.not_to have_tags }
476 477 478
    end
  end

Kamil Trzcinski committed
479
  describe '#any_runners_online?' do
480 481 482 483 484 485
    subject { build.any_runners_online? }

    context 'when no runners' do
      it { is_expected.to be_falsey }
    end

486
    context 'when there are runners' do
487
      let(:runner) { create(:ci_runner) }
488 489

      before do
490
        build.project.runners << runner
491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506
        runner.update_attributes(contacted_at: 1.second.ago)
      end

      it { is_expected.to be_truthy }

      it 'that is inactive' do
        runner.update_attributes(active: false)
        is_expected.to be_falsey
      end

      it 'that is not online' do
        runner.update_attributes(contacted_at: nil)
        is_expected.to be_falsey
      end

      it 'that cannot handle build' do
507
        expect_any_instance_of(Ci::Runner).to receive(:can_pick?).and_return(false)
508 509 510 511 512
        is_expected.to be_falsey
      end
    end
  end

Kamil Trzcinski committed
513
  describe '#stuck?' do
514
    subject { build.stuck? }
515

516 517 518 519
    context "when commit_status.status is pending" do
      before do
        build.status = 'pending'
      end
520

521
      it { is_expected.to be_truthy }
522

523 524
      context "and there are specific runner" do
        let(:runner) { create(:ci_runner, contacted_at: 1.second.ago) }
525

526 527 528
        before do
          build.project.runners << runner
          runner.save
529
        end
530 531

        it { is_expected.to be_falsey }
532 533 534
      end
    end

535 536
    %w[success failed canceled running].each do |state|
      context "when commit_status.status is #{state}" do
537 538 539
        before do
          build.status = state
        end
540 541 542 543 544

        it { is_expected.to be_falsey }
      end
    end
  end
545

Kamil Trzcinski committed
546
  describe '#artifacts?' do
547 548 549
    subject { build.artifacts? }

    context 'artifacts archive does not exist' do
550 551 552 553
      before do
        build.update_attributes(artifacts_file: nil)
      end

554 555 556 557
      it { is_expected.to be_falsy }
    end

    context 'artifacts archive exists' do
558
      let(:build) { create(:ci_build, :artifacts) }
559
      it { is_expected.to be_truthy }
560 561 562 563 564 565 566 567 568 569

      context 'is expired' do
        before { build.update(artifacts_expire_at: Time.now - 7.days)  }
        it { is_expected.to be_falsy }
      end

      context 'is not expired' do
        before { build.update(artifacts_expire_at: Time.now + 7.days)  }
        it { is_expected.to be_truthy }
      end
570 571 572
    end
  end

573 574 575 576 577
  describe '#artifacts_expired?' do
    subject { build.artifacts_expired? }

    context 'is expired' do
      before { build.update(artifacts_expire_at: Time.now - 7.days)  }
Kamil Trzcinski committed
578 579

      it { is_expected.to be_truthy }
580 581 582 583
    end

    context 'is not expired' do
      before { build.update(artifacts_expire_at: Time.now + 7.days)  }
Kamil Trzcinski committed
584 585

      it { is_expected.to be_falsey }
586 587
    end
  end
588

Kamil Trzcinski committed
589
  describe '#artifacts_metadata?' do
590
    subject { build.artifacts_metadata? }
591
    context 'artifacts metadata does not exist' do
592 593 594
      it { is_expected.to be_falsy }
    end

595
    context 'artifacts archive is a zip file and metadata exists' do
596
      let(:build) { create(:ci_build, :artifacts) }
597 598 599
      it { is_expected.to be_truthy }
    end
  end
Kamil Trzcinski committed
600
  describe '#repo_url' do
601
    let(:build) { create(:ci_build) }
602 603 604 605 606 607 608
    let(:project) { build.project }

    subject { build.repo_url }

    it { is_expected.to be_a(String) }
    it { is_expected.to end_with(".git") }
    it { is_expected.to start_with(project.web_url[0..6]) }
Kamil Trzcinski committed
609
    it { is_expected.to include(build.token) }
610 611 612
    it { is_expected.to include('gitlab-ci-token') }
    it { is_expected.to include(project.web_url[7..-1]) }
  end
613

614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631
  describe '#artifacts_expire_in' do
    subject { build.artifacts_expire_in }
    it { is_expected.to be_nil }

    context 'when artifacts_expire_at is specified' do
      let(:expire_at) { Time.now + 7.days }

      before { build.artifacts_expire_at = expire_at }

      it { is_expected.to be_within(5).of(expire_at - Time.now) }
    end
  end

  describe '#artifacts_expire_in=' do
    subject { build.artifacts_expire_in }

    it 'when assigning valid duration' do
      build.artifacts_expire_in = '7 days'
632

633 634 635 636
      is_expected.to be_within(10).of(7.days.to_i)
    end

    it 'when assigning invalid duration' do
637
      expect { build.artifacts_expire_in = '7 elephants' }.to raise_error(ChronicDuration::DurationParseError)
638 639 640 641 642
      is_expected.to be_nil
    end

    it 'when resseting value' do
      build.artifacts_expire_in = nil
643

644 645 646 647 648 649 650 651 652
      is_expected.to be_nil
    end
  end

  describe '#keep_artifacts!' do
    let(:build) { create(:ci_build, artifacts_expire_at: Time.now + 7.days) }

    it 'to reset expire_at' do
      build.keep_artifacts!
653

654 655 656 657
      expect(build.artifacts_expire_at).to be_nil
    end
  end

Kamil Trzcinski committed
658
  describe '#depends_on_builds' do
659 660 661 662
    let!(:build) { create(:ci_build, pipeline: pipeline, name: 'build', stage_idx: 0, stage: 'build') }
    let!(:rspec_test) { create(:ci_build, pipeline: pipeline, name: 'rspec', stage_idx: 1, stage: 'test') }
    let!(:rubocop_test) { create(:ci_build, pipeline: pipeline, name: 'rubocop', stage_idx: 1, stage: 'test') }
    let!(:staging) { create(:ci_build, pipeline: pipeline, name: 'staging', stage_idx: 2, stage: 'deploy') }
663

664
    it 'expects to have no dependents if this is first build' do
665 666 667
      expect(build.depends_on_builds).to be_empty
    end

668
    it 'expects to have one dependent if this is test' do
669 670 671
      expect(rspec_test.depends_on_builds.map(&:id)).to contain_exactly(build.id)
    end

672
    it 'expects to have all builds from build and test stage if this is last' do
673 674 675
      expect(staging.depends_on_builds.map(&:id)).to contain_exactly(build.id, rspec_test.id, rubocop_test.id)
    end

676
    it 'expects to have retried builds instead the original ones' do
677 678 679 680 681
      retried_rspec = Ci::Build.retry(rspec_test)
      expect(staging.depends_on_builds.map(&:id)).to contain_exactly(build.id, retried_rspec.id, rubocop_test.id)
    end
  end

682 683 684
  def create_mr(build, pipeline, factory: :merge_request, created_at: Time.now)
    create(factory, source_project_id: pipeline.gl_project_id,
                    target_project_id: pipeline.gl_project_id,
685 686
                    source_branch: build.ref,
                    created_at: created_at)
687 688
  end

Kamil Trzcinski committed
689
  describe '#merge_request' do
690
    context 'when a MR has a reference to the pipeline' do
691
      before do
692
        @merge_request = create_mr(build, pipeline, factory: :merge_request)
693

694
        commits = [double(id: pipeline.sha)]
695 696 697 698 699 700 701 702 703
        allow(@merge_request).to receive(:commits).and_return(commits)
        allow(MergeRequest).to receive_message_chain(:includes, :where, :reorder).and_return([@merge_request])
      end

      it 'returns the single associated MR' do
        expect(build.merge_request.id).to eq(@merge_request.id)
      end
    end

704
    context 'when there is not a MR referencing the pipeline' do
705 706 707 708 709
      it 'returns nil' do
        expect(build.merge_request).to be_nil
      end
    end

710
    context 'when more than one MR have a reference to the pipeline' do
711
      before do
712
        @merge_request = create_mr(build, pipeline, factory: :merge_request)
713
        @merge_request.close!
714
        @merge_request2 = create_mr(build, pipeline, factory: :merge_request)
715

716
        commits = [double(id: pipeline.sha)]
717 718 719 720 721 722 723 724 725 726 727 728
        allow(@merge_request).to receive(:commits).and_return(commits)
        allow(@merge_request2).to receive(:commits).and_return(commits)
        allow(MergeRequest).to receive_message_chain(:includes, :where, :reorder).and_return([@merge_request, @merge_request2])
      end

      it 'returns the first MR' do
        expect(build.merge_request.id).to eq(@merge_request.id)
      end
    end

    context 'when a Build is created after the MR' do
      before do
729 730 731
        @merge_request = create_mr(build, pipeline, factory: :merge_request_with_diffs)
        pipeline2 = create(:ci_pipeline, project: project)
        @build2 = create(:ci_build, pipeline: pipeline2)
732

733
        commits = [double(id: pipeline.sha), double(id: pipeline2.sha)]
734 735 736 737 738 739 740 741 742
        allow(@merge_request).to receive(:commits).and_return(commits)
        allow(MergeRequest).to receive_message_chain(:includes, :where, :reorder).and_return([@merge_request])
      end

      it 'returns the current MR' do
        expect(@build2.merge_request.id).to eq(@merge_request.id)
      end
    end
  end
743 744 745

  describe 'build erasable' do
    shared_examples 'erasable' do
746
      it 'removes artifact file' do
747 748 749
        expect(build.artifacts_file.exists?).to be_falsy
      end

750
      it 'removes artifact metadata file' do
751 752 753
        expect(build.artifacts_metadata.exists?).to be_falsy
      end

754
      it 'erases build trace in trace file' do
755 756 757
        expect(build.trace).to be_empty
      end

758
      it 'sets erased to true' do
759 760 761
        expect(build.erased?).to be true
      end

762
      it 'sets erase date' do
763
        expect(build.erased_at).not_to be_falsy
764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782
      end
    end

    context 'build is not erasable' do
      let!(:build) { create(:ci_build) }

      describe '#erase' do
        subject { build.erase }

        it { is_expected.to be false }
      end

      describe '#erasable?' do
        subject { build.erasable? }
        it { is_expected.to eq false }
      end
    end

    context 'build is erasable' do
783
      let!(:build) { create(:ci_build, :trace, :success, :artifacts) }
784 785

      describe '#erase' do
786 787 788
        before do
          build.erase(erased_by: user)
        end
789 790 791 792 793 794

        context 'erased by user' do
          let!(:user) { create(:user, username: 'eraser') }

          include_examples 'erasable'

795
          it 'records user who erased a build' do
796 797 798 799 800 801 802 803 804
            expect(build.erased_by).to eq user
          end
        end

        context 'erased by system' do
          let(:user) { nil }

          include_examples 'erasable'

805
          it 'does not set user who erased a build' do
806 807 808 809 810 811 812
            expect(build.erased_by).to be_nil
          end
        end
      end

      describe '#erasable?' do
        subject { build.erasable? }
813
        it { is_expected.to be_truthy }
814 815 816
      end

      describe '#erased?' do
817
        let!(:build) { create(:ci_build, :trace, :success, :artifacts) }
818 819 820
        subject { build.erased? }

        context 'build has not been erased' do
821
          it { is_expected.to be_falsey }
822 823 824
        end

        context 'build has been erased' do
825 826 827
          before do
            build.erase
          end
828

829
          it { is_expected.to be_truthy }
830 831 832 833 834
        end
      end

      context 'metadata and build trace are not available' do
        let!(:build) { create(:ci_build, :success, :artifacts) }
835

836 837 838
        before do
          build.remove_artifacts_metadata!
        end
839 840

        describe '#erase' do
841
          it 'does not raise error' do
842
            expect { build.erase }.not_to raise_error
843 844 845 846 847
          end
        end
      end
    end
  end
848 849 850 851 852 853

  describe '#commit' do
    it 'returns commit pipeline has been created for' do
      expect(build.commit).to eq project.commit
    end
  end
854

855 856 857
  describe '#when' do
    subject { build.when }

858
    context 'when `when` is undefined' do
859 860 861 862 863 864 865 866 867
      before do
        build.when = nil
      end

      context 'use from gitlab-ci.yml' do
        before do
          stub_ci_pipeline_yaml_file(config)
        end

868
        context 'when config is not found' do
869 870 871 872 873
          let(:config) { nil }

          it { is_expected.to eq('on_success') }
        end

874
        context 'when config does not have a questioned job' do
875 876 877 878 879 880 881 882 883 884 885
          let(:config) do
            YAML.dump({
                        test_other: {
                          script: 'Hello World'
                        }
                      })
          end

          it { is_expected.to eq('on_success') }
        end

886
        context 'when config has `when`' do
887 888 889 890 891 892 893 894 895 896 897 898 899 900 901
          let(:config) do
            YAML.dump({
                        test: {
                          script: 'Hello World',
                          when: 'always'
                        }
                      })
          end

          it { is_expected.to eq('always') }
        end
      end
    end
  end

902 903
  describe '#retryable?' do
    context 'when build is running' do
904 905 906
      before do
        build.run!
      end
907

908
      it { expect(build).not_to be_retryable }
909 910 911
    end

    context 'when build is finished' do
912 913 914 915
      before do
        build.success!
      end

916
      it { expect(build).to be_retryable }
917 918 919
    end
  end

920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948
  describe '#manual?' do
    before do
      build.update(when: value)
    end

    subject { build.manual? }

    context 'when is set to manual' do
      let(:value) { 'manual' }

      it { is_expected.to be_truthy }
    end

    context 'when set to something else' do
      let(:value) { 'something else' }

      it { is_expected.to be_falsey }
    end
  end

  describe '#other_actions' do
    let(:build) { create(:ci_build, :manual, pipeline: pipeline) }
    let!(:other_build) { create(:ci_build, :manual, pipeline: pipeline, name: 'other action') }

    subject { build.other_actions }

    it 'returns other actions' do
      is_expected.to contain_exactly(other_build)
    end
949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964

    context 'when build is retried' do
      let!(:new_build) { Ci::Build.retry(build) }

      it 'does not return any of them' do
        is_expected.not_to include(build, new_build)
      end
    end

    context 'when other build is retried' do
      let!(:retried_build) { Ci::Build.retry(other_build) }

      it 'returns a retried build' do
        is_expected.to contain_exactly(retried_build)
      end
    end
965 966 967 968 969 970 971
  end

  describe '#play' do
    let(:build) { create(:ci_build, :manual, pipeline: pipeline) }

    subject { build.play }

972
    it 'enqueues a build' do
973 974 975 976
      is_expected.to be_pending
      is_expected.to eq(build)
    end

977 978
    context 'for successful build' do
      before do
979
        build.update(status: 'success')
980
      end
981 982 983 984 985 986 987

      it 'creates a new build' do
        is_expected.to be_pending
        is_expected.not_to eq(build)
      end
    end
  end
988 989 990 991

  describe '#when' do
    subject { build.when }

992
    context 'when `when` is undefined' do
993 994 995 996 997 998 999 1000 1001
      before do
        build.when = nil
      end

      context 'use from gitlab-ci.yml' do
        before do
          stub_ci_pipeline_yaml_file(config)
        end

1002
        context 'when config is not found' do
1003 1004 1005 1006 1007
          let(:config) { nil }

          it { is_expected.to eq('on_success') }
        end

1008
        context 'when config does not have a questioned job' do
1009 1010
          let(:config) do
            YAML.dump({
1011 1012 1013 1014
                        test_other: {
                          script: 'Hello World'
                        }
                      })
1015 1016 1017 1018 1019
          end

          it { is_expected.to eq('on_success') }
        end

1020
        context 'when config has when' do
1021 1022
          let(:config) do
            YAML.dump({
1023 1024 1025 1026 1027
                        test: {
                          script: 'Hello World',
                          when: 'always'
                        }
                      })
1028 1029 1030 1031 1032 1033 1034
          end

          it { is_expected.to eq('always') }
        end
      end
    end
  end
1035 1036 1037 1038 1039

  describe '#retryable?' do
    context 'when build is running' do
      before { build.run! }

1040
      it 'returns false' do
1041
        expect(build).not_to be_retryable
1042 1043 1044 1045
      end
    end

    context 'when build is finished' do
1046 1047 1048
      before do
        build.success!
      end
1049

1050
      it 'returns true' do
1051
        expect(build).to be_retryable
1052 1053 1054
      end
    end
  end
1055
end