diff options
Diffstat (limited to 'spec/services/ci/unlock_artifacts_service_spec.rb')
-rw-r--r-- | spec/services/ci/unlock_artifacts_service_spec.rb | 280 |
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 |