pipeline_spec.rb 17.8 KB
Newer Older
D
Douwe Maan 已提交
1 2
require 'spec_helper'

3
describe Ci::Pipeline, models: true do
K
Kamil Trzcinski 已提交
4
  let(:project) { FactoryGirl.create :empty_project }
5
  let(:pipeline) { FactoryGirl.create :ci_pipeline, project: project }
D
Douwe Maan 已提交
6

K
Kamil Trzcinski 已提交
7
  it { is_expected.to belong_to(:project) }
8 9
  it { is_expected.to belong_to(:user) }

10
  it { is_expected.to have_many(:statuses) }
K
Kamil Trzcinski 已提交
11
  it { is_expected.to have_many(:trigger_requests) }
D
Dmitriy Zaporozhets 已提交
12
  it { is_expected.to have_many(:builds) }
13

D
Dmitriy Zaporozhets 已提交
14
  it { is_expected.to validate_presence_of :sha }
K
Kamil Trzcinski 已提交
15
  it { is_expected.to validate_presence_of :status }
D
Douwe Maan 已提交
16

D
Dmitriy Zaporozhets 已提交
17 18 19
  it { is_expected.to respond_to :git_author_name }
  it { is_expected.to respond_to :git_author_email }
  it { is_expected.to respond_to :short_sha }
D
Douwe Maan 已提交
20

21
  describe '#valid_commit_sha' do
D
Douwe Maan 已提交
22 23
    context 'commit.sha can not start with 00000000' do
      before do
24 25
        pipeline.sha = '0' * 40
        pipeline.valid_commit_sha
D
Douwe Maan 已提交
26 27
      end

28
      it('commit errors should not be empty') { expect(pipeline.errors).not_to be_empty }
D
Douwe Maan 已提交
29 30 31
    end
  end

32
  describe '#short_sha' do
33
    subject { pipeline.short_sha }
D
Douwe Maan 已提交
34

D
Dmitriy Zaporozhets 已提交
35 36 37
    it 'has 8 items' do
      expect(subject.size).to eq(8)
    end
38
    it { expect(pipeline.sha).to start_with(subject) }
D
Douwe Maan 已提交
39 40
  end

41
  describe '#create_next_builds' do
K
Kamil Trzcinski 已提交
42 43
  end

44
  describe '#retried' do
45
    subject { pipeline.retried }
K
Kamil Trzcinski 已提交
46 47

    before do
48 49
      @build1 = FactoryGirl.create :ci_build, pipeline: pipeline, name: 'deploy'
      @build2 = FactoryGirl.create :ci_build, pipeline: pipeline, name: 'deploy'
K
Kamil Trzcinski 已提交
50 51 52
    end

    it 'returns old builds' do
53
      is_expected.to contain_exactly(@build1)
K
Kamil Trzcinski 已提交
54 55 56
    end
  end

57
  describe '#create_builds' do
58
    let!(:pipeline) { FactoryGirl.create :ci_pipeline, project: project, ref: 'master', tag: false }
K
Kamil Trzcinski 已提交
59 60

    def create_builds(trigger_request = nil)
61
      pipeline.create_builds(nil, trigger_request)
D
Douwe Maan 已提交
62 63
    end

64
    def create_next_builds
65
      pipeline.create_next_builds(pipeline.builds.order(:id).last)
K
Kamil Trzcinski 已提交
66 67 68 69
    end

    it 'creates builds' do
      expect(create_builds).to be_truthy
70 71
      pipeline.builds.update_all(status: "success")
      expect(pipeline.builds.count(:all)).to eq(2)
D
Douwe Maan 已提交
72

K
Kamil Trzcinski 已提交
73
      expect(create_next_builds).to be_truthy
74 75
      pipeline.builds.update_all(status: "success")
      expect(pipeline.builds.count(:all)).to eq(4)
D
Douwe Maan 已提交
76

K
Kamil Trzcinski 已提交
77
      expect(create_next_builds).to be_truthy
78 79
      pipeline.builds.update_all(status: "success")
      expect(pipeline.builds.count(:all)).to eq(5)
D
Douwe Maan 已提交
80

K
Kamil Trzcinski 已提交
81
      expect(create_next_builds).to be_falsey
D
Douwe Maan 已提交
82 83
    end

84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
    context 'custom stage with first job allowed to fail' do
      let(:yaml) do
        {
          stages: ['clean', 'test'],
          clean_job: {
            stage: 'clean',
            allow_failure: true,
            script: 'BUILD',
          },
          test_job: {
            stage: 'test',
            script: 'TEST',
          },
        }
      end

      before do
