summaryrefslogtreecommitdiff
path: root/spec/services/ci
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-02-27 18:09:21 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2020-02-27 18:09:21 +0000
commite0fa0638a422c3e20d4423c9bb69d79afc9c7d3d (patch)
tree9abb3c0706576bbda895fe9539a55556930606e2 /spec/services/ci
parentf8d15ca65390475e356b06dedc51e10ccd179f86 (diff)
downloadgitlab-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.rb161
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