summaryrefslogtreecommitdiff
path: root/spec/services/ci/unlock_artifacts_service_spec.rb
diff options
context:
space:
mode:
Diffstat (limited to 'spec/services/ci/unlock_artifacts_service_spec.rb')
-rw-r--r--spec/services/ci/unlock_artifacts_service_spec.rb280
1 files changed, 217 insertions, 63 deletions
diff --git a/spec/services/ci/unlock_artifacts_service_spec.rb b/spec/services/ci/unlock_artifacts_service_spec.rb
index 8d289a867ba..8ee07fc44c8 100644
--- a/spec/services/ci/unlock_artifacts_service_spec.rb
+++ b/spec/services/ci/unlock_artifacts_service_spec.rb
@@ -3,93 +3,247 @@
require 'spec_helper'
RSpec.describe Ci::UnlockArtifactsService do
- describe '#execute' do
- subject(:execute) { described_class.new(pipeline.project, pipeline.user).execute(ci_ref, before_pipeline) }
+ using RSpec::Parameterized::TableSyntax
+
+ where(:tag, :ci_update_unlocked_job_artifacts) do
+ false | false
+ false | true
+ true | false
+ true | true
+ end
+
+ with_them do
+ let(:ref) { 'master' }
+ let(:ref_path) { tag ? "#{::Gitlab::Git::TAG_REF_PREFIX}#{ref}" : "#{::Gitlab::Git::BRANCH_REF_PREFIX}#{ref}" }
+ let(:ci_ref) { create(:ci_ref, ref_path: ref_path) }
+ let(:project) { ci_ref.project }
+ let(:source_job) { create(:ci_build, pipeline: pipeline) }
+
+ let!(:old_unlocked_pipeline) { create(:ci_pipeline, :with_persisted_artifacts, ref: ref, tag: tag, project: project, locked: :unlocked) }
+ let!(:older_pipeline) { create(:ci_pipeline, :with_persisted_artifacts, ref: ref, tag: tag, project: project, locked: :artifacts_locked) }
+ let!(:older_ambiguous_pipeline) { create(:ci_pipeline, :with_persisted_artifacts, ref: ref, tag: !tag, project: project, locked: :artifacts_locked) }
+ let!(:pipeline) { create(:ci_pipeline, :with_persisted_artifacts, ref: ref, tag: tag, project: project, locked: :artifacts_locked) }
+ let!(:child_pipeline) { create(:ci_pipeline, :with_persisted_artifacts, ref: ref, tag: tag, project: project, locked: :artifacts_locked) }
+ let!(:newer_pipeline) { create(:ci_pipeline, :with_persisted_artifacts, ref: ref, tag: tag, project: project, locked: :artifacts_locked) }
+ let!(:other_ref_pipeline) { create(:ci_pipeline, :with_persisted_artifacts, ref: 'other_ref', tag: tag, project: project, locked: :artifacts_locked) }
+ let!(:sources_pipeline) { create(:ci_sources_pipeline, source_job: source_job, source_project: project, pipeline: child_pipeline, project: project) }
before do
stub_const("#{described_class}::BATCH_SIZE", 1)
+ stub_feature_flags(ci_update_unlocked_job_artifacts: ci_update_unlocked_job_artifacts)
end
- [true, false].each do |tag|
- context "when tag is #{tag}" do
- let(:ref) { 'master' }
- let(:ref_path) { tag ? "#{::Gitlab::Git::TAG_REF_PREFIX}#{ref}" : "#{::Gitlab::Git::BRANCH_REF_PREFIX}#{ref}" }
- let(:ci_ref) { create(:ci_ref, ref_path: ref_path) }
+ describe '#execute' do
+ subject(:execute) { described_class.new(pipeline.project, pipeline.user).execute(ci_ref, before_pipeline) }
+
+ context 'when running on a ref before a pipeline' do
+ let(:before_pipeline) { pipeline }
+
+ it 'unlocks artifacts from older pipelines' do
+ expect { execute }.to change { older_pipeline.reload.locked }.from('artifacts_locked').to('unlocked')
+ end
+
+ it 'does not unlock artifacts for tag or branch with same name as ref' do
+ expect { execute }.not_to change { older_ambiguous_pipeline.reload.locked }.from('artifacts_locked')
+ end
+
+ it 'does not unlock artifacts from newer pipelines' do
+ expect { execute }.not_to change { newer_pipeline.reload.locked }.from('artifacts_locked')
+ end
+
+ it 'does not lock artifacts from old unlocked pipelines' do
+ expect { execute }.not_to change { old_unlocked_pipeline.reload.locked }.from('unlocked')
+ end
+
+ it 'does not unlock artifacts from the same pipeline' do
+ expect { execute }.not_to change { pipeline.reload.locked }.from('artifacts_locked')
+ end
- let!(:old_unlocked_pipeline) { create(:ci_pipeline, ref: ref, tag: tag, project: ci_ref.project, locked: :unlocked) }
- let!(:older_pipeline) { create(:ci_pipeline, ref: ref, tag: tag, project: ci_ref.project, locked: :artifacts_locked) }
- let!(:older_ambiguous_pipeline) { create(:ci_pipeline, ref: ref, tag: !tag, project: ci_ref.project, locked: :artifacts_locked) }
- let!(:pipeline) { create(:ci_pipeline, ref: ref, tag: tag, project: ci_ref.project, locked: :artifacts_locked) }
- let!(:child_pipeline) { create(:ci_pipeline, ref: ref, tag: tag, project: ci_ref.project, locked: :artifacts_locked) }
- let!(:newer_pipeline) { create(:ci_pipeline, ref: ref, tag: tag, project: ci_ref.project, locked: :artifacts_locked) }
- let!(:other_ref_pipeline) { create(:ci_pipeline, ref: 'other_ref', tag: tag, project: ci_ref.project, locked: :artifacts_locked) }
+ it 'does not unlock artifacts for other refs' do
+ expect { execute }.not_to change { other_ref_pipeline.reload.locked }.from('artifacts_locked')
+ end
- before do
- create(:ci_sources_pipeline,
- source_job: create(:ci_build, pipeline: pipeline),
- source_project: ci_ref.project,
- pipeline: child_pipeline,
- project: ci_ref.project)
+ it 'does not unlock artifacts for child pipeline' do
+ expect { execute }.not_to change { child_pipeline.reload.locked }.from('artifacts_locked')
end
- context 'when running on a ref before a pipeline' do
- let(:before_pipeline) { pipeline }
+ it 'unlocks job artifact records' do
+ pending unless ci_update_unlocked_job_artifacts
- it 'unlocks artifacts from older pipelines' do
- expect { execute }.to change { older_pipeline.reload.locked }.from('artifacts_locked').to('unlocked')
- end
+ expect { execute }.to change { ::Ci::JobArtifact.artifact_unlocked.count }.from(0).to(2)
+ end
+ end
- it 'does not unlock artifacts for tag or branch with same name as ref' do
- expect { execute }.not_to change { older_ambiguous_pipeline.reload.locked }.from('artifacts_locked')
- end
+ context 'when running on just the ref' do
+ let(:before_pipeline) { nil }
- it 'does not unlock artifacts from newer pipelines' do
- expect { execute }.not_to change { newer_pipeline.reload.locked }.from('artifacts_locked')
- end
+ it 'unlocks artifacts from older pipelines' do
+ expect { execute }.to change { older_pipeline.reload.locked }.from('artifacts_locked').to('unlocked')
+ end
- it 'does not lock artifacts from old unlocked pipelines' do
- expect { execute }.not_to change { old_unlocked_pipeline.reload.locked }.from('unlocked')
- end
+ it 'unlocks artifacts from newer pipelines' do
+ expect { execute }.to change { newer_pipeline.reload.locked }.from('artifacts_locked').to('unlocked')
+ end
- it 'does not unlock artifacts from the same pipeline' do
- expect { execute }.not_to change { pipeline.reload.locked }.from('artifacts_locked')
- end
+ it 'unlocks artifacts from the same pipeline' do
+ expect { execute }.to change { pipeline.reload.locked }.from('artifacts_locked').to('unlocked')
+ end
- it 'does not unlock artifacts for other refs' do
- expect { execute }.not_to change { other_ref_pipeline.reload.locked }.from('artifacts_locked')
- end
+ it 'does not unlock artifacts for tag or branch with same name as ref' do
+ expect { execute }.not_to change { older_ambiguous_pipeline.reload.locked }.from('artifacts_locked')
+ end
- it 'does not unlock artifacts for child pipeline' do
- expect { execute }.not_to change { child_pipeline.reload.locked }.from('artifacts_locked')
- end
+ it 'does not lock artifacts from old unlocked pipelines' do
+ expect { execute }.not_to change { old_unlocked_pipeline.reload.locked }.from('unlocked')
end
- context 'when running on just the ref' do
- let(:before_pipeline) { nil }
+ it 'does not unlock artifacts for other refs' do
+ expect { execute }.not_to change { other_ref_pipeline.reload.locked }.from('artifacts_locked')
+ end
- it 'unlocks artifacts from older pipelines' do
- expect { execute }.to change { older_pipeline.reload.locked }.from('artifacts_locked').to('unlocked')
- end
+ it 'unlocks job artifact records' do
+ pending unless ci_update_unlocked_job_artifacts
- it 'unlocks artifacts from newer pipelines' do
- expect { execute }.to change { newer_pipeline.reload.locked }.from('artifacts_locked').to('unlocked')
- end
+ expect { execute }.to change { ::Ci::JobArtifact.artifact_unlocked.count }.from(0).to(8)
+ end
+ end
+ end
- it 'unlocks artifacts from the same pipeline' do
- expect { execute }.to change { pipeline.reload.locked }.from('artifacts_locked').to('unlocked')
- end
+ describe '#unlock_pipelines_query' do
+ subject { described_class.new(pipeline.project, pipeline.user).unlock_pipelines_query(ci_ref, before_pipeline) }
+
+ context 'when running on a ref before a pipeline' do
+ let(:before_pipeline) { pipeline }
+
+ it 'produces the expected SQL string' do
+ expect(subject.squish).to eq <<~SQL.squish
+ UPDATE
+ "ci_pipelines"
+ SET
+ "locked" = 0
+ WHERE
+ "ci_pipelines"."id" IN
+ (SELECT
+ "ci_pipelines"."id"
+ FROM
+ "ci_pipelines"
+ WHERE
+ "ci_pipelines"."ci_ref_id" = #{ci_ref.id}
+ AND "ci_pipelines"."locked" = 1
+ AND (ci_pipelines.id < #{before_pipeline.id})
+ AND "ci_pipelines"."id" NOT IN
+ (WITH RECURSIVE
+ "base_and_descendants"
+ AS
+ ((SELECT
+ "ci_pipelines".*
+ FROM
+ "ci_pipelines"
+ WHERE
+ "ci_pipelines"."id" = #{before_pipeline.id})
+ UNION
+ (SELECT
+ "ci_pipelines".*
+ FROM
+ "ci_pipelines",
+ "base_and_descendants",
+ "ci_sources_pipelines"
+ WHERE
+ "ci_sources_pipelines"."pipeline_id" = "ci_pipelines"."id"
+ AND "ci_sources_pipelines"."source_pipeline_id" = "base_and_descendants"."id"
+ AND "ci_sources_pipelines"."source_project_id" = "ci_sources_pipelines"."project_id"))
+ SELECT
+ "id"
+ FROM
+ "base_and_descendants"
+ AS
+ "ci_pipelines")
+ LIMIT 1
+ FOR UPDATE
+ SKIP LOCKED)
+ RETURNING ("ci_pipelines"."id")
+ SQL
+ end
+ end
- it 'does not unlock artifacts for tag or branch with same name as ref' do
- expect { execute }.not_to change { older_ambiguous_pipeline.reload.locked }.from('artifacts_locked')
- end
+ context 'when running on just the ref' do
+ let(:before_pipeline) { nil }
+
+ it 'produces the expected SQL string' do
+ expect(subject.squish).to eq <<~SQL.squish
+ UPDATE
+ "ci_pipelines"
+ SET
+ "locked" = 0
+ WHERE
+ "ci_pipelines"."id" IN
+ (SELECT
+ "ci_pipelines"."id"
+ FROM
+ "ci_pipelines"
+ WHERE
+ "ci_pipelines"."ci_ref_id" = #{ci_ref.id}
+ AND "ci_pipelines"."locked" = 1
+ LIMIT 1
+ FOR UPDATE
+ SKIP LOCKED)
+ RETURNING
+ ("ci_pipelines"."id")
+ SQL
+ end
+ end
+ end
- it 'does not lock artifacts from old unlocked pipelines' do
- expect { execute }.not_to change { old_unlocked_pipeline.reload.locked }.from('unlocked')
- end
+ describe '#unlock_job_artifacts_query' do
+ subject { described_class.new(pipeline.project, pipeline.user).unlock_job_artifacts_query(pipeline_ids) }
+
+ context 'when running on a ref before a pipeline' do
+ let(:before_pipeline) { pipeline }
+ let(:pipeline_ids) { [older_pipeline.id] }
+
+ it 'produces the expected SQL string' do
+ expect(subject.squish).to eq <<~SQL.squish
+ UPDATE
+ "ci_job_artifacts"
+ SET
+ "locked" = 0
+ WHERE
+ "ci_job_artifacts"."job_id" IN
+ (SELECT
+ "ci_builds"."id"
+ FROM
+ "ci_builds"
+ WHERE
+ "ci_builds"."type" = 'Ci::Build'
+ AND "ci_builds"."commit_id" = #{older_pipeline.id})
+ RETURNING
+ ("ci_job_artifacts"."id")
+ SQL
+ end
+ end
- it 'does not unlock artifacts for other refs' do
- expect { execute }.not_to change { other_ref_pipeline.reload.locked }.from('artifacts_locked')
- end
+ context 'when running on just the ref' do
+ let(:before_pipeline) { nil }
+ let(:pipeline_ids) { [older_pipeline.id, newer_pipeline.id, pipeline.id] }
+
+ it 'produces the expected SQL string' do
+ expect(subject.squish).to eq <<~SQL.squish
+ UPDATE
+ "ci_job_artifacts"
+ SET
+ "locked" = 0
+ WHERE
+ "ci_job_artifacts"."job_id" IN
+ (SELECT
+ "ci_builds"."id"
+ FROM
+ "ci_builds"
+ WHERE
+ "ci_builds"."type" = 'Ci::Build'
+ AND "ci_builds"."commit_id" IN (#{pipeline_ids.join(', ')}))
+ RETURNING
+ ("ci_job_artifacts"."id")
+ SQL
end
end
end