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

Douwe Maan's avatar
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's avatar
Kamil Trzcinski committed
15
  it { is_expected.to validate_presence_of :ref }
16

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

Kamil Trzcinski's avatar
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') }
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
22
    subject { Ci::Build.first_pending }
23

Dmitriy Zaporozhets's avatar
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's avatar
Kamil Trzcinski committed
28
  describe '#create_from' do
29 30 31 32
    before do
      build.status = 'success'
      build.save
    end
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
33
    let(:create_from_build) { Ci::Build.create_from build }
34

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

Kamil Trzcinski's avatar
Kamil Trzcinski committed
42
  describe '#ignored?' do
43 44
    subject { build.ignored? }

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's avatar
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's avatar
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's avatar
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's avatar
Dmitriy Zaporozhets committed
85
        it { is_expected.to be_truthy }
86 87 88 89
      end
    end
  end

Kamil Trzcinski's avatar
Kamil Trzcinski committed
90
  describe '#trace' do
91 92
    subject { build.trace_html }

Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
93
    it { is_expected.to be_empty }
94

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

Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
101
      it { is_expected.to include(text) }
102
      it { expect(subject.length).to be >= text.length }
103
    end
104

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

      before do
Kamil Trzcinski's avatar
Kamil Trzcinski committed
109
        build.project.update_attributes(runners_token: token)
110 111 112
        build.update_attributes(trace: token)
      end

113
      it { is_expected.not_to include(token) }
114
    end
115 116
  end

117 118 119 120
  # TODO: build timeout
  # describe :timeout do
  #   subject { build.timeout }
  #
121
  #   it { is_expected.to eq(pipeline.project.timeout) }
122
  # end
123

Kamil Trzcinski's avatar
Kamil Trzcinski committed
124
  describe '#options' do
Valery Sizov's avatar
Valery Sizov committed
125
    let(:options) do
126
      {
Valery Sizov's avatar
Valery Sizov committed
127 128
        image: "ruby:2.1",
        services: [
129 130 131
          "postgres"
        ]
      }
Valery Sizov's avatar
Valery Sizov committed
132
    end
133 134

    subject { build.options }
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
135
    it { is_expected.to eq(options) }
136 137
  end

138 139 140 141 142 143
  # TODO: allow_git_fetch
  # describe :allow_git_fetch do
  #   subject { build.allow_git_fetch }
  #
  #   it { is_expected.to eq(project.allow_git_fetch) }
  # end
144

Kamil Trzcinski's avatar
Kamil Trzcinski committed
145
  describe '#project' do
146 147
    subject { build.project }

148
    it { is_expected.to eq(pipeline.project) }
149 150
  end

Kamil Trzcinski's avatar
Kamil Trzcinski committed
151
  describe '#project_id' do
152 153
    subject { build.project_id }

154
    it { is_expected.to eq(pipeline.project_id) }
155 156
  end

Kamil Trzcinski's avatar
Kamil Trzcinski committed
157
  describe '#project_name' do
158 159
    subject { build.project_name }

Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
160
    it { is_expected.to eq(project.name) }
161 162
  end

Kamil Trzcinski's avatar
Kamil Trzcinski committed
163
  describe '#extract_coverage' do
164 165 166
    context 'valid content & regex' do
      subject { build.extract_coverage('Coverage 1033 / 1051 LOC (98.29%) covered', '\(\d+.\d+\%\) covered') }

Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
167
      it { is_expected.to eq(98.29) }
168 169 170 171 172
    end

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

Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
173
      it { is_expected.to be_nil }
174 175 176 177 178
    end

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

Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
179
      it { is_expected.to be_nil }
180 181 182 183 184
    end

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

Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
185
      it { is_expected.to eq(98.29) }
186
    end
Jared Szechy's avatar
Jared Szechy committed
187 188 189 190 191 192

    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
193 194
  end

Kamil Trzcinski's avatar
Kamil Trzcinski committed
195
  describe '#variables' do
196
    let(:container_registry_enabled) { false }
197 198
    let(:predefined_variables) do
      [
199 200 201 202 203 204 205 206 207
        { 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 },
208 209 210
        { 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 },
211 212 213 214 215
        { 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 },
216
        { key: 'CI_PIPELINE_ID', value: pipeline.id.to_s, public: true }
217 218
      ]
    end
219

220 221 222 223
    before do
      stub_container_registry_config(enabled: container_registry_enabled, host_port: 'registry.example.com')
    end

224
    subject { build.variables }
225

226
    context 'returns variables' do
227
      before do
228
        build.yaml_variables = []
229
      end
230

231 232
      it { is_expected.to eq(predefined_variables) }
    end
233

234 235 236
    context 'when build is for tag' do
      let(:tag_variable) do
        { key: 'CI_BUILD_TAG', value: 'master', public: true }
237 238
      end

