diff options
Diffstat (limited to 'spec/models/ci/pipeline_spec.rb')
-rw-r--r-- | spec/models/ci/pipeline_spec.rb | 237 |
1 files changed, 218 insertions, 19 deletions
diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb index 721b20e0cb2..71b7628ef10 100644 --- a/spec/models/ci/pipeline_spec.rb +++ b/spec/models/ci/pipeline_spec.rb @@ -88,24 +88,38 @@ describe Ci::Pipeline, models: true do context 'no failed builds' do before do - FactoryGirl.create :ci_build, name: "rspec", pipeline: pipeline, status: 'success' + create_build('rspec', 'success') end - it 'be not retryable' do + it 'is not retryable' do is_expected.to be_falsey end + + context 'one canceled job' do + before do + create_build('rubocop', 'canceled') + end + + it 'is retryable' do + is_expected.to be_truthy + end + end end context 'with failed builds' do before do - FactoryGirl.create :ci_build, name: "rspec", pipeline: pipeline, status: 'running' - FactoryGirl.create :ci_build, name: "rubocop", pipeline: pipeline, status: 'failed' + create_build('rspec', 'running') + create_build('rubocop', 'failed') end - it 'be retryable' do + it 'is retryable' do is_expected.to be_truthy end end + + def create_build(name, status) + create(:ci_build, name: name, status: status, pipeline: pipeline) + end end describe '#stages' do @@ -124,21 +138,32 @@ describe Ci::Pipeline, models: true do describe 'state machine' do let(:current) { Time.now.change(usec: 0) } - let(:build) { create :ci_build, name: 'build1', pipeline: pipeline } + let(:build) { create_build('build1', 0) } + let(:build_b) { create_build('build2', 0) } + let(:build_c) { create_build('build3', 0) } describe '#duration' do before do - travel_to(current - 120) do - pipeline.run + travel_to(current + 30) do + build.run! + build.success! + build_b.run! + build_c.run! end - travel_to(current) do - pipeline.succeed + travel_to(current + 40) do + build_b.drop! + end + + travel_to(current + 70) do + build_c.success! end end it 'matches sum of builds duration' do - expect(pipeline.reload.duration).to eq(120) + pipeline.reload + + expect(pipeline.duration).to eq(40) end end @@ -169,6 +194,36 @@ describe Ci::Pipeline, models: true do expect(pipeline.reload.finished_at).to be_nil end end + + describe 'merge request metrics' do + let(:project) { FactoryGirl.create :project } + let(:pipeline) { FactoryGirl.create(:ci_empty_pipeline, status: 'created', project: project, ref: 'master', sha: project.repository.commit('master').id) } + let!(:merge_request) { create(:merge_request, source_project: project, source_branch: pipeline.ref) } + + before do + expect(PipelineMetricsWorker).to receive(:perform_async).with(pipeline.id) + end + + context 'when transitioning to running' do + it 'schedules metrics workers' do + pipeline.run + end + end + + context 'when transitioning to success' do + it 'schedules metrics workers' do + pipeline.succeed + end + end + end + + def create_build(name, queued_at = current, started_from = 0) + create(:ci_build, + name: name, + pipeline: pipeline, + queued_at: queued_at, + started_at: queued_at + started_from) + end end describe '#branch?' do @@ -195,6 +250,36 @@ describe Ci::Pipeline, models: true do end end + context 'with non-empty project' do + let(:project) { create(:project) } + + let(:pipeline) do + create(:ci_pipeline, + project: project, + ref: project.default_branch, + sha: project.commit.sha) + end + + describe '#latest?' do + context 'with latest sha' do + it 'returns true' do + expect(pipeline).to be_latest + end + end + + context 'with not latest sha' do + before do + pipeline.update( + sha: project.commit("#{project.default_branch}~1").sha) + end + + it 'returns false' do + expect(pipeline).not_to be_latest + end + end + end + end + describe '#manual_actions' do subject { pipeline.manual_actions } @@ -318,8 +403,8 @@ describe Ci::Pipeline, models: true do end describe '#execute_hooks' do - let!(:build_a) { create_build('a') } - let!(:build_b) { create_build('b') } + let!(:build_a) { create_build('a', 0) } + let!(:build_b) { create_build('b', 1) } let!(:hook) do create(:project_hook, project: project, pipeline_events: enabled) @@ -343,7 +428,7 @@ describe Ci::Pipeline, models: true do build_b.enqueue end - it 'receive a pending event once' do + it 'receives a pending event once' do expect(WebMock).to have_requested_pipeline_hook('pending').once end end @@ -356,7 +441,7 @@ describe Ci::Pipeline, models: true do build_b.run end - it 'receive a running event once' do + it 'receives a running event once' do expect(WebMock).to have_requested_pipeline_hook('running').once end end @@ -364,14 +449,26 @@ describe Ci::Pipeline, models: true do context 'when all builds succeed' do before do build_a.success - build_b.success + + # We have to reload build_b as this is in next stage and it gets triggered by PipelineProcessWorker + build_b.reload.success end - it 'receive a success event once' do + it 'receives a success event once' do expect(WebMock).to have_requested_pipeline_hook('success').once end end + context 'when stage one failed' do + before do + build_a.drop + end + + it 'receives a failed event once' do + expect(WebMock).to have_requested_pipeline_hook('failed').once + end + end + def have_requested_pipeline_hook(status) have_requested(:post, hook.url).with do |req| json_body = JSON.parse(req.body) @@ -395,8 +492,110 @@ describe Ci::Pipeline, models: true do end end - def create_build(name) - create(:ci_build, :created, pipeline: pipeline, name: name) + def create_build(name, stage_idx) + create(:ci_build, + :created, + pipeline: pipeline, + name: name, + stage_idx: stage_idx) + end + end + + describe "#merge_requests" do + let(:project) { FactoryGirl.create :project } + let(:pipeline) { FactoryGirl.create(:ci_empty_pipeline, status: 'created', project: project, ref: 'master', sha: project.repository.commit('master').id) } + + it "returns merge requests whose `diff_head_sha` matches the pipeline's SHA" do + merge_request = create(:merge_request, source_project: project, source_branch: pipeline.ref) + + expect(pipeline.merge_requests).to eq([merge_request]) + end + + it "doesn't return merge requests whose source branch doesn't match the pipeline's ref" do + create(:merge_request, source_project: project, source_branch: 'feature', target_branch: 'master') + + expect(pipeline.merge_requests).to be_empty + end + + it "doesn't return merge requests whose `diff_head_sha` doesn't match the pipeline's SHA" do + create(:merge_request, source_project: project, source_branch: pipeline.ref) + allow_any_instance_of(MergeRequest).to receive(:diff_head_sha) { '97de212e80737a608d939f648d959671fb0a0142b' } + + expect(pipeline.merge_requests).to be_empty + end + end + + describe 'notifications when pipeline success or failed' do + let(:project) { create(:project) } + + let(:pipeline) do + create(:ci_pipeline, + project: project, + sha: project.commit('master').sha, + user: create(:user)) + end + + before do + reset_delivered_emails! + + project.team << [pipeline.user, Gitlab::Access::DEVELOPER] + + perform_enqueued_jobs do + pipeline.enqueue + pipeline.run + end + end + + shared_examples 'sending a notification' do + it 'sends an email' do + should_only_email(pipeline.user, kind: :bcc) + end + end + + shared_examples 'not sending any notification' do + it 'does not send any email' do + should_not_email_anyone + end + end + + context 'with success pipeline' do + before do + perform_enqueued_jobs do + pipeline.succeed + end + end + + it_behaves_like 'sending a notification' + end + + context 'with failed pipeline' do + before do + perform_enqueued_jobs do + pipeline.drop + end + end + + it_behaves_like 'sending a notification' + end + + context 'with skipped pipeline' do + before do + perform_enqueued_jobs do + pipeline.skip + end + end + + it_behaves_like 'not sending any notification' + end + + context 'with cancelled pipeline' do + before do + perform_enqueued_jobs do + pipeline.cancel + end + end + + it_behaves_like 'not sending any notification' end end end |