101
        stub_ci_pipeline_yaml_file(YAML.dump(yaml))
102 103 104 105
        create_builds
      end

      it 'properly schedules builds' do
106 107 108
        expect(pipeline.builds.pluck(:status)).to contain_exactly('pending')
        pipeline.builds.running_or_pending.each(&:drop)
        expect(pipeline.builds.pluck(:status)).to contain_exactly('pending', 'failed')
109 110 111
      end
    end

R
Robert Speicher 已提交
112
    context 'properly creates builds when "when" is defined' do
K
Kamil Trzcinski 已提交
113
      let(:yaml) do
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
        {
          stages: ["build", "test", "test_failure", "deploy", "cleanup"],
          build: {
            stage: "build",
            script: "BUILD",
          },
          test: {
            stage: "test",
            script: "TEST",
          },
          test_failure: {
            stage: "test_failure",
            script: "ON test failure",
            when: "on_failure",
          },
          deploy: {
            stage: "deploy",
            script: "PUBLISH",
          },
          cleanup: {
            stage: "cleanup",
            script: "TIDY UP",
            when: "always",
          }
        }
K
Kamil Trzcinski 已提交
139
      end
140 141

      before do
142
        stub_ci_pipeline_yaml_file(YAML.dump(yaml))
143 144
      end

145 146 147
      context 'when builds are successful' do
        it 'properly creates builds' do
          expect(create_builds).to be_truthy
148 149 150
          expect(pipeline.builds.pluck(:name)).to contain_exactly('build')
          expect(pipeline.builds.pluck(:status)).to contain_exactly('pending')
          pipeline.builds.running_or_pending.each(&:success)
151

152 153 154
          expect(pipeline.builds.pluck(:name)).to contain_exactly('build', 'test')
          expect(pipeline.builds.pluck(:status)).to contain_exactly('success', 'pending')
          pipeline.builds.running_or_pending.each(&:success)
155

156 157 158
          expect(pipeline.builds.pluck(:name)).to contain_exactly('build', 'test', 'deploy')
          expect(pipeline.builds.pluck(:status)).to contain_exactly('success', 'success', 'pending')
          pipeline.builds.running_or_pending.each(&:success)
159

160 161 162
          expect(pipeline.builds.pluck(:name)).to contain_exactly('build', 'test', 'deploy', 'cleanup')
          expect(pipeline.builds.pluck(:status)).to contain_exactly('success', 'success', 'success', 'pending')
          pipeline.builds.running_or_pending.each(&:success)
163

164 165 166
          expect(pipeline.builds.pluck(:status)).to contain_exactly('success', 'success', 'success', 'success')
          pipeline.reload
          expect(pipeline.status).to eq('success')
167
        end
168 169
      end

170 171 172
      context 'when test job fails' do
        it 'properly creates builds' do
          expect(create_builds).to be_truthy
173 174 175
          expect(pipeline.builds.pluck(:name)).to contain_exactly('build')
          expect(pipeline.builds.pluck(:status)).to contain_exactly('pending')
          pipeline.builds.running_or_pending.each(&:success)
176

177 178 179
          expect(pipeline.builds.pluck(:name)).to contain_exactly('build', 'test')
          expect(pipeline.builds.pluck(:status)).to contain_exactly('success', 'pending')
          pipeline.builds.running_or_pending.each(&:drop)
180

181 182 183
          expect(pipeline.builds.pluck(:name)).to contain_exactly('build', 'test', 'test_failure')
          expect(pipeline.builds.pluck(:status)).to contain_exactly('success', 'failed', 'pending')
          pipeline.builds.running_or_pending.each(&:success)
184

185 186 187
          expect(pipeline.builds.pluck(:name)).to contain_exactly('build', 'test', 'test_failure', 'cleanup')
          expect(pipeline.builds.pluck(:status)).to contain_exactly('success', 'failed', 'success', 'pending')
          pipeline.builds.running_or_pending.each(&:success)
188

189 190 191
          expect(pipeline.builds.pluck(:status)).to contain_exactly('success', 'failed', 'success', 'success')
          pipeline.reload
          expect(pipeline.status).to eq('failed')
192
        end
193 194
      end

195 196 197
      context 'when test and test_failure jobs fail' do
        it 'properly creates builds' do
          expect(create_builds).to be_truthy
