diff options
author | GitLab Bot <gitlab-bot@gitlab.com> | 2021-05-19 15:44:42 +0000 |
---|---|---|
committer | GitLab Bot <gitlab-bot@gitlab.com> | 2021-05-19 15:44:42 +0000 |
commit | 4555e1b21c365ed8303ffb7a3325d773c9b8bf31 (patch) | |
tree | 5423a1c7516cffe36384133ade12572cf709398d /spec/services/ci | |
parent | e570267f2f6b326480d284e0164a6464ba4081bc (diff) | |
download | gitlab-ce-4555e1b21c365ed8303ffb7a3325d773c9b8bf31.tar.gz |
Add latest changes from gitlab-org/gitlab@13-12-stable-eev13.12.0-rc42
Diffstat (limited to 'spec/services/ci')
33 files changed, 449 insertions, 164 deletions
diff --git a/spec/services/ci/change_variable_service_spec.rb b/spec/services/ci/change_variable_service_spec.rb index 7acdd4e834f..f86a87132b1 100644 --- a/spec/services/ci/change_variable_service_spec.rb +++ b/spec/services/ci/change_variable_service_spec.rb @@ -6,6 +6,7 @@ RSpec.describe Ci::ChangeVariableService do let(:service) { described_class.new(container: group, current_user: user, params: params) } let_it_be(:user) { create(:user) } + let(:group) { create(:group) } describe '#execute' do diff --git a/spec/services/ci/change_variables_service_spec.rb b/spec/services/ci/change_variables_service_spec.rb index 5f1207eaf58..b710ca78554 100644 --- a/spec/services/ci/change_variables_service_spec.rb +++ b/spec/services/ci/change_variables_service_spec.rb @@ -6,6 +6,7 @@ RSpec.describe Ci::ChangeVariablesService do let(:service) { described_class.new(container: group, current_user: user, params: params) } let_it_be(:user) { create(:user) } + let(:group) { spy(:group, variables: []) } let(:params) { { variables_attributes: [{ key: 'new_variable', value: 'variable_value' }] } } diff --git a/spec/services/ci/create_downstream_pipeline_service_spec.rb b/spec/services/ci/create_downstream_pipeline_service_spec.rb index dd10fb017aa..8bab7856375 100644 --- a/spec/services/ci/create_downstream_pipeline_service_spec.rb +++ b/spec/services/ci/create_downstream_pipeline_service_spec.rb @@ -3,9 +3,11 @@ require 'spec_helper' RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do + include Ci::SourcePipelineHelpers + let_it_be(:user) { create(:user) } let(:upstream_project) { create(:project, :repository) } - let_it_be(:downstream_project) { create(:project, :repository) } + let_it_be(:downstream_project, refind: true) { create(:project, :repository) } let!(:upstream_pipeline) do create(:ci_pipeline, :running, project: upstream_project) @@ -394,6 +396,47 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do end end + context 'when relationship between pipelines is cyclical' do + before do + pipeline_a = create(:ci_pipeline, project: upstream_project) + pipeline_b = create(:ci_pipeline, project: downstream_project) + pipeline_c = create(:ci_pipeline, project: upstream_project) + + create_source_pipeline(pipeline_a, pipeline_b) + create_source_pipeline(pipeline_b, pipeline_c) + create_source_pipeline(pipeline_c, upstream_pipeline) + end + + it 'does not create a new pipeline' do + expect { service.execute(bridge) } + .not_to change { Ci::Pipeline.count } + end + + it 'changes status of the bridge build' do + service.execute(bridge) + + expect(bridge.reload).to be_failed + expect(bridge.failure_reason).to eq 'pipeline_loop_detected' + end + + context 'when ci_drop_cyclical_triggered_pipelines is not enabled' do + before do + stub_feature_flags(ci_drop_cyclical_triggered_pipelines: false) + end + + it 'creates a new pipeline' do + expect { service.execute(bridge) } + .to change { Ci::Pipeline.count } + end + + it 'expect bridge build not to be failed' do + service.execute(bridge) + + expect(bridge.reload).not_to be_failed + end + end + end + context 'when downstream pipeline creation errors out' do let(:stub_config) { false } diff --git a/spec/services/ci/create_pipeline_service/cross_project_pipeline_spec.rb b/spec/services/ci/create_pipeline_service/cross_project_pipeline_spec.rb index d4e9946ac46..b3b8e34dd8e 100644 --- a/spec/services/ci/create_pipeline_service/cross_project_pipeline_spec.rb +++ b/spec/services/ci/create_pipeline_service/cross_project_pipeline_spec.rb @@ -4,6 +4,7 @@ require 'spec_helper' RSpec.describe Ci::CreatePipelineService, '#execute' do let_it_be(:group) { create(:group, name: 'my-organization') } + let(:upstream_project) { create(:project, :repository, name: 'upstream', group: group) } let(:downstram_project) { create(:project, :repository, name: 'downstream', group: group) } let(:user) { create(:user) } diff --git a/spec/services/ci/create_pipeline_service/custom_config_content_spec.rb b/spec/services/ci/create_pipeline_service/custom_config_content_spec.rb index 6320a16d646..42c3f52541b 100644 --- a/spec/services/ci/create_pipeline_service/custom_config_content_spec.rb +++ b/spec/services/ci/create_pipeline_service/custom_config_content_spec.rb @@ -4,6 +4,7 @@ require 'spec_helper' RSpec.describe Ci::CreatePipelineService do let_it_be(:project) { create(:project, :repository) } let_it_be(:user) { project.owner } + let(:ref) { 'refs/heads/master' } let(:service) { described_class.new(project, user, { ref: ref }) } diff --git a/spec/services/ci/create_pipeline_service/dry_run_spec.rb b/spec/services/ci/create_pipeline_service/dry_run_spec.rb index c21a4ef0917..0fb500f5729 100644 --- a/spec/services/ci/create_pipeline_service/dry_run_spec.rb +++ b/spec/services/ci/create_pipeline_service/dry_run_spec.rb @@ -5,6 +5,7 @@ require 'spec_helper' RSpec.describe Ci::CreatePipelineService do let_it_be(:project) { create(:project, :repository) } let_it_be(:user) { project.owner } + let(:ref) { 'refs/heads/master' } let(:service) { described_class.new(project, user, { ref: ref }) } diff --git a/spec/services/ci/create_pipeline_service/environment_spec.rb b/spec/services/ci/create_pipeline_service/environment_spec.rb index 0ed63012325..e77591298ad 100644 --- a/spec/services/ci/create_pipeline_service/environment_spec.rb +++ b/spec/services/ci/create_pipeline_service/environment_spec.rb @@ -5,6 +5,7 @@ require 'spec_helper' RSpec.describe Ci::CreatePipelineService do let_it_be(:project) { create(:project, :repository) } let_it_be(:developer) { create(:user) } + let(:service) { described_class.new(project, user, ref: 'master') } let(:user) { developer } diff --git a/spec/services/ci/create_pipeline_service/parameter_content_spec.rb b/spec/services/ci/create_pipeline_service/parameter_content_spec.rb index 90b8baa23a7..94500a550c6 100644 --- a/spec/services/ci/create_pipeline_service/parameter_content_spec.rb +++ b/spec/services/ci/create_pipeline_service/parameter_content_spec.rb @@ -5,6 +5,7 @@ require 'spec_helper' RSpec.describe Ci::CreatePipelineService do let_it_be(:project) { create(:project, :repository) } let_it_be(:user) { project.owner } + let(:service) { described_class.new(project, user, { ref: 'refs/heads/master' }) } let(:content) do <<~EOY diff --git a/spec/services/ci/create_pipeline_service/parent_child_pipeline_spec.rb b/spec/services/ci/create_pipeline_service/parent_child_pipeline_spec.rb index 5ea75c2253b..512cf546e6a 100644 --- a/spec/services/ci/create_pipeline_service/parent_child_pipeline_spec.rb +++ b/spec/services/ci/create_pipeline_service/parent_child_pipeline_spec.rb @@ -5,6 +5,7 @@ require 'spec_helper' RSpec.describe Ci::CreatePipelineService, '#execute' do let_it_be(:project) { create(:project, :repository) } let_it_be(:user) { create(:user) } + let(:ref_name) { 'master' } let(:service) do diff --git a/spec/services/ci/create_pipeline_service_spec.rb b/spec/services/ci/create_pipeline_service_spec.rb index 98c85234fe7..9fdce1ae926 100644 --- a/spec/services/ci/create_pipeline_service_spec.rb +++ b/spec/services/ci/create_pipeline_service_spec.rb @@ -7,6 +7,7 @@ RSpec.describe Ci::CreatePipelineService do let_it_be(:project, reload: true) { create(:project, :repository) } let_it_be(:user, reload: true) { project.owner } + let(:ref_name) { 'refs/heads/master' } before do @@ -101,14 +102,6 @@ RSpec.describe Ci::CreatePipelineService do execute_service end - describe 'recording a conversion event' do - it 'schedules a record conversion event worker' do - expect(Experiments::RecordConversionEventWorker).to receive(:perform_async).with(:ci_syntax_templates_b, user.id) - - pipeline - end - end - context 'when merge requests already exist for this source branch' do let(:merge_request_1) do create(:merge_request, source_branch: 'feature', target_branch: "master", source_project: project) @@ -539,7 +532,7 @@ RSpec.describe Ci::CreatePipelineService do it 'pull it from Auto-DevOps' do pipeline = execute_service expect(pipeline).to be_auto_devops_source - expect(pipeline.builds.map(&:name)).to match_array(%w[brakeman-sast build code_quality eslint-sast secret_detection_default_branch test]) + expect(pipeline.builds.map(&:name)).to match_array(%w[brakeman-sast build code_quality eslint-sast secret_detection_default_branch semgrep-sast test]) end end diff --git a/spec/services/ci/create_web_ide_terminal_service_spec.rb b/spec/services/ci/create_web_ide_terminal_service_spec.rb index c1acf8fd60c..0804773442d 100644 --- a/spec/services/ci/create_web_ide_terminal_service_spec.rb +++ b/spec/services/ci/create_web_ide_terminal_service_spec.rb @@ -5,6 +5,7 @@ require 'spec_helper' RSpec.describe Ci::CreateWebIdeTerminalService do let_it_be(:project) { create(:project, :repository) } let_it_be(:user) { create(:user) } + let(:ref) { 'master' } describe '#execute' do @@ -20,6 +21,13 @@ RSpec.describe Ci::CreateWebIdeTerminalService do expect(subject[:pipeline].stages.count).to eq(1) expect(subject[:pipeline].builds.count).to eq(1) end + + it 'calls ensure_project_iid explicitly' do + expect_next_instance_of(Ci::Pipeline) do |instance| + expect(instance).to receive(:ensure_project_iid!).twice + end + subject + end end before do diff --git a/spec/services/ci/delete_unit_tests_service_spec.rb b/spec/services/ci/delete_unit_tests_service_spec.rb new file mode 100644 index 00000000000..4c63c513d48 --- /dev/null +++ b/spec/services/ci/delete_unit_tests_service_spec.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Ci::DeleteUnitTestsService do + describe '#execute' do + let!(:unit_test_1) { create(:ci_unit_test) } + let!(:unit_test_2) { create(:ci_unit_test) } + let!(:unit_test_3) { create(:ci_unit_test) } + let!(:unit_test_4) { create(:ci_unit_test) } + let!(:unit_test_1_recent_failure) { create(:ci_unit_test_failure, unit_test: unit_test_1) } + let!(:unit_test_1_old_failure) { create(:ci_unit_test_failure, unit_test: unit_test_1, failed_at: 15.days.ago) } + let!(:unit_test_2_old_failure) { create(:ci_unit_test_failure, unit_test: unit_test_2, failed_at: 15.days.ago) } + let!(:unit_test_3_old_failure) { create(:ci_unit_test_failure, unit_test: unit_test_3, failed_at: 15.days.ago) } + let!(:unit_test_4_old_failure) { create(:ci_unit_test_failure, unit_test: unit_test_4, failed_at: 15.days.ago) } + + before do + stub_const("#{described_class.name}::BATCH_SIZE", 2) + + described_class.new.execute + end + + it 'does not delete unit test failures not older than 14 days' do + expect(unit_test_1_recent_failure.reload).to be_persisted + end + + it 'deletes unit test failures older than 14 days' do + ids = [ + unit_test_1_old_failure, + unit_test_2_old_failure, + unit_test_3_old_failure, + unit_test_4_old_failure + ].map(&:id) + + result = Ci::UnitTestFailure.where(id: ids) + + expect(result).to be_empty + end + + it 'deletes unit tests that have no more associated unit test failures' do + ids = [ + unit_test_2, + unit_test_3, + unit_test_4 + ].map(&:id) + + result = Ci::UnitTest.where(id: ids) + + expect(result).to be_empty + end + end +end diff --git a/spec/services/ci/destroy_pipeline_service_spec.rb b/spec/services/ci/destroy_pipeline_service_spec.rb index 6977c99e335..302233cea5a 100644 --- a/spec/services/ci/destroy_pipeline_service_spec.rb +++ b/spec/services/ci/destroy_pipeline_service_spec.rb @@ -3,7 +3,8 @@ require 'spec_helper' RSpec.describe ::Ci::DestroyPipelineService do - let(:project) { create(:project, :repository) } + let_it_be(:project) { create(:project, :repository) } + let!(:pipeline) { create(:ci_pipeline, :success, project: project, sha: project.commit.id) } subject { described_class.new(project, user).execute(pipeline) } @@ -17,13 +18,16 @@ RSpec.describe ::Ci::DestroyPipelineService do expect { pipeline.reload }.to raise_error(ActiveRecord::RecordNotFound) end - it 'clears the cache', :use_clean_rails_memory_store_caching do + it 'clears the cache', :use_clean_rails_redis_caching do create(:commit_status, :success, pipeline: pipeline, ref: pipeline.ref) expect(project.pipeline_status.has_status?).to be_truthy subject + # We need to reset lazy_latest_pipeline cache to simulate a new request + BatchLoader::Executor.clear_current + # Need to use find to avoid memoization expect(Project.find(project.id).pipeline_status.has_status?).to be_falsey end @@ -57,6 +61,10 @@ RSpec.describe ::Ci::DestroyPipelineService do expect { artifact.reload }.to raise_error(ActiveRecord::RecordNotFound) end + + it 'inserts deleted objects for object storage files' do + expect { subject }.to change { Ci::DeletedObject.count } + end end end end diff --git a/spec/services/ci/expire_pipeline_cache_service_spec.rb b/spec/services/ci/expire_pipeline_cache_service_spec.rb index 3dbf2dbb8f1..613bbe45e68 100644 --- a/spec/services/ci/expire_pipeline_cache_service_spec.rb +++ b/spec/services/ci/expire_pipeline_cache_service_spec.rb @@ -6,6 +6,7 @@ RSpec.describe Ci::ExpirePipelineCacheService do let_it_be(:user) { create(:user) } let_it_be(:project) { create(:project) } let_it_be(:pipeline) { create(:ci_pipeline, project: project) } + subject { described_class.new } describe '#execute' do @@ -14,12 +15,14 @@ RSpec.describe Ci::ExpirePipelineCacheService do new_mr_pipelines_path = "/#{project.full_path}/-/merge_requests/new.json" pipeline_path = "/#{project.full_path}/-/pipelines/#{pipeline.id}.json" graphql_pipeline_path = "/api/graphql:pipelines/id/#{pipeline.id}" + graphql_pipeline_sha_path = "/api/graphql:pipelines/sha/#{pipeline.sha}" expect_next_instance_of(Gitlab::EtagCaching::Store) do |store| expect(store).to receive(:touch).with(pipelines_path) expect(store).to receive(:touch).with(new_mr_pipelines_path) expect(store).to receive(:touch).with(pipeline_path) expect(store).to receive(:touch).with(graphql_pipeline_path) + expect(store).to receive(:touch).with(graphql_pipeline_sha_path) end subject.execute(pipeline) @@ -49,7 +52,7 @@ RSpec.describe Ci::ExpirePipelineCacheService do let(:project_with_repo) { create(:project, :repository) } let!(:pipeline_with_commit) { create(:ci_pipeline, :success, project: project_with_repo, sha: project_with_repo.commit.id) } - it 'clears the cache', :use_clean_rails_memory_store_caching do + it 'clears the cache', :use_clean_rails_redis_caching do create(:commit_status, :success, pipeline: pipeline_with_commit, ref: pipeline_with_commit.ref) # Sanity check @@ -59,6 +62,9 @@ RSpec.describe Ci::ExpirePipelineCacheService do pipeline_with_commit.destroy! + # We need to reset lazy_latest_pipeline cache to simulate a new request + BatchLoader::Executor.clear_current + # Need to use find to avoid memoization expect(Project.find(project_with_repo.id).pipeline_status.has_status?).to be_falsey end diff --git a/spec/services/ci/external_pull_requests/create_pipeline_service_spec.rb b/spec/services/ci/external_pull_requests/create_pipeline_service_spec.rb index 0cbeaa5446b..e25dd351bb3 100644 --- a/spec/services/ci/external_pull_requests/create_pipeline_service_spec.rb +++ b/spec/services/ci/external_pull_requests/create_pipeline_service_spec.rb @@ -6,6 +6,7 @@ RSpec.describe Ci::ExternalPullRequests::CreatePipelineService do describe '#execute' do let_it_be(:project) { create(:project, :auto_devops, :repository) } let_it_be(:user) { create(:user) } + let(:pull_request) { create(:external_pull_request, project: project) } before do diff --git a/spec/services/ci/find_exposed_artifacts_service_spec.rb b/spec/services/ci/find_exposed_artifacts_service_spec.rb index 287f5c4b929..32d96471f16 100644 --- a/spec/services/ci/find_exposed_artifacts_service_spec.rb +++ b/spec/services/ci/find_exposed_artifacts_service_spec.rb @@ -15,6 +15,7 @@ RSpec.describe Ci::FindExposedArtifactsService do end let_it_be(:project) { create(:project) } + let(:user) { nil } after do diff --git a/spec/services/ci/generate_codequality_mr_diff_report_service_spec.rb b/spec/services/ci/generate_codequality_mr_diff_report_service_spec.rb index 5d747a09f2a..63bc7a1caf8 100644 --- a/spec/services/ci/generate_codequality_mr_diff_report_service_spec.rb +++ b/spec/services/ci/generate_codequality_mr_diff_report_service_spec.rb @@ -10,7 +10,7 @@ RSpec.describe Ci::GenerateCodequalityMrDiffReportService do subject { service.execute(base_pipeline, head_pipeline) } context 'when head pipeline has codequality mr diff report' do - let!(:merge_request) { create(:merge_request, :with_codequality_mr_diff_reports, source_project: project) } + let!(:merge_request) { create(:merge_request, :with_codequality_mr_diff_reports, source_project: project, id: 123456789) } let!(:service) { described_class.new(project, nil, id: merge_request.id) } let!(:head_pipeline) { merge_request.head_pipeline } let!(:base_pipeline) { nil } @@ -18,7 +18,7 @@ RSpec.describe Ci::GenerateCodequalityMrDiffReportService do it 'returns status and data', :aggregate_failures do expect_any_instance_of(Ci::PipelineArtifact) do |instance| expect(instance).to receive(:present) - expect(instance).to receive(:for_files).with(merge_request.new_paths).and_call_original + expect(instance).to receive(:for_files).with(merge_request).and_call_original end expect(subject[:status]).to eq(:parsed) diff --git a/spec/services/ci/job_artifacts/create_service_spec.rb b/spec/services/ci/job_artifacts/create_service_spec.rb index 22aa9e62c6f..97c65dc005e 100644 --- a/spec/services/ci/job_artifacts/create_service_spec.rb +++ b/spec/services/ci/job_artifacts/create_service_spec.rb @@ -4,6 +4,7 @@ require 'spec_helper' RSpec.describe Ci::JobArtifacts::CreateService do let_it_be(:project) { create(:project) } + let(:service) { described_class.new(job) } let(:job) { create(:ci_build, project: project) } let(:artifacts_sha256) { '0' * 64 } diff --git a/spec/services/ci/job_artifacts/destroy_associations_service_spec.rb b/spec/services/ci/job_artifacts/destroy_associations_service_spec.rb new file mode 100644 index 00000000000..b1a4741851b --- /dev/null +++ b/spec/services/ci/job_artifacts/destroy_associations_service_spec.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Ci::JobArtifacts::DestroyAssociationsService do + let(:artifacts) { Ci::JobArtifact.all } + let(:service) { described_class.new(artifacts) } + + let_it_be(:artifact, refind: true) do + create(:ci_job_artifact) + end + + before do + artifact.file = fixture_file_upload(Rails.root.join('spec/fixtures/ci_build_artifacts.zip'), 'application/zip') + artifact.save! + end + + describe '#destroy_records' do + it 'removes artifacts without updating statistics' do + expect(ProjectStatistics).not_to receive(:increment_statistic) + + expect { service.destroy_records }.to change { Ci::JobArtifact.count } + end + + context 'when there are no artifacts' do + let(:artifacts) { Ci::JobArtifact.none } + + it 'does not raise error' do + expect { service.destroy_records }.not_to raise_error + end + end + end + + describe '#update_statistics' do + before do + service.destroy_records + end + + it 'updates project statistics' do + expect(ProjectStatistics).to receive(:increment_statistic).once + .with(artifact.project, :build_artifacts_size, -artifact.file.size) + + service.update_statistics + end + + context 'when there are no artifacts' do + let(:artifacts) { Ci::JobArtifact.none } + + it 'does not raise error' do + expect { service.update_statistics }.not_to raise_error + end + end + end +end diff --git a/spec/services/ci/job_artifacts/destroy_batch_service_spec.rb b/spec/services/ci/job_artifacts/destroy_batch_service_spec.rb index 52aaf73d67e..2cedbf93d74 100644 --- a/spec/services/ci/job_artifacts/destroy_batch_service_spec.rb +++ b/spec/services/ci/job_artifacts/destroy_batch_service_spec.rb @@ -3,8 +3,6 @@ require 'spec_helper' RSpec.describe Ci::JobArtifacts::DestroyBatchService do - include ExclusiveLeaseHelpers - let(:artifacts) { Ci::JobArtifact.all } let(:service) { described_class.new(artifacts, pick_up_at: Time.current) } @@ -25,14 +23,6 @@ RSpec.describe Ci::JobArtifacts::DestroyBatchService do expect { subject }.to change { Ci::DeletedObject.count }.by(1) end - it 'resets project statistics' do - expect(ProjectStatistics).to receive(:increment_statistic).once - .with(artifact.project, :build_artifacts_size, -artifact.file.size) - .and_call_original - - execute - end - it 'does not remove the files' do expect { execute }.not_to change { artifact.file.exists? } end @@ -44,6 +34,29 @@ RSpec.describe Ci::JobArtifacts::DestroyBatchService do execute end + + context 'ProjectStatistics' do + it 'resets project statistics' do + expect(ProjectStatistics).to receive(:increment_statistic).once + .with(artifact.project, :build_artifacts_size, -artifact.file.size) + .and_call_original + + execute + end + + context 'with update_stats: false' do + it 'does not update project statistics' do + expect(ProjectStatistics).not_to receive(:increment_statistic) + + service.execute(update_stats: false) + end + + it 'returns size statistics' do + expect(service.execute(update_stats: false)).to match( + a_hash_including(statistics_updates: { artifact.project => -artifact.file.size })) + end + end + end end context 'when failed to destroy artifact' do @@ -65,16 +78,12 @@ RSpec.describe Ci::JobArtifacts::DestroyBatchService do context 'when there are no artifacts' do let(:artifacts) { Ci::JobArtifact.none } - before do - artifact.destroy! - end - it 'does not raise error' do expect { execute }.not_to raise_error end it 'reports the number of destroyed artifacts' do - is_expected.to eq(destroyed_artifacts_count: 0, status: :success) + is_expected.to eq(destroyed_artifacts_count: 0, statistics_updates: {}, status: :success) end end end diff --git a/spec/services/ci/parse_dotenv_artifact_service_spec.rb b/spec/services/ci/parse_dotenv_artifact_service_spec.rb index 91b81af9fd1..7536e04f2de 100644 --- a/spec/services/ci/parse_dotenv_artifact_service_spec.rb +++ b/spec/services/ci/parse_dotenv_artifact_service_spec.rb @@ -5,6 +5,7 @@ require 'spec_helper' RSpec.describe Ci::ParseDotenvArtifactService do let_it_be(:project) { create(:project) } let_it_be(:pipeline) { create(:ci_pipeline, project: project) } + let(:build) { create(:ci_build, pipeline: pipeline, project: project) } let(:service) { described_class.new(project, nil) } @@ -24,7 +25,7 @@ RSpec.describe Ci::ParseDotenvArtifactService do context 'when parse error happens' do before do - allow(service).to receive(:scan_line!) { raise described_class::ParserError.new('Invalid Format') } + allow(service).to receive(:scan_line!) { raise described_class::ParserError, 'Invalid Format' } end it 'returns error' do diff --git a/spec/services/ci/pipeline_artifacts/create_code_quality_mr_diff_report_service_spec.rb b/spec/services/ci/pipeline_artifacts/create_code_quality_mr_diff_report_service_spec.rb index 0c48f15d726..5568052e346 100644 --- a/spec/services/ci/pipeline_artifacts/create_code_quality_mr_diff_report_service_spec.rb +++ b/spec/services/ci/pipeline_artifacts/create_code_quality_mr_diff_report_service_spec.rb @@ -4,58 +4,76 @@ require 'spec_helper' RSpec.describe ::Ci::PipelineArtifacts::CreateCodeQualityMrDiffReportService do describe '#execute' do - subject(:pipeline_artifact) { described_class.new.execute(pipeline) } + let(:merge_request) { create(:merge_request) } + let(:project) { merge_request.project } + let(:head_pipeline) { create(:ci_pipeline, :success, :with_codequality_reports, project: project, merge_requests_as_head_pipeline: [merge_request]) } + let(:base_pipeline) { create(:ci_pipeline, :success, project: project, ref: merge_request.target_branch, sha: merge_request.diff_base_sha) } - context 'when pipeline has codequality reports' do - let(:project) { create(:project, :repository) } + subject { described_class.new(head_pipeline).execute } - describe 'pipeline completed status' do - using RSpec::Parameterized::TableSyntax + context 'when there are codequality reports' do + context 'when pipeline passes' do + context 'when degradations are present' do + context 'when degradations already present in target branch pipeline' do + before do + create(:ci_build, :success, :codequality_reports, name: 'codequality', pipeline: base_pipeline, project: project) + end - where(:status, :result) do - :success | 1 - :failed | 1 - :canceled | 1 - :skipped | 1 - end + it "does not persist a pipeline artifact" do + expect { subject }.not_to change { Ci::PipelineArtifact.count } + end + end + + context 'when degradation is not present in target branch pipeline' do + before do + create(:ci_build, :success, :codequality_reports_without_degradation, name: 'codequality', pipeline: base_pipeline, project: project) + end - with_them do - let(:pipeline) { create(:ci_pipeline, :with_codequality_reports, status: status, project: project) } + it 'persists a pipeline artifact' do + expect { subject }.to change { Ci::PipelineArtifact.count }.by(1) + end - it 'creates a pipeline artifact' do - expect { pipeline_artifact }.to change(Ci::PipelineArtifact, :count).by(result) - end + it 'persists the default file name' do + subject - it 'persists the default file name' do - expect(pipeline_artifact.file.filename).to eq('code_quality_mr_diff.json') - end + pipeline_artifact = Ci::PipelineArtifact.first - it 'sets expire_at to 1 week' do - freeze_time do - expect(pipeline_artifact.expire_at).to eq(1.week.from_now) + expect(pipeline_artifact.file.filename).to eq('code_quality_mr_diff.json') end - end - end - end - context 'when pipeline artifact has already been created' do - let(:pipeline) { create(:ci_pipeline, :with_codequality_reports, project: project) } + it 'sets expire_at to 1 week' do + freeze_time do + subject + + pipeline_artifact = Ci::PipelineArtifact.first + + expect(pipeline_artifact.expire_at).to eq(1.week.from_now) + end + end - it 'does not persist the same artifact twice' do - 2.times { described_class.new.execute(pipeline) } + it 'does not persist the same artifact twice' do + 2.times { described_class.new(head_pipeline).execute } - expect(Ci::PipelineArtifact.count).to eq(1) + expect { subject }.not_to change { Ci::PipelineArtifact.count } + end + end end end end - context 'when pipeline is not completed and codequality report does not exist' do - let(:pipeline) { create(:ci_pipeline, :running) } + context 'when there are no codequality reports for head pipeline' do + let(:head_pipeline) { create(:ci_pipeline, :success, project: project, merge_requests_as_head_pipeline: [merge_request]) } + + it "does not persist a pipeline artifact" do + expect { subject }.not_to change { Ci::PipelineArtifact.count } + end + end - it 'does not persist data' do - pipeline_artifact + context 'when there are no codequality reports for base pipeline' do + let(:head_pipeline) { create(:ci_pipeline, :success, project: project, merge_requests_as_head_pipeline: [merge_request]) } - expect(Ci::PipelineArtifact.count).to eq(0) + it "does not persist a pipeline artifact" do + expect { subject }.not_to change { Ci::PipelineArtifact.count } end end end diff --git a/spec/services/ci/pipeline_artifacts/destroy_all_expired_service_spec.rb b/spec/services/ci/pipeline_artifacts/destroy_all_expired_service_spec.rb index 3dc4f35df22..eb664043567 100644 --- a/spec/services/ci/pipeline_artifacts/destroy_all_expired_service_spec.rb +++ b/spec/services/ci/pipeline_artifacts/destroy_all_expired_service_spec.rb @@ -30,7 +30,7 @@ RSpec.describe Ci::PipelineArtifacts::DestroyAllExpiredService do stub_const('::Ci::PipelineArtifacts::DestroyAllExpiredService::LOOP_LIMIT', 1) stub_const('::Ci::PipelineArtifacts::DestroyAllExpiredService::BATCH_SIZE', 1) - create_list(:ci_pipeline_artifact, 2, expire_at: 1.week.ago) + create_list(:ci_pipeline_artifact, 2, :unlocked, expire_at: 1.week.ago) end it 'destroys one artifact' do @@ -46,7 +46,7 @@ RSpec.describe Ci::PipelineArtifacts::DestroyAllExpiredService do before do stub_const('Ci::PipelineArtifacts::DestroyAllExpiredService::BATCH_SIZE', 1) - create_list(:ci_pipeline_artifact, 2, expire_at: 1.week.ago) + create_list(:ci_pipeline_artifact, 2, :unlocked, expire_at: 1.week.ago) end it 'destroys all expired artifacts' do @@ -60,7 +60,21 @@ RSpec.describe Ci::PipelineArtifacts::DestroyAllExpiredService do context 'when artifacts are not expired' do before do - create(:ci_pipeline_artifact, expire_at: 2.days.from_now) + create(:ci_pipeline_artifact, :unlocked, expire_at: 2.days.from_now) + end + + it 'does not destroy pipeline artifacts' do + expect { subject }.not_to change { Ci::PipelineArtifact.count } + end + + it 'reports the number of destroyed artifacts' do + is_expected.to eq(0) + end + end + + context 'when pipeline is locked' do + before do + create(:ci_pipeline_artifact, expire_at: 2.weeks.ago) end it 'does not destroy pipeline artifacts' do diff --git a/spec/services/ci/pipeline_bridge_status_service_spec.rb b/spec/services/ci/pipeline_bridge_status_service_spec.rb index 584b23bb3aa..1346f68c952 100644 --- a/spec/services/ci/pipeline_bridge_status_service_spec.rb +++ b/spec/services/ci/pipeline_bridge_status_service_spec.rb @@ -5,6 +5,7 @@ require 'spec_helper' RSpec.describe Ci::PipelineBridgeStatusService do let(:user) { build(:user) } let_it_be(:project) { create(:project) } + let(:pipeline) { build(:ci_pipeline, project: project) } describe '#execute' do diff --git a/spec/services/ci/pipeline_processing/atomic_processing_service_spec.rb b/spec/services/ci/pipeline_processing/atomic_processing_service_spec.rb index bc8b6b2d113..a66d3898c5c 100644 --- a/spec/services/ci/pipeline_processing/atomic_processing_service_spec.rb +++ b/spec/services/ci/pipeline_processing/atomic_processing_service_spec.rb @@ -1,8 +1,8 @@ # frozen_string_literal: true require 'spec_helper' -require_relative 'shared_processing_service.rb' -require_relative 'shared_processing_service_tests_with_yaml.rb' +require_relative 'shared_processing_service' +require_relative 'shared_processing_service_tests_with_yaml' RSpec.describe Ci::PipelineProcessing::AtomicProcessingService do it_behaves_like 'Pipeline Processing Service' diff --git a/spec/services/ci/pipeline_processing/test_cases/dag_test_manual_post_test_needs_deploy_is_stage.yml b/spec/services/ci/pipeline_processing/test_cases/dag_test_manual_post_test_needs_deploy_is_stage.yml new file mode 100644 index 00000000000..03d5781395d --- /dev/null +++ b/spec/services/ci/pipeline_processing/test_cases/dag_test_manual_post_test_needs_deploy_is_stage.yml @@ -0,0 +1,50 @@ +config: + stages: [build, test, post_test, deploy] + + build: + stage: build + script: exit 0 + + test: + stage: test + script: exit 0 + when: manual + + post_test: + stage: post_test + script: exit 0 + needs: [test] + + deploy: + stage: deploy + script: exit 0 + +init: + expect: + pipeline: pending + stages: + build: pending + test: created + post_test: created + deploy: created + jobs: + build: pending + test: created + post_test: created + deploy: created + +transitions: + - event: success + jobs: [build] + expect: + pipeline: running + stages: + build: success + test: skipped + post_test: skipped + deploy: pending + jobs: + build: success + test: manual + post_test: skipped + deploy: pending diff --git a/spec/services/ci/pipeline_trigger_service_spec.rb b/spec/services/ci/pipeline_trigger_service_spec.rb index 36055779a2e..080ca1cf0cd 100644 --- a/spec/services/ci/pipeline_trigger_service_spec.rb +++ b/spec/services/ci/pipeline_trigger_service_spec.rb @@ -13,12 +13,35 @@ RSpec.describe Ci::PipelineTriggerService do describe '#execute' do let_it_be(:user) { create(:user) } + let(:result) { described_class.new(project, user, params).execute } before do project.add_developer(user) end + shared_examples 'detecting an unprocessable pipeline trigger' do + context 'when the pipeline was not created successfully' do + let(:fail_pipeline) do + receive(:execute).and_wrap_original do |original, *args| + pipeline = original.call(*args) + pipeline.update!(failure_reason: 'unknown_failure') + pipeline + end + end + + before do + allow_next(Ci::CreatePipelineService).to fail_pipeline + end + + it 'has the correct status code' do + expect { result }.to change { Ci::Pipeline.count } + expect(result).to be_error + expect(result.http_status).to eq(:unprocessable_entity) + end + end + end + context 'with a trigger token' do let(:trigger) { create(:ci_trigger, project: project, owner: user) } @@ -62,7 +85,7 @@ RSpec.describe Ci::PipelineTriggerService do it 'ignores [ci skip] and create as general' do expect { result }.to change { Ci::Pipeline.count }.by(1) - expect(result[:status]).to eq(:success) + expect(result).to be_success end end @@ -77,19 +100,22 @@ RSpec.describe Ci::PipelineTriggerService do expect(result[:pipeline].trigger_requests.last.variables).to be_nil end end + + it_behaves_like 'detecting an unprocessable pipeline trigger' end - context 'when params have a non-existsed ref' do + context 'when params have a non-existant ref' do let(:params) { { token: trigger.token, ref: 'invalid-ref', variables: nil } } it 'does not trigger a pipeline' do expect { result }.not_to change { Ci::Pipeline.count } - expect(result[:http_status]).to eq(400) + expect(result).to be_error + expect(result.http_status).to eq(:bad_request) end end end - context 'when params have a non-existsed trigger token' do + context 'when params have a non-existant trigger token' do let(:params) { { token: 'invalid-token', ref: nil, variables: nil } } it 'does not trigger a pipeline' do @@ -172,14 +198,17 @@ RSpec.describe Ci::PipelineTriggerService do expect(job.sourced_pipelines.last.pipeline_id).to eq(result[:pipeline].id) end end + + it_behaves_like 'detecting an unprocessable pipeline trigger' end - context 'when params have a non-existsed ref' do + context 'when params have a non-existant ref' do let(:params) { { token: job.token, ref: 'invalid-ref', variables: nil } } - it 'does not job a pipeline' do + it 'does not trigger a job in the pipeline' do expect { result }.not_to change { Ci::Pipeline.count } - expect(result[:http_status]).to eq(400) + expect(result).to be_error + expect(result.http_status).to eq(:bad_request) end end end diff --git a/spec/services/ci/process_pipeline_service_spec.rb b/spec/services/ci/process_pipeline_service_spec.rb index 254bd19c808..b5bf0adadaf 100644 --- a/spec/services/ci/process_pipeline_service_spec.rb +++ b/spec/services/ci/process_pipeline_service_spec.rb @@ -3,8 +3,7 @@ require 'spec_helper' RSpec.describe Ci::ProcessPipelineService do - let(:user) { create(:user) } - let(:project) { create(:project) } + let_it_be(:project) { create(:project) } let(:pipeline) do create(:ci_empty_pipeline, ref: 'master', project: project) @@ -24,8 +23,6 @@ RSpec.describe Ci::ProcessPipelineService do stub_ci_pipeline_to_return_yaml_file stub_not_protect_default_branch - project.add_developer(user) - allow(subject).to receive(:metrics).and_return(metrics) end @@ -69,6 +66,14 @@ RSpec.describe Ci::ProcessPipelineService do subject.execute end + it 'logs the project and pipeline id' do + expect(Gitlab::AppJsonLogger).to receive(:info).with(event: 'update_retried_is_used', + project_id: project.id, + pipeline_id: pipeline.id) + + subject.execute + end + context 'when the previous build has already retried column true' do before do build_retried.update_columns(retried: true) diff --git a/spec/services/ci/prometheus_metrics/observe_histograms_service_spec.rb b/spec/services/ci/prometheus_metrics/observe_histograms_service_spec.rb index 2eef852b0f4..0b100af5902 100644 --- a/spec/services/ci/prometheus_metrics/observe_histograms_service_spec.rb +++ b/spec/services/ci/prometheus_metrics/observe_histograms_service_spec.rb @@ -4,6 +4,7 @@ require 'spec_helper' RSpec.describe Ci::PrometheusMetrics::ObserveHistogramsService do let_it_be(:project) { create(:project) } + let(:params) { {} } subject(:execute) { described_class.new(project, params).execute } @@ -54,32 +55,6 @@ RSpec.describe Ci::PrometheusMetrics::ObserveHistogramsService do end end - context 'with feature flag disabled' do - before do - stub_feature_flags(ci_accept_frontend_prometheus_metrics: false) - end - - let(:params) do - { - histograms: [ - { name: 'pipeline_graph_link_calculation_duration_seconds', value: '4' } - ] - } - end - - it 'does not register the metrics' do - execute - - expect(histogram_data).to be_nil - end - - it 'returns an empty body and status code' do - is_expected.to be_success - expect(subject.http_status).to eq(:accepted) - expect(subject.payload).to eq({}) - end - end - def histogram_data(name = :pipeline_graph_link_calculation_duration_seconds) Gitlab::Metrics.registry.get(name)&.get({}) end diff --git a/spec/services/ci/register_job_service_spec.rb b/spec/services/ci/register_job_service_spec.rb index 02b48e8ba06..839a3c53f07 100644 --- a/spec/services/ci/register_job_service_spec.rb +++ b/spec/services/ci/register_job_service_spec.rb @@ -7,6 +7,7 @@ module Ci let_it_be(:group) { create(:group) } let_it_be(:project, reload: true) { create(:project, group: group, shared_runners_enabled: false, group_runners_enabled: false) } let_it_be(:pipeline) { create(:ci_pipeline, project: project) } + let!(:shared_runner) { create(:ci_runner, :instance) } let!(:specific_runner) { create(:ci_runner, :project, projects: [project]) } let!(:group_runner) { create(:ci_runner, :group, groups: [group]) } @@ -81,31 +82,69 @@ module Ci let!(:build2_project2) { FactoryBot.create :ci_build, pipeline: pipeline2 } let!(:build1_project3) { FactoryBot.create :ci_build, pipeline: pipeline3 } - it 'prefers projects without builds first' do - # it gets for one build from each of the projects - expect(execute(shared_runner)).to eq(build1_project1) - expect(execute(shared_runner)).to eq(build1_project2) - expect(execute(shared_runner)).to eq(build1_project3) - - # then it gets a second build from each of the projects - expect(execute(shared_runner)).to eq(build2_project1) - expect(execute(shared_runner)).to eq(build2_project2) + context 'when using fair scheduling' do + context 'when all builds are pending' do + it 'prefers projects without builds first' do + # it gets for one build from each of the projects + expect(execute(shared_runner)).to eq(build1_project1) + expect(execute(shared_runner)).to eq(build1_project2) + expect(execute(shared_runner)).to eq(build1_project3) + + # then it gets a second build from each of the projects + expect(execute(shared_runner)).to eq(build2_project1) + expect(execute(shared_runner)).to eq(build2_project2) + + # in the end the third build + expect(execute(shared_runner)).to eq(build3_project1) + end + end - # in the end the third build - expect(execute(shared_runner)).to eq(build3_project1) + context 'when some builds transition to success' do + it 'equalises number of running builds' do + # after finishing the first build for project 1, get a second build from the same project + expect(execute(shared_runner)).to eq(build1_project1) + build1_project1.reload.success + expect(execute(shared_runner)).to eq(build2_project1) + + expect(execute(shared_runner)).to eq(build1_project2) + build1_project2.reload.success + expect(execute(shared_runner)).to eq(build2_project2) + expect(execute(shared_runner)).to eq(build1_project3) + expect(execute(shared_runner)).to eq(build3_project1) + end + end end - it 'equalises number of running builds' do - # after finishing the first build for project 1, get a second build from the same project - expect(execute(shared_runner)).to eq(build1_project1) - build1_project1.reload.success - expect(execute(shared_runner)).to eq(build2_project1) + context 'when using DEFCON mode that disables fair scheduling' do + before do + stub_feature_flags(ci_queueing_disaster_recovery: true) + end + + context 'when all builds are pending' do + it 'returns builds in order of creation (FIFO)' do + # it gets for one build from each of the projects + expect(execute(shared_runner)).to eq(build1_project1) + expect(execute(shared_runner)).to eq(build2_project1) + expect(execute(shared_runner)).to eq(build3_project1) + expect(execute(shared_runner)).to eq(build1_project2) + expect(execute(shared_runner)).to eq(build2_project2) + expect(execute(shared_runner)).to eq(build1_project3) + end + end - expect(execute(shared_runner)).to eq(build1_project2) - build1_project2.reload.success - expect(execute(shared_runner)).to eq(build2_project2) - expect(execute(shared_runner)).to eq(build1_project3) - expect(execute(shared_runner)).to eq(build3_project1) + context 'when some builds transition to success' do + it 'returns builds in order of creation (FIFO)' do + expect(execute(shared_runner)).to eq(build1_project1) + build1_project1.reload.success + expect(execute(shared_runner)).to eq(build2_project1) + + expect(execute(shared_runner)).to eq(build3_project1) + build2_project1.reload.success + expect(execute(shared_runner)).to eq(build1_project2) + expect(execute(shared_runner)).to eq(build2_project2) + expect(execute(shared_runner)).to eq(build1_project3) + end + end end end @@ -477,10 +516,6 @@ module Ci end end - before do - stub_feature_flags(ci_validate_build_dependencies_override: false) - end - let!(:pre_stage_job) { create(:ci_build, :success, pipeline: pipeline, name: 'test', stage_idx: 0) } let!(:pending_job) do @@ -491,37 +526,7 @@ module Ci subject { execute(specific_runner) } - context 'when validates for dependencies is enabled' do - before do - stub_feature_flags(ci_validate_build_dependencies_override: false) - end - - it_behaves_like 'validation is active' - - context 'when the main feature flag is enabled for a specific project' do - before do - stub_feature_flags(ci_validate_build_dependencies: pipeline.project) - end - - it_behaves_like 'validation is active' - end - - context 'when the main feature flag is enabled for a different project' do - before do - stub_feature_flags(ci_validate_build_dependencies: create(:project)) - end - - it_behaves_like 'validation is not active' - end - end - - context 'when validates for dependencies is disabled' do - before do - stub_feature_flags(ci_validate_build_dependencies_override: true) - end - - it_behaves_like 'validation is not active' - end + it_behaves_like 'validation is active' end context 'when build is degenerated' do diff --git a/spec/services/ci/resource_groups/assign_resource_from_resource_group_service_spec.rb b/spec/services/ci/resource_groups/assign_resource_from_resource_group_service_spec.rb index 6c69a7f3b11..a741e3b49e7 100644 --- a/spec/services/ci/resource_groups/assign_resource_from_resource_group_service_spec.rb +++ b/spec/services/ci/resource_groups/assign_resource_from_resource_group_service_spec.rb @@ -5,6 +5,7 @@ require 'spec_helper' RSpec.describe Ci::ResourceGroups::AssignResourceFromResourceGroupService do let_it_be(:project) { create(:project) } let_it_be(:user) { create(:user) } + let(:service) { described_class.new(project, user) } describe '#execute' do diff --git a/spec/services/ci/retry_build_service_spec.rb b/spec/services/ci/retry_build_service_spec.rb index 7dd3d963e56..86bda868625 100644 --- a/spec/services/ci/retry_build_service_spec.rb +++ b/spec/services/ci/retry_build_service_spec.rb @@ -18,6 +18,7 @@ RSpec.describe Ci::RetryBuildService do end let_it_be_with_refind(:build) { create(:ci_build, pipeline: pipeline, stage_id: stage.id) } + let(:user) { developer } let(:service) do diff --git a/spec/services/ci/stop_environments_service_spec.rb b/spec/services/ci/stop_environments_service_spec.rb index 5a0b7f23556..d5ef67c871c 100644 --- a/spec/services/ci/stop_environments_service_spec.rb +++ b/spec/services/ci/stop_environments_service_spec.rb @@ -188,6 +188,7 @@ RSpec.describe Ci::StopEnvironmentsService do let_it_be(:project) { create(:project, :repository) } let_it_be(:user) { create(:user) } + let(:environments) { Environment.available } before_all do |