commit_spec.rb 14.9 KB
Newer Older
1 2
# == Schema Information
#
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
3
# Table name: ci_commits
4
#
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
5 6 7 8 9 10 11 12 13 14 15
#  id            :integer          not null, primary key
#  project_id    :integer
#  ref           :string(255)
#  sha           :string(255)
#  before_sha    :string(255)
#  push_data     :text
#  created_at    :datetime
#  updated_at    :datetime
#  tag           :boolean          default(FALSE)
#  yaml_errors   :text
#  committed_at  :datetime
Stan Hu's avatar
Stan Hu committed
16
#  gl_project_id :integer
17 18 19 20
#

require 'spec_helper'

Douwe Maan's avatar
Douwe Maan committed
21
describe Ci::Commit, models: true do
22 23
  let(:project) { FactoryGirl.create :empty_project }
  let(:commit) { FactoryGirl.create :ci_commit, project: project }
24

25
  it { is_expected.to belong_to(:project) }
26 27
  it { is_expected.to have_many(:statuses) }
  it { is_expected.to have_many(:trigger_requests) }
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
28 29
  it { is_expected.to have_many(:builds) }
  it { is_expected.to validate_presence_of :sha }
30

Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
31 32 33
  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 }
34

35 36 37 38
  describe :ordered do
    let(:project) { FactoryGirl.create :empty_project }

    it 'returns ordered list of commits' do
39
      commit1 = FactoryGirl.create :ci_commit, committed_at: 1.hour.ago, project: project
Gabriel Mazetto's avatar
Gabriel Mazetto committed
40
      commit2 = FactoryGirl.create :ci_commit, committed_at: 2.hours.ago, project: project
41 42 43 44
      expect(project.ci_commits.ordered).to eq([commit2, commit1])
    end

    it 'returns commits ordered by committed_at and id, with nulls last' do
45 46
      commit1 = FactoryGirl.create :ci_commit, committed_at: 1.hour.ago, project: project
      commit2 = FactoryGirl.create :ci_commit, committed_at: nil, project: project
Gabriel Mazetto's avatar
Gabriel Mazetto committed
47
      commit3 = FactoryGirl.create :ci_commit, committed_at: 2.hours.ago, project: project
48
      commit4 = FactoryGirl.create :ci_commit, committed_at: nil, project: project
49 50 51 52
      expect(project.ci_commits.ordered).to eq([commit2, commit4, commit3, commit1])
    end
  end

53 54 55
  describe :last_build do
    subject { commit.last_build }
    before do
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
56 57
      @first = FactoryGirl.create :ci_build, commit: commit, created_at: Date.yesterday
      @second = FactoryGirl.create :ci_build, commit: commit
58 59
    end

Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
60 61
    it { is_expected.to be_a(Ci::Build) }
    it('returns with the most recently created build') { is_expected.to eq(@second) }
62 63 64 65
  end

  describe :retry do
    before do
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
66 67
      @first = FactoryGirl.create :ci_build, commit: commit, created_at: Date.yesterday
      @second = FactoryGirl.create :ci_build, commit: commit
68 69
    end

70
    it "creates only a new build" do
71
      expect(commit.builds.count(:all)).to eq 2
72
      expect(commit.statuses.count(:all)).to eq 2
73 74
      commit.retry
      expect(commit.builds.count(:all)).to eq 3
75
      expect(commit.statuses.count(:all)).to eq 3
76 77 78 79 80 81 82 83 84 85
    end
  end

  describe :valid_commit_sha do
    context 'commit.sha can not start with 00000000' do
      before do
        commit.sha = '0' * 40
        commit.valid_commit_sha
      end

Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
86
      it('commit errors should not be empty') { expect(commit.errors).not_to be_empty }
87 88 89 90 91 92
    end
  end

  describe :short_sha do
    subject { commit.short_sha }

Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
93 94 95 96
    it 'has 8 items' do
      expect(subject.size).to eq(8)
    end
    it { expect(commit.sha).to start_with(subject) }
97 98
  end

Kamil Trzcinski's avatar
Kamil Trzcinski committed
99 100 101 102
  describe :stage do
    subject { commit.stage }

    before do
103 104
      @second = FactoryGirl.create :commit_status, commit: commit, name: 'deploy', stage: 'deploy', stage_idx: 1, status: 'pending'
      @first = FactoryGirl.create :commit_status, commit: commit, name: 'test', stage: 'test', stage_idx: 0, status: 'pending'
Kamil Trzcinski's avatar
Kamil Trzcinski committed
105 106 107 108 109 110 111 112
    end

    it 'returns first running stage' do
      is_expected.to eq('test')
    end

    context 'first build succeeded' do
      before do
113
        @first.success
Kamil Trzcinski's avatar
Kamil Trzcinski committed
114 115 116 117 118 119 120 121 122
      end

      it 'returns last running stage' do
        is_expected.to eq('deploy')
      end
    end

    context 'all builds succeeded' do
      before do
123 124
        @first.success
        @second.success
Kamil Trzcinski's avatar
Kamil Trzcinski committed
125 126 127 128 129 130 131 132
      end

      it 'returns nil' do
        is_expected.to be_nil
      end
    end
  end