239 240
      before do
        build.update_attributes(tag: true)
241
      end
242

243 244
      it { is_expected.to include(tag_variable) }
    end
245

246 247 248
    context 'when secure variable is defined' do
      let(:secure_variable) do
        { key: 'SECRET_KEY', value: 'secret_value', public: false }
Valery Sizov's avatar
Valery Sizov committed
249
      end
250

251
      before do
252
        build.project.variables << Ci::Variable.new(key: 'SECRET_KEY', value: 'secret_value')
253
      end
254

255 256
      it { is_expected.to include(secure_variable) }
    end
257

258 259 260 261
    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's avatar
Katarzyna Kobierska committed
262
        { key: :TRIGGER_KEY_1, value: 'TRIGGER_VALUE_1', public: false }
263 264 265 266
      end
      let(:predefined_trigger_variable) do
        { key: 'CI_BUILD_TRIGGERED', value: 'true', public: true }
      end
267

268 269
      before do
        build.trigger_request = trigger_request
270
      end
271

272 273
      it { is_expected.to include(user_trigger_variable) }
      it { is_expected.to include(predefined_trigger_variable) }
274
    end
275

276
    context 'when yaml_variables are undefined' do
277
      before do
278
        build.yaml_variables = nil
279
      end
280

281 282 283
      context 'use from gitlab-ci.yml' do
        before do
          stub_ci_pipeline_yaml_file(config)
Valery Sizov's avatar
Valery Sizov committed
284
        end
285

286
        context 'when config is not found' do
287 288 289
          let(:config) { nil }

          it { is_expected.to eq(predefined_variables) }
290 291
        end

292
        context 'when config does not have a questioned job' do
293 294
          let(:config) do
            YAML.dump({
295 296 297 298
              test_other: {
                script: 'Hello World'
              }
            })
299
          end
300

301 302 303
          it { is_expected.to eq(predefined_variables) }
        end

304
        context 'when config has variables' do
305 306
          let(:config) do
            YAML.dump({
307 308 309 310 311 312 313
              test: {
                script: 'Hello World',
                variables: {
                  KEY: 'value'
                }
              }
            })
Valery Sizov's avatar
Valery Sizov committed
314
          end
315 316
          let(:variables) do
            [{ key: :KEY, value: 'value', public: true }]
317
          end
318

319 320 321 322
          it { is_expected.to eq(predefined_variables + variables) }
        end
      end
    end
323

324 325 326
    context 'when container registry is enabled' do
      let(:container_registry_enabled) { true }
      let(:ci_registry) do
Kamil Trzcinski's avatar
Kamil Trzcinski committed
327
        { key: 'CI_REGISTRY',  value: 'registry.example.com',  public: true }
328 329
      end
      let(:ci_registry_image) do
Kamil Trzcinski's avatar
Kamil Trzcinski committed
330
        { key: 'CI_REGISTRY_IMAGE',  value: project.container_registry_repository_url, public: true }
331 332 333 334 335 336 337 338 339 340 341 342 343 344
      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

      context 'and is enabled for project' do
        before do
          project.update(container_registry_enabled: true)
345
        end
346 347 348

        it { is_expected.to include(ci_registry) }
        it { is_expected.to include(ci_registry_image) }
349 350
      end
    end
351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366

    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'] }
367 368 369 370
        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'] }
371 372
      end

373
      it { is_expected.to eq(%w[predefined project pipeline yaml secret]) }
374
    end
375
  end
Kamil Trzcinski's avatar
Kamil Trzcinski committed
376

377 378 379 380 381 382 383 384
  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: []) }
385
      it { is_expected.not_to have_tags }
386 387 388
    end
  end

Kamil Trzcinski's avatar
Kamil Trzcinski committed
389
  describe '#any_runners_online?' do
390 391 392 393 394 395
    subject { build.any_runners_online? }

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

396
    context 'when there are runners' do
397
      let(:runner) { create(:ci_runner) }
398 399

      before do
400
        build.project.runners << runner
401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416
        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
417
        expect_any_instance_of(Ci::Runner).to receive(:can_pick?).and_return(false)
418 419 420 421 422
        is_expected.to be_falsey
      end
    end
  end

Kamil Trzcinski's avatar
Kamil Trzcinski committed
423
  describe '#stuck?' do
424
    subject { build.stuck? }
425

426 427 428 429
    context "when commit_status.status is pending" do
      before do
        build.status = 'pending'
      end
430

431
      it { is_expected.to be_truthy }
432

433 434
      context "and there are specific runner" do
        let(:runner) { create(:ci_runner, contacted_at: 1.second.ago) }
435

436 437 438
        before do
          build.project.runners << runner
          runner.save
439
        end
440 441

        it { is_expected.to be_falsey }
442 443 444
      end
    end

