diff options
Diffstat (limited to 'spec/services/ci')
16 files changed, 558 insertions, 113 deletions
diff --git a/spec/services/ci/abort_pipelines_service_spec.rb b/spec/services/ci/abort_pipelines_service_spec.rb index e31a45cb123..db25faff70f 100644 --- a/spec/services/ci/abort_pipelines_service_spec.rb +++ b/spec/services/ci/abort_pipelines_service_spec.rb @@ -7,24 +7,51 @@ RSpec.describe Ci::AbortPipelinesService do let_it_be(:project) { create(:project, namespace: user.namespace) } let_it_be(:cancelable_pipeline, reload: true) { create(:ci_pipeline, :running, project: project, user: user) } - let_it_be(:manual_pipeline, reload: true) { create(:ci_pipeline, status: :manual, project: project, user: user) } # not cancelable + let_it_be(:manual_pipeline, reload: true) { create(:ci_pipeline, status: :manual, project: project, user: user) } let_it_be(:other_users_pipeline, reload: true) { create(:ci_pipeline, :running, project: project, user: create(:user)) } # not this user's pipeline + let_it_be(:cancelable_build, reload: true) { create(:ci_build, :running, pipeline: cancelable_pipeline) } let_it_be(:non_cancelable_build, reload: true) { create(:ci_build, :success, pipeline: cancelable_pipeline) } let_it_be(:cancelable_stage, reload: true) { create(:ci_stage_entity, name: 'stageA', status: :running, pipeline: cancelable_pipeline, project: project) } let_it_be(:non_cancelable_stage, reload: true) { create(:ci_stage_entity, name: 'stageB', status: :success, pipeline: cancelable_pipeline, project: project) } + let_it_be(:manual_pipeline_cancelable_build, reload: true) { create(:ci_build, :created, pipeline: manual_pipeline) } + let_it_be(:manual_pipeline_non_cancelable_build, reload: true) { create(:ci_build, :manual, pipeline: manual_pipeline) } + let_it_be(:manual_pipeline_cancelable_stage, reload: true) { create(:ci_stage_entity, name: 'stageA', status: :created, pipeline: manual_pipeline, project: project) } + let_it_be(:manual_pipeline_non_cancelable_stage, reload: true) { create(:ci_stage_entity, name: 'stageB', status: :success, pipeline: manual_pipeline, project: project) } + describe '#execute' do - def expect_correct_cancellations + def expect_correct_pipeline_cancellations expect(cancelable_pipeline.finished_at).not_to be_nil - expect(cancelable_pipeline.status).to eq('failed') - expect((cancelable_pipeline.stages - [non_cancelable_stage]).map(&:status)).to all(eq('failed')) - expect(cancelable_build.status).to eq('failed') + expect(cancelable_pipeline).to be_failed + + expect(manual_pipeline.finished_at).not_to be_nil + expect(manual_pipeline).to be_failed + end + + def expect_correct_stage_cancellations + expect(cancelable_pipeline.stages - [non_cancelable_stage]).to all(be_failed) + expect(manual_pipeline.stages - [manual_pipeline_non_cancelable_stage]).to all(be_failed) + + expect(non_cancelable_stage).not_to be_failed + expect(manual_pipeline_non_cancelable_stage).not_to be_failed + end + + def expect_correct_build_cancellations + expect(cancelable_build).to be_failed expect(cancelable_build.finished_at).not_to be_nil - expect(manual_pipeline.status).not_to eq('failed') - expect(non_cancelable_stage.status).not_to eq('failed') - expect(non_cancelable_build.status).not_to eq('failed') + expect(manual_pipeline_cancelable_build).to be_failed + expect(manual_pipeline_cancelable_build.finished_at).not_to be_nil + + expect(non_cancelable_build).not_to be_failed + expect(manual_pipeline_non_cancelable_build).not_to be_failed + end + + def expect_correct_cancellations + expect_correct_pipeline_cancellations + expect_correct_stage_cancellations + expect_correct_build_cancellations end context 'with project pipelines' do diff --git a/spec/services/ci/after_requeue_job_service_spec.rb b/spec/services/ci/after_requeue_job_service_spec.rb index d2acf3ad2f1..2f2baa15945 100644 --- a/spec/services/ci/after_requeue_job_service_spec.rb +++ b/spec/services/ci/after_requeue_job_service_spec.rb @@ -2,69 +2,236 @@ require 'spec_helper' -RSpec.describe Ci::AfterRequeueJobService do - let_it_be(:project) { create(:project) } +RSpec.describe Ci::AfterRequeueJobService, :sidekiq_inline do + let_it_be(:project) { create(:project, :empty_repo) } let_it_be(:user) { project.first_owner } - let(:pipeline) { create(:ci_pipeline, project: project) } + before_all do + project.repository.create_file(user, 'init', 'init', message: 'init', branch_name: 'master') + end - let!(:build1) { create(:ci_build, name: 'build1', pipeline: pipeline, stage_idx: 0) } - let!(:test1) { create(:ci_build, :success, name: 'test1', pipeline: pipeline, stage_idx: 1) } - let!(:test2) { create(:ci_build, :skipped, name: 'test2', pipeline: pipeline, stage_idx: 1) } - let!(:test3) { create(:ci_build, :skipped, :dependent, name: 'test3', pipeline: pipeline, stage_idx: 1, needed: build1) } - let!(:deploy) { create(:ci_build, :skipped, :dependent, name: 'deploy', pipeline: pipeline, stage_idx: 2, needed: test3) } + subject(:service) { described_class.new(project, user) } - subject(:execute_service) { described_class.new(project, user).execute(build1) } + context 'stage-dag mixed pipeline' do + let(:config) do + <<-EOY + stages: [a, b, c] - shared_examples 'processing subsequent skipped jobs' do - it 'marks subsequent skipped jobs as processable' do - expect(test1.reload).to be_success - expect(test2.reload).to be_skipped - expect(test3.reload).to be_skipped - expect(deploy.reload).to be_skipped + a1: + stage: a + script: exit $(($RANDOM % 2)) + + a2: + stage: a + script: exit 0 + needs: [a1] - execute_service + b1: + stage: b + script: exit 0 + needs: [] - expect(test1.reload).to be_success - expect(test2.reload).to be_created - expect(test3.reload).to be_created - expect(deploy.reload).to be_created + b2: + stage: b + script: exit 0 + needs: [a2] + + c1: + stage: c + script: exit 0 + needs: [b2] + + c2: + stage: c + script: exit 0 + EOY end - end - it_behaves_like 'processing subsequent skipped jobs' - - context 'when there is a job need from the same stage' do - let!(:build2) do - create(:ci_build, - :skipped, - :dependent, - name: 'build2', - pipeline: pipeline, - stage_idx: 0, - scheduling_type: :dag, - needed: build1) + let(:pipeline) do + Ci::CreatePipelineService.new(project, user, { ref: 'master' }).execute(:push).payload end - shared_examples 'processing the same stage job' do - it 'marks subsequent skipped jobs as processable' do - expect { execute_service }.to change { build2.reload.status }.from('skipped').to('created') - end + let(:a1) { find_job('a1') } + let(:b1) { find_job('b1') } + + before do + stub_ci_pipeline_yaml_file(config) + check_jobs_statuses( + a1: 'pending', + a2: 'created', + b1: 'pending', + b2: 'created', + c1: 'created', + c2: 'created' + ) + + b1.success! + check_jobs_statuses( + a1: 'pending', + a2: 'created', + b1: 'success', + b2: 'created', + c1: 'created', + c2: 'created' + ) + + a1.drop! + check_jobs_statuses( + a1: 'failed', + a2: 'skipped', + b1: 'success', + b2: 'skipped', + c1: 'skipped', + c2: 'skipped' + ) + + new_a1 = Ci::RetryBuildService.new(project, user).clone!(a1) + new_a1.enqueue! + check_jobs_statuses( + a1: 'pending', + a2: 'skipped', + b1: 'success', + b2: 'skipped', + c1: 'skipped', + c2: 'skipped' + ) end - it_behaves_like 'processing subsequent skipped jobs' - it_behaves_like 'processing the same stage job' + it 'marks subsequent skipped jobs as processable' do + execute_after_requeue_service(a1) + + check_jobs_statuses( + a1: 'pending', + a2: 'created', + b1: 'success', + b2: 'created', + c1: 'created', + c2: 'created' + ) + end end - context 'when the pipeline is a downstream pipeline and the bridge is depended' do - let!(:trigger_job) { create(:ci_bridge, :strategy_depend, name: 'trigger_job', status: 'success') } + context 'stage-dag mixed pipeline with some same-stage needs' do + let(:config) do + <<-EOY + stages: [a, b, c] + + a1: + stage: a + script: exit $(($RANDOM % 2)) + + a2: + stage: a + script: exit 0 + needs: [a1] + + b1: + stage: b + script: exit 0 + needs: [b2] + + b2: + stage: b + script: exit 0 + + c1: + stage: c + script: exit 0 + needs: [b2] + + c2: + stage: c + script: exit 0 + EOY + end + + let(:pipeline) do + Ci::CreatePipelineService.new(project, user, { ref: 'master' }).execute(:push).payload + end + + let(:a1) { find_job('a1') } before do - create(:ci_sources_pipeline, pipeline: pipeline, source_job: trigger_job) + stub_ci_pipeline_yaml_file(config) + check_jobs_statuses( + a1: 'pending', + a2: 'created', + b1: 'created', + b2: 'created', + c1: 'created', + c2: 'created' + ) + + a1.drop! + check_jobs_statuses( + a1: 'failed', + a2: 'skipped', + b1: 'skipped', + b2: 'skipped', + c1: 'skipped', + c2: 'skipped' + ) + + new_a1 = Ci::RetryBuildService.new(project, user).clone!(a1) + new_a1.enqueue! + check_jobs_statuses( + a1: 'pending', + a2: 'skipped', + b1: 'skipped', + b2: 'skipped', + c1: 'skipped', + c2: 'skipped' + ) end - it 'marks source bridge as pending' do - expect { execute_service }.to change { trigger_job.reload.status }.from('success').to('pending') + it 'marks subsequent skipped jobs as processable' do + execute_after_requeue_service(a1) + + check_jobs_statuses( + a1: 'pending', + a2: 'created', + b1: 'created', + b2: 'created', + c1: 'created', + c2: 'created' + ) + end + + context 'when the FF ci_fix_order_of_subsequent_jobs is disabled' do + before do + stub_feature_flags(ci_fix_order_of_subsequent_jobs: false) + end + + it 'does not mark b1 as processable' do + execute_after_requeue_service(a1) + + check_jobs_statuses( + a1: 'pending', + a2: 'created', + b1: 'skipped', + b2: 'created', + c1: 'created', + c2: 'created' + ) + end end end + + private + + def find_job(name) + processables.find_by!(name: name) + end + + def check_jobs_statuses(statuses) + expect(processables.order(:name).pluck(:name, :status)).to contain_exactly(*statuses.stringify_keys.to_a) + end + + def processables + pipeline.processables.latest + end + + def execute_after_requeue_service(processable) + service.execute(processable) + end end diff --git a/spec/services/ci/create_downstream_pipeline_service_spec.rb b/spec/services/ci/create_downstream_pipeline_service_spec.rb index 43eb57df66c..6142704b00e 100644 --- a/spec/services/ci/create_downstream_pipeline_service_spec.rb +++ b/spec/services/ci/create_downstream_pipeline_service_spec.rb @@ -485,14 +485,6 @@ RSpec.describe Ci::CreateDownstreamPipelineService, '#execute' do end it_behaves_like 'detects cyclical pipelines' - - context 'when ci_drop_cyclical_triggered_pipelines is not enabled' do - before do - stub_feature_flags(ci_drop_cyclical_triggered_pipelines: false) - end - - it_behaves_like 'passes cyclical pipeline precondition' - end end context 'when source in the ancestry differ' do diff --git a/spec/services/ci/create_pipeline_service/artifacts_spec.rb b/spec/services/ci/create_pipeline_service/artifacts_spec.rb new file mode 100644 index 00000000000..1ec30d68666 --- /dev/null +++ b/spec/services/ci/create_pipeline_service/artifacts_spec.rb @@ -0,0 +1,67 @@ +# frozen_string_literal: true +require 'spec_helper' + +RSpec.describe Ci::CreatePipelineService do + let_it_be(:project) { create(:project, :repository) } + let_it_be(:user) { project.first_owner } + + let(:ref) { 'refs/heads/master' } + let(:source) { :push } + + let(:service) { described_class.new(project, user, { ref: ref }) } + let(:pipeline) { service.execute(source).payload } + + describe 'artifacts:' do + before do + stub_ci_pipeline_yaml_file(config) + allow_next_instance_of(Ci::BuildScheduleWorker) do |instance| + allow(instance).to receive(:perform).and_return(true) + end + end + + describe 'reports:' do + context 'with valid config' do + let(:config) do + <<~YAML + test-job: + script: "echo 'hello world' > cobertura.xml" + artifacts: + reports: + coverage_report: + coverage_format: 'cobertura' + path: 'cobertura.xml' + + dependency-scanning-job: + script: "echo 'hello world' > gl-dependency-scanning-report.json" + artifacts: + reports: + dependency_scanning: 'gl-dependency-scanning-report.json' + YAML + end + + it 'creates pipeline with builds' do + expect(pipeline).to be_persisted + expect(pipeline).not_to have_yaml_errors + expect(pipeline.builds.pluck(:name)).to contain_exactly('test-job', 'dependency-scanning-job') + end + end + + context 'with invalid config' do + let(:config) do + <<~YAML + test-job: + script: "echo 'hello world' > cobertura.xml" + artifacts: + reports: + foo: 'bar' + YAML + end + + it 'creates pipeline with yaml errors' do + expect(pipeline).to be_persisted + expect(pipeline).to have_yaml_errors + end + end + end + end +end 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 c28bc9d8c13..f593707f460 100644 --- a/spec/services/ci/create_pipeline_service/parameter_content_spec.rb +++ b/spec/services/ci/create_pipeline_service/parameter_content_spec.rb @@ -15,7 +15,7 @@ RSpec.describe Ci::CreatePipelineService do variables: DAST_VERSION: 1 - SECURE_ANALYZERS_PREFIX: "registry.gitlab.com/gitlab-org/security-products/analyzers" + SECURE_ANALYZERS_PREFIX: "registry.gitlab.com/security-products" dast: stage: dast diff --git a/spec/services/ci/create_pipeline_service/tags_spec.rb b/spec/services/ci/create_pipeline_service/tags_spec.rb index 61c2415fa33..0774f9fff2a 100644 --- a/spec/services/ci/create_pipeline_service/tags_spec.rb +++ b/spec/services/ci/create_pipeline_service/tags_spec.rb @@ -81,31 +81,6 @@ RSpec.describe Ci::CreatePipelineService do end end - context 'when the feature flag is disabled' do - before do - stub_feature_flags(ci_bulk_insert_tags: false) - end - - it 'executes N+1s queries' do - stub_yaml_config(config_without_tags) - - # warm up the cached objects so we get a more accurate count - create_pipeline - - control = ActiveRecord::QueryRecorder.new(skip_cached: false) do - create_pipeline - end - - stub_yaml_config(config) - - expect { pipeline } - .to exceed_all_query_limit(control) - .with_threshold(4) - - expect(pipeline).to be_created_successfully - end - end - context 'when tags are already persisted' do it 'does not execute N+1 queries' do # warm up the cached objects so we get a more accurate count diff --git a/spec/services/ci/destroy_secure_file_service_spec.rb b/spec/services/ci/destroy_secure_file_service_spec.rb new file mode 100644 index 00000000000..6a30d33f4ca --- /dev/null +++ b/spec/services/ci/destroy_secure_file_service_spec.rb @@ -0,0 +1,32 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe ::Ci::DestroySecureFileService do + let_it_be(:maintainer_user) { create(:user) } + let_it_be(:developer_user) { create(:user) } + let_it_be(:project) { create(:project) } + let_it_be(:secure_file) { create(:ci_secure_file, project: project) } + let_it_be(:project_member) { create(:project_member, :maintainer, user: maintainer_user, project: project) } + let_it_be(:project_member2) { create(:project_member, :developer, user: developer_user, project: project) } + + subject { described_class.new(project, user).execute(secure_file) } + + context 'user is a maintainer' do + let(:user) { maintainer_user } + + it 'destroys the secure file' do + subject + + expect { secure_file.reload }.to raise_error(ActiveRecord::RecordNotFound) + end + end + + context 'user is a developer' do + let(:user) { developer_user } + + it 'raises an exception' do + expect { subject }.to raise_error(Gitlab::Access::AccessDeniedError) + end + end +end diff --git a/spec/services/ci/job_artifacts/create_service_spec.rb b/spec/services/ci/job_artifacts/create_service_spec.rb index 2d309bfe425..b8487e438a9 100644 --- a/spec/services/ci/job_artifacts/create_service_spec.rb +++ b/spec/services/ci/job_artifacts/create_service_spec.rb @@ -175,7 +175,7 @@ RSpec.describe Ci::JobArtifacts::CreateService do end expect(subject[:status]).to eq(:success) - expect(job.job_variables.as_json).to contain_exactly( + expect(job.job_variables.as_json(only: [:key, :value, :source])).to contain_exactly( hash_including('key' => 'KEY1', 'value' => 'VAR1', 'source' => 'dotenv'), hash_including('key' => 'KEY2', 'value' => 'VAR2', 'source' => 'dotenv')) end diff --git a/spec/services/ci/parse_dotenv_artifact_service_spec.rb b/spec/services/ci/parse_dotenv_artifact_service_spec.rb index 6bf22b7c8b2..aaab849cd93 100644 --- a/spec/services/ci/parse_dotenv_artifact_service_spec.rb +++ b/spec/services/ci/parse_dotenv_artifact_service_spec.rb @@ -18,7 +18,7 @@ RSpec.describe Ci::ParseDotenvArtifactService do it 'parses the artifact' do expect(subject[:status]).to eq(:success) - expect(build.job_variables.as_json).to contain_exactly( + expect(build.job_variables.as_json(only: [:key, :value])).to contain_exactly( hash_including('key' => 'KEY1', 'value' => 'VAR1'), hash_including('key' => 'KEY2', 'value' => 'VAR2')) end @@ -57,7 +57,7 @@ RSpec.describe Ci::ParseDotenvArtifactService do expect(subject[:status]).to eq(:success) - expect(build.job_variables.as_json).to contain_exactly( + expect(build.job_variables.as_json(only: [:key, :value])).to contain_exactly( hash_including('key' => 'KEY1', 'value' => 'VAR4'), hash_including('key' => 'KEY2', 'value' => 'VAR3')) end @@ -101,7 +101,7 @@ RSpec.describe Ci::ParseDotenvArtifactService do it 'trims the trailing space' do subject - expect(build.job_variables.as_json).to contain_exactly( + expect(build.job_variables.as_json(only: [:key, :value])).to contain_exactly( hash_including('key' => 'KEY1', 'value' => 'VAR1')) end end @@ -112,7 +112,7 @@ RSpec.describe Ci::ParseDotenvArtifactService do it 'parses the dotenv data' do subject - expect(build.job_variables.as_json).to contain_exactly( + expect(build.job_variables.as_json(only: [:key, :value])).to contain_exactly( hash_including('key' => 'KEY', 'value' => 'VARCONTAINING=EQLS')) end end @@ -133,7 +133,7 @@ RSpec.describe Ci::ParseDotenvArtifactService do it 'parses the dotenv data' do subject - expect(build.job_variables.as_json).to contain_exactly( + expect(build.job_variables.as_json(only: [:key, :value])).to contain_exactly( hash_including('key' => 'skateboard', 'value' => '🛹')) end end @@ -154,7 +154,7 @@ RSpec.describe Ci::ParseDotenvArtifactService do it 'parses the dotenv data' do subject - expect(build.job_variables.as_json).to contain_exactly( + expect(build.job_variables.as_json(only: [:key, :value])).to contain_exactly( hash_including('key' => 'KEY1', 'value' => 'V A R 1')) end end @@ -165,7 +165,7 @@ RSpec.describe Ci::ParseDotenvArtifactService do it 'parses the value as-is' do subject - expect(build.job_variables.as_json).to contain_exactly( + expect(build.job_variables.as_json(only: [:key, :value])).to contain_exactly( hash_including('key' => 'KEY1', 'value' => '"VAR1"')) end end @@ -176,7 +176,7 @@ RSpec.describe Ci::ParseDotenvArtifactService do it 'parses the value as-is' do subject - expect(build.job_variables.as_json).to contain_exactly( + expect(build.job_variables.as_json(only: [:key, :value])).to contain_exactly( hash_including('key' => 'KEY1', 'value' => "'VAR1'")) end end @@ -187,7 +187,7 @@ RSpec.describe Ci::ParseDotenvArtifactService do it 'parses the value as-is' do subject - expect(build.job_variables.as_json).to contain_exactly( + expect(build.job_variables.as_json(only: [:key, :value])).to contain_exactly( hash_including('key' => 'KEY1', 'value' => '" VAR1 "')) end end @@ -208,7 +208,7 @@ RSpec.describe Ci::ParseDotenvArtifactService do it 'parses the dotenv data' do subject - expect(build.job_variables.as_json).to contain_exactly( + expect(build.job_variables.as_json(only: [:key, :value])).to contain_exactly( hash_including('key' => 'KEY1', 'value' => '')) end end @@ -250,7 +250,7 @@ RSpec.describe Ci::ParseDotenvArtifactService do it 'does not support variable expansion in dotenv parser' do subject - expect(build.job_variables.as_json).to contain_exactly( + expect(build.job_variables.as_json(only: [:key, :value])).to contain_exactly( hash_including('key' => 'KEY1', 'value' => 'VAR1'), hash_including('key' => 'KEY2', 'value' => '${KEY1}_Test')) end @@ -284,7 +284,7 @@ RSpec.describe Ci::ParseDotenvArtifactService do it 'does not support comment in dotenv parser' do subject - expect(build.job_variables.as_json).to contain_exactly( + expect(build.job_variables.as_json(only: [:key, :value])).to contain_exactly( hash_including('key' => 'KEY1', 'value' => 'VAR1 # This is variable')) end end diff --git a/spec/services/ci/retry_pipeline_service_spec.rb b/spec/services/ci/retry_pipeline_service_spec.rb index 12106b70969..df1e159b5c0 100644 --- a/spec/services/ci/retry_pipeline_service_spec.rb +++ b/spec/services/ci/retry_pipeline_service_spec.rb @@ -137,7 +137,7 @@ RSpec.describe Ci::RetryPipelineService, '#execute' do end end - context 'when the last stage was skipepd' do + context 'when the last stage was skipped' do before do create_build('build 1', :success, 0) create_build('test 2', :failed, 1) @@ -336,12 +336,32 @@ RSpec.describe Ci::RetryPipelineService, '#execute' do expect(pipeline.reload).to be_running end end + + context 'when user is not allowed to retry build' do + before do + build = create(:ci_build, pipeline: pipeline, status: :failed) + allow_next_instance_of(Ci::RetryBuildService) do |service| + allow(service).to receive(:can?).with(user, :update_build, build).and_return(false) + end + end + + it 'returns an error' do + response = service.execute(pipeline) + + expect(response.http_status).to eq(:forbidden) + expect(response.errors).to include('403 Forbidden') + expect(pipeline.reload).not_to be_running + end + end end context 'when user is not allowed to retry pipeline' do - it 'raises an error' do - expect { service.execute(pipeline) } - .to raise_error Gitlab::Access::AccessDeniedError + it 'returns an error' do + response = service.execute(pipeline) + + expect(response.http_status).to eq(:forbidden) + expect(response.errors).to include('403 Forbidden') + expect(pipeline.reload).not_to be_running end end @@ -359,9 +379,12 @@ RSpec.describe Ci::RetryPipelineService, '#execute' do create_build('verify', :canceled, 1) end - it 'raises an error' do - expect { service.execute(pipeline) } - .to raise_error Gitlab::Access::AccessDeniedError + it 'returns an error' do + response = service.execute(pipeline) + + expect(response.http_status).to eq(:forbidden) + expect(response.errors).to include('403 Forbidden') + expect(pipeline.reload).not_to be_running end end @@ -372,9 +395,12 @@ RSpec.describe Ci::RetryPipelineService, '#execute' do create_build('verify', :canceled, 2) end - it 'raises an error' do - expect { service.execute(pipeline) } - .to raise_error Gitlab::Access::AccessDeniedError + it 'returns an error' do + response = service.execute(pipeline) + + expect(response.http_status).to eq(:forbidden) + expect(response.errors).to include('403 Forbidden') + expect(pipeline.reload).not_to be_running end end end diff --git a/spec/services/ci/runners/assign_runner_service_spec.rb b/spec/services/ci/runners/assign_runner_service_spec.rb new file mode 100644 index 00000000000..00b176bb759 --- /dev/null +++ b/spec/services/ci/runners/assign_runner_service_spec.rb @@ -0,0 +1,40 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe ::Ci::Runners::AssignRunnerService, '#execute' do + subject { described_class.new(runner, project, user).execute } + + let_it_be(:runner) { create(:ci_runner, :project, projects: [project]) } + let_it_be(:project) { create(:project) } + + context 'without user' do + let(:user) { nil } + + it 'does not call assign_to on runner and returns false' do + expect(runner).not_to receive(:assign_to) + + is_expected.to eq(false) + end + end + + context 'with unauthorized user' do + let(:user) { build(:user) } + + it 'does not call assign_to on runner and returns false' do + expect(runner).not_to receive(:assign_to) + + is_expected.to eq(false) + end + end + + context 'with admin user', :enable_admin_mode do + let(:user) { create_default(:user, :admin) } + + it 'calls assign_to on runner and returns value unchanged' do + expect(runner).to receive(:assign_to).with(project, user).once.and_return('assign_to return value') + + is_expected.to eq('assign_to return value') + end + end +end diff --git a/spec/services/ci/register_runner_service_spec.rb b/spec/services/ci/runners/register_runner_service_spec.rb index 491582bbd13..f43fd823078 100644 --- a/spec/services/ci/register_runner_service_spec.rb +++ b/spec/services/ci/runners/register_runner_service_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe ::Ci::RegisterRunnerService, '#execute' do +RSpec.describe ::Ci::Runners::RegisterRunnerService, '#execute' do let(:registration_token) { 'abcdefg123456' } let(:token) { } let(:args) { {} } diff --git a/spec/services/ci/runners/reset_registration_token_service_spec.rb b/spec/services/ci/runners/reset_registration_token_service_spec.rb new file mode 100644 index 00000000000..c4bfff51cc8 --- /dev/null +++ b/spec/services/ci/runners/reset_registration_token_service_spec.rb @@ -0,0 +1,76 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe ::Ci::Runners::ResetRegistrationTokenService, '#execute' do + subject { described_class.new(scope, current_user).execute } + + let_it_be(:user) { build(:user) } + let_it_be(:admin_user) { create(:user, :admin) } + + shared_examples 'a registration token reset operation' do + context 'without user' do + let(:current_user) { nil } + + it 'does not reset registration token and returns nil' do + expect(scope).not_to receive(token_reset_method_name) + + is_expected.to be_nil + end + end + + context 'with unauthorized user' do + let(:current_user) { user } + + it 'does not reset registration token and returns nil' do + expect(scope).not_to receive(token_reset_method_name) + + is_expected.to be_nil + end + end + + context 'with admin user', :enable_admin_mode do + let(:current_user) { admin_user } + + it 'resets registration token and returns value unchanged' do + expect(scope).to receive(token_reset_method_name).once do + expect(scope).to receive(token_method_name).once.and_return("#{token_method_name} return value") + end + + is_expected.to eq("#{token_method_name} return value") + end + end + end + + context 'with instance scope' do + let_it_be(:scope) { create(:application_setting) } + + before do + allow(ApplicationSetting).to receive(:current).and_return(scope) + allow(ApplicationSetting).to receive(:current_without_cache).and_return(scope) + end + + it_behaves_like 'a registration token reset operation' do + let(:token_method_name) { :runners_registration_token } + let(:token_reset_method_name) { :reset_runners_registration_token! } + end + end + + context 'with group scope' do + let_it_be(:scope) { create(:group) } + + it_behaves_like 'a registration token reset operation' do + let(:token_method_name) { :runners_token } + let(:token_reset_method_name) { :reset_runners_token! } + end + end + + context 'with project scope' do + let_it_be(:scope) { create(:project) } + + it_behaves_like 'a registration token reset operation' do + let(:token_method_name) { :runners_token } + let(:token_reset_method_name) { :reset_runners_token! } + end + end +end diff --git a/spec/services/ci/runners/unassign_runner_service_spec.rb b/spec/services/ci/runners/unassign_runner_service_spec.rb new file mode 100644 index 00000000000..3fb6925f4bd --- /dev/null +++ b/spec/services/ci/runners/unassign_runner_service_spec.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe ::Ci::Runners::UnassignRunnerService, '#execute' do + subject(:service) { described_class.new(runner_project, user).execute } + + let_it_be(:runner) { create(:ci_runner, :project, projects: [project]) } + let_it_be(:project) { create(:project) } + + let(:runner_project) { runner.runner_projects.last } + + context 'without user' do + let(:user) { nil } + + it 'does not destroy runner_project', :aggregate_failures do + expect(runner_project).not_to receive(:destroy) + expect { service }.not_to change { runner.runner_projects.count }.from(1) + + is_expected.to eq(false) + end + end + + context 'with unauthorized user' do + let(:user) { build(:user) } + + it 'does not call destroy on runner_project' do + expect(runner).not_to receive(:destroy) + + service + end + end + + context 'with admin user', :enable_admin_mode do + let(:user) { create_default(:user, :admin) } + + it 'destroys runner_project' do + expect(runner_project).to receive(:destroy).once + + service + end + end +end diff --git a/spec/services/ci/unregister_runner_service_spec.rb b/spec/services/ci/runners/unregister_runner_service_spec.rb index f427e04f228..df1a0a90067 100644 --- a/spec/services/ci/unregister_runner_service_spec.rb +++ b/spec/services/ci/runners/unregister_runner_service_spec.rb @@ -2,8 +2,8 @@ require 'spec_helper' -RSpec.describe ::Ci::UnregisterRunnerService, '#execute' do - subject { described_class.new(runner).execute } +RSpec.describe ::Ci::Runners::UnregisterRunnerService, '#execute' do + subject { described_class.new(runner, 'some_token').execute } let(:runner) { create(:ci_runner) } diff --git a/spec/services/ci/update_runner_service_spec.rb b/spec/services/ci/runners/update_runner_service_spec.rb index eee80bfef47..b02ea8f58b0 100644 --- a/spec/services/ci/update_runner_service_spec.rb +++ b/spec/services/ci/runners/update_runner_service_spec.rb @@ -2,7 +2,7 @@ require 'spec_helper' -RSpec.describe Ci::UpdateRunnerService do +RSpec.describe Ci::Runners::UpdateRunnerService do let(:runner) { create(:ci_runner) } describe '#update' do |