debian-mirror-gitlab/spec/services/projects/destroy_service_spec.rb

618 lines
22 KiB
Ruby
Raw Normal View History

2019-07-31 22:56:46 +05:30
# frozen_string_literal: true
2015-09-11 14:41:01 +05:30
require 'spec_helper'
2023-04-23 21:23:45 +05:30
RSpec.describe Projects::DestroyService, :aggregate_failures, :event_store_publisher, feature_category: :projects do
2018-03-17 18:26:18 +05:30
include ProjectForksHelper
2023-03-04 22:38:38 +05:30
include BatchDestroyDependentAssociationsHelper
2018-03-17 18:26:18 +05:30
2020-03-13 15:44:24 +05:30
let_it_be(:user) { create(:user) }
2021-09-30 23:02:18 +05:30
2017-08-17 22:00:37 +05:30
let!(:project) { create(:project, :repository, namespace: user.namespace) }
2020-03-13 15:44:24 +05:30
let(:path) { project.repository.disk_path }
let(:async) { false } # execute or async_execute
2015-09-11 14:41:01 +05:30
2017-08-17 22:00:37 +05:30
before do
stub_container_registry_config(enabled: true)
stub_container_registry_tags(repository: :any, tags: [])
end
shared_examples 'deleting the project' do
2022-04-04 11:22:00 +05:30
it 'deletes the project', :sidekiq_inline do
2020-03-13 15:44:24 +05:30
destroy_project(project, user, {})
2017-08-17 22:00:37 +05:30
expect(Project.unscoped.all).not_to include(project)
2017-09-10 17:25:29 +05:30
2019-12-21 20:55:43 +05:30
expect(project.gitlab_shell.repository_exists?(project.repository_storage, path + '.git')).to be_falsey
2017-08-17 22:00:37 +05:30
end
2022-04-04 11:22:00 +05:30
2022-08-13 15:12:31 +05:30
it 'publishes a ProjectDeletedEvent' do
expected_data = {
project_id: project.id,
namespace_id: project.namespace_id,
root_namespace_id: project.root_namespace.id
}
2022-04-04 11:22:00 +05:30
2022-05-07 20:08:51 +05:30
expect { destroy_project(project, user, {}) }.to publish_event(Projects::ProjectDeletedEvent).with(expected_data)
2022-04-04 11:22:00 +05:30
end
2017-08-17 22:00:37 +05:30
end
shared_examples 'deleting the project with pipeline and build' do
2021-04-17 20:07:23 +05:30
context 'with pipeline and build related records', :sidekiq_inline do # which has optimistic locking
2017-08-17 22:00:37 +05:30
let!(:pipeline) { create(:ci_pipeline, project: project) }
2021-04-17 20:07:23 +05:30
let!(:build) { create(:ci_build, :artifacts, :with_runner_session, pipeline: pipeline) }
let!(:trace_chunks) { create(:ci_build_trace_chunk, build: build) }
let!(:job_variables) { create(:ci_job_variable, job: build) }
let!(:report_result) { create(:ci_build_report_result, build: build) }
let!(:pending_state) { create(:ci_build_pending_state, build: build) }
2021-11-18 22:05:49 +05:30
let!(:pipeline_artifact) { create(:ci_pipeline_artifact, pipeline: pipeline) }
2022-05-07 20:08:51 +05:30
let!(:secure_file) { create(:ci_secure_file, project: project) }
2021-04-17 20:07:23 +05:30
2021-11-18 22:05:49 +05:30
it 'deletes build and pipeline related records' do
expect { destroy_project(project, user, {}) }
.to change { Ci::Build.count }.by(-1)
2021-04-17 20:07:23 +05:30
.and change { Ci::BuildTraceChunk.count }.by(-1)
.and change { Ci::JobArtifact.count }.by(-2)
2021-11-18 22:05:49 +05:30
.and change { Ci::DeletedObject.count }.by(2)
.and change { Ci::PipelineArtifact.count }.by(-1)
2021-04-17 20:07:23 +05:30
.and change { Ci::JobVariable.count }.by(-1)
.and change { Ci::BuildPendingState.count }.by(-1)
.and change { Ci::BuildReportResult.count }.by(-1)
.and change { Ci::BuildRunnerSession.count }.by(-1)
2021-11-18 22:05:49 +05:30
.and change { Ci::Pipeline.count }.by(-1)
2022-05-07 20:08:51 +05:30
.and change { Ci::SecureFile.count }.by(-1)
2021-04-17 20:07:23 +05:30
end
2022-01-26 12:08:38 +05:30
it 'avoids N+1 queries' do
recorder = ActiveRecord::QueryRecorder.new { destroy_project(project, user, {}) }
2021-04-17 20:07:23 +05:30
2022-01-26 12:08:38 +05:30
project = create(:project, :repository, namespace: user.namespace)
pipeline = create(:ci_pipeline, project: project)
builds = create_list(:ci_build, 3, :artifacts, pipeline: pipeline)
create(:ci_pipeline_artifact, pipeline: pipeline)
create_list(:ci_build_trace_chunk, 3, build: builds[0])
2021-04-17 20:07:23 +05:30
2022-03-02 08:16:31 +05:30
expect { destroy_project(project, project.first_owner, {}) }.not_to exceed_query_limit(recorder)
2021-04-17 20:07:23 +05:30
end
2017-08-17 22:00:37 +05:30
it_behaves_like 'deleting the project'
2022-07-23 23:45:48 +05:30
context 'when project is undergoing refresh' do
let!(:build_artifacts_size_refresh) { create(:project_build_artifacts_size_refresh, :pending, project: project) }
it 'does not log about artifact deletion but continues to delete artifacts' do
expect(Gitlab::ProjectStatsRefreshConflictsLogger).not_to receive(:warn_artifact_deletion_during_stats_refresh)
expect { destroy_project(project, user, {}) }
.to change { Ci::JobArtifact.count }.by(-2)
.and change { Projects::BuildArtifactsSizeRefresh.count }.by(-1)
end
end
2017-08-17 22:00:37 +05:30
end
end
2017-09-10 17:25:29 +05:30
shared_examples 'handles errors thrown during async destroy' do |error_message|
it 'does not allow the error to bubble up' do
expect do
2020-03-13 15:44:24 +05:30
destroy_project(project, user, {})
2017-09-10 17:25:29 +05:30
end.not_to raise_error
end
2022-03-02 08:16:31 +05:30
it 'reports the error' do
expect(Gitlab::ErrorTracking).to receive(:track_exception).and_call_original
destroy_project(project, user, {})
end
2017-09-10 17:25:29 +05:30
it 'unmarks the project as "pending deletion"' do
2020-03-13 15:44:24 +05:30
destroy_project(project, user, {})
2017-09-10 17:25:29 +05:30
expect(project.reload.pending_delete).to be(false)
end
it 'stores an error message in `projects.delete_error`' do
2020-03-13 15:44:24 +05:30
destroy_project(project, user, {})
2017-09-10 17:25:29 +05:30
expect(project.reload.delete_error).to be_present
expect(project.delete_error).to include(error_message)
end
end
2022-04-04 11:22:00 +05:30
context "deleting a project with merge requests" do
let!(:merge_request) { create(:merge_request, source_project: project) }
before do
allow(project).to receive(:destroy!).and_return(true)
end
2022-08-13 15:12:31 +05:30
[MergeRequestDiffCommit, MergeRequestDiffFile].each do |model|
it "deletes #{model} records of the merge request" do
merge_request_diffs = merge_request.merge_request_diffs
expect(merge_request_diffs.size).to eq(1)
2022-04-04 11:22:00 +05:30
2022-08-13 15:12:31 +05:30
records_count = model.where(merge_request_diff_id: merge_request_diffs.first.id).count
2022-04-04 11:22:00 +05:30
2022-08-13 15:12:31 +05:30
expect { destroy_project(project, user, {}) }.to change { model.count }.by(-records_count)
end
2022-04-04 11:22:00 +05:30
end
end
2022-10-11 01:57:18 +05:30
context 'deleting a project with merge request diffs' do
let!(:merge_request) { create(:merge_request, source_project: project) }
let!(:another_project_mr) { create(:merge_request, source_project: create(:project)) }
it 'deletes merge request diffs' do
merge_request_diffs = merge_request.merge_request_diffs
expect(merge_request_diffs.size).to eq(1)
expect { destroy_project(project, user, {}) }.to change(MergeRequestDiff, :count).by(-1)
expect { another_project_mr.reload }.not_to raise_error
end
end
2021-04-17 20:07:23 +05:30
it_behaves_like 'deleting the project'
2018-10-15 14:42:47 +05:30
2023-04-23 21:23:45 +05:30
context 'personal projects count cache' do
context 'when the executor is the creator of the project itself' do
it 'invalidates personal_project_count cache of the the owner of the personal namespace' do
expect(user).to receive(:invalidate_personal_projects_count)
2018-05-09 12:01:36 +05:30
2023-04-23 21:23:45 +05:30
destroy_project(project, user, {})
end
end
context 'when the executor is the instance administrator', :enable_admin_mode do
it 'invalidates personal_project_count cache of the the owner of the personal namespace' do
expect(user).to receive(:invalidate_personal_projects_count)
destroy_project(project, create(:admin), {})
end
end
2021-04-17 20:07:23 +05:30
end
2022-01-26 12:08:38 +05:30
context 'with running pipelines' do
2021-11-18 22:05:49 +05:30
let!(:pipelines) { create_list(:ci_pipeline, 3, :running, project: project) }
let(:destroy_pipeline_service) { double('DestroyPipelineService', execute: nil) }
2021-04-29 21:17:54 +05:30
2022-01-26 12:08:38 +05:30
it 'bulks-fails with AbortPipelineService and then executes DestroyPipelineService for each pipelines' do
allow(::Ci::DestroyPipelineService).to receive(:new).and_return(destroy_pipeline_service)
2021-11-18 22:05:49 +05:30
2022-01-26 12:08:38 +05:30
expect(::Ci::AbortPipelinesService)
.to receive_message_chain(:new, :execute)
.with(project.all_pipelines, :project_deleted)
2021-11-18 22:05:49 +05:30
2022-01-26 12:08:38 +05:30
pipelines.each do |pipeline|
expect(destroy_pipeline_service).to receive(:execute).with(pipeline)
2021-11-18 22:05:49 +05:30
end
2022-01-26 12:08:38 +05:30
destroy_project(project, user, {})
2021-04-29 21:17:54 +05:30
end
2021-04-17 20:07:23 +05:30
end
context 'when project has remote mirrors' do
let!(:project) do
create(:project, :repository, namespace: user.namespace).tap do |project|
project.remote_mirrors.create!(url: 'http://test.com')
end
2018-05-09 12:01:36 +05:30
end
2018-11-20 20:47:30 +05:30
2021-04-17 20:07:23 +05:30
it 'destroys them' do
expect(RemoteMirror.count).to eq(1)
2021-02-11 23:33:58 +05:30
destroy_project(project, user, {})
2021-04-17 20:07:23 +05:30
expect(RemoteMirror.count).to eq(0)
2021-02-11 23:33:58 +05:30
end
2021-04-17 20:07:23 +05:30
end
2021-02-11 23:33:58 +05:30
2021-04-17 20:07:23 +05:30
context 'when project has exports' do
let!(:project_with_export) do
create(:project, :repository, namespace: user.namespace).tap do |project|
2023-06-20 00:43:36 +05:30
create(
:import_export_upload,
project: project,
export_file: fixture_file_upload('spec/fixtures/project_export.tar.gz')
)
2018-11-20 20:47:30 +05:30
end
2021-04-17 20:07:23 +05:30
end
2018-11-20 20:47:30 +05:30
2021-04-17 20:07:23 +05:30
it 'destroys project and export' do
expect do
destroy_project(project_with_export, user, {})
end.to change(ImportExportUpload, :count).by(-1)
2015-09-11 14:41:01 +05:30
2021-04-17 20:07:23 +05:30
expect(Project.all).not_to include(project_with_export)
2015-09-11 14:41:01 +05:30
end
2021-04-17 20:07:23 +05:30
end
2015-09-11 14:41:01 +05:30
2021-04-17 20:07:23 +05:30
context 'Sidekiq fake' do
before do
# Dont run sidekiq to check if renamed repository exists
Sidekiq::Testing.fake! { destroy_project(project, user, {}) }
end
2020-03-13 15:44:24 +05:30
2021-04-17 20:07:23 +05:30
it { expect(Project.all).not_to include(project) }
2016-06-02 11:05:42 +05:30
2021-04-17 20:07:23 +05:30
it do
expect(project.gitlab_shell.repository_exists?(project.repository_storage, path + '.git')).to be_falsey
2019-10-12 21:52:04 +05:30
end
2021-04-17 20:07:23 +05:30
end
2019-10-12 21:52:04 +05:30
2021-04-17 20:07:23 +05:30
context 'when flushing caches fail due to Git errors' do
before do
allow(project.repository).to receive(:before_delete).and_raise(::Gitlab::Git::CommandError)
allow(Gitlab::GitLogger).to receive(:warn).with(
class: Repositories::DestroyService.name,
container_id: project.id,
disk_path: project.disk_path,
message: 'Gitlab::Git::CommandError').and_call_original
end
2016-11-03 12:29:30 +05:30
2021-04-17 20:07:23 +05:30
it_behaves_like 'deleting the project'
end
2017-08-17 22:00:37 +05:30
2021-04-17 20:07:23 +05:30
context 'when flushing caches fail due to Redis' do
before do
new_user = create(:user)
2022-08-13 15:12:31 +05:30
project.team.add_member(new_user, Gitlab::Access::DEVELOPER)
2021-04-17 20:07:23 +05:30
allow_any_instance_of(described_class).to receive(:flush_caches).and_raise(::Redis::CannotConnectError)
2016-11-03 12:29:30 +05:30
end
2021-04-17 20:07:23 +05:30
it 'keeps project team intact upon an error' do
perform_enqueued_jobs do
destroy_project(project, user, {})
rescue ::Redis::CannotConnectError
2017-08-17 22:00:37 +05:30
end
2021-04-17 20:07:23 +05:30
expect(project.team.members.count).to eq 2
2016-06-02 11:05:42 +05:30
end
2021-04-17 20:07:23 +05:30
end
context 'with async_execute', :sidekiq_inline do
let(:async) { true }
2016-06-02 11:05:42 +05:30
2021-04-17 20:07:23 +05:30
context 'async delete of project with private issue visibility' do
2020-11-24 15:15:51 +05:30
before do
2021-04-17 20:07:23 +05:30
project.project_feature.update_attribute("issues_access_level", ProjectFeature::PRIVATE)
2020-11-24 15:15:51 +05:30
end
2017-08-17 22:00:37 +05:30
2021-04-17 20:07:23 +05:30
it_behaves_like 'deleting the project'
2020-11-24 15:15:51 +05:30
end
2021-04-17 20:07:23 +05:30
it_behaves_like 'deleting the project with pipeline and build'
2017-09-10 17:25:29 +05:30
2021-04-17 20:07:23 +05:30
context 'errors' do
context 'when `remove_legacy_registry_tags` fails' do
2017-09-10 17:25:29 +05:30
before do
2021-04-17 20:07:23 +05:30
expect_any_instance_of(described_class)
.to receive(:remove_legacy_registry_tags).and_return(false)
2017-09-10 17:25:29 +05:30
end
2021-04-17 20:07:23 +05:30
it_behaves_like 'handles errors thrown during async destroy', "Failed to remove some tags"
2017-09-10 17:25:29 +05:30
end
2021-04-17 20:07:23 +05:30
context 'when `remove_repository` fails' do
before do
expect_any_instance_of(described_class)
.to receive(:remove_repository).and_return(false)
2017-09-10 17:25:29 +05:30
end
2021-04-17 20:07:23 +05:30
it_behaves_like 'handles errors thrown during async destroy', "Failed to remove project repository"
end
2017-09-10 17:25:29 +05:30
2021-04-17 20:07:23 +05:30
context 'when `execute` raises expected error' do
before do
expect_any_instance_of(Project)
.to receive(:destroy!).and_raise(StandardError.new("Other error message"))
2017-09-10 17:25:29 +05:30
end
2021-04-17 20:07:23 +05:30
it_behaves_like 'handles errors thrown during async destroy', "Other error message"
end
2017-09-10 17:25:29 +05:30
2021-04-17 20:07:23 +05:30
context 'when `execute` raises unexpected error' do
before do
expect_any_instance_of(Project)
.to receive(:destroy!).and_raise(Exception.new('Other error message'))
2017-09-10 17:25:29 +05:30
end
2016-06-02 11:05:42 +05:30
2021-04-17 20:07:23 +05:30
it 'allows error to bubble up and rolls back project deletion' do
expect do
destroy_project(project, user, {})
end.to raise_error(Exception, 'Other error message')
2017-08-17 22:00:37 +05:30
2021-04-17 20:07:23 +05:30
expect(project.reload.pending_delete).to be(false)
expect(project.delete_error).to include("Other error message")
2020-11-24 15:15:51 +05:30
end
2017-08-17 22:00:37 +05:30
end
2020-11-24 15:15:51 +05:30
end
2021-12-11 22:18:48 +05:30
context 'for an archived project' do
before do
project.update!(archived: true)
end
it_behaves_like 'deleting the project with pipeline and build'
end
2021-04-17 20:07:23 +05:30
end
2017-08-17 22:00:37 +05:30
2021-04-17 20:07:23 +05:30
describe 'container registry' do
context 'when there are regular container repositories' do
let(:container_repository) { create(:container_repository) }
2017-08-17 22:00:37 +05:30
2021-04-17 20:07:23 +05:30
before do
2023-06-20 00:43:36 +05:30
stub_container_registry_tags(repository: project.full_path + '/image', tags: ['tag'])
2021-04-17 20:07:23 +05:30
project.container_repositories << container_repository
end
2017-08-17 22:00:37 +05:30
2021-04-17 20:07:23 +05:30
context 'when image repository deletion succeeds' do
2023-04-23 21:23:45 +05:30
it 'returns true' do
expect_next_instance_of(Projects::ContainerRepository::CleanupTagsService) do |instance|
expect(instance).to receive(:execute).and_return(status: :success)
end
2017-08-17 22:00:37 +05:30
2023-04-23 21:23:45 +05:30
expect(destroy_project(project, user)).to be true
end
end
context 'when image repository deletion raises an error' do
it 'returns false' do
expect_next_instance_of(Projects::ContainerRepository::CleanupTagsService) do |service|
expect(service).to receive(:execute).and_raise(RuntimeError)
end
expect(destroy_project(project, user)).to be false
2017-08-17 22:00:37 +05:30
end
2021-04-17 20:07:23 +05:30
end
2019-10-12 21:52:04 +05:30
2021-04-17 20:07:23 +05:30
context 'when image repository deletion fails' do
2023-04-23 21:23:45 +05:30
it 'returns false' do
expect_next_instance_of(Projects::ContainerRepository::DestroyService) do |service|
expect(service).to receive(:execute).and_return({ status: :error })
end
2020-11-24 15:15:51 +05:30
2021-04-17 20:07:23 +05:30
expect(destroy_project(project, user)).to be false
2019-10-12 21:52:04 +05:30
end
2021-04-17 20:07:23 +05:30
end
2019-10-12 21:52:04 +05:30
2021-04-17 20:07:23 +05:30
context 'when registry is disabled' do
before do
stub_container_registry_config(enabled: false)
end
2019-10-12 21:52:04 +05:30
2021-04-17 20:07:23 +05:30
it 'does not attempting to remove any tags' do
expect(Projects::ContainerRepository::DestroyService).not_to receive(:new)
2020-11-24 15:15:51 +05:30
2021-04-17 20:07:23 +05:30
destroy_project(project, user)
2019-10-12 21:52:04 +05:30
end
end
2021-04-17 20:07:23 +05:30
end
2016-06-02 11:05:42 +05:30
2021-04-17 20:07:23 +05:30
context 'when there are tags for legacy root repository' do
before do
2023-06-20 00:43:36 +05:30
stub_container_registry_tags(repository: project.full_path, tags: ['tag'])
2021-04-17 20:07:23 +05:30
end
2017-08-17 22:00:37 +05:30
2021-04-17 20:07:23 +05:30
context 'when image repository tags deletion succeeds' do
it 'removes tags' do
2023-04-23 21:23:45 +05:30
expect_next_instance_of(Projects::ContainerRepository::DestroyService) do |service|
expect(service).to receive(:execute).and_return({ status: :sucess })
end
2016-06-02 11:05:42 +05:30
2021-04-17 20:07:23 +05:30
destroy_project(project, user)
2017-08-17 22:00:37 +05:30
end
2021-04-17 20:07:23 +05:30
end
2016-06-02 11:05:42 +05:30
2021-04-17 20:07:23 +05:30
context 'when image repository tags deletion fails' do
it 'raises an exception' do
2023-04-23 21:23:45 +05:30
expect_next_instance_of(Projects::ContainerRepository::DestroyService) do |service|
expect(service).to receive(:execute).and_return({ status: :error })
end
2017-08-17 22:00:37 +05:30
2021-04-17 20:07:23 +05:30
expect(destroy_project(project, user)).to be false
2017-08-17 22:00:37 +05:30
end
2023-04-23 21:23:45 +05:30
end
end
context 'when there are no tags for legacy root repository' do
before do
2023-06-20 00:43:36 +05:30
stub_container_registry_tags(repository: project.full_path, tags: [])
2023-04-23 21:23:45 +05:30
end
it 'does not try to destroy the repository' do
expect(Projects::ContainerRepository::DestroyService).not_to receive(:new)
destroy_project(project, user)
2017-08-17 22:00:37 +05:30
end
2016-06-02 11:05:42 +05:30
end
2021-04-17 20:07:23 +05:30
end
2015-09-11 14:41:01 +05:30
2021-04-17 20:07:23 +05:30
context 'for a forked project with LFS objects' do
let(:forked_project) { fork_project(project, user) }
2018-03-17 18:26:18 +05:30
2021-04-17 20:07:23 +05:30
before do
project.lfs_objects << create(:lfs_object)
forked_project.reload
end
2018-03-17 18:26:18 +05:30
2021-04-17 20:07:23 +05:30
it 'destroys the fork' do
expect { destroy_project(forked_project, user) }
.not_to raise_error
2018-03-17 18:26:18 +05:30
end
2021-04-17 20:07:23 +05:30
end
2018-03-17 18:26:18 +05:30
2021-04-17 20:07:23 +05:30
context 'as the root of a fork network' do
let!(:fork_1) { fork_project(project, user) }
let!(:fork_2) { fork_project(project, user) }
2018-03-17 18:26:18 +05:30
2021-04-17 20:07:23 +05:30
it 'updates the fork network with the project name' do
fork_network = project.fork_network
2020-01-01 13:55:28 +05:30
2021-04-17 20:07:23 +05:30
destroy_project(project, user)
2018-03-17 18:26:18 +05:30
2021-04-17 20:07:23 +05:30
fork_network.reload
2018-03-17 18:26:18 +05:30
2021-04-17 20:07:23 +05:30
expect(fork_network.deleted_root_project_name).to eq(project.full_name)
expect(fork_network.root_project).to be_nil
2018-03-17 18:26:18 +05:30
end
2021-04-17 20:07:23 +05:30
end
2018-03-17 18:26:18 +05:30
2022-07-23 23:45:48 +05:30
context 'repository removal' do
it 'removal of existing repos' do
expect_next_instances_of(Repositories::DestroyService, 2) do |instance|
expect(instance).to receive(:execute).and_return(status: :success)
2019-03-02 22:35:43 +05:30
end
2022-07-23 23:45:48 +05:30
described_class.new(project, user, {}).execute
2019-03-02 22:35:43 +05:30
end
2021-04-17 20:07:23 +05:30
end
2019-03-02 22:35:43 +05:30
2021-04-17 20:07:23 +05:30
context 'snippets' do
let!(:snippet1) { create(:project_snippet, project: project, author: user) }
let!(:snippet2) { create(:project_snippet, project: project, author: user) }
2020-04-08 14:13:33 +05:30
2021-04-17 20:07:23 +05:30
it 'does not include snippets when deleting in batches' do
expect(project).to receive(:destroy_dependent_associations_in_batches).with({ exclude: [:container_repositories, :snippets] })
2020-04-08 14:13:33 +05:30
2021-04-17 20:07:23 +05:30
destroy_project(project, user)
end
2020-04-08 14:13:33 +05:30
2022-10-11 01:57:18 +05:30
it 'calls the bulk snippet destroy service with the skip_authorization param set to true' do
2021-04-17 20:07:23 +05:30
expect(project.snippets.count).to eq 2
2020-04-08 14:13:33 +05:30
2022-05-07 20:08:51 +05:30
expect_next_instance_of(Snippets::BulkDestroyService, user, project.snippets) do |instance|
2022-10-11 01:57:18 +05:30
expect(instance).to receive(:execute).with(skip_authorization: true).and_call_original
2022-05-07 20:08:51 +05:30
end
2020-04-08 14:13:33 +05:30
2021-04-17 20:07:23 +05:30
expect do
destroy_project(project, user)
end.to change(Snippet, :count).by(-2)
2020-11-24 15:15:51 +05:30
end
2020-04-08 14:13:33 +05:30
2021-04-17 20:07:23 +05:30
context 'when an error is raised deleting snippets' do
2022-05-07 20:08:51 +05:30
let(:error_message) { 'foo' }
2021-04-17 20:07:23 +05:30
it 'does not delete project' do
allow_next_instance_of(Snippets::BulkDestroyService) do |instance|
2022-05-07 20:08:51 +05:30
allow(instance).to receive(:execute).and_return(ServiceResponse.error(message: error_message))
2021-04-17 20:07:23 +05:30
end
2020-11-24 15:15:51 +05:30
2022-05-07 20:08:51 +05:30
expect(Gitlab::AppLogger).to receive(:error).with("Snippet deletion failed on #{project.full_path} with the following message: #{error_message}")
expect(Gitlab::AppLogger).to receive(:error).with(/Failed to remove project snippets/)
2021-04-17 20:07:23 +05:30
expect(destroy_project(project, user)).to be_falsey
expect(project.gitlab_shell.repository_exists?(project.repository_storage, path + '.git')).to be_truthy
2020-04-08 14:13:33 +05:30
end
end
end
2021-06-08 01:23:25 +05:30
context 'when project has webhooks' do
let!(:web_hook1) { create(:project_hook, project: project) }
let!(:web_hook2) { create(:project_hook, project: project) }
let!(:another_project_web_hook) { create(:project_hook) }
let!(:web_hook_log) { create(:web_hook_log, web_hook: web_hook1) }
it 'deletes webhooks and logs related to project' do
expect_next_instance_of(WebHooks::DestroyService, user) do |instance|
2022-08-13 15:12:31 +05:30
expect(instance).to receive(:execute).with(web_hook1).and_call_original
2021-06-08 01:23:25 +05:30
end
expect_next_instance_of(WebHooks::DestroyService, user) do |instance|
2022-08-13 15:12:31 +05:30
expect(instance).to receive(:execute).with(web_hook2).and_call_original
2021-06-08 01:23:25 +05:30
end
expect do
destroy_project(project, user)
end.to change(WebHook, :count).by(-2)
end
context 'when an error is raised deleting webhooks' do
before do
allow_next_instance_of(WebHooks::DestroyService) do |instance|
2022-08-13 15:12:31 +05:30
allow(instance).to receive(:execute).and_return(message: 'foo', status: :error)
2021-06-08 01:23:25 +05:30
end
end
it_behaves_like 'handles errors thrown during async destroy', "Failed to remove webhooks"
end
end
2021-09-30 23:02:18 +05:30
context 'when project has project bots' do
let!(:project_bot) { create(:user, :project_bot).tap { |user| project.add_maintainer(user) } }
it 'deletes bot user as well' do
2022-10-11 01:57:18 +05:30
expect_next_instance_of(Users::DestroyService, user) do |instance|
expect(instance).to receive(:execute).with(project_bot, skip_authorization: true).and_call_original
end
destroy_project(project, user)
2021-09-30 23:02:18 +05:30
end
end
2022-01-26 12:08:38 +05:30
context 'when project has events' do
let!(:event) { create(:event, :created, project: project, target: project, author: user) }
it 'deletes events from the project' do
expect do
destroy_project(project, user)
end.to change(Event, :count).by(-1)
end
context 'when an error is returned while deleting events' do
it 'does not delete project' do
allow_next_instance_of(Events::DestroyService) do |instance|
allow(instance).to receive(:execute).and_return(ServiceResponse.error(message: 'foo'))
end
expect(destroy_project(project, user)).to be_falsey
expect(project.delete_error).to include('Failed to remove events')
end
end
end
2021-04-17 20:07:23 +05:30
context 'error while destroying', :sidekiq_inline do
let!(:pipeline) { create(:ci_pipeline, project: project) }
let!(:builds) { create_list(:ci_build, 2, :artifacts, pipeline: pipeline) }
let!(:build_trace) { create(:ci_build_trace_chunk, build: builds[0]) }
2020-11-24 15:15:51 +05:30
2021-04-17 20:07:23 +05:30
it 'deletes on retry' do
# We can expect this to timeout for very large projects
# TODO: remove allow_next_instance_of: https://gitlab.com/gitlab-org/gitlab/-/issues/220440
allow_any_instance_of(Ci::Build).to receive(:destroy).and_raise('boom')
destroy_project(project, user, {})
allow_any_instance_of(Ci::Build).to receive(:destroy).and_call_original
destroy_project(project, user, {})
2020-11-24 15:15:51 +05:30
2021-04-17 20:07:23 +05:30
expect(Project.unscoped.all).not_to include(project)
expect(project.gitlab_shell.repository_exists?(project.repository_storage, path + '.git')).to be_falsey
expect(project.all_pipelines).to be_empty
expect(project.builds).to be_empty
end
2020-11-24 15:15:51 +05:30
end
2023-03-04 22:38:38 +05:30
context 'associations destoyed in batches' do
let!(:merge_request) { create(:merge_request, source_project: project) }
let!(:issue) { create(:issue, project: project) }
let!(:label) { create(:label, project: project) }
it 'destroys the associations marked as `dependent: :destroy`, in batches' do
query_recorder = ActiveRecord::QueryRecorder.new do
destroy_project(project, user, {})
end
expect(project.merge_requests).to be_empty
expect(project.issues).to be_empty
expect(project.labels).to be_empty
expected_queries = [
delete_in_batches_regexps(:merge_requests, :target_project_id, project, [merge_request]),
delete_in_batches_regexps(:issues, :project_id, project, [issue]),
delete_in_batches_regexps(:labels, :project_id, project, [label])
].flatten
expect(query_recorder.log).to include(*expected_queries)
end
end
2020-03-13 15:44:24 +05:30
def destroy_project(project, user, params = {})
described_class.new(project, user, params).public_send(async ? :async_execute : :execute)
2018-05-09 12:01:36 +05:30
end
2015-09-11 14:41:01 +05:30
end