From 9266cd5e8b543ab356df3fba78bf9e01536a180d Mon Sep 17 00:00:00 2001 From: Shinya Maeda Date: Wed, 26 Sep 2018 19:12:48 +0900 Subject: [PATCH] Add unit tests for Ci::Build. Fix validation on state transition --- app/models/ci/build.rb | 8 +- spec/factories/ci/builds.rb | 12 +++ spec/models/ci/build_spec.rb | 153 +++++++++++++++++++++++++++++++++++ 3 files changed, 168 insertions(+), 5 deletions(-) diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index 1209e7ef696..f83dfa5d1c4 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -168,10 +168,8 @@ module Ci end event :enqueue_scheduled do - transition scheduled: :pending do - validate do |build| - build.scheduled_at && build.scheduled_at < Time.now - end + transition scheduled: :pending, if: ->(build) do + build.scheduled_at && build.scheduled_at < Time.now end end @@ -269,7 +267,7 @@ module Ci end def action? - %w[manual scheduled].include?(self.when) + %w[manual delayed].include?(self.when) end # rubocop: disable CodeReuse/ServiceClass diff --git a/spec/factories/ci/builds.rb b/spec/factories/ci/builds.rb index aea6b5d6b2f..73fa16fe6bf 100644 --- a/spec/factories/ci/builds.rb +++ b/spec/factories/ci/builds.rb @@ -70,6 +70,18 @@ FactoryBot.define do status 'created' end + trait :scheduled do + schedulable + status 'scheduled' + scheduled_at 1.minute.since + end + + trait :expired_scheduled do + schedulable + status 'scheduled' + scheduled_at 1.minute.ago + end + trait :manual do status 'manual' self.when 'manual' diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb index e82d93d5935..939017e7ee7 100644 --- a/spec/models/ci/build_spec.rb +++ b/spec/models/ci/build_spec.rb @@ -209,6 +209,147 @@ describe Ci::Build do end end + describe '#schedulable?' do + subject { build.schedulable? } + + context 'when build is schedulable' do + let(:build) { create(:ci_build, :created, :schedulable, project: project) } + + it { expect(subject).to be_truthy } + + context 'when feature flag is diabled' do + before do + stub_feature_flags(ci_enable_scheduled_build: false) + end + + it { expect(subject).to be_falsy } + end + end + + context 'when build is not schedulable' do + let(:build) { create(:ci_build, :created, project: project) } + + it { expect(subject).to be_falsy } + end + end + + describe '#schedule' do + subject { build.schedule } + + before do + project.add_developer(user) + end + + let(:build) { create(:ci_build, :created, :schedulable, user: user, project: project) } + + it 'transits to scheduled' do + subject + + expect(build).to be_scheduled + end + + it 'updates scheduled_at column' do + subject + + expect(build.scheduled_at).not_to be_nil + end + + it 'schedules BuildScheduleWorker at the right time' do + Timecop.freeze do + expect(Ci::BuildScheduleWorker) + .to receive(:perform_at).with(1.minute.since, build.id) + + subject + end + end + end + + describe '#unschedule' do + subject { build.unschedule } + + context 'when build is scheduled' do + let(:build) { create(:ci_build, :scheduled, pipeline: pipeline) } + + it 'cleans scheduled_at column' do + subject + + expect(build.scheduled_at).to be_nil + end + + it 'transits to manual' do + subject + + expect(build).to be_manual + end + end + + context 'when build is not scheduled' do + let(:build) { create(:ci_build, :created, pipeline: pipeline) } + + it 'does not transit status' do + subject + + expect(build).to be_created + end + end + end + + describe '#options_scheduled_at' do + subject { build.options_scheduled_at } + + let(:build) { build_stubbed(:ci_build, options: option) } + + context 'when start_in is 1 day' do + let(:option) { { start_in: '1 day' } } + + it 'returns date after 1 day' do + Timecop.freeze do + is_expected.to eq(1.day.since) + end + end + end + + context 'when start_in is 1 week' do + let(:option) { { start_in: '1 week' } } + + it 'returns date after 1 week' do + Timecop.freeze do + is_expected.to eq(1.week.since) + end + end + end + end + + describe '#enqueue_scheduled' do + subject { build.enqueue_scheduled } + + context 'when build is scheduled and the right time has not come yet' do + let(:build) { create(:ci_build, :scheduled, pipeline: pipeline) } + + it 'does not transits the status' do + subject + + expect(build).to be_scheduled + end + end + + context 'when build is scheduled and the right time has already come' do + let(:build) { create(:ci_build, :expired_scheduled, pipeline: pipeline) } + + it 'cleans scheduled_at column' do + subject + + expect(build.scheduled_at).to be_nil + end + + it 'transits to pending' do + subject + + expect(build).to be_pending + end + end + end + describe '#any_runners_online?' do subject { build.any_runners_online? } @@ -1193,6 +1334,12 @@ describe Ci::Build do it { is_expected.to be_truthy } end + context 'when is set to delayed' do + let(:value) { 'delayed' } + + it { is_expected.to be_truthy } + end + context 'when set to something else' do let(:value) { 'something else' } @@ -1463,6 +1610,12 @@ describe Ci::Build do end end + context 'when build is scheduled' do + subject { build_stubbed(:ci_build, :scheduled) } + + it { is_expected.to be_playable } + end + context 'when build is not a manual action' do subject { build_stubbed(:ci_build, :success) }