133
  describe :create_next_builds do
Kamil Trzcinski's avatar
Kamil Trzcinski committed
134 135
  end

136 137 138 139 140 141 142 143 144 145
  describe :refs do
    subject { commit.refs }

    before do
      FactoryGirl.create :commit_status, commit: commit, name: 'deploy'
      FactoryGirl.create :commit_status, commit: commit, name: 'deploy', ref: 'develop'
      FactoryGirl.create :commit_status, commit: commit, name: 'deploy', ref: 'master'
    end

    it 'returns all refs' do
Kamil Trzcinski's avatar
Kamil Trzcinski committed
146
      is_expected.to contain_exactly('master', 'develop', nil)
147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
    end
  end

  describe :retried do
    subject { commit.retried }

    before do
      @commit1 = FactoryGirl.create :ci_build, commit: commit, name: 'deploy'
      @commit2 = FactoryGirl.create :ci_build, commit: commit, name: 'deploy'
    end

    it 'returns old builds' do
      is_expected.to contain_exactly(@commit1)
    end
  end

Kamil Trzcinski's avatar
Kamil Trzcinski committed
163
  describe :create_builds do
164
    let!(:commit) { FactoryGirl.create :ci_commit, project: project }
Kamil Trzcinski's avatar
Kamil Trzcinski committed
165 166 167

    def create_builds(trigger_request = nil)
      commit.create_builds('master', false, nil, trigger_request)
168 169
    end

170 171
    def create_next_builds
      commit.create_next_builds(commit.builds.order(:id).last)
Kamil Trzcinski's avatar
Kamil Trzcinski committed
172 173 174 175
    end

    it 'creates builds' do
      expect(create_builds).to be_truthy
176 177
      commit.builds.update_all(status: "success")
      expect(commit.builds.count(:all)).to eq(2)
178

Kamil Trzcinski's avatar
Kamil Trzcinski committed
179
      expect(create_next_builds).to be_truthy
180 181
      commit.builds.update_all(status: "success")
      expect(commit.builds.count(:all)).to eq(4)
182

Kamil Trzcinski's avatar
Kamil Trzcinski committed
183
      expect(create_next_builds).to be_truthy
184 185
      commit.builds.update_all(status: "success")
      expect(commit.builds.count(:all)).to eq(5)
186

Kamil Trzcinski's avatar
Kamil Trzcinski committed
187
      expect(create_next_builds).to be_falsey
188 189
    end

Kamil Trzcinski's avatar
Kamil Trzcinski committed
190 191 192 193
    context 'for different ref' do
      def create_develop_builds
        commit.create_builds('develop', false, nil, nil)
      end
194

Kamil Trzcinski's avatar
Kamil Trzcinski committed
195 196
      it 'creates builds' do
        expect(create_builds).to be_truthy
197 198
        commit.builds.update_all(status: "success")
        expect(commit.builds.count(:all)).to eq(2)
Kamil Trzcinski's avatar
Kamil Trzcinski committed
199 200

        expect(create_develop_builds).to be_truthy
201 202
        commit.builds.update_all(status: "success")
        expect(commit.builds.count(:all)).to eq(4)
Kamil Trzcinski's avatar
Kamil Trzcinski committed
203 204 205
        expect(commit.refs.size).to eq(2)
        expect(commit.builds.pluck(:name).uniq.size).to eq(2)
      end
206 207 208
    end

    context 'for build triggers' do
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
209 210
      let(:trigger) { FactoryGirl.create :ci_trigger, project: project }
      let(:trigger_request) { FactoryGirl.create :ci_trigger_request, commit: commit, trigger: trigger }
211 212

      it 'creates builds' do
Kamil Trzcinski's avatar
Kamil Trzcinski committed
213
        expect(create_builds(trigger_request)).to be_truthy
214
        expect(commit.builds.count(:all)).to eq(2)
215 216 217
      end

      it 'rebuilds commit' do
Kamil Trzcinski's avatar
Kamil Trzcinski committed
218
        expect(create_builds).to be_truthy
219
        expect(commit.builds.count(:all)).to eq(2)
220

Kamil Trzcinski's avatar
Kamil Trzcinski committed
221
        expect(create_builds(trigger_request)).to be_truthy
222
        expect(commit.builds.count(:all)).to eq(4)
223 224 225
      end

      it 'creates next builds' do
Kamil Trzcinski's avatar
Kamil Trzcinski committed
226
        expect(create_builds(trigger_request)).to be_truthy
227 228
        expect(commit.builds.count(:all)).to eq(2)
        commit.builds.update_all(status: "success")
229

230 231
        expect(create_next_builds).to be_truthy
        expect(commit.builds.count(:all)).to eq(4)
232 233 234 235
      end

      context 'for [ci skip]' do
        before do
Kamil Trzcinski's avatar
Kamil Trzcinski committed
236
          allow(commit).to receive(:git_commit_message) { 'message [ci skip]' }
237 238 239
        end

        it 'rebuilds commit' do
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
240
          expect(commit.status).to eq('skipped')
241
          expect(create_builds).to be_truthy
