Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFrancisco Javier López <fjlopez@gitlab.com>2018-04-06 18:23:49 +0300
committerDouwe Maan <douwe@gitlab.com>2018-04-06 18:23:49 +0300
commitf20912df033d07c46b0989012244d96d0a12b66d (patch)
tree6207b8face17f9b7166ba1a5e047032e3927e53e /spec/services
parent44f4a674e2a87d104f700265d835aba000c589f0 (diff)
Extend API for importing a project export with overwrite support
Diffstat (limited to 'spec/services')
-rw-r--r--spec/services/projects/destroy_service_spec.rb22
-rw-r--r--spec/services/projects/gitlab_projects_import_service_spec.rb26
-rw-r--r--spec/services/projects/move_access_service_spec.rb114
-rw-r--r--spec/services/projects/move_deploy_keys_projects_service_spec.rb58
-rw-r--r--spec/services/projects/move_forks_service_spec.rb96
-rw-r--r--spec/services/projects/move_lfs_objects_projects_service_spec.rb55
-rw-r--r--spec/services/projects/move_notification_settings_service_spec.rb56
-rw-r--r--spec/services/projects/move_project_authorizations_service_spec.rb56
-rw-r--r--spec/services/projects/move_project_group_links_service_spec.rb65
-rw-r--r--spec/services/projects/move_project_members_service_spec.rb65
-rw-r--r--spec/services/projects/move_users_star_projects_service_spec.rb42
-rw-r--r--spec/services/projects/overwrite_project_service_spec.rb198
12 files changed, 852 insertions, 1 deletions
diff --git a/spec/services/projects/destroy_service_spec.rb b/spec/services/projects/destroy_service_spec.rb
index 9bb1cda565c..a66e3c5e995 100644
--- a/spec/services/projects/destroy_service_spec.rb
+++ b/spec/services/projects/destroy_service_spec.rb
@@ -248,6 +248,28 @@ describe Projects::DestroyService do
end
end
+ context '#attempt_restore_repositories' do
+ let(:path) { project.disk_path + '.git' }
+
+ before do
+ expect(project.gitlab_shell.exists?(project.repository_storage_path, path)).to be_truthy
+ expect(project.gitlab_shell.exists?(project.repository_storage_path, remove_path)).to be_falsey
+
+ # Dont run sidekiq to check if renamed repository exists
+ Sidekiq::Testing.fake! { destroy_project(project, user, {}) }
+
+ expect(project.gitlab_shell.exists?(project.repository_storage_path, path)).to be_falsey
+ expect(project.gitlab_shell.exists?(project.repository_storage_path, remove_path)).to be_truthy
+ end
+
+ it 'restores the repositories' do
+ Sidekiq::Testing.fake! { described_class.new(project, user).attempt_repositories_rollback }
+
+ expect(project.gitlab_shell.exists?(project.repository_storage_path, path)).to be_truthy
+ expect(project.gitlab_shell.exists?(project.repository_storage_path, remove_path)).to be_falsey
+ end
+ end
+
def destroy_project(project, user, params = {})
if async
Projects::DestroyService.new(project, user, params).async_execute
diff --git a/spec/services/projects/gitlab_projects_import_service_spec.rb b/spec/services/projects/gitlab_projects_import_service_spec.rb
index 880b2aae66a..ee1a886f5d6 100644
--- a/spec/services/projects/gitlab_projects_import_service_spec.rb
+++ b/spec/services/projects/gitlab_projects_import_service_spec.rb
@@ -4,7 +4,8 @@ describe Projects::GitlabProjectsImportService do
set(:namespace) { create(:namespace) }
let(:path) { 'test-path' }
let(:file) { fixture_file_upload(Rails.root + 'spec/fixtures/doc_sample.txt', 'text/plain') }
- let(:import_params) { { namespace_id: namespace.id, path: path, file: file } }
+ let(:overwrite) { false }
+ let(:import_params) { { namespace_id: namespace.id, path: path, file: file, overwrite: overwrite } }
subject { described_class.new(namespace.owner, import_params) }
describe '#execute' do
@@ -37,5 +38,28 @@ describe Projects::GitlabProjectsImportService do
expect(project.import_data.data['override_params']['description']).to eq('Hello')
end
end
+
+ context 'when there is a project with the same path' do
+ let(:existing_project) { create(:project, namespace: namespace) }
+ let(:path) { existing_project.path}
+
+ it 'does not create the project' do
+ project = subject.execute
+
+ expect(project).to be_invalid
+ expect(project).not_to be_persisted
+ end
+
+ context 'when overwrite param is set' do
+ let(:overwrite) { true }
+
+ it 'creates a project in a temporary full_path' do
+ project = subject.execute
+
+ expect(project).to be_valid
+ expect(project).to be_persisted
+ end
+ end
+ end
end
end
diff --git a/spec/services/projects/move_access_service_spec.rb b/spec/services/projects/move_access_service_spec.rb
new file mode 100644
index 00000000000..a820ebd91f4
--- /dev/null
+++ b/spec/services/projects/move_access_service_spec.rb
@@ -0,0 +1,114 @@
+require 'spec_helper'
+
+describe Projects::MoveAccessService do
+ let(:user) { create(:user) }
+ let(:group) { create(:group) }
+ let(:project_with_access) { create(:project, namespace: user.namespace) }
+ let(:master_user) { create(:user) }
+ let(:reporter_user) { create(:user) }
+ let(:developer_user) { create(:user) }
+ let(:master_group) { create(:group) }
+ let(:reporter_group) { create(:group) }
+ let(:developer_group) { create(:group) }
+
+ before do
+ project_with_access.add_master(master_user)
+ project_with_access.add_developer(developer_user)
+ project_with_access.add_reporter(reporter_user)
+ project_with_access.project_group_links.create(group: master_group, group_access: Gitlab::Access::MASTER)
+ project_with_access.project_group_links.create(group: developer_group, group_access: Gitlab::Access::DEVELOPER)
+ project_with_access.project_group_links.create(group: reporter_group, group_access: Gitlab::Access::REPORTER)
+ end
+
+ subject { described_class.new(target_project, user) }
+
+ describe '#execute' do
+ shared_examples 'move the accesses' do
+ it do
+ expect(project_with_access.project_members.count).to eq 4
+ expect(project_with_access.project_group_links.count).to eq 3
+ expect(project_with_access.authorized_users.count).to eq 4
+
+ subject.execute(project_with_access)
+
+ expect(project_with_access.project_members.count).to eq 0
+ expect(project_with_access.project_group_links.count).to eq 0
+ expect(project_with_access.authorized_users.count).to eq 1
+ expect(target_project.project_members.count).to eq 4
+ expect(target_project.project_group_links.count).to eq 3
+ expect(target_project.authorized_users.count).to eq 4
+ end
+
+ it 'rollbacks if an exception is raised' do
+ allow(subject).to receive(:success).and_raise(StandardError)
+
+ expect { subject.execute(project_with_groups) }.to raise_error(StandardError)
+
+ expect(project_with_access.project_members.count).to eq 4
+ expect(project_with_access.project_group_links.count).to eq 3
+ expect(project_with_access.authorized_users.count).to eq 4
+ end
+ end
+
+ context 'when both projects are in the same namespace' do
+ let(:target_project) { create(:project, namespace: user.namespace) }
+
+ it 'does not refresh project owner authorized projects' do
+ allow(project_with_access).to receive(:namespace).and_return(user.namespace)
+ expect(project_with_access.namespace).not_to receive(:refresh_project_authorizations)
+ expect(target_project.namespace).not_to receive(:refresh_project_authorizations)
+
+ subject.execute(project_with_access)
+ end
+
+ it_behaves_like 'move the accesses'
+ end
+
+ context 'when projects are in different namespaces' do
+ let(:target_project) { create(:project, namespace: group) }
+
+ before do
+ group.add_owner(user)
+ end
+
+ it 'refreshes both project owner authorized projects' do
+ allow(project_with_access).to receive(:namespace).and_return(user.namespace)
+ expect(user.namespace).to receive(:refresh_project_authorizations).once
+ expect(group).to receive(:refresh_project_authorizations).once
+
+ subject.execute(project_with_access)
+ end
+
+ it_behaves_like 'move the accesses'
+ end
+
+ context 'when remove_remaining_elements is false' do
+ let(:target_project) { create(:project, namespace: user.namespace) }
+ let(:options) { { remove_remaining_elements: false } }
+
+ it 'does not remove remaining memberships' do
+ target_project.add_master(master_user)
+
+ subject.execute(project_with_access, options)
+
+ expect(project_with_access.project_members.count).not_to eq 0
+ end
+
+ it 'does not remove remaining group links' do
+ target_project.project_group_links.create(group: master_group, group_access: Gitlab::Access::MASTER)
+
+ subject.execute(project_with_access, options)
+
+ expect(project_with_access.project_group_links.count).not_to eq 0
+ end
+
+ it 'does not remove remaining authorizations' do
+ target_project.add_developer(developer_user)
+
+ subject.execute(project_with_access, options)
+
+ expect(project_with_access.project_authorizations.count).not_to eq 0
+ end
+ end
+ end
+end
diff --git a/spec/services/projects/move_deploy_keys_projects_service_spec.rb b/spec/services/projects/move_deploy_keys_projects_service_spec.rb
new file mode 100644
index 00000000000..c548edf39a8
--- /dev/null
+++ b/spec/services/projects/move_deploy_keys_projects_service_spec.rb
@@ -0,0 +1,58 @@
+require 'spec_helper'
+
+describe Projects::MoveDeployKeysProjectsService do
+ let!(:user) { create(:user) }
+ let!(:project_with_deploy_keys) { create(:project, namespace: user.namespace) }
+ let!(:target_project) { create(:project, namespace: user.namespace) }
+
+ subject { described_class.new(target_project, user) }
+
+ describe '#execute' do
+ before do
+ create_list(:deploy_keys_project, 2, project: project_with_deploy_keys)
+ end
+
+ it 'moves the user\'s deploy keys from one project to another' do
+ expect(project_with_deploy_keys.deploy_keys_projects.count).to eq 2
+ expect(target_project.deploy_keys_projects.count).to eq 0
+
+ subject.execute(project_with_deploy_keys)
+
+ expect(project_with_deploy_keys.deploy_keys_projects.count).to eq 0
+ expect(target_project.deploy_keys_projects.count).to eq 2
+ end
+
+ it 'does not link existent deploy_keys in the current project' do
+ target_project.deploy_keys << project_with_deploy_keys.deploy_keys.first
+
+ expect(project_with_deploy_keys.deploy_keys_projects.count).to eq 2
+ expect(target_project.deploy_keys_projects.count).to eq 1
+
+ subject.execute(project_with_deploy_keys)
+
+ expect(project_with_deploy_keys.deploy_keys_projects.count).to eq 0
+ expect(target_project.deploy_keys_projects.count).to eq 2
+ end
+
+ it 'rollbacks changes if transaction fails' do
+ allow(subject).to receive(:success).and_raise(StandardError)
+
+ expect { subject.execute(project_with_deploy_keys) }.to raise_error(StandardError)
+
+ expect(project_with_deploy_keys.deploy_keys_projects.count).to eq 2
+ expect(target_project.deploy_keys_projects.count).to eq 0
+ end
+
+ context 'when remove_remaining_elements is false' do
+ let(:options) { { remove_remaining_elements: false } }
+
+ it 'does not remove remaining deploy keys projects' do
+ target_project.deploy_keys << project_with_deploy_keys.deploy_keys.first
+
+ subject.execute(project_with_deploy_keys, options)
+
+ expect(project_with_deploy_keys.deploy_keys_projects.count).not_to eq 0
+ end
+ end
+ end
+end
diff --git a/spec/services/projects/move_forks_service_spec.rb b/spec/services/projects/move_forks_service_spec.rb
new file mode 100644
index 00000000000..f4a5a7f9fc2
--- /dev/null
+++ b/spec/services/projects/move_forks_service_spec.rb
@@ -0,0 +1,96 @@
+require 'spec_helper'
+
+describe Projects::MoveForksService do
+ include ProjectForksHelper
+
+ let!(:user) { create(:user) }
+ let!(:project_with_forks) { create(:project, namespace: user.namespace) }
+ let!(:target_project) { create(:project, namespace: user.namespace) }
+ let!(:lvl1_forked_project_1) { fork_project(project_with_forks, user) }
+ let!(:lvl1_forked_project_2) { fork_project(project_with_forks, user) }
+ let!(:lvl2_forked_project_1_1) { fork_project(lvl1_forked_project_1, user) }
+ let!(:lvl2_forked_project_1_2) { fork_project(lvl1_forked_project_1, user) }
+
+ subject { described_class.new(target_project, user) }
+
+ describe '#execute' do
+ context 'when moving a root forked project' do
+ it 'moves the descendant forks' do
+ expect(project_with_forks.forks.count).to eq 2
+ expect(target_project.forks.count).to eq 0
+
+ subject.execute(project_with_forks)
+
+ expect(project_with_forks.forks.count).to eq 0
+ expect(target_project.forks.count).to eq 2
+ expect(lvl1_forked_project_1.forked_from_project).to eq target_project
+ expect(lvl1_forked_project_1.fork_network_member.forked_from_project).to eq target_project
+ expect(lvl1_forked_project_2.forked_from_project).to eq target_project
+ expect(lvl1_forked_project_2.fork_network_member.forked_from_project).to eq target_project
+ end
+
+ it 'updates the fork network' do
+ expect(project_with_forks.fork_network.root_project).to eq project_with_forks
+ expect(project_with_forks.fork_network.fork_network_members.map(&:project)).to include project_with_forks
+
+ subject.execute(project_with_forks)
+
+ expect(target_project.reload.fork_network.root_project).to eq target_project
+ expect(target_project.fork_network.fork_network_members.map(&:project)).not_to include project_with_forks
+ end
+ end
+
+ context 'when moving a intermediate forked project' do
+ it 'moves the descendant forks' do
+ expect(lvl1_forked_project_1.forks.count).to eq 2
+ expect(target_project.forks.count).to eq 0
+
+ subject.execute(lvl1_forked_project_1)
+
+ expect(lvl1_forked_project_1.forks.count).to eq 0
+ expect(target_project.forks.count).to eq 2
+ expect(lvl2_forked_project_1_1.forked_from_project).to eq target_project
+ expect(lvl2_forked_project_1_1.fork_network_member.forked_from_project).to eq target_project
+ expect(lvl2_forked_project_1_2.forked_from_project).to eq target_project
+ expect(lvl2_forked_project_1_2.fork_network_member.forked_from_project).to eq target_project
+ end
+
+ it 'moves the ascendant fork' do
+ subject.execute(lvl1_forked_project_1)
+
+ expect(target_project.forked_from_project).to eq project_with_forks
+ expect(target_project.fork_network_member.forked_from_project).to eq project_with_forks
+ end
+
+ it 'does not update fork network' do
+ subject.execute(lvl1_forked_project_1)
+
+ expect(target_project.reload.fork_network.root_project).to eq project_with_forks
+ end
+ end
+
+ context 'when moving a leaf forked project' do
+ it 'moves the ascendant fork' do
+ subject.execute(lvl2_forked_project_1_1)
+
+ expect(target_project.forked_from_project).to eq lvl1_forked_project_1
+ expect(target_project.fork_network_member.forked_from_project).to eq lvl1_forked_project_1
+ end
+
+ it 'does not update fork network' do
+ subject.execute(lvl2_forked_project_1_1)
+
+ expect(target_project.reload.fork_network.root_project).to eq project_with_forks
+ end
+ end
+
+ it 'rollbacks changes if transaction fails' do
+ allow(subject).to receive(:success).and_raise(StandardError)
+
+ expect { subject.execute(project_with_forks) }.to raise_error(StandardError)
+
+ expect(project_with_forks.forks.count).to eq 2
+ expect(target_project.forks.count).to eq 0
+ end
+ end
+end
diff --git a/spec/services/projects/move_lfs_objects_projects_service_spec.rb b/spec/services/projects/move_lfs_objects_projects_service_spec.rb
new file mode 100644
index 00000000000..517a24a982a
--- /dev/null
+++ b/spec/services/projects/move_lfs_objects_projects_service_spec.rb
@@ -0,0 +1,55 @@
+require 'spec_helper'
+
+describe Projects::MoveLfsObjectsProjectsService do
+ let!(:user) { create(:user) }
+ let!(:project_with_lfs_objects) { create(:project, namespace: user.namespace) }
+ let!(:target_project) { create(:project, namespace: user.namespace) }
+
+ subject { described_class.new(target_project, user) }
+
+ before do
+ create_list(:lfs_objects_project, 3, project: project_with_lfs_objects)
+ end
+
+ describe '#execute' do
+ it 'links the lfs objects from existent in source project' do
+ expect(target_project.lfs_objects.count).to eq 0
+
+ subject.execute(project_with_lfs_objects)
+
+ expect(project_with_lfs_objects.reload.lfs_objects.count).to eq 0
+ expect(target_project.reload.lfs_objects.count).to eq 3
+ end
+
+ it 'does not link existent lfs_object in the current project' do
+ target_project.lfs_objects << project_with_lfs_objects.lfs_objects.first(2)
+
+ expect(target_project.lfs_objects.count).to eq 2
+
+ subject.execute(project_with_lfs_objects)
+
+ expect(target_project.lfs_objects.count).to eq 3
+ end
+
+ it 'rollbacks changes if transaction fails' do
+ allow(subject).to receive(:success).and_raise(StandardError)
+
+ expect { subject.execute(project_with_lfs_objects) }.to raise_error(StandardError)
+
+ expect(project_with_lfs_objects.lfs_objects.count).to eq 3
+ expect(target_project.lfs_objects.count).to eq 0
+ end
+
+ context 'when remove_remaining_elements is false' do
+ let(:options) { { remove_remaining_elements: false } }
+
+ it 'does not remove remaining lfs objects' do
+ target_project.lfs_objects << project_with_lfs_objects.lfs_objects.first(2)
+
+ subject.execute(project_with_lfs_objects, options)
+
+ expect(project_with_lfs_objects.lfs_objects.count).not_to eq 0
+ end
+ end
+ end
+end
diff --git a/spec/services/projects/move_notification_settings_service_spec.rb b/spec/services/projects/move_notification_settings_service_spec.rb
new file mode 100644
index 00000000000..24d69eef86a
--- /dev/null
+++ b/spec/services/projects/move_notification_settings_service_spec.rb
@@ -0,0 +1,56 @@
+require 'spec_helper'
+
+describe Projects::MoveNotificationSettingsService do
+ let(:user) { create(:user) }
+ let(:project_with_notifications) { create(:project, namespace: user.namespace) }
+ let(:target_project) { create(:project, namespace: user.namespace) }
+
+ subject { described_class.new(target_project, user) }
+
+ describe '#execute' do
+ context 'with notification settings' do
+ before do
+ create_list(:notification_setting, 2, source: project_with_notifications)
+ end
+
+ it 'moves the user\'s notification settings from one project to another' do
+ expect(project_with_notifications.notification_settings.count).to eq 3
+ expect(target_project.notification_settings.count).to eq 1
+
+ subject.execute(project_with_notifications)
+
+ expect(project_with_notifications.notification_settings.count).to eq 0
+ expect(target_project.notification_settings.count).to eq 3
+ end
+
+ it 'rollbacks changes if transaction fails' do
+ allow(subject).to receive(:success).and_raise(StandardError)
+
+ expect { subject.execute(project_with_notifications) }.to raise_error(StandardError)
+
+ expect(project_with_notifications.notification_settings.count).to eq 3
+ expect(target_project.notification_settings.count).to eq 1
+ end
+ end
+
+ it 'does not move existent notification settings in the current project' do
+ expect(project_with_notifications.notification_settings.count).to eq 1
+ expect(target_project.notification_settings.count).to eq 1
+ expect(user.notification_settings.count).to eq 2
+
+ subject.execute(project_with_notifications)
+
+ expect(user.notification_settings.count).to eq 1
+ end
+
+ context 'when remove_remaining_elements is false' do
+ let(:options) { { remove_remaining_elements: false } }
+
+ it 'does not remove remaining notification settings' do
+ subject.execute(project_with_notifications, options)
+
+ expect(project_with_notifications.notification_settings.count).not_to eq 0
+ end
+ end
+ end
+end
diff --git a/spec/services/projects/move_project_authorizations_service_spec.rb b/spec/services/projects/move_project_authorizations_service_spec.rb
new file mode 100644
index 00000000000..f7262b9b887
--- /dev/null
+++ b/spec/services/projects/move_project_authorizations_service_spec.rb
@@ -0,0 +1,56 @@
+require 'spec_helper'
+
+describe Projects::MoveProjectAuthorizationsService do
+ let!(:user) { create(:user) }
+ let(:project_with_users) { create(:project, namespace: user.namespace) }
+ let(:target_project) { create(:project, namespace: user.namespace) }
+ let(:master_user) { create(:user) }
+ let(:reporter_user) { create(:user) }
+ let(:developer_user) { create(:user) }
+
+ subject { described_class.new(target_project, user) }
+
+ describe '#execute' do
+ before do
+ project_with_users.add_master(master_user)
+ project_with_users.add_developer(developer_user)
+ project_with_users.add_reporter(reporter_user)
+ end
+
+ it 'moves the authorizations from one project to another' do
+ expect(project_with_users.authorized_users.count).to eq 4
+ expect(target_project.authorized_users.count).to eq 1
+
+ subject.execute(project_with_users)
+
+ expect(project_with_users.authorized_users.count).to eq 0
+ expect(target_project.authorized_users.count).to eq 4
+ end
+
+ it 'does not move existent authorizations to the current project' do
+ target_project.add_master(developer_user)
+ target_project.add_developer(reporter_user)
+
+ expect(project_with_users.authorized_users.count).to eq 4
+ expect(target_project.authorized_users.count).to eq 3
+
+ subject.execute(project_with_users)
+
+ expect(project_with_users.authorized_users.count).to eq 0
+ expect(target_project.authorized_users.count).to eq 4
+ end
+
+ context 'when remove_remaining_elements is false' do
+ let(:options) { { remove_remaining_elements: false } }
+
+ it 'does not remove remaining project authorizations' do
+ target_project.add_master(developer_user)
+ target_project.add_developer(reporter_user)
+
+ subject.execute(project_with_users, options)
+
+ expect(project_with_users.project_authorizations.count).not_to eq 0
+ end
+ end
+ end
+end
diff --git a/spec/services/projects/move_project_group_links_service_spec.rb b/spec/services/projects/move_project_group_links_service_spec.rb
new file mode 100644
index 00000000000..e3d06e6d3d7
--- /dev/null
+++ b/spec/services/projects/move_project_group_links_service_spec.rb
@@ -0,0 +1,65 @@
+require 'spec_helper'
+
+describe Projects::MoveProjectGroupLinksService do
+ let!(:user) { create(:user) }
+ let(:project_with_groups) { create(:project, namespace: user.namespace) }
+ let(:target_project) { create(:project, namespace: user.namespace) }
+ let(:master_group) { create(:group) }
+ let(:reporter_group) { create(:group) }
+ let(:developer_group) { create(:group) }
+
+ subject { described_class.new(target_project, user) }
+
+ describe '#execute' do
+ before do
+ project_with_groups.project_group_links.create(group: master_group, group_access: Gitlab::Access::MASTER)
+ project_with_groups.project_group_links.create(group: developer_group, group_access: Gitlab::Access::DEVELOPER)
+ project_with_groups.project_group_links.create(group: reporter_group, group_access: Gitlab::Access::REPORTER)
+ end
+
+ it 'moves the group links from one project to another' do
+ expect(project_with_groups.project_group_links.count).to eq 3
+ expect(target_project.project_group_links.count).to eq 0
+
+ subject.execute(project_with_groups)
+
+ expect(project_with_groups.project_group_links.count).to eq 0
+ expect(target_project.project_group_links.count).to eq 3
+ end
+
+ it 'does not move existent group links in the current project' do
+ target_project.project_group_links.create(group: master_group, group_access: Gitlab::Access::MASTER)
+ target_project.project_group_links.create(group: developer_group, group_access: Gitlab::Access::DEVELOPER)
+
+ expect(project_with_groups.project_group_links.count).to eq 3
+ expect(target_project.project_group_links.count).to eq 2
+
+ subject.execute(project_with_groups)
+
+ expect(project_with_groups.project_group_links.count).to eq 0
+ expect(target_project.project_group_links.count).to eq 3
+ end
+
+ it 'rollbacks changes if transaction fails' do
+ allow(subject).to receive(:success).and_raise(StandardError)
+
+ expect { subject.execute(project_with_groups) }.to raise_error(StandardError)
+
+ expect(project_with_groups.project_group_links.count).to eq 3
+ expect(target_project.project_group_links.count).to eq 0
+ end
+
+ context 'when remove_remaining_elements is false' do
+ let(:options) { { remove_remaining_elements: false } }
+
+ it 'does not remove remaining project group links' do
+ target_project.project_group_links.create(group: master_group, group_access: Gitlab::Access::MASTER)
+ target_project.project_group_links.create(group: developer_group, group_access: Gitlab::Access::DEVELOPER)
+
+ subject.execute(project_with_groups, options)
+
+ expect(project_with_groups.project_group_links.count).not_to eq 0
+ end
+ end
+ end
+end
diff --git a/spec/services/projects/move_project_members_service_spec.rb b/spec/services/projects/move_project_members_service_spec.rb
new file mode 100644
index 00000000000..9c9a2d2fde1
--- /dev/null
+++ b/spec/services/projects/move_project_members_service_spec.rb
@@ -0,0 +1,65 @@
+require 'spec_helper'
+
+describe Projects::MoveProjectMembersService do
+ let!(:user) { create(:user) }
+ let(:project_with_users) { create(:project, namespace: user.namespace) }
+ let(:target_project) { create(:project, namespace: user.namespace) }
+ let(:master_user) { create(:user) }
+ let(:reporter_user) { create(:user) }
+ let(:developer_user) { create(:user) }
+
+ subject { described_class.new(target_project, user) }
+
+ describe '#execute' do
+ before do
+ project_with_users.add_master(master_user)
+ project_with_users.add_developer(developer_user)
+ project_with_users.add_reporter(reporter_user)
+ end
+
+ it 'moves the members from one project to another' do
+ expect(project_with_users.project_members.count).to eq 4
+ expect(target_project.project_members.count).to eq 1
+
+ subject.execute(project_with_users)
+
+ expect(project_with_users.project_members.count).to eq 0
+ expect(target_project.project_members.count).to eq 4
+ end
+
+ it 'does not move existent members to the current project' do
+ target_project.add_master(developer_user)
+ target_project.add_developer(reporter_user)
+
+ expect(project_with_users.project_members.count).to eq 4
+ expect(target_project.project_members.count).to eq 3
+
+ subject.execute(project_with_users)
+
+ expect(project_with_users.project_members.count).to eq 0
+ expect(target_project.project_members.count).to eq 4
+ end
+
+ it 'rollbacks changes if transaction fails' do
+ allow(subject).to receive(:success).and_raise(StandardError)
+
+ expect { subject.execute(project_with_users) }.to raise_error(StandardError)
+
+ expect(project_with_users.project_members.count).to eq 4
+ expect(target_project.project_members.count).to eq 1
+ end
+
+ context 'when remove_remaining_elements is false' do
+ let(:options) { { remove_remaining_elements: false } }
+
+ it 'does not remove remaining project members' do
+ target_project.add_master(developer_user)
+ target_project.add_developer(reporter_user)
+
+ subject.execute(project_with_users, options)
+
+ expect(project_with_users.project_members.count).not_to eq 0
+ end
+ end
+ end
+end
diff --git a/spec/services/projects/move_users_star_projects_service_spec.rb b/spec/services/projects/move_users_star_projects_service_spec.rb
new file mode 100644
index 00000000000..e0545c5a21b
--- /dev/null
+++ b/spec/services/projects/move_users_star_projects_service_spec.rb
@@ -0,0 +1,42 @@
+require 'spec_helper'
+
+describe Projects::MoveUsersStarProjectsService do
+ let!(:user) { create(:user) }
+ let!(:project_with_stars) { create(:project, namespace: user.namespace) }
+ let!(:target_project) { create(:project, namespace: user.namespace) }
+
+ subject { described_class.new(target_project, user) }
+
+ describe '#execute' do
+ before do
+ create_list(:users_star_project, 2, project: project_with_stars)
+ end
+
+ it 'moves the user\'s stars from one project to another' do
+ expect(project_with_stars.users_star_projects.count).to eq 2
+ expect(project_with_stars.star_count).to eq 2
+ expect(target_project.users_star_projects.count).to eq 0
+ expect(target_project.star_count).to eq 0
+
+ subject.execute(project_with_stars)
+ project_with_stars.reload
+ target_project.reload
+
+ expect(project_with_stars.users_star_projects.count).to eq 0
+ expect(project_with_stars.star_count).to eq 0
+ expect(target_project.users_star_projects.count).to eq 2
+ expect(target_project.star_count).to eq 2
+ end
+
+ it 'rollbacks changes if transaction fails' do
+ allow(subject).to receive(:success).and_raise(StandardError)
+
+ expect { subject.execute(project_with_stars) }.to raise_error(StandardError)
+
+ expect(project_with_stars.users_star_projects.count).to eq 2
+ expect(project_with_stars.star_count).to eq 2
+ expect(target_project.users_star_projects.count).to eq 0
+ expect(target_project.star_count).to eq 0
+ end
+ end
+end
diff --git a/spec/services/projects/overwrite_project_service_spec.rb b/spec/services/projects/overwrite_project_service_spec.rb
new file mode 100644
index 00000000000..252c61f4224
--- /dev/null
+++ b/spec/services/projects/overwrite_project_service_spec.rb
@@ -0,0 +1,198 @@
+require 'spec_helper'
+
+describe Projects::OverwriteProjectService do
+ include ProjectForksHelper
+
+ let(:user) { create(:user) }
+ let(:project_from) { create(:project, namespace: user.namespace) }
+ let(:project_to) { create(:project, namespace: user.namespace) }
+ let!(:lvl1_forked_project_1) { fork_project(project_from, user) }
+ let!(:lvl1_forked_project_2) { fork_project(project_from, user) }
+ let!(:lvl2_forked_project_1_1) { fork_project(lvl1_forked_project_1, user) }
+ let!(:lvl2_forked_project_1_2) { fork_project(lvl1_forked_project_1, user) }
+
+ subject { described_class.new(project_to, user) }
+
+ before do
+ allow(project_to).to receive(:import_data).and_return(double(data: { 'original_path' => project_from.path }))
+ end
+
+ describe '#execute' do
+ shared_examples 'overwrite actions' do
+ it 'moves deploy keys' do
+ deploy_keys_count = project_from.deploy_keys_projects.count
+
+ subject.execute(project_from)
+
+ expect(project_to.deploy_keys_projects.count).to eq deploy_keys_count
+ end
+
+ it 'moves notification settings' do
+ notification_count = project_from.notification_settings.count
+
+ subject.execute(project_from)
+
+ expect(project_to.notification_settings.count).to eq notification_count
+ end
+
+ it 'moves users stars' do
+ stars_count = project_from.users_star_projects.count
+
+ subject.execute(project_from)
+ project_to.reload
+
+ expect(project_to.users_star_projects.count).to eq stars_count
+ expect(project_to.star_count).to eq stars_count
+ end
+
+ it 'moves project group links' do
+ group_links_count = project_from.project_group_links.count
+
+ subject.execute(project_from)
+
+ expect(project_to.project_group_links.count).to eq group_links_count
+ end
+
+ it 'moves memberships and authorizations' do
+ members_count = project_from.project_members.count
+ project_authorizations = project_from.project_authorizations.count
+
+ subject.execute(project_from)
+
+ expect(project_to.project_members.count).to eq members_count
+ expect(project_to.project_authorizations.count).to eq project_authorizations
+ end
+
+ context 'moves lfs objects relationships' do
+ before do
+ create_list(:lfs_objects_project, 3, project: project_from)
+ end
+
+ it do
+ lfs_objects_count = project_from.lfs_objects.count
+
+ subject.execute(project_from)
+
+ expect(project_to.lfs_objects.count).to eq lfs_objects_count
+ end
+ end
+
+ it 'removes the original project' do
+ subject.execute(project_from)
+
+ expect { Project.find(project_from.id) }.to raise_error(ActiveRecord::RecordNotFound)
+ end
+
+ it 'renames the project' do
+ subject.execute(project_from)
+
+ expect(project_to.full_path).to eq project_from.full_path
+ end
+ end
+
+ context 'when project does not have any relation' do
+ it_behaves_like 'overwrite actions'
+ end
+
+ context 'when project with elements' do
+ it_behaves_like 'overwrite actions' do
+ let(:master_user) { create(:user) }
+ let(:reporter_user) { create(:user) }
+ let(:developer_user) { create(:user) }
+ let(:master_group) { create(:group) }
+ let(:reporter_group) { create(:group) }
+ let(:developer_group) { create(:group) }
+
+ before do
+ create_list(:deploy_keys_project, 2, project: project_from)
+ create_list(:notification_setting, 2, source: project_from)
+ create_list(:users_star_project, 2, project: project_from)
+ project_from.project_group_links.create(group: master_group, group_access: Gitlab::Access::MASTER)
+ project_from.project_group_links.create(group: developer_group, group_access: Gitlab::Access::DEVELOPER)
+ project_from.project_group_links.create(group: reporter_group, group_access: Gitlab::Access::REPORTER)
+ project_from.add_master(master_user)
+ project_from.add_developer(developer_user)
+ project_from.add_reporter(reporter_user)
+ end
+ end
+ end
+
+ context 'forks' do
+ context 'when moving a root forked project' do
+ it 'moves the descendant forks' do
+ expect(project_from.forks.count).to eq 2
+ expect(project_to.forks.count).to eq 0
+
+ subject.execute(project_from)
+
+ expect(project_from.forks.count).to eq 0
+ expect(project_to.forks.count).to eq 2
+ expect(lvl1_forked_project_1.forked_from_project).to eq project_to
+ expect(lvl1_forked_project_1.fork_network_member.forked_from_project).to eq project_to
+ expect(lvl1_forked_project_2.forked_from_project).to eq project_to
+ expect(lvl1_forked_project_2.fork_network_member.forked_from_project).to eq project_to
+ end
+
+ it 'updates the fork network' do
+ expect(project_from.fork_network.root_project).to eq project_from
+ expect(project_from.fork_network.fork_network_members.map(&:project)).to include project_from
+
+ subject.execute(project_from)
+
+ expect(project_to.reload.fork_network.root_project).to eq project_to
+ expect(project_to.fork_network.fork_network_members.map(&:project)).not_to include project_from
+ end
+ end
+ context 'when moving a intermediate forked project' do
+ let(:project_to) { create(:project, namespace: lvl1_forked_project_1.namespace) }
+
+ it 'moves the descendant forks' do
+ expect(lvl1_forked_project_1.forks.count).to eq 2
+ expect(project_to.forks.count).to eq 0
+
+ subject.execute(lvl1_forked_project_1)
+
+ expect(lvl1_forked_project_1.forks.count).to eq 0
+ expect(project_to.forks.count).to eq 2
+ expect(lvl2_forked_project_1_1.forked_from_project).to eq project_to
+ expect(lvl2_forked_project_1_1.fork_network_member.forked_from_project).to eq project_to
+ expect(lvl2_forked_project_1_2.forked_from_project).to eq project_to
+ expect(lvl2_forked_project_1_2.fork_network_member.forked_from_project).to eq project_to
+ end
+
+ it 'moves the ascendant fork' do
+ subject.execute(lvl1_forked_project_1)
+
+ expect(project_to.reload.forked_from_project).to eq project_from
+ expect(project_to.fork_network_member.forked_from_project).to eq project_from
+ end
+
+ it 'does not update fork network' do
+ subject.execute(lvl1_forked_project_1)
+
+ expect(project_to.reload.fork_network.root_project).to eq project_from
+ end
+ end
+ end
+
+ context 'if an exception is raised' do
+ it 'rollbacks changes' do
+ updated_at = project_from.updated_at
+
+ allow(subject).to receive(:rename_project).and_raise(StandardError)
+
+ expect { subject.execute(project_from) }.to raise_error(StandardError)
+ expect(Project.find(project_from.id)).not_to be_nil
+ expect(project_from.reload.updated_at.change(usec: 0)).to eq updated_at.change(usec: 0)
+ end
+
+ it 'tries to restore the original project repositories' do
+ allow(subject).to receive(:rename_project).and_raise(StandardError)
+
+ expect(subject).to receive(:attempt_restore_repositories).with(project_from)
+
+ expect { subject.execute(project_from) }.to raise_error(StandardError)
+ end
+ end
+ end
+end