diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2020-02-27 18:09:21 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2020-02-27 18:09:21 +0000 |
commit | e0fa0638a422c3e20d4423c9bb69d79afc9c7d3d (patch) | |
tree | 9abb3c0706576bbda895fe9539a55556930606e2 /spec/services/ci | |
parent | f8d15ca65390475e356b06dedc51e10ccd179f86 (diff) | |
download | gitlab-ce-e0fa0638a422c3e20d4423c9bb69d79afc9c7d3d.tar.gz |
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'spec/services/ci')
-rw-r--r-- | spec/services/ci/update_ci_ref_status_service_spec.rb | 161 |
1 files changed, 161 insertions, 0 deletions
diff --git a/spec/services/ci/update_ci_ref_status_service_spec.rb b/spec/services/ci/update_ci_ref_status_service_spec.rb new file mode 100644 index 00000000000..2b069452a55 --- /dev/null +++ b/spec/services/ci/update_ci_ref_status_service_spec.rb @@ -0,0 +1,161 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Ci::UpdateCiRefStatusService do + describe '#call' do + subject { described_class.new(pipeline) } + + shared_examples 'creates ci_ref' do + it 'creates a ci_ref with the pipeline attributes' do + expect do + expect(subject.call).to eq(true) + end.to change { Ci::Ref.count }.by(1) + + created_ref = pipeline.reload.ref_status + %w[ref tag project status].each do |attr| + expect(created_ref[attr]).to eq(pipeline[attr]) + end + end + + it 'calls PipelineNotificationWorker pasing the ref_status' do + expect(PipelineNotificationWorker).to receive(:perform_async).with(pipeline.id, ref_status: pipeline.status) + + subject.call + end + end + + shared_examples 'updates ci_ref' do + where(:ref_status, :pipeline_status, :next_status) do + [ + %w[failed success fixed], + %w[failed failed failed], + %w[success success success], + %w[success failed failed] + ] + end + + with_them do + let(:ci_ref) { create(:ci_ref, status: ref_status) } + let(:pipeline) { create(:ci_pipeline, status: pipeline_status, project: ci_ref.project, ref: ci_ref.ref) } + + it 'sets ci_ref.status to next_status' do + expect do + expect(subject.call).to eq(true) + expect(ci_ref.reload.status).to eq(next_status) + end.not_to change { Ci::Ref.count } + end + + it 'calls PipelineNotificationWorker pasing the ref_status' do + expect(PipelineNotificationWorker).to receive(:perform_async).with(pipeline.id, ref_status: next_status) + + subject.call + end + end + end + + shared_examples 'does a noop' do + it "doesn't change ci_ref" do + expect do + expect do + expect(subject.call).to eq(false) + end.not_to change { ci_ref.reload.status } + end.not_to change { Ci::Ref.count } + end + + it "doesn't call PipelineNotificationWorker" do + expect(PipelineNotificationWorker).not_to receive(:perform_async) + + subject.call + end + end + + context "ci_ref doesn't exists" do + let(:pipeline) { create(:ci_pipeline, :success, ref: 'new-ref') } + + it_behaves_like 'creates ci_ref' + + context 'when an ActiveRecord::RecordNotUnique validation is raised' do + let(:ci_ref) { create(:ci_ref, status: 'failed') } + let(:pipeline) { create(:ci_pipeline, status: :success, project: ci_ref.project, ref: ci_ref.ref) } + + it 'reloads the ci_ref and retries once' do + subject.instance_variable_set("@ref", subject.send(:build_ref)) + + expect do + expect(subject.call).to eq(true) + end.not_to change { Ci::Ref.count } + expect(ci_ref.reload.status).to eq('fixed') + end + + it 'raises error on multiple retries' do + allow_any_instance_of(Ci::Ref).to receive(:update) + .and_raise(ActiveRecord::RecordNotUnique) + + expect { subject.call }.to raise_error(ActiveRecord::RecordNotUnique) + end + end + end + + context 'ci_ref exists' do + let!(:ci_ref) { create(:ci_ref, status: 'failed') } + let(:pipeline) { ci_ref.pipelines.first } + + it_behaves_like 'updates ci_ref' + + context 'pipeline status is invalid' do + let!(:pipeline) { create(:ci_pipeline, :running, project: ci_ref.project, ref: ci_ref.ref, tag: ci_ref.tag) } + + it_behaves_like 'does a noop' + end + + context 'newer pipeline finished' do + let(:newer_pipeline) { create(:ci_pipeline, :success, project: ci_ref.project, ref: ci_ref.ref, tag: ci_ref.tag) } + + before do + ci_ref.update!(last_updated_by_pipeline: newer_pipeline) + end + + it_behaves_like 'does a noop' + end + + context 'ref is stale' do + let(:pipeline1) { create(:ci_pipeline, :success, project: ci_ref.project, ref: ci_ref.ref, tag: ci_ref.tag) } + let(:pipeline2) { create(:ci_pipeline, :success, project: ci_ref.project, ref: ci_ref.ref, tag: ci_ref.tag) } + + it 'reloads the ref and retry' do + service1 = described_class.new(pipeline1) + service2 = described_class.new(pipeline2) + + service2.send(:ref) + service1.call + expect(ci_ref.reload.status).to eq('fixed') + expect do + expect(service2.call).to eq(true) + # We expect 'success' in this case rather than 'fixed' because + # the ref is correctly reloaded on stale error. + expect(ci_ref.reload.status).to eq('success') + end.not_to change { Ci::Ref.count } + end + + it 'aborts when a newer pipeline finished' do + service1 = described_class.new(pipeline1) + service2 = described_class.new(pipeline2) + + service2.call + expect do + expect(service1.call).to eq(false) + expect(ci_ref.reload.status).to eq('fixed') + end.not_to change { Ci::Ref.count } + end + end + + context 'ref exists as both tag/branch and tag' do + let(:pipeline) { create(:ci_pipeline, :failed, project: ci_ref.project, ref: ci_ref.ref, tag: true) } + let!(:branch_pipeline) { create(:ci_pipeline, :success, project: ci_ref.project, ref: ci_ref.ref, tag: false) } + + it_behaves_like 'creates ci_ref' + end + end + end +end |