Kamil Trzcinski's avatar
Kamil Trzcinski committed
242 243 244 245

          # since everything in Ci::Commit is cached we need to fetch a new object
          new_commit = Ci::Commit.find_by_id(commit.id)
          expect(new_commit.status).to eq('pending')
246 247 248
        end
      end
    end
249

250
    context 'properly creates builds when "when" is defined' do
Kamil Trzcinski's avatar
Kamil Trzcinski committed
251
      let(:yaml) do
252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276
        {
          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",
          }
        }
Kamil Trzcinski's avatar
Kamil Trzcinski committed
277
      end
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 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371

      before do
        stub_ci_commit_yaml_file(YAML.dump(yaml))
      end

      it 'properly creates builds' do
        expect(create_builds).to be_truthy
        expect(commit.builds.pluck(:name)).to contain_exactly('build')
        expect(commit.builds.pluck(:status)).to contain_exactly('pending')
        commit.builds.running_or_pending.each(&:success)

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

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

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

        expect(commit.builds.pluck(:status)).to contain_exactly('success', 'success', 'success', 'success')
        expect(commit.status).to eq('success')
      end

      it 'properly creates builds when test fails' do
        expect(create_builds).to be_truthy
        expect(commit.builds.pluck(:name)).to contain_exactly('build')
        expect(commit.builds.pluck(:status)).to contain_exactly('pending')
        commit.builds.running_or_pending.each(&:success)

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

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

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

        expect(commit.builds.pluck(:status)).to contain_exactly('success', 'failed', 'success', 'success')
        expect(commit.status).to eq('failed')
      end

      it 'properly creates builds when test and test_failure fails' do
        expect(create_builds).to be_truthy
        expect(commit.builds.pluck(:name)).to contain_exactly('build')
        expect(commit.builds.pluck(:status)).to contain_exactly('pending')
        commit.builds.running_or_pending.each(&:success)

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

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

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

        expect(commit.builds.pluck(:name)).to contain_exactly('build', 'test', 'test_failure', 'cleanup')
        expect(commit.builds.pluck(:status)).to contain_exactly('success', 'failed', 'failed', 'success')
        expect(commit.status).to eq('failed')
      end

      it 'properly creates builds when deploy fails' do
        expect(create_builds).to be_truthy
        expect(commit.builds.pluck(:name)).to contain_exactly('build')
        expect(commit.builds.pluck(:status)).to contain_exactly('pending')
        commit.builds.running_or_pending.each(&:success)

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

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

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

        expect(commit.builds.pluck(:status)).to contain_exactly('success', 'success', 'failed', 'success')
        expect(commit.status).to eq('failed')
      end
    end
372 373 374
  end

  describe "#finished_at" do
Kamil Trzcinski's avatar
WIP  
Kamil Trzcinski committed
375
    let(:commit) { FactoryGirl.create :ci_commit }
376 377

    it "returns finished_at of latest build" do
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
378
      build = FactoryGirl.create :ci_build, commit: commit, finished_at: Time.now - 60
379
      FactoryGirl.create :ci_build, commit: commit, finished_at: Time.now - 120
380

Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
381
      expect(commit.finished_at.to_i).to eq(build.finished_at.to_i)
382 383 384
    end

    it "returns nil if there is no finished build" do
385
      FactoryGirl.create :ci_not_started_build, commit: commit
386

Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
387
      expect(commit.finished_at).to be_nil
388 389 390 391
    end
  end

  describe "coverage" do
392 393
    let(:project) { FactoryGirl.create :empty_project, build_coverage_regex: "/.*/" }
    let(:commit) { FactoryGirl.create :ci_commit, project: project }
394 395

    it "calculates average when there are two builds with coverage" do
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
396 397
      FactoryGirl.create :ci_build, name: "rspec", coverage: 30, commit: commit
      FactoryGirl.create :ci_build, name: "rubocop", coverage: 40, commit: commit
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
398
      expect(commit.coverage).to eq("35.00")
399 400 401
    end

    it "calculates average when there are two builds with coverage and one with nil" do
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
402 403 404
      FactoryGirl.create :ci_build, name: "rspec", coverage: 30, commit: commit
      FactoryGirl.create :ci_build, name: "rubocop", coverage: 40, commit: commit
      FactoryGirl.create :ci_build, commit: commit
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
405
      expect(commit.coverage).to eq("35.00")
406 407 408
    end

    it "calculates average when there are two builds with coverage and one is retried" do
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
409 410 411
      FactoryGirl.create :ci_build, name: "rspec", coverage: 30, commit: commit
      FactoryGirl.create :ci_build, name: "rubocop", coverage: 30, commit: commit
      FactoryGirl.create :ci_build, name: "rubocop", coverage: 40, commit: commit
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
412
      expect(commit.coverage).to eq("35.00")
413 414 415
    end

    it "calculates average when there is one build without coverage" do
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
416
      FactoryGirl.create :ci_build, commit: commit
Dmitriy Zaporozhets's avatar
Dmitriy Zaporozhets committed
417
      expect(commit.coverage).to be_nil
418 419 420
    end
  end
end