summaryrefslogtreecommitdiff
path: root/spec/services/ci
diff options
context:
space:
mode:
Diffstat (limited to 'spec/services/ci')
-rw-r--r--spec/services/ci/after_requeue_job_service_spec.rb23
-rw-r--r--spec/services/ci/create_pipeline_service/rate_limit_spec.rb91
-rw-r--r--spec/services/ci/create_pipeline_service_spec.rb14
-rw-r--r--spec/services/ci/create_web_ide_terminal_service_spec.rb2
-rw-r--r--spec/services/ci/job_artifacts/destroy_all_expired_service_spec.rb46
-rw-r--r--spec/services/ci/job_artifacts/destroy_batch_service_spec.rb12
-rw-r--r--spec/services/ci/job_artifacts/update_unknown_locked_status_service_spec.rb145
-rw-r--r--spec/services/ci/pipeline_processing/atomic_processing_service_spec.rb6
-rw-r--r--spec/services/ci/register_job_service_spec.rb14
-rw-r--r--spec/services/ci/retry_job_service_spec.rb (renamed from spec/services/ci/retry_build_service_spec.rb)32
-rw-r--r--spec/services/ci/retry_pipeline_service_spec.rb2
11 files changed, 329 insertions, 58 deletions
diff --git a/spec/services/ci/after_requeue_job_service_spec.rb b/spec/services/ci/after_requeue_job_service_spec.rb
index 2f2baa15945..c9bd44f78e2 100644
--- a/spec/services/ci/after_requeue_job_service_spec.rb
+++ b/spec/services/ci/after_requeue_job_service_spec.rb
@@ -85,7 +85,7 @@ RSpec.describe Ci::AfterRequeueJobService, :sidekiq_inline do
c2: 'skipped'
)
- new_a1 = Ci::RetryBuildService.new(project, user).clone!(a1)
+ new_a1 = Ci::RetryJobService.new(project, user).clone!(a1)
new_a1.enqueue!
check_jobs_statuses(
a1: 'pending',
@@ -172,7 +172,7 @@ RSpec.describe Ci::AfterRequeueJobService, :sidekiq_inline do
c2: 'skipped'
)
- new_a1 = Ci::RetryBuildService.new(project, user).clone!(a1)
+ new_a1 = Ci::RetryJobService.new(project, user).clone!(a1)
new_a1.enqueue!
check_jobs_statuses(
a1: 'pending',
@@ -196,25 +196,6 @@ RSpec.describe Ci::AfterRequeueJobService, :sidekiq_inline do
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
diff --git a/spec/services/ci/create_pipeline_service/rate_limit_spec.rb b/spec/services/ci/create_pipeline_service/rate_limit_spec.rb
new file mode 100644
index 00000000000..caea165cc6c
--- /dev/null
+++ b/spec/services/ci/create_pipeline_service/rate_limit_spec.rb
@@ -0,0 +1,91 @@
+# frozen_string_literal: true
+require 'spec_helper'
+
+RSpec.describe Ci::CreatePipelineService, :freeze_time, :clean_gitlab_redis_rate_limiting do
+ describe 'rate limiting' do
+ let_it_be(:project) { create(:project, :repository) }
+ let_it_be(:user) { project.first_owner }
+
+ let(:ref) { 'refs/heads/master' }
+
+ before do
+ stub_ci_pipeline_yaml_file(gitlab_ci_yaml)
+ stub_feature_flags(ci_throttle_pipelines_creation_dry_run: false)
+
+ allow(Gitlab::ApplicationRateLimiter).to receive(:rate_limits)
+ .and_return(pipelines_create: { threshold: 1, interval: 1.minute })
+ end
+
+ context 'when user is under the limit' do
+ let(:pipeline) { create_pipelines(count: 1) }
+
+ it 'allows pipeline creation' do
+ expect(pipeline).to be_created_successfully
+ expect(pipeline.statuses).not_to be_empty
+ end
+ end
+
+ context 'when user is over the limit' do
+ let(:pipeline) { create_pipelines }
+
+ it 'blocks pipeline creation' do
+ throttle_message = 'Too many pipelines created in the last minute. Try again later.'
+
+ expect(pipeline).not_to be_persisted
+ expect(pipeline.statuses).to be_empty
+ expect(pipeline.errors.added?(:base, throttle_message)).to be_truthy
+ end
+ end
+
+ context 'with different users' do
+ let(:other_user) { create(:user) }
+
+ before do
+ project.add_maintainer(other_user)
+ end
+
+ it 'allows other members to create pipelines' do
+ blocked_pipeline = create_pipelines(user: user)
+ allowed_pipeline = create_pipelines(count: 1, user: other_user)
+
+ expect(blocked_pipeline).not_to be_persisted
+ expect(allowed_pipeline).to be_created_successfully
+ end
+ end
+
+ context 'with different commits' do
+ it 'allows user to create pipeline' do
+ blocked_pipeline = create_pipelines(ref: ref)
+ allowed_pipeline = create_pipelines(count: 1, ref: 'refs/heads/feature')
+
+ expect(blocked_pipeline).not_to be_persisted
+ expect(allowed_pipeline).to be_created_successfully
+ end
+ end
+
+ context 'with different projects' do
+ let_it_be(:other_project) { create(:project, :repository) }
+
+ before do
+ other_project.add_maintainer(user)
+ end
+
+ it 'allows user to create pipeline' do
+ blocked_pipeline = create_pipelines(project: project)
+ allowed_pipeline = create_pipelines(count: 1, project: other_project)
+
+ expect(blocked_pipeline).not_to be_persisted
+ expect(allowed_pipeline).to be_created_successfully
+ end
+ end
+ end
+
+ def create_pipelines(attrs = {})
+ attrs.reverse_merge!(user: user, ref: ref, project: project, count: 2)
+
+ service = described_class.new(attrs[:project], attrs[:user], { ref: attrs[:ref] })
+
+ attrs[:count].pred.times { service.execute(:push) }
+ service.execute(:push).payload
+ end
+end
diff --git a/spec/services/ci/create_pipeline_service_spec.rb b/spec/services/ci/create_pipeline_service_spec.rb
index a7026f5062e..943d70ba142 100644
--- a/spec/services/ci/create_pipeline_service_spec.rb
+++ b/spec/services/ci/create_pipeline_service_spec.rb
@@ -12,6 +12,10 @@ RSpec.describe Ci::CreatePipelineService do
before do
stub_ci_pipeline_to_return_yaml_file
+
+ # Disable rate limiting for pipeline creation
+ allow(Gitlab::ApplicationRateLimiter).to receive(:rate_limits)
+ .and_return(pipelines_create: { threshold: 0, interval: 1.minute })
end
describe '#execute' do
@@ -526,7 +530,7 @@ RSpec.describe Ci::CreatePipelineService do
let(:ci_yaml) do
<<-EOS
image:
- name: ruby:2.7
+ name: image:1.0
ports:
- 80
EOS
@@ -538,12 +542,12 @@ RSpec.describe Ci::CreatePipelineService do
context 'in the job image' do
let(:ci_yaml) do
<<-EOS
- image: ruby:2.7
+ image: image:1.0
test:
script: rspec
image:
- name: ruby:2.7
+ name: image:1.0
ports:
- 80
EOS
@@ -555,11 +559,11 @@ RSpec.describe Ci::CreatePipelineService do
context 'in the service' do
let(:ci_yaml) do
<<-EOS
- image: ruby:2.7
+ image: image:1.0
test:
script: rspec
- image: ruby:2.7
+ image: image:1.0
services:
- name: test
ports:
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 0804773442d..3462b48cfe7 100644
--- a/spec/services/ci/create_web_ide_terminal_service_spec.rb
+++ b/spec/services/ci/create_web_ide_terminal_service_spec.rb
@@ -60,7 +60,7 @@ RSpec.describe Ci::CreateWebIdeTerminalService do
<<-EOS
terminal:
image:
- name: ruby:2.7
+ name: image:1.0
ports:
- 80
script: rspec
diff --git a/spec/services/ci/job_artifacts/destroy_all_expired_service_spec.rb b/spec/services/ci/job_artifacts/destroy_all_expired_service_spec.rb
index e95a449d615..1c6963e4a31 100644
--- a/spec/services/ci/job_artifacts/destroy_all_expired_service_spec.rb
+++ b/spec/services/ci/job_artifacts/destroy_all_expired_service_spec.rb
@@ -19,8 +19,23 @@ RSpec.describe Ci::JobArtifacts::DestroyAllExpiredService, :clean_gitlab_redis_s
let!(:artifact) { create(:ci_job_artifact, :expired, job: job, locked: job.pipeline.locked) }
context 'with preloaded relationships' do
+ let(:second_artifact) { create(:ci_job_artifact, :expired, :junit, job: job) }
+
+ let(:more_artifacts) do
+ [
+ create(:ci_job_artifact, :expired, :sast, job: job),
+ create(:ci_job_artifact, :expired, :metadata, job: job),
+ create(:ci_job_artifact, :expired, :codequality, job: job),
+ create(:ci_job_artifact, :expired, :accessibility, job: job)
+ ]
+ end
+
before do
- stub_const("#{described_class}::LARGE_LOOP_LIMIT", 1)
+ stub_const("#{described_class}::LOOP_LIMIT", 1)
+
+ # This artifact-with-file is created before the control execution to ensure
+ # that the DeletedObject operations are accounted for in the query count.
+ second_artifact
end
context 'with ci_destroy_unlocked_job_artifacts feature flag disabled' do
@@ -28,19 +43,12 @@ RSpec.describe Ci::JobArtifacts::DestroyAllExpiredService, :clean_gitlab_redis_s
stub_feature_flags(ci_destroy_unlocked_job_artifacts: false)
end
- it 'performs the smallest number of queries for job_artifacts' do
- log = ActiveRecord::QueryRecorder.new { subject }
+ it 'performs a consistent number of queries' do
+ control = ActiveRecord::QueryRecorder.new { service.execute }
- # SELECT expired ci_job_artifacts - 3 queries from each_batch
- # PRELOAD projects, routes, project_statistics
- # BEGIN
- # INSERT into ci_deleted_objects
- # DELETE loaded ci_job_artifacts
- # DELETE security_findings -- for EE
- # COMMIT
- # SELECT next expired ci_job_artifacts
+ more_artifacts
- expect(log.count).to be_within(1).of(10)
+ expect { subject }.not_to exceed_query_limit(control.count)
end
end
@@ -49,9 +57,12 @@ RSpec.describe Ci::JobArtifacts::DestroyAllExpiredService, :clean_gitlab_redis_s
stub_feature_flags(ci_destroy_unlocked_job_artifacts: true)
end
- it 'performs the smallest number of queries for job_artifacts' do
- log = ActiveRecord::QueryRecorder.new { subject }
- expect(log.count).to be_within(1).of(8)
+ it 'performs a consistent number of queries' do
+ control = ActiveRecord::QueryRecorder.new { service.execute }
+
+ more_artifacts
+
+ expect { subject }.not_to exceed_query_limit(control.count)
end
end
end
@@ -119,7 +130,7 @@ RSpec.describe Ci::JobArtifacts::DestroyAllExpiredService, :clean_gitlab_redis_s
let!(:artifact) { create(:ci_job_artifact, :expired, job: job, locked: job.pipeline.locked) }
before do
- stub_const("#{described_class}::LARGE_LOOP_LIMIT", 10)
+ stub_const("#{described_class}::LOOP_LIMIT", 10)
end
context 'when the import fails' do
@@ -189,8 +200,7 @@ RSpec.describe Ci::JobArtifacts::DestroyAllExpiredService, :clean_gitlab_redis_s
context 'when loop reached loop limit' do
before do
- stub_feature_flags(ci_artifact_fast_removal_large_loop_limit: false)
- stub_const("#{described_class}::SMALL_LOOP_LIMIT", 1)
+ stub_const("#{described_class}::LOOP_LIMIT", 1)
end
it 'destroys one artifact' do
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 67d664a617b..5e77041a632 100644
--- a/spec/services/ci/job_artifacts/destroy_batch_service_spec.rb
+++ b/spec/services/ci/job_artifacts/destroy_batch_service_spec.rb
@@ -3,7 +3,7 @@
require 'spec_helper'
RSpec.describe Ci::JobArtifacts::DestroyBatchService do
- let(:artifacts) { Ci::JobArtifact.where(id: [artifact_with_file.id, artifact_without_file.id]) }
+ let(:artifacts) { Ci::JobArtifact.where(id: [artifact_with_file.id, artifact_without_file.id, trace_artifact.id]) }
let(:service) { described_class.new(artifacts, pick_up_at: Time.current) }
let_it_be(:artifact_with_file, refind: true) do
@@ -18,6 +18,10 @@ RSpec.describe Ci::JobArtifacts::DestroyBatchService do
create(:ci_job_artifact)
end
+ let_it_be(:trace_artifact, refind: true) do
+ create(:ci_job_artifact, :trace, :expired)
+ end
+
describe '.execute' do
subject(:execute) { service.execute }
@@ -42,6 +46,12 @@ RSpec.describe Ci::JobArtifacts::DestroyBatchService do
execute
end
+ it 'preserves trace artifacts and removes any timestamp' do
+ expect { subject }
+ .to change { trace_artifact.reload.expire_at }.from(trace_artifact.expire_at).to(nil)
+ .and not_change { Ci::JobArtifact.exists?(trace_artifact.id) }
+ end
+
context 'ProjectStatistics' do
it 'resets project statistics' do
expect(ProjectStatistics).to receive(:increment_statistic).once
diff --git a/spec/services/ci/job_artifacts/update_unknown_locked_status_service_spec.rb b/spec/services/ci/job_artifacts/update_unknown_locked_status_service_spec.rb
new file mode 100644
index 00000000000..67412e41fb8
--- /dev/null
+++ b/spec/services/ci/job_artifacts/update_unknown_locked_status_service_spec.rb
@@ -0,0 +1,145 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Ci::JobArtifacts::UpdateUnknownLockedStatusService, :clean_gitlab_redis_shared_state do
+ include ExclusiveLeaseHelpers
+
+ let(:service) { described_class.new }
+
+ describe '.execute' do
+ subject { service.execute }
+
+ let_it_be(:locked_pipeline) { create(:ci_pipeline, :artifacts_locked) }
+ let_it_be(:pipeline) { create(:ci_pipeline, :unlocked) }
+ let_it_be(:locked_job) { create(:ci_build, :success, pipeline: locked_pipeline) }
+ let_it_be(:job) { create(:ci_build, :success, pipeline: pipeline) }
+
+ let!(:unknown_unlocked_artifact) do
+ create(:ci_job_artifact, :junit, expire_at: 1.hour.ago, job: job, locked: Ci::JobArtifact.lockeds[:unknown])
+ end
+
+ let!(:unknown_locked_artifact) do
+ create(:ci_job_artifact, :lsif,
+ expire_at: 1.day.ago,
+ job: locked_job,
+ locked: Ci::JobArtifact.lockeds[:unknown]
+ )
+ end
+
+ let!(:unlocked_artifact) do
+ create(:ci_job_artifact, :archive, expire_at: 1.hour.ago, job: job, locked: Ci::JobArtifact.lockeds[:unlocked])
+ end
+
+ let!(:locked_artifact) do
+ create(:ci_job_artifact, :sast, :raw,
+ expire_at: 1.day.ago,
+ job: locked_job,
+ locked: Ci::JobArtifact.lockeds[:artifacts_locked]
+ )
+ end
+
+ context 'when artifacts are expired' do
+ it 'sets artifact_locked when the pipeline is locked' do
+ expect { service.execute }
+ .to change { unknown_locked_artifact.reload.locked }.from('unknown').to('artifacts_locked')
+ .and not_change { Ci::JobArtifact.exists?(locked_artifact.id) }
+ end
+
+ it 'destroys the artifact when the pipeline is unlocked' do
+ expect { subject }.to change { Ci::JobArtifact.exists?(unknown_unlocked_artifact.id) }.from(true).to(false)
+ end
+
+ it 'does not update ci_job_artifact rows with known locked values' do
+ expect { service.execute }
+ .to not_change(locked_artifact, :attributes)
+ .and not_change { Ci::JobArtifact.exists?(locked_artifact.id) }
+ .and not_change(unlocked_artifact, :attributes)
+ .and not_change { Ci::JobArtifact.exists?(unlocked_artifact.id) }
+ end
+
+ it 'logs the counts of affected artifacts' do
+ expect(subject).to eq({ removed: 1, locked: 1 })
+ end
+ end
+
+ context 'in a single iteration' do
+ before do
+ stub_const("#{described_class}::BATCH_SIZE", 1)
+ end
+
+ context 'due to the LOOP_TIMEOUT' do
+ before do
+ stub_const("#{described_class}::LOOP_TIMEOUT", 0.seconds)
+ end
+
+ it 'affects the earliest expired artifact first' do
+ subject
+
+ expect(unknown_locked_artifact.reload.locked).to eq('artifacts_locked')
+ expect(unknown_unlocked_artifact.reload.locked).to eq('unknown')
+ end
+
+ it 'reports the number of destroyed artifacts' do
+ is_expected.to eq({ removed: 0, locked: 1 })
+ end
+ end
+
+ context 'due to @loop_limit' do
+ before do
+ stub_const("#{described_class}::LARGE_LOOP_LIMIT", 1)
+ end
+
+ it 'affects the most recently expired artifact first' do
+ subject
+
+ expect(unknown_locked_artifact.reload.locked).to eq('artifacts_locked')
+ expect(unknown_unlocked_artifact.reload.locked).to eq('unknown')
+ end
+
+ it 'reports the number of destroyed artifacts' do
+ is_expected.to eq({ removed: 0, locked: 1 })
+ end
+ end
+ end
+
+ context 'when artifact is not expired' do
+ let!(:unknown_unlocked_artifact) do
+ create(:ci_job_artifact, :junit,
+ expire_at: 1.year.from_now,
+ job: job,
+ locked: Ci::JobArtifact.lockeds[:unknown]
+ )
+ end
+
+ it 'does not change the locked status' do
+ expect { service.execute }.not_to change { unknown_unlocked_artifact.locked }
+ expect(Ci::JobArtifact.exists?(unknown_unlocked_artifact.id)).to eq(true)
+ end
+ end
+
+ context 'when exclusive lease has already been taken by the other instance' do
+ before do
+ stub_exclusive_lease_taken(described_class::EXCLUSIVE_LOCK_KEY, timeout: described_class::LOCK_TIMEOUT)
+ end
+
+ it 'raises an error and' do
+ expect { subject }.to raise_error(Gitlab::ExclusiveLeaseHelpers::FailedToObtainLockError)
+ end
+ end
+
+ context 'when there are no unknown status artifacts' do
+ before do
+ Ci::JobArtifact.update_all(locked: :unlocked)
+ end
+
+ it 'does not raise error' do
+ expect { subject }.not_to raise_error
+ end
+
+ it 'reports the number of destroyed artifacts' do
+ is_expected.to eq({ removed: 0, locked: 0 })
+ end
+ end
+ end
+end
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 7365ad162d2..5bc508447c1 100644
--- a/spec/services/ci/pipeline_processing/atomic_processing_service_spec.rb
+++ b/spec/services/ci/pipeline_processing/atomic_processing_service_spec.rb
@@ -725,7 +725,7 @@ RSpec.describe Ci::PipelineProcessing::AtomicProcessingService do
expect(builds_names).to eq ['build:1', 'build:2', 'test:1', 'test:2']
- Ci::Build.retry(pipeline.builds.find_by(name: 'test:2'), user).reset.success!
+ Ci::RetryJobService.new(pipeline.project, user).execute(pipeline.builds.find_by(name: 'test:2'))[:job].reset.success!
expect(builds_names).to eq ['build:1', 'build:2', 'test:1', 'test:2',
'test:2', 'deploy:1', 'deploy:2']
@@ -1111,11 +1111,11 @@ RSpec.describe Ci::PipelineProcessing::AtomicProcessingService do
end
def enqueue_scheduled(name)
- builds.scheduled.find_by(name: name).enqueue_scheduled
+ builds.scheduled.find_by(name: name).enqueue!
end
def retry_build(name)
- Ci::Build.retry(builds.find_by(name: name), user)
+ Ci::RetryJobService.new(project, user).execute(builds.find_by(name: name))
end
def manual_actions
diff --git a/spec/services/ci/register_job_service_spec.rb b/spec/services/ci/register_job_service_spec.rb
index 245118e71fa..74adbc4efc8 100644
--- a/spec/services/ci/register_job_service_spec.rb
+++ b/spec/services/ci/register_job_service_spec.rb
@@ -103,6 +103,20 @@ module Ci
pending_job.create_queuing_entry!
end
+ context 'when build owner has been blocked' do
+ let(:user) { create(:user, :blocked) }
+
+ before do
+ pending_job.update!(user: user)
+ end
+
+ it 'does not pick the build and drops the build' do
+ expect(execute(shared_runner)).to be_falsey
+
+ expect(pending_job.reload).to be_user_blocked
+ end
+ end
+
context 'for multiple builds' do
let!(:project2) { create :project, shared_runners_enabled: true }
let!(:pipeline2) { create :ci_pipeline, project: project2 }
diff --git a/spec/services/ci/retry_build_service_spec.rb b/spec/services/ci/retry_job_service_spec.rb
index 2421fd56c47..25aab73ab01 100644
--- a/spec/services/ci/retry_build_service_spec.rb
+++ b/spec/services/ci/retry_job_service_spec.rb
@@ -2,7 +2,7 @@
require 'spec_helper'
-RSpec.describe Ci::RetryBuildService do
+RSpec.describe Ci::RetryJobService do
let_it_be(:reporter) { create(:user) }
let_it_be(:developer) { create(:user) }
let_it_be(:project) { create(:project, :repository) }
@@ -17,7 +17,7 @@ RSpec.describe Ci::RetryBuildService do
name: 'test')
end
- let_it_be_with_refind(:build) { create(:ci_build, pipeline: pipeline, stage_id: stage.id) }
+ let_it_be_with_refind(:build) { create(:ci_build, :success, pipeline: pipeline, stage_id: stage.id) }
let(:user) { developer }
@@ -30,7 +30,7 @@ RSpec.describe Ci::RetryBuildService do
project.add_reporter(reporter)
end
- clone_accessors = described_class.clone_accessors.without(described_class.extra_accessors)
+ clone_accessors = ::Ci::Build.clone_accessors.without(::Ci::Build.extra_accessors)
reject_accessors =
%i[id status user token token_encrypted coverage trace runner
@@ -94,6 +94,10 @@ RSpec.describe Ci::RetryBuildService do
create(:terraform_state_version, build: build)
end
+ before do
+ build.update!(retried: false, status: :success)
+ end
+
describe 'clone accessors' do
let(:forbidden_associations) do
Ci::Build.reflect_on_all_associations.each_with_object(Set.new) do |assoc, memo|
@@ -156,8 +160,8 @@ RSpec.describe Ci::RetryBuildService do
Ci::Build.attribute_aliases.keys.map(&:to_sym) +
Ci::Build.reflect_on_all_associations.map(&:name) +
[:tag_list, :needs_attributes, :job_variables_attributes] -
- # ee-specific accessors should be tested in ee/spec/services/ci/retry_build_service_spec.rb instead
- described_class.extra_accessors -
+ # ee-specific accessors should be tested in ee/spec/services/ci/retry_job_service_spec.rb instead
+ Ci::Build.extra_accessors -
[:dast_site_profiles_build, :dast_scanner_profiles_build] # join tables
current_accessors.uniq!
@@ -170,7 +174,7 @@ RSpec.describe Ci::RetryBuildService do
describe '#execute' do
let(:new_build) do
travel_to(1.second.from_now) do
- service.execute(build)
+ service.execute(build)[:job]
end
end
@@ -248,7 +252,7 @@ RSpec.describe Ci::RetryBuildService do
context 'when build has scheduling_type' do
it 'does not call populate_scheduling_type!' do
- expect_any_instance_of(Ci::Pipeline).not_to receive(:ensure_scheduling_type!)
+ expect_any_instance_of(Ci::Pipeline).not_to receive(:ensure_scheduling_type!) # rubocop: disable RSpec/AnyInstanceOf
expect(new_build.scheduling_type).to eq('stage')
end
@@ -286,6 +290,18 @@ RSpec.describe Ci::RetryBuildService do
expect { service.execute(build) }
.to raise_error Gitlab::Access::AccessDeniedError
end
+
+ context 'when the job is not retryable' do
+ let(:build) { create(:ci_build, :created, pipeline: pipeline) }
+
+ it 'returns a ServiceResponse error' do
+ response = service.execute(build)
+
+ expect(response).to be_a(ServiceResponse)
+ expect(response).to be_error
+ expect(response.message).to eq("Job cannot be retried")
+ end
+ end
end
end
@@ -342,7 +358,7 @@ RSpec.describe Ci::RetryBuildService do
end
shared_examples_for 'when build with dynamic environment is retried' do
- let_it_be(:other_developer) { create(:user).tap { |u| project.add_developer(other_developer) } }
+ let_it_be(:other_developer) { create(:user).tap { |u| project.add_developer(u) } }
let(:environment_name) { 'review/$CI_COMMIT_REF_SLUG-$GITLAB_USER_ID' }
diff --git a/spec/services/ci/retry_pipeline_service_spec.rb b/spec/services/ci/retry_pipeline_service_spec.rb
index df1e159b5c0..24272801480 100644
--- a/spec/services/ci/retry_pipeline_service_spec.rb
+++ b/spec/services/ci/retry_pipeline_service_spec.rb
@@ -340,7 +340,7 @@ RSpec.describe Ci::RetryPipelineService, '#execute' do
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_next_instance_of(Ci::RetryJobService) do |service|
allow(service).to receive(:can?).with(user, :update_build, build).and_return(false)
end
end