198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217
          expect(pipeline.builds.pluck(:name)).to contain_exactly('build')
          expect(pipeline.builds.pluck(:status)).to contain_exactly('pending')
          pipeline.builds.running_or_pending.each(&:success)

          expect(pipeline.builds.pluck(:name)).to contain_exactly('build', 'test')
          expect(pipeline.builds.pluck(:status)).to contain_exactly('success', 'pending')
          pipeline.builds.running_or_pending.each(&:drop)

          expect(pipeline.builds.pluck(:name)).to contain_exactly('build', 'test', 'test_failure')
          expect(pipeline.builds.pluck(:status)).to contain_exactly('success', 'failed', 'pending')
          pipeline.builds.running_or_pending.each(&:drop)

          expect(pipeline.builds.pluck(:name)).to contain_exactly('build', 'test', 'test_failure', 'cleanup')
          expect(pipeline.builds.pluck(:status)).to contain_exactly('success', 'failed', 'failed', 'pending')
          pipeline.builds.running_or_pending.each(&:success)

          expect(pipeline.builds.pluck(:name)).to contain_exactly('build', 'test', 'test_failure', 'cleanup')
          expect(pipeline.builds.pluck(:status)).to contain_exactly('success', 'failed', 'failed', 'success')
          pipeline.reload
          expect(pipeline.status).to eq('failed')
218
        end
219 220
      end

221 222 223
      context 'when deploy job fails' do
        it 'properly creates builds' do
          expect(create_builds).to be_truthy
224 225 226
          expect(pipeline.builds.pluck(:name)).to contain_exactly('build')
          expect(pipeline.builds.pluck(:status)).to contain_exactly('pending')
          pipeline.builds.running_or_pending.each(&:success)
227

228 229 230
          expect(pipeline.builds.pluck(:name)).to contain_exactly('build', 'test')
          expect(pipeline.builds.pluck(:status)).to contain_exactly('success', 'pending')
          pipeline.builds.running_or_pending.each(&:success)
231

232 233 234
          expect(pipeline.builds.pluck(:name)).to contain_exactly('build', 'test', 'deploy')
          expect(pipeline.builds.pluck(:status)).to contain_exactly('success', 'success', 'pending')
          pipeline.builds.running_or_pending.each(&:drop)
235

236 237 238
          expect(pipeline.builds.pluck(:name)).to contain_exactly('build', 'test', 'deploy', 'cleanup')
          expect(pipeline.builds.pluck(:status)).to contain_exactly('success', 'success', 'failed', 'pending')
          pipeline.builds.running_or_pending.each(&:success)
239

240 241 242
          expect(pipeline.builds.pluck(:status)).to contain_exactly('success', 'success', 'failed', 'success')
          pipeline.reload
          expect(pipeline.status).to eq('failed')
243
        end
244
      end
245 246 247 248

      context 'when build is canceled in the second stage' do
        it 'does not schedule builds after build has been canceled' do
          expect(create_builds).to be_truthy
249 250 251
          expect(pipeline.builds.pluck(:name)).to contain_exactly('build')
          expect(pipeline.builds.pluck(:status)).to contain_exactly('pending')
          pipeline.builds.running_or_pending.each(&:success)
252

253
          expect(pipeline.builds.running_or_pending).not_to be_empty
254

255 256 257
          expect(pipeline.builds.pluck(:name)).to contain_exactly('build', 'test')
          expect(pipeline.builds.pluck(:status)).to contain_exactly('success', 'pending')
          pipeline.builds.running_or_pending.each(&:cancel)
258

259 260
          expect(pipeline.builds.running_or_pending).to be_empty
          expect(pipeline.reload.status).to eq('canceled')
261 262
        end
      end
263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 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 324

      context 'when listing manual actions' do
        let(:yaml) do
          {
            stages: ["build", "test", "test_failure", "deploy", "cleanup"],
            build: {
              stage: "build",
              script: "BUILD",
            },
            test: {
              stage: "test",
              script: "TEST",
            },
            test_failure: {
              stage: "test_failure",
              script: "ON test failure",
              when: "on_failure",
            },
            deploy: {
              stage: "deploy",
              script: "PUBLISH",
            },
            production: {
              stage: "deploy",
              script: "PUBLISH",
              when: "manual",
            },
            cleanup: {
              stage: "cleanup",
              script: "TIDY UP",
              when: "always",
            },
            clear_cache: {
              stage: "cleanup",
              script: "CLEAR CACHE",
              when: "manual",
            }
          }
        end

        it 'returns only for skipped builds' do
          # currently all builds are created
          expect(create_builds).to be_truthy
          expect(manual_actions).to be_empty

          # succeed stage build
          pipeline.builds.running_or_pending.each(&:success)
          expect(manual_actions).to be_empty

          # succeed stage test
          pipeline.builds.running_or_pending.each(&:success)
          expect(manual_actions).to be_one # production

          # succeed stage deploy
          pipeline.builds.running_or_pending.each(&:success)
          expect(manual_actions).to be_many # production and clear cache
        end

        def manual_actions
          pipeline.manual_actions
        end
      end