445 446
    %w[success failed canceled running].each do |state|
      context "when commit_status.status is #{state}" do
447 448 449
        before do
          build.status = state
        end
450 451 452 453 454

        it { is_expected.to be_falsey }
      end
    end
  end
455

Kamil Trzcinski's avatar
Kamil Trzcinski committed
456
  describe '#artifacts?' do
457 458 459
    subject { build.artifacts? }

    context 'artifacts archive does not exist' do
460 461 462 463
      before do
        build.update_attributes(artifacts_file: nil)
      end

464 465 466 467
      it { is_expected.to be_falsy }
    end

    context 'artifacts archive exists' do
468
      let(:build) { create(:ci_build, :artifacts) }
469
      it { is_expected.to be_truthy }
470 471 472 473 474 475 476 477 478 479

      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
480 481 482
    end
  end

483 484 485 486 487
  describe '#artifacts_expired?' do
    subject { build.artifacts_expired? }

    context 'is expired' do
      before { build.update(artifacts_expire_at: Time.now - 7.days)  }
Kamil Trzcinski's avatar
Kamil Trzcinski committed
488 489

      it { is_expected.to be_truthy }
490 491 492 493
    end

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

      it { is_expected.to be_falsey }
496 497
    end
  end
498

Kamil Trzcinski's avatar
Kamil Trzcinski committed
499
  describe '#artifacts_metadata?' do
500
    subject { build.artifacts_metadata? }
501
    context 'artifacts metadata does not exist' do
502 503 504
      it { is_expected.to be_falsy }
    end

505
    context 'artifacts archive is a zip file and metadata exists' do
506
      let(:build) { create(:ci_build, :artifacts) }
507 508 509
      it { is_expected.to be_truthy }
    end
  end
Kamil Trzcinski's avatar
Kamil Trzcinski committed
510
  describe '#repo_url' do
511
    let(:build) { create(:ci_build) }
512 513 514 515 516 517 518
    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's avatar
Kamil Trzcinski committed
519
    it { is_expected.to include(build.token) }
520 521 522
    it { is_expected.to include('gitlab-ci-token') }
    it { is_expected.to include(project.web_url[7..-1]) }
  end
523

524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541
  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'
542

543 544 545 546
      is_expected.to be_within(10).of(7.days.to_i)
    end

    it 'when assigning invalid duration' do
547
      expect { build.artifacts_expire_in = '7 elephants' }.to raise_error(ChronicDuration::DurationParseError)
548 549 550 551 552
      is_expected.to be_nil
    end

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

554 555 556 557 558 559 560 561 562
      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!
563

564 565 566 567
      expect(build.artifacts_expire_at).to be_nil
    end
  end

Kamil Trzcinski's avatar
Kamil Trzcinski committed
568
  describe '#depends_on_builds' do
569 570 571 572
    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') }
573

574
    it 'expects to have no dependents if this is first build' do
575 576 577
      expect(build.depends_on_builds).to be_empty
    end

578
    it 'expects to have one dependent if this is test' do
579 580 581
      expect(rspec_test.depends_on_builds.map(&:id)).to contain_exactly(build.id)
    end

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

586
    it 'expects to have retried builds instead the original ones' do
587 588 589 590 591
      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

592 593 594
  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,
595 596
                    source_branch: build.ref,
                    created_at: created_at)
597 598
  end

Kamil Trzcinski's avatar
Kamil Trzcinski committed
599
  describe '#merge_request' do
600
    context 'when a MR has a reference to the pipeline' do
601
      before do
602
        @merge_request = create_mr(build, pipeline, factory: :merge_request)
603

604
        commits = [double(id: pipeline.sha)]
605 606 607 608 609 610 611 612 613
        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

614
    context 'when there is not a MR referencing the pipeline' do
615 616 617 618 619
      it 'returns nil' do
        expect(build.merge_request).to be_nil
      end
    end

620
    context 'when more than one MR have a reference to the pipeline' do
621
      before do
622
        @merge_request = create_mr(build, pipeline, factory: :merge_request)
623
        @merge_request.close!
624
        @merge_request2 = create_mr(build, pipeline, factory: :merge_request)
625

626
        commits = [double(id: pipeline.sha)]
627 628 629 630 631 632 633 634 635 636 637 638
        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
639 640 641
        @merge_request = create_mr(build, pipeline, factory: :merge_request_with_diffs)
        pipeline2 = create(:ci_pipeline, project: project)
        @build2 = create(:ci_build, pipeline: pipeline2)
642

643
        commits = [double(id: pipeline.sha), double(id: pipeline2.sha)]
644 645 646 647 648 649 650 651 652
        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
653 654 655

  describe 'build erasable' do
    shared_examples 'erasable' do
656
      it 'removes artifact file' do
657 658 659
        expect(build.artifacts_file.exists?).to be_falsy
      end

