diff options
Diffstat (limited to 'spec/services/ci')
8 files changed, 333 insertions, 54 deletions
diff --git a/spec/services/ci/job_artifacts/create_service_spec.rb b/spec/services/ci/job_artifacts/create_service_spec.rb index 7e471bf39a1..a23ba250daf 100644 --- a/spec/services/ci/job_artifacts/create_service_spec.rb +++ b/spec/services/ci/job_artifacts/create_service_spec.rb @@ -321,6 +321,45 @@ RSpec.describe Ci::JobArtifacts::CreateService, :clean_gitlab_redis_shared_state end end + shared_examples_for 'handling annotations' do |storage_type| + context 'when artifact type is annotations' do + let(:params) do + { + 'artifact_type' => 'annotations', + 'artifact_format' => 'gzip' + }.with_indifferent_access + end + + if storage_type == :object_storage + let(:object_body) { File.read('spec/fixtures/gl-annotations.json.gz') } + let(:upload_filename) { 'gl-annotations.json.gz' } + + before do + stub_request(:get, %r{s3.amazonaws.com/#{remote_path}}) + .to_return(status: 200, body: File.read('spec/fixtures/gl-annotations.json.gz')) + end + else + let(:artifacts_file) do + file_to_upload('spec/fixtures/gl-annotations.json.gz', sha256: artifacts_sha256) + end + end + + it 'calls parse service' do + expect_any_instance_of(Ci::ParseAnnotationsArtifactService) do |service| + expect(service).to receive(:execute).once.and_call_original + end + + expect(execute[:status]).to eq(:success) + expect(job.job_annotations.as_json).to contain_exactly( + hash_including('name' => 'external_links', 'data' => [ + hash_including('external_link' => hash_including('label' => 'URL 1', 'url' => 'https://url1.example.com/')), + hash_including('external_link' => hash_including('label' => 'URL 2', 'url' => 'https://url2.example.com/')) + ]) + ) + end + end + end + shared_examples_for 'handling object storage errors' do shared_examples 'rescues object storage error' do |klass, message, expected_message| it "handles #{klass}" do @@ -495,6 +534,7 @@ RSpec.describe Ci::JobArtifacts::CreateService, :clean_gitlab_redis_shared_state it_behaves_like 'handling uploads' it_behaves_like 'handling dotenv', :object_storage + it_behaves_like 'handling annotations', :object_storage it_behaves_like 'handling object storage errors' it_behaves_like 'validating requirements' end @@ -506,6 +546,7 @@ RSpec.describe Ci::JobArtifacts::CreateService, :clean_gitlab_redis_shared_state it_behaves_like 'handling uploads' it_behaves_like 'handling dotenv', :local_storage + it_behaves_like 'handling annotations', :local_storage it_behaves_like 'validating requirements' end end diff --git a/spec/services/ci/parse_annotations_artifact_service_spec.rb b/spec/services/ci/parse_annotations_artifact_service_spec.rb new file mode 100644 index 00000000000..4847447230b --- /dev/null +++ b/spec/services/ci/parse_annotations_artifact_service_spec.rb @@ -0,0 +1,182 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Ci::ParseAnnotationsArtifactService, feature_category: :build_artifacts do + let_it_be(:project) { create(:project) } + + let_it_be_with_reload(:build) { create(:ci_build, project: project) } + let(:service) { described_class.new(project, nil) } + + describe '#execute' do + subject { service.execute(artifact) } + + context 'when build has an annotations artifact' do + let_it_be(:artifact) { create(:ci_job_artifact, :annotations, job: build) } + + context 'when artifact does not have the specified blob' do + before do + allow(artifact).to receive(:each_blob) + end + + it 'parses nothing' do + expect(subject[:status]).to eq(:success) + + expect(build.job_annotations).to be_empty + end + end + + context 'when artifact has the specified blob' do + let(:blob) { data.to_json } + + before do + allow(artifact).to receive(:each_blob).and_yield(blob) + end + + context 'when valid annotations are given' do + let(:data) do + { + external_links: [ + { + external_link: { + label: 'URL 1', + url: 'https://url1.example.com/' + } + }, + { + external_link: { + label: 'URL 2', + url: 'https://url2.example.com/' + } + } + ] + } + end + + it 'parses the artifact' do + subject + + expect(build.job_annotations.as_json).to contain_exactly( + hash_including('name' => 'external_links', 'data' => [ + hash_including('external_link' => hash_including('label' => 'URL 1', 'url' => 'https://url1.example.com/')), + hash_including('external_link' => hash_including('label' => 'URL 2', 'url' => 'https://url2.example.com/')) + ]) + ) + end + end + + context 'when valid annotations are given and annotation list name is the same' do + before do + build.job_annotations.create!(name: 'external_links', data: [ + { + external_link: { + label: 'URL 1', + url: 'https://url1.example.com/' + } + } + ]) + end + + let(:data) do + { + external_links: [ + { + external_link: { + label: 'URL 2', + url: 'https://url2.example.com/' + } + } + ] + } + end + + it 'parses the artifact' do + subject + + expect(build.job_annotations.as_json).to contain_exactly( + hash_including('name' => 'external_links', 'data' => [ + hash_including('external_link' => hash_including('label' => 'URL 2', 'url' => 'https://url2.example.com/')) + ]) + ) + end + end + + context 'when invalid JSON is given' do + let(:blob) { 'Invalid JSON!' } + + it 'returns error' do + expect(subject[:status]).to eq(:error) + expect(subject[:http_status]).to eq(:bad_request) + end + end + + context 'when root is not an object' do + let(:data) { [] } + + it 'returns error' do + expect(subject[:status]).to eq(:error) + expect(subject[:message]).to eq('Annotations files must be a JSON object') + expect(subject[:http_status]).to eq(:bad_request) + end + end + + context 'when item is not a valid annotation list' do + let(:data) { { external_links: {} } } + + it 'returns error' do + expect(subject[:status]).to eq(:error) + expect(subject[:message]).to eq('Validation failed: Data must be a valid json schema') + expect(subject[:http_status]).to eq(:bad_request) + end + end + + context 'when more than limitated annotations are specified in annotations' do + let(:data) do + { + external_links_1: [ + { + external_link: { + label: 'URL', + url: 'https://example.com/' + } + } + ], + external_links_2: [ + { + external_link: { + label: 'URL', + url: 'https://example.com/' + } + } + ] + } + end + + before do + allow(service).to receive(:annotations_num_limit).and_return(1) + end + + it 'returns error' do + expect(subject[:status]).to eq(:error) + expect(subject[:message]).to eq( + "Annotations files cannot have more than #{service.send(:annotations_num_limit)} annotation lists") + expect(subject[:http_status]).to eq(:bad_request) + end + end + end + + context 'when artifact size is too big' do + before do + allow(artifact.file).to receive(:size) { service.send(:annotations_size_limit) + 1.kilobyte } + end + + it 'returns error' do + expect(subject[:status]).to eq(:error) + expect(subject[:message]).to eq( + "Annotations Artifact Too Big. Maximum Allowable Size: #{service.send(:annotations_size_limit)}") + expect(subject[:http_status]).to eq(:bad_request) + end + end + end + end +end diff --git a/spec/services/ci/pipeline_creation/cancel_redundant_pipelines_service_spec.rb b/spec/services/ci/pipeline_creation/cancel_redundant_pipelines_service_spec.rb index 905ccf164ca..82a8e425cd0 100644 --- a/spec/services/ci/pipeline_creation/cancel_redundant_pipelines_service_spec.rb +++ b/spec/services/ci/pipeline_creation/cancel_redundant_pipelines_service_spec.rb @@ -20,6 +20,38 @@ RSpec.describe Ci::PipelineCreation::CancelRedundantPipelinesService, feature_ca create(:ci_build, :interruptible, pipeline: pipeline) end + shared_examples 'time limits pipeline cancellation' do + context 'with old pipelines' do + let(:old_pipeline) { create(:ci_pipeline, project: project, created_at: 5.days.ago) } + + before do + create(:ci_build, :interruptible, :pending, pipeline: old_pipeline) + end + + it 'ignores old pipelines' do + execute + + expect(build_statuses(prev_pipeline)).to contain_exactly('canceled', 'success', 'canceled') + expect(build_statuses(pipeline)).to contain_exactly('pending') + expect(build_statuses(old_pipeline)).to contain_exactly('pending') + end + + context 'with lower_interval_for_canceling_redundant_pipelines disabled' do + before do + stub_feature_flags(lower_interval_for_canceling_redundant_pipelines: false) + end + + it 'cancels pipelines created more than 3 days ago' do + execute + + expect(build_statuses(prev_pipeline)).to contain_exactly('canceled', 'success', 'canceled') + expect(build_statuses(pipeline)).to contain_exactly('pending') + expect(build_statuses(old_pipeline)).to contain_exactly('canceled') + end + end + end + end + describe '#execute!' do subject(:execute) { service.execute } @@ -218,6 +250,8 @@ RSpec.describe Ci::PipelineCreation::CancelRedundantPipelinesService, feature_ca expect(build_statuses(pipeline.reload)).to contain_exactly('pending') end + + it_behaves_like 'time limits pipeline cancellation' end context 'when auto-cancel is disabled' do @@ -452,6 +486,8 @@ RSpec.describe Ci::PipelineCreation::CancelRedundantPipelinesService, feature_ca expect(build_statuses(pipeline.reload)).to contain_exactly('pending') end + + it_behaves_like 'time limits pipeline cancellation' end context 'when auto-cancel is disabled' 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 15f2cc0990c..93dc9481bf0 100644 --- a/spec/services/ci/pipeline_processing/atomic_processing_service_spec.rb +++ b/spec/services/ci/pipeline_processing/atomic_processing_service_spec.rb @@ -4,6 +4,7 @@ require 'spec_helper' RSpec.describe Ci::PipelineProcessing::AtomicProcessingService, feature_category: :continuous_integration do include RepoHelpers + include ExclusiveLeaseHelpers describe 'Pipeline Processing Service Tests With Yaml' do let_it_be(:project) { create(:project, :repository) } @@ -1233,6 +1234,19 @@ RSpec.describe Ci::PipelineProcessing::AtomicProcessingService, feature_category end end + context 'when the exclusive lease is taken' do + let(:lease_key) { "ci/pipeline_processing/atomic_processing_service::pipeline_id:#{pipeline.id}" } + + it 'skips pipeline processing' do + create_build('linux', stage_idx: 0) + + stub_exclusive_lease_taken(lease_key) + + expect(Gitlab::AppJsonLogger).to receive(:info).with(a_hash_including(message: /^Cannot obtain an exclusive lease/)) + expect(process_pipeline).to be_falsy + end + end + private def all_builds diff --git a/spec/services/ci/pipeline_schedules/create_service_spec.rb b/spec/services/ci/pipeline_schedules/create_service_spec.rb index a01c71432c3..3fc093c13da 100644 --- a/spec/services/ci/pipeline_schedules/create_service_spec.rb +++ b/spec/services/ci/pipeline_schedules/create_service_spec.rb @@ -3,9 +3,11 @@ require 'spec_helper' RSpec.describe Ci::PipelineSchedules::CreateService, feature_category: :continuous_integration do - let_it_be(:user) { create(:user) } let_it_be(:reporter) { create(:user) } - let_it_be(:project) { create(:project, :public, :repository) } + let_it_be_with_reload(:user) { create(:user) } + let_it_be_with_reload(:project) { create(:project, :public, :repository) } + + subject(:service) { described_class.new(project, user, params) } before_all do project.add_maintainer(user) @@ -82,5 +84,7 @@ RSpec.describe Ci::PipelineSchedules::CreateService, feature_category: :continuo end end end + + it_behaves_like 'pipeline schedules checking variables permission' end end diff --git a/spec/services/ci/pipeline_schedules/update_service_spec.rb b/spec/services/ci/pipeline_schedules/update_service_spec.rb index 5c1354bd5aa..834bbcfcfeb 100644 --- a/spec/services/ci/pipeline_schedules/update_service_spec.rb +++ b/spec/services/ci/pipeline_schedules/update_service_spec.rb @@ -3,18 +3,18 @@ require 'spec_helper' RSpec.describe Ci::PipelineSchedules::UpdateService, feature_category: :continuous_integration do - let_it_be(:user) { create(:user) } + let_it_be_with_reload(:user) { create(:user) } + let_it_be_with_reload(:project) { create(:project, :public, :repository) } + let_it_be_with_reload(:pipeline_schedule) { create(:ci_pipeline_schedule, project: project, owner: user) } let_it_be(:reporter) { create(:user) } - let_it_be(:project) { create(:project, :public, :repository) } - let_it_be(:pipeline_schedule) do - create(:ci_pipeline_schedule, project: project, owner: user, ref: 'master') - end let_it_be(:pipeline_schedule_variable) do create(:ci_pipeline_schedule_variable, key: 'foo', value: 'foovalue', pipeline_schedule: pipeline_schedule) end + subject(:service) { described_class.new(pipeline_schedule, user, params) } + before_all do project.add_maintainer(user) project.add_reporter(reporter) @@ -125,5 +125,7 @@ RSpec.describe Ci::PipelineSchedules::UpdateService, feature_category: :continuo end end end + + it_behaves_like 'pipeline schedules checking variables permission' end end diff --git a/spec/services/ci/retry_job_service_spec.rb b/spec/services/ci/retry_job_service_spec.rb index f15f4a16d4f..caed9815fb3 100644 --- a/spec/services/ci/retry_job_service_spec.rb +++ b/spec/services/ci/retry_job_service_spec.rb @@ -208,6 +208,45 @@ RSpec.describe Ci::RetryJobService, feature_category: :continuous_integration do end end + shared_examples_for 'creates associations for a deployable job' do |factory_type| + context 'when a job with a deployment is retried' do + let!(:job) do + create(factory_type, :with_deployment, :deploy_to_production, pipeline: pipeline, ci_stage: stage) + end + + it 'creates a new deployment' do + expect { new_job }.to change { Deployment.count }.by(1) + end + + it 'does not create a new environment' do + expect { new_job }.not_to change { Environment.count } + end + end + + context 'when a job with a dynamic environment is retried' do + let_it_be(:other_developer) { create(:user).tap { |u| project.add_developer(u) } } + + let(:environment_name) { 'review/$CI_COMMIT_REF_SLUG-$GITLAB_USER_ID' } + + let!(:job) do + create(factory_type, :with_deployment, + environment: environment_name, + options: { environment: { name: environment_name } }, + pipeline: pipeline, + ci_stage: stage, + user: other_developer) + end + + it 'creates a new deployment' do + expect { new_job }.to change { Deployment.count }.by(1) + end + + it 'does not create a new environment' do + expect { new_job }.not_to change { Environment.count } + end + end + end + describe '#clone!' do let(:new_job) { service.clone!(job) } @@ -219,6 +258,7 @@ RSpec.describe Ci::RetryJobService, feature_category: :continuous_integration do include_context 'retryable bridge' it_behaves_like 'clones the job' + it_behaves_like 'creates associations for a deployable job', :ci_bridge context 'when given variables' do let(:new_job) { service.clone!(job, variables: job_variables_attributes) } @@ -235,43 +275,7 @@ RSpec.describe Ci::RetryJobService, feature_category: :continuous_integration do let(:job) { job_to_clone } it_behaves_like 'clones the job' - - context 'when a build with a deployment is retried' do - let!(:job) do - create(:ci_build, :with_deployment, :deploy_to_production, pipeline: pipeline, ci_stage: stage) - end - - it 'creates a new deployment' do - expect { new_job }.to change { Deployment.count }.by(1) - end - - it 'does not create a new environment' do - expect { new_job }.not_to change { Environment.count } - end - end - - context 'when a build with a dynamic environment is retried' do - let_it_be(:other_developer) { create(:user).tap { |u| project.add_developer(u) } } - - let(:environment_name) { 'review/$CI_COMMIT_REF_SLUG-$GITLAB_USER_ID' } - - let!(:job) do - create(:ci_build, :with_deployment, - environment: environment_name, - options: { environment: { name: environment_name } }, - pipeline: pipeline, - ci_stage: stage, - user: other_developer) - end - - it 'creates a new deployment' do - expect { new_job }.to change { Deployment.count }.by(1) - end - - it 'does not create a new environment' do - expect { new_job }.not_to change { Environment.count } - end - end + it_behaves_like 'creates associations for a deployable job', :ci_build context 'when given variables' do let(:new_job) { service.clone!(job, variables: job_variables_attributes) } diff --git a/spec/services/ci/retry_pipeline_service_spec.rb b/spec/services/ci/retry_pipeline_service_spec.rb index fc2c66e7f73..6d991baafd0 100644 --- a/spec/services/ci/retry_pipeline_service_spec.rb +++ b/spec/services/ci/retry_pipeline_service_spec.rb @@ -451,22 +451,18 @@ RSpec.describe Ci::RetryPipelineService, '#execute', feature_category: :continuo before do project.add_maintainer(user) - create(:merge_request, - source_project: forked_project, - target_project: project, - source_branch: 'fixes', - allow_collaboration: true) - create_build('rspec 1', :failed, test_stage) - end - it 'allows to retry failed pipeline' do - allow_any_instance_of(Project).to receive(:branch_allows_collaboration?).and_return(true) + create_build('rspec 1', :failed, test_stage, project: project, ref: pipeline.ref) + allow_any_instance_of(Project).to receive(:empty_repo?).and_return(false) + allow_any_instance_of(Project).to receive(:branch_allows_collaboration?).and_return(true) + end + it 'allows to retry failed pipeline' do service.execute(pipeline) expect(build('rspec 1')).to be_pending - expect(pipeline.reload).to be_running + expect(pipeline).to be_running end end |