325
    end
326 327

    context 'when no builds created' do
328 329
      let(:pipeline) { build(:ci_pipeline) }

330 331 332 333 334 335
      before do
        stub_ci_pipeline_yaml_file(YAML.dump(before_script: ['ls']))
      end

      it 'returns false' do
        expect(pipeline.create_builds(nil)).to be_falsey
336
        expect(pipeline).not_to be_persisted
337 338
      end
    end
D
Douwe Maan 已提交
339 340 341
  end

  describe "#finished_at" do
342
    let(:pipeline) { FactoryGirl.create :ci_pipeline }
D
Douwe Maan 已提交
343 344

    it "returns finished_at of latest build" do
345 346
      build = FactoryGirl.create :ci_build, pipeline: pipeline, finished_at: Time.now - 60
      FactoryGirl.create :ci_build, pipeline: pipeline, finished_at: Time.now - 120
D
Douwe Maan 已提交
347

348
      expect(pipeline.finished_at.to_i).to eq(build.finished_at.to_i)
D
Douwe Maan 已提交
349 350 351
    end

    it "returns nil if there is no finished build" do
352
      FactoryGirl.create :ci_not_started_build, pipeline: pipeline
D
Douwe Maan 已提交
353

354
      expect(pipeline.finished_at).to be_nil
D
Douwe Maan 已提交
355 356 357 358
    end
  end

  describe "coverage" do
K
Kamil Trzcinski 已提交
359
    let(:project) { FactoryGirl.create :empty_project, build_coverage_regex: "/.*/" }
360
    let(:pipeline) { FactoryGirl.create :ci_pipeline, project: project }
D
Douwe Maan 已提交
361 362

    it "calculates average when there are two builds with coverage" do
363 364 365
      FactoryGirl.create :ci_build, name: "rspec", coverage: 30, pipeline: pipeline
      FactoryGirl.create :ci_build, name: "rubocop", coverage: 40, pipeline: pipeline
      expect(pipeline.coverage).to eq("35.00")
D
Douwe Maan 已提交
366 367 368
    end

    it "calculates average when there are two builds with coverage and one with nil" do
369 370 371 372
      FactoryGirl.create :ci_build, name: "rspec", coverage: 30, pipeline: pipeline
      FactoryGirl.create :ci_build, name: "rubocop", coverage: 40, pipeline: pipeline
      FactoryGirl.create :ci_build, pipeline: pipeline
      expect(pipeline.coverage).to eq("35.00")
D
Douwe Maan 已提交
373 374 375
    end

    it "calculates average when there are two builds with coverage and one is retried" do
376 377 378 379
      FactoryGirl.create :ci_build, name: "rspec", coverage: 30, pipeline: pipeline
      FactoryGirl.create :ci_build, name: "rubocop", coverage: 30, pipeline: pipeline
      FactoryGirl.create :ci_build, name: "rubocop", coverage: 40, pipeline: pipeline
      expect(pipeline.coverage).to eq("35.00")
D
Douwe Maan 已提交
380 381 382
    end

    it "calculates average when there is one build without coverage" do
383 384
      FactoryGirl.create :ci_build, pipeline: pipeline
      expect(pipeline.coverage).to be_nil
D
Douwe Maan 已提交
385 386
    end
  end
K
Kamil Trzcinski 已提交
387 388

  describe '#retryable?' do
389
    subject { pipeline.retryable? }
K
Kamil Trzcinski 已提交
390 391 392

    context 'no failed builds' do
      before do
393
        FactoryGirl.create :ci_build, name: "rspec", pipeline: pipeline, status: 'success'
K
Kamil Trzcinski 已提交
394 395 396 397 398 399 400 401 402
      end

      it 'be not retryable' do
        is_expected.to be_falsey
      end
    end

    context 'with failed builds' do
      before do