660
      it 'removes artifact metadata file' do
661 662 663
        expect(build.artifacts_metadata.exists?).to be_falsy
      end

664
      it 'erases build trace in trace file' do
665 666 667
        expect(build.trace).to be_empty
      end

668
      it 'sets erased to true' do
669 670 671
        expect(build.erased?).to be true
      end

672
      it 'sets erase date' do
673
        expect(build.erased_at).not_to be_falsy
674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692
      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
Kamil Trzcinski's avatar
Kamil Trzcinski committed
693
      let!(:build) { create(:ci_build, :trace, :success, :artifacts) }
694 695

      describe '#erase' do
696 697 698
        before do
          build.erase(erased_by: user)
        end
699 700 701 702 703 704

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

          include_examples 'erasable'

705
          it 'records user who erased a build' do
706 707 708 709 710 711 712 713 714
            expect(build.erased_by).to eq user
          end
        end

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

          include_examples 'erasable'

715
          it 'does not set user who erased a build' do
716 717 718 719 720 721 722
            expect(build.erased_by).to be_nil
          end
        end
      end

      describe '#erasable?' do
        subject { build.erasable? }
723
        it { is_expected.to be_truthy }
724 725 726
      end

      describe '#erased?' do
Kamil Trzcinski's avatar
Kamil Trzcinski committed
727
        let!(:build) { create(:ci_build, :trace, :success, :artifacts) }
728 729 730
        subject { build.erased? }

        context 'build has not been erased' do
731
          it { is_expected.to be_falsey }
732 733 734
        end

        context 'build has been erased' do
735 736 737
          before do
            build.erase
          end
738

739
          it { is_expected.to be_truthy }
740 741 742 743 744
        end
      end

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

746 747 748
        before do
          build.remove_artifacts_metadata!
        end
749 750

        describe '#erase' do
751
          it 'does not raise error' do
752
            expect { build.erase }.not_to raise_error
753 754 755 756 757
          end
        end
      end
    end
  end
758 759 760 761 762 763

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

765 766 767
  describe '#when' do
    subject { build.when }

768
    context 'when `when` is undefined' do
769 770 771 772 773 774 775 776 777
      before do
        build.when = nil
      end

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

778
        context 'when config is not found' do
779 780 781 782 783
          let(:config) { nil }

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

784
        context 'when config does not have a questioned job' do
785 786 787 788 789 790 791 792 793 794 795
          let(:config) do
            YAML.dump({
                        test_other: {
                          script: 'Hello World'
                        }
                      })
          end

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

796
        context 'when config has `when`' do
797 798 799 800 801 802 803 804 805 806 807 808 809 810 811
          let(:config) do
            YAML.dump({
                        test: {
                          script: 'Hello World',
                          when: 'always'
                        }
                      })
          end

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

812 813
  describe '#retryable?' do
    context 'when build is running' do
814 815 816
      before do
        build.run!
      end
817

818
      it { expect(build).not_to be_retryable }
819 820 821
    end

    context 'when build is finished' do
822 823 824 825
      before do
        build.success!
      end

826
      it { expect(build).to be_retryable }
827 828 829
    end
  end

830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858
  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
859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874

    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
875 876 877 878 879 880 881
  end

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

    subject { build.play }

882
    it 'enqueues a build' do
883 884 885 886
      is_expected.to be_pending
      is_expected.to eq(build)
    end

887 888
    context 'for successful build' do
      before do
Kamil Trzcinski's avatar
Kamil Trzcinski committed
889
        build.update(status: 'success')
890
      end
891 892 893 894 895 896 897

      it 'creates a new build' do
        is_expected.to be_pending
        is_expected.not_to eq(build)
      end
    end
  end
898 899 900 901

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

902
    context 'when `when` is undefined' do
903
      before do
904
        build.when = nil
905 906 907 908 909 910 911
      end

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

912
        context 'when config is not found' do
913 914 915 916 917
          let(:config) { nil }

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

918
        context 'when config does not have a questioned job' do
919 920
          let(:config) do
            YAML.dump({
921 922 923 924
                        test_other: {
                          script: 'Hello World'
                        }
                      })
925 926 927 928 929
          end

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

930
        context 'when config has when' do
931 932
          let(:config) do
            YAML.dump({
933 934 935 936 937
                        test: {
                          script: 'Hello World',
                          when: 'always'
                        }
                      })
938 939 940 941 942 943 944
          end

          it { is_expected.to eq('always') }
        end
      end
    end
  end
945 946 947 948 949

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

950
      it 'returns false' do
951 952 953 954 955 956 957
        expect(build.retryable?).to be false
      end
    end

    context 'when build is finished' do
      before { build.success! }

958
      it 'returns true' do
959 960 961 962
        expect(build.retryable?).to be true
      end
    end
  end
963
end