403 404
        FactoryGirl.create :ci_build, name: "rspec", pipeline: pipeline, status: 'running'
        FactoryGirl.create :ci_build, name: "rubocop", pipeline: pipeline, status: 'failed'
K
Kamil Trzcinski 已提交
405 406 407 408 409 410 411 412 413
      end

      it 'be retryable' do
        is_expected.to be_truthy
      end
    end
  end

  describe '#stages' do
414 415
    let(:pipeline2) { FactoryGirl.create :ci_pipeline, project: project }
    subject { CommitStatus.where(pipeline: [pipeline, pipeline2]).stages }
K
Kamil Trzcinski 已提交
416 417

    before do
418 419
      FactoryGirl.create :ci_build, pipeline: pipeline2, stage: 'test', stage_idx: 1
      FactoryGirl.create :ci_build, pipeline: pipeline, stage: 'build', stage_idx: 0
K
Kamil Trzcinski 已提交
420 421 422 423 424 425 426 427 428
    end

    it 'return all stages' do
      is_expected.to eq(%w(build test))
    end
  end

  describe '#update_state' do
    it 'execute update_state after touching object' do
429 430
      expect(pipeline).to receive(:update_state).and_return(true)
      pipeline.touch
K
Kamil Trzcinski 已提交
431 432 433
    end

    context 'dependent objects' do
434
      let(:commit_status) { build :commit_status, pipeline: pipeline }
K
Kamil Trzcinski 已提交
435 436

      it 'execute update_state after saving dependent object' do
437
        expect(pipeline).to receive(:update_state).and_return(true)
K
Kamil Trzcinski 已提交
438 439 440 441 442
        commit_status.save
      end
    end

    context 'update state' do
K
Kamil Trzcinski 已提交
443
      let(:current) { Time.now.change(usec: 0) }
444
      let(:build) { FactoryGirl.create :ci_build, :success, pipeline: pipeline, started_at: current - 120, finished_at: current - 60 }
K
Kamil Trzcinski 已提交
445 446 447 448 449 450 451

      before do
        build
      end

      [:status, :started_at, :finished_at, :duration].each do |param|
        it "update #{param}" do
452
          expect(pipeline.send(param)).to eq(build.send(param))
K
Kamil Trzcinski 已提交
453 454 455 456
        end
      end
    end
  end
K
Kamil Trzcinski 已提交
457 458

  describe '#branch?' do
459
    subject { pipeline.branch? }
K
Kamil Trzcinski 已提交
460 461 462

    context 'is not a tag' do
      before do
463
        pipeline.tag = false
K
Kamil Trzcinski 已提交
464 465 466 467 468 469 470 471 472
      end

      it 'return true when tag is set to false' do
        is_expected.to be_truthy
      end
    end

    context 'is not a tag' do
      before do
473
        pipeline.tag = true
K
Kamil Trzcinski 已提交
474 475 476 477 478 479 480
      end

      it 'return false when tag is set to true' do
        is_expected.to be_falsey
      end
    end
  end
481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504

  describe '#manual_actions' do
    subject { pipeline.manual_actions }

    it 'when none defined' do
      is_expected.to be_empty
    end

    context 'when action defined' do
      let!(:manual) { create(:ci_build, :manual, pipeline: pipeline, name: 'deploy') }

      it 'returns one action' do
        is_expected.to contain_exactly(manual)
      end

      context 'there are multiple of the same name' do
        let!(:manual2) { create(:ci_build, :manual, pipeline: pipeline, name: 'deploy') }

        it 'returns latest one' do
          is_expected.to contain_exactly(manual2)
        end
      end
    end
  end
505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530

  describe '#with_warnings?' do
    subject { pipeline.with_warnings? }

    context 'build which is allowed to fail fails' do
      before do
        FactoryGirl.create :ci_build, :success, pipeline: pipeline, name: 'rspec'
        FactoryGirl.create :ci_build, :allowed_to_fail, :failed, pipeline: pipeline, name: 'rubocop'
      end
      
      it 'returns true' do
        is_expected.to be_truthy
      end
    end

    context 'build which is allowed to fail succeeds' do
      before do
        FactoryGirl.create :ci_build, :success, pipeline: pipeline, name: 'rspec'
        FactoryGirl.create :ci_build, :allowed_to_fail, :success, pipeline: pipeline, name: 'rubocop'
      end
      
      it 'returns false' do
        is_expected.to be_falsey
      end
    end
  end
D
Douwe Maan 已提交
531
end