2019-07-31 22:56:46 +05:30
# frozen_string_literal: true
2019-12-04 20:38:33 +05:30
require 'spec_helper'
2018-03-17 18:26:18 +05:30
2023-03-17 16:20:25 +05:30
RSpec . describe Groups :: TransferService , :sidekiq_inline , feature_category : :subgroups do
2021-11-18 22:05:49 +05:30
shared_examples 'project namespace path is in sync with project path' do
it 'keeps project and project namespace attributes in sync' do
projects_with_project_namespace . each do | project |
project . reload
expect ( project . full_path ) . to eq ( " #{ group_full_path } / #{ project . path } " )
expect ( project . project_namespace . full_path ) . to eq ( project . full_path )
expect ( project . project_namespace . parent ) . to eq ( project . namespace )
expect ( project . project_namespace . visibility_level ) . to eq ( project . visibility_level )
end
end
end
2021-02-22 17:27:13 +05:30
let_it_be ( :user ) { create ( :user ) }
2022-06-21 17:19:12 +05:30
let_it_be ( :new_parent_group ) { create ( :group , :public , :crm_enabled ) }
2021-04-29 21:17:54 +05:30
2018-03-17 18:26:18 +05:30
let! ( :group_member ) { create ( :group_member , :owner , group : group , user : user ) }
let ( :transfer_service ) { described_class . new ( group , user ) }
2022-08-27 11:52:29 +05:30
shared_examples 'publishes a GroupTransferedEvent' do
it do
expect { transfer_service . execute ( target ) }
. to publish_event ( Groups :: GroupTransferedEvent )
. with (
group_id : group . id ,
old_root_namespace_id : group . root_ancestor . id ,
new_root_namespace_id : target . root_ancestor . id
)
end
end
2020-10-24 23:57:45 +05:30
context 'handling packages' do
2023-07-09 08:55:56 +05:30
let_it_be ( :group ) { create ( :group ) }
let_it_be ( :new_group ) { create ( :group ) }
2021-04-29 21:17:54 +05:30
2023-07-09 08:55:56 +05:30
let_it_be ( :project ) { create ( :project , namespace : group ) }
2020-10-24 23:57:45 +05:30
before do
group . add_owner ( user )
new_group & . add_owner ( user )
end
context 'with an npm package' do
2023-07-09 08:55:56 +05:30
let_it_be ( :npm_package ) { create ( :npm_package , project : project , name : " @testscope/test " ) }
2020-10-24 23:57:45 +05:30
2023-07-09 08:55:56 +05:30
shared_examples 'transfer allowed' do
it 'allows transfer' do
2020-10-24 23:57:45 +05:30
transfer_service . execute ( new_group )
2023-07-09 08:55:56 +05:30
expect ( transfer_service . error ) . to be nil
expect ( group . parent ) . to eq ( new_group )
2020-10-24 23:57:45 +05:30
end
end
2023-07-09 08:55:56 +05:30
it_behaves_like 'transfer allowed'
2020-10-24 23:57:45 +05:30
context 'with a project within subgroup' do
2021-02-22 17:27:13 +05:30
let_it_be ( :root_group ) { create ( :group ) }
let_it_be ( :group ) { create ( :group , parent : root_group ) }
2023-07-09 08:55:56 +05:30
let_it_be ( :project ) { create ( :project , namespace : group ) }
2020-10-24 23:57:45 +05:30
before do
root_group . add_owner ( user )
end
2023-07-09 08:55:56 +05:30
it_behaves_like 'transfer allowed'
2020-10-24 23:57:45 +05:30
context 'without a root namespace change' do
2023-07-09 08:55:56 +05:30
let_it_be ( :new_group ) { create ( :group , parent : root_group ) }
it_behaves_like 'transfer allowed'
end
context 'with namespaced packages present' do
let_it_be ( :package ) { create ( :npm_package , project : project , name : " @ #{ project . root_namespace . path } /test " ) }
2020-10-24 23:57:45 +05:30
2023-07-09 08:55:56 +05:30
it 'does not allow transfer' do
2020-10-24 23:57:45 +05:30
transfer_service . execute ( new_group )
2023-07-09 08:55:56 +05:30
expect ( transfer_service . error ) . to eq ( 'Transfer failed: Group contains projects with NPM packages scoped to the current root level group.' )
expect ( group . parent ) . not_to eq ( new_group )
end
context 'namespaced package is pending destruction' do
let! ( :group ) { create ( :group ) }
before do
package . pending_destruction!
end
it_behaves_like 'transfer allowed'
2020-10-24 23:57:45 +05:30
end
end
context 'when transferring a group into a root group' do
2023-07-09 08:55:56 +05:30
let_it_be ( :root_group ) { create ( :group ) }
let_it_be ( :group ) { create ( :group , parent : root_group ) }
let_it_be ( :new_group ) { nil }
2020-10-24 23:57:45 +05:30
2023-07-09 08:55:56 +05:30
it_behaves_like 'transfer allowed'
2020-10-24 23:57:45 +05:30
end
end
end
context 'without an npm package' do
context 'when transferring a group into a root group' do
let ( :group ) { create ( :group , parent : create ( :group ) ) }
it 'allows transfer' do
transfer_service . execute ( nil )
expect ( transfer_service . error ) . to be nil
expect ( group . parent ) . to be_nil
end
end
end
end
2018-03-17 18:26:18 +05:30
shared_examples 'ensuring allowed transfer for a group' do
2018-12-05 23:21:45 +05:30
context " when there's an exception on GitLab shell directories " do
2018-03-17 18:26:18 +05:30
before do
2020-01-01 13:55:28 +05:30
allow_next_instance_of ( described_class ) do | instance |
allow ( instance ) . to receive ( :update_group_attributes ) . and_raise ( Gitlab :: UpdatePathError , 'namespace directory cannot be moved' )
end
2018-03-17 18:26:18 +05:30
create ( :group_member , :owner , group : new_parent_group , user : user )
end
2019-07-07 11:18:12 +05:30
it 'returns false' do
2018-03-17 18:26:18 +05:30
expect ( transfer_service . execute ( new_parent_group ) ) . to be_falsy
end
2019-07-07 11:18:12 +05:30
it 'adds an error on group' do
2018-03-17 18:26:18 +05:30
transfer_service . execute ( new_parent_group )
expect ( transfer_service . error ) . to eq ( 'Transfer failed: namespace directory cannot be moved' )
end
end
end
describe '#execute' do
context 'when transforming a group into a root group' do
2021-02-22 17:27:13 +05:30
let_it_be_with_reload ( :group ) { create ( :group , :public , :nested ) }
2018-03-17 18:26:18 +05:30
it_behaves_like 'ensuring allowed transfer for a group'
context 'when the group is already a root group' do
let ( :group ) { create ( :group , :public ) }
2019-07-07 11:18:12 +05:30
it 'adds an error on group' do
2018-03-17 18:26:18 +05:30
transfer_service . execute ( nil )
expect ( transfer_service . error ) . to eq ( 'Transfer failed: Group is already a root group.' )
end
end
context 'when the user does not have the right policies' do
2021-02-22 17:27:13 +05:30
let_it_be ( :group_member ) { create ( :group_member , :guest , group : group , user : user ) }
2018-03-17 18:26:18 +05:30
2019-07-07 11:18:12 +05:30
it " returns false " do
2018-03-17 18:26:18 +05:30
expect ( transfer_service . execute ( nil ) ) . to be_falsy
end
2019-07-07 11:18:12 +05:30
it " adds an error on group " do
2018-03-17 18:26:18 +05:30
transfer_service . execute ( new_parent_group )
expect ( transfer_service . error ) . to eq ( " Transfer failed: You don't have enough permissions. " )
end
end
context 'when there is a group with the same path' do
2021-02-22 17:27:13 +05:30
let_it_be ( :group ) { create ( :group , :public , :nested , path : 'not-unique' ) }
2018-03-17 18:26:18 +05:30
before do
create ( :group , path : 'not-unique' )
end
2019-07-07 11:18:12 +05:30
it 'returns false' do
2018-03-17 18:26:18 +05:30
expect ( transfer_service . execute ( nil ) ) . to be_falsy
end
2019-07-07 11:18:12 +05:30
it 'adds an error on group' do
2018-03-17 18:26:18 +05:30
transfer_service . execute ( nil )
2021-12-11 22:18:48 +05:30
expect ( transfer_service . error ) . to eq ( 'Transfer failed: The parent group already has a subgroup or a project with the same path.' )
2018-03-17 18:26:18 +05:30
end
end
context 'when the group is a subgroup and the transfer is valid' do
2021-02-22 17:27:13 +05:30
let_it_be ( :subgroup1 ) { create ( :group , :private , parent : group ) }
let_it_be ( :subgroup2 ) { create ( :group , :internal , parent : group ) }
let_it_be ( :project1 ) { create ( :project , :repository , :private , namespace : group ) }
2018-03-17 18:26:18 +05:30
before do
transfer_service . execute ( nil )
group . reload
end
2019-07-07 11:18:12 +05:30
it 'updates group attributes' do
2018-03-17 18:26:18 +05:30
expect ( group . parent ) . to be_nil
end
2019-07-07 11:18:12 +05:30
it 'updates group children path' do
2018-03-17 18:26:18 +05:30
group . children . each do | subgroup |
expect ( subgroup . full_path ) . to eq ( " #{ group . path } / #{ subgroup . path } " )
end
end
2019-07-07 11:18:12 +05:30
it 'updates group projects path' do
2018-03-17 18:26:18 +05:30
group . projects . each do | project |
expect ( project . full_path ) . to eq ( " #{ group . path } / #{ project . path } " )
end
end
2021-11-18 22:05:49 +05:30
context 'when projects have project namespaces' do
let_it_be ( :project1 ) { create ( :project , :private , namespace : group ) }
let_it_be ( :project2 ) { create ( :project , :private , namespace : group ) }
it_behaves_like 'project namespace path is in sync with project path' do
2023-01-13 00:05:48 +05:30
let ( :group_full_path ) { group . path . to_s }
2021-11-18 22:05:49 +05:30
let ( :projects_with_project_namespace ) { [ project1 , project2 ] }
end
end
2018-03-17 18:26:18 +05:30
end
end
context 'when transferring a subgroup into another group' do
2021-02-22 17:27:13 +05:30
let_it_be_with_reload ( :group ) { create ( :group , :public , :nested ) }
2018-03-17 18:26:18 +05:30
it_behaves_like 'ensuring allowed transfer for a group'
context 'when the new parent group is the same as the previous parent group' do
2021-02-22 17:27:13 +05:30
let_it_be ( :group ) { create ( :group , :public , :nested , parent : new_parent_group ) }
2018-03-17 18:26:18 +05:30
2019-07-07 11:18:12 +05:30
it 'returns false' do
2018-03-17 18:26:18 +05:30
expect ( transfer_service . execute ( new_parent_group ) ) . to be_falsy
end
2019-07-07 11:18:12 +05:30
it 'adds an error on group' do
2018-03-17 18:26:18 +05:30
transfer_service . execute ( new_parent_group )
expect ( transfer_service . error ) . to eq ( 'Transfer failed: Group is already associated to the parent group.' )
end
end
context 'when the user does not have the right policies' do
2021-02-22 17:27:13 +05:30
let_it_be ( :group_member ) { create ( :group_member , :guest , group : group , user : user ) }
2018-03-17 18:26:18 +05:30
2019-07-07 11:18:12 +05:30
it " returns false " do
2018-03-17 18:26:18 +05:30
expect ( transfer_service . execute ( new_parent_group ) ) . to be_falsy
end
2019-07-07 11:18:12 +05:30
it " adds an error on group " do
2018-03-17 18:26:18 +05:30
transfer_service . execute ( new_parent_group )
expect ( transfer_service . error ) . to eq ( " Transfer failed: You don't have enough permissions. " )
end
end
context 'when the parent has a group with the same path' do
before do
create ( :group_member , :owner , group : new_parent_group , user : user )
group . update_attribute ( :path , " not-unique " )
create ( :group , path : " not-unique " , parent : new_parent_group )
end
2019-07-07 11:18:12 +05:30
it 'returns false' do
2018-03-17 18:26:18 +05:30
expect ( transfer_service . execute ( new_parent_group ) ) . to be_falsy
end
2019-07-07 11:18:12 +05:30
it 'adds an error on group' do
2018-03-17 18:26:18 +05:30
transfer_service . execute ( new_parent_group )
2021-12-11 22:18:48 +05:30
expect ( transfer_service . error ) . to eq ( 'Transfer failed: The parent group already has a subgroup or a project with the same path.' )
2018-03-17 18:26:18 +05:30
end
end
context 'when the parent group has a project with the same path' do
2021-02-22 17:27:13 +05:30
let_it_be_with_reload ( :group ) { create ( :group , :public , :nested , path : 'foo' ) }
2021-11-18 22:05:49 +05:30
let_it_be ( :membership ) { create ( :group_member , :owner , group : new_parent_group , user : user ) }
let_it_be ( :project ) { create ( :project , path : 'foo' , namespace : new_parent_group ) }
2018-03-17 18:26:18 +05:30
2019-07-07 11:18:12 +05:30
it 'adds an error on group' do
2021-12-11 22:18:48 +05:30
expect ( transfer_service . execute ( new_parent_group ) ) . to be_falsy
expect ( transfer_service . error ) . to eq ( 'Transfer failed: The parent group already has a subgroup or a project with the same path.' )
2018-03-17 18:26:18 +05:30
end
end
2021-12-11 22:18:48 +05:30
context 'when projects have project namespaces' do
let_it_be ( :project ) { create ( :project , path : 'foo' , namespace : new_parent_group ) }
before do
transfer_service . execute ( new_parent_group )
end
it_behaves_like 'project namespace path is in sync with project path' do
2023-01-13 00:05:48 +05:30
let ( :group_full_path ) { new_parent_group . full_path . to_s }
2021-12-11 22:18:48 +05:30
let ( :projects_with_project_namespace ) { [ project ] }
end
end
2018-03-17 18:26:18 +05:30
context 'when the group is allowed to be transferred' do
2021-06-08 01:23:25 +05:30
let_it_be ( :new_parent_group , reload : true ) { create ( :group , :public ) }
2021-12-11 22:18:48 +05:30
let_it_be ( :new_parent_group_integration ) { create ( :integrations_slack , :group , group : new_parent_group , webhook : 'http://new-group.slack.com' ) }
2021-02-22 17:27:13 +05:30
2018-03-17 18:26:18 +05:30
before do
2021-02-22 17:27:13 +05:30
allow ( PropagateIntegrationWorker ) . to receive ( :perform_async )
2018-03-17 18:26:18 +05:30
create ( :group_member , :owner , group : new_parent_group , user : user )
2021-02-22 17:27:13 +05:30
2018-03-17 18:26:18 +05:30
transfer_service . execute ( new_parent_group )
end
context 'when the group has a lower visibility than the parent group' do
let ( :new_parent_group ) { create ( :group , :public ) }
let ( :group ) { create ( :group , :private , :nested ) }
2019-07-07 11:18:12 +05:30
it 'does not update the visibility for the group' do
2018-03-17 18:26:18 +05:30
group . reload
expect ( group . private? ) . to be_truthy
expect ( group . visibility_level ) . not_to eq ( new_parent_group . visibility_level )
end
end
context 'when the group has a higher visibility than the parent group' do
let ( :new_parent_group ) { create ( :group , :private ) }
let ( :group ) { create ( :group , :public , :nested ) }
2019-07-07 11:18:12 +05:30
it 'updates visibility level based on the parent group' do
2018-03-17 18:26:18 +05:30
group . reload
expect ( group . private? ) . to be_truthy
expect ( group . visibility_level ) . to eq ( new_parent_group . visibility_level )
end
end
2021-02-22 17:27:13 +05:30
context 'with a group integration' do
2021-06-08 01:23:25 +05:30
let ( :new_created_integration ) { Integration . find_by ( group : group ) }
2021-02-22 17:27:13 +05:30
context 'with an inherited integration' do
2021-09-30 23:02:18 +05:30
let_it_be ( :instance_integration ) { create ( :integrations_slack , :instance , webhook : 'http://project.slack.com' ) }
2021-12-11 22:18:48 +05:30
let_it_be ( :group_integration ) { create ( :integrations_slack , :group , group : group , webhook : 'http://group.slack.com' , inherit_from_id : instance_integration . id ) }
2021-02-22 17:27:13 +05:30
it 'replaces inherited integrations' , :aggregate_failures do
expect ( new_created_integration . webhook ) . to eq ( new_parent_group_integration . webhook )
expect ( PropagateIntegrationWorker ) . to have_received ( :perform_async ) . with ( new_created_integration . id )
2021-06-08 01:23:25 +05:30
expect ( Integration . count ) . to eq ( 3 )
2021-02-22 17:27:13 +05:30
end
end
context 'with a custom integration' do
2021-12-11 22:18:48 +05:30
let_it_be ( :group_integration ) { create ( :integrations_slack , :group , group : group , webhook : 'http://group.slack.com' ) }
2021-02-22 17:27:13 +05:30
it 'does not updates the integrations' , :aggregate_failures do
expect { transfer_service . execute ( new_parent_group ) } . not_to change { group_integration . webhook }
expect ( PropagateIntegrationWorker ) . not_to have_received ( :perform_async )
end
end
end
2019-07-07 11:18:12 +05:30
it 'updates visibility for the group based on the parent group' do
2018-03-17 18:26:18 +05:30
expect ( group . visibility_level ) . to eq ( new_parent_group . visibility_level )
end
2019-07-07 11:18:12 +05:30
it 'updates parent group to the new parent' do
2018-03-17 18:26:18 +05:30
expect ( group . parent ) . to eq ( new_parent_group )
end
2019-07-07 11:18:12 +05:30
it 'returns the group as children of the new parent' do
2018-03-17 18:26:18 +05:30
expect ( new_parent_group . children . count ) . to eq ( 1 )
expect ( new_parent_group . children . first ) . to eq ( group )
end
2019-07-07 11:18:12 +05:30
it 'creates a redirect for the group' do
2018-05-09 12:01:36 +05:30
expect ( group . redirect_routes . count ) . to eq ( 1 )
2018-03-17 18:26:18 +05:30
end
end
2021-01-03 14:25:43 +05:30
context 'shared runners configuration' do
before do
create ( :group_member , :owner , group : new_parent_group , user : user )
end
context 'if parent group has disabled shared runners but allows overrides' do
let ( :new_parent_group ) { create ( :group , shared_runners_enabled : false , allow_descendants_override_disabled_shared_runners : true ) }
it 'calls update service' do
2023-03-17 16:20:25 +05:30
expect ( Groups :: UpdateSharedRunnersService ) . to receive ( :new ) . with ( group , user , { shared_runners_setting : Namespace :: SR_DISABLED_AND_OVERRIDABLE } ) . and_call_original
2021-01-03 14:25:43 +05:30
transfer_service . execute ( new_parent_group )
end
end
context 'if parent group does not allow shared runners' do
let ( :new_parent_group ) { create ( :group , shared_runners_enabled : false , allow_descendants_override_disabled_shared_runners : false ) }
it 'calls update service' do
2021-11-18 22:05:49 +05:30
expect ( Groups :: UpdateSharedRunnersService ) . to receive ( :new ) . with ( group , user , { shared_runners_setting : Namespace :: SR_DISABLED_AND_UNOVERRIDABLE } ) . and_call_original
2021-01-03 14:25:43 +05:30
transfer_service . execute ( new_parent_group )
end
end
context 'if parent group allows shared runners' do
let ( :group ) { create ( :group , :public , :nested , shared_runners_enabled : false ) }
let ( :new_parent_group ) { create ( :group , shared_runners_enabled : true ) }
it 'does not call update service and keeps them disabled on the group' do
expect ( Groups :: UpdateSharedRunnersService ) . not_to receive ( :new )
transfer_service . execute ( new_parent_group )
expect ( group . reload . shared_runners_enabled ) . to be_falsy
end
end
end
2020-06-23 00:09:42 +05:30
context 'when a group is transferred to its subgroup' do
let ( :new_parent_group ) { create ( :group , parent : group ) }
it 'does not execute the transfer' do
expect ( transfer_service . execute ( new_parent_group ) ) . to be_falsy
expect ( transfer_service . error ) . to match ( / Cannot transfer group to one of its subgroup / )
end
end
2018-03-17 18:26:18 +05:30
context 'when transferring a group with group descendants' do
let! ( :subgroup1 ) { create ( :group , :private , parent : group ) }
let! ( :subgroup2 ) { create ( :group , :internal , parent : group ) }
before do
create ( :group_member , :owner , group : new_parent_group , user : user )
transfer_service . execute ( new_parent_group )
end
2019-07-07 11:18:12 +05:30
it 'updates subgroups path' do
2018-03-17 18:26:18 +05:30
new_parent_path = new_parent_group . path
group . children . each do | subgroup |
expect ( subgroup . full_path ) . to eq ( " #{ new_parent_path } / #{ group . path } / #{ subgroup . path } " )
end
end
2019-07-07 11:18:12 +05:30
it 'creates redirects for the subgroups' do
2018-05-09 12:01:36 +05:30
expect ( group . redirect_routes . count ) . to eq ( 1 )
expect ( subgroup1 . redirect_routes . count ) . to eq ( 1 )
expect ( subgroup2 . redirect_routes . count ) . to eq ( 1 )
2018-03-17 18:26:18 +05:30
end
context 'when the new parent has a higher visibility than the children' do
2019-07-07 11:18:12 +05:30
it 'does not update the children visibility' do
2018-03-17 18:26:18 +05:30
expect ( subgroup1 . private? ) . to be_truthy
expect ( subgroup2 . internal? ) . to be_truthy
end
end
context 'when the new parent has a lower visibility than the children' do
let! ( :subgroup1 ) { create ( :group , :public , parent : group ) }
let! ( :subgroup2 ) { create ( :group , :public , parent : group ) }
let ( :new_parent_group ) { create ( :group , :private ) }
2019-07-07 11:18:12 +05:30
it 'updates children visibility to match the new parent' do
2018-03-17 18:26:18 +05:30
group . children . each do | subgroup |
expect ( subgroup . private? ) . to be_truthy
end
end
end
end
context 'when transferring a group with project descendants' do
let! ( :project1 ) { create ( :project , :repository , :private , namespace : group ) }
let! ( :project2 ) { create ( :project , :repository , :internal , namespace : group ) }
before do
TestEnv . clean_test_path
create ( :group_member , :owner , group : new_parent_group , user : user )
2022-08-13 15:12:31 +05:30
allow ( transfer_service ) . to receive ( :update_project_settings )
2018-03-17 18:26:18 +05:30
transfer_service . execute ( new_parent_group )
end
2019-07-07 11:18:12 +05:30
it 'updates projects path' do
2018-03-17 18:26:18 +05:30
new_parent_path = new_parent_group . path
group . projects . each do | project |
2023-07-09 08:55:56 +05:30
expect ( project . full_path ) . to eq ( " #{ new_parent_path } / #{ group . path } / #{ project . path } " )
2018-03-17 18:26:18 +05:30
end
end
2019-07-07 11:18:12 +05:30
it 'creates permanent redirects for the projects' do
2018-05-09 12:01:36 +05:30
expect ( group . redirect_routes . count ) . to eq ( 1 )
expect ( project1 . redirect_routes . count ) . to eq ( 1 )
expect ( project2 . redirect_routes . count ) . to eq ( 1 )
2018-03-17 18:26:18 +05:30
end
context 'when the new parent has a higher visibility than the projects' do
2019-07-07 11:18:12 +05:30
it 'does not update projects visibility' do
2018-03-17 18:26:18 +05:30
expect ( project1 . private? ) . to be_truthy
expect ( project2 . internal? ) . to be_truthy
end
2021-11-18 22:05:49 +05:30
it_behaves_like 'project namespace path is in sync with project path' do
let ( :group_full_path ) { " #{ new_parent_group . path } / #{ group . path } " }
let ( :projects_with_project_namespace ) { [ project1 , project2 ] }
end
2018-03-17 18:26:18 +05:30
end
context 'when the new parent has a lower visibility than the projects' do
let! ( :project1 ) { create ( :project , :repository , :public , namespace : group ) }
let! ( :project2 ) { create ( :project , :repository , :public , namespace : group ) }
2021-11-18 22:05:49 +05:30
let! ( :new_parent_group ) { create ( :group , :private ) }
2018-03-17 18:26:18 +05:30
2019-07-07 11:18:12 +05:30
it 'updates projects visibility to match the new parent' do
2018-03-17 18:26:18 +05:30
group . projects . each do | project |
expect ( project . private? ) . to be_truthy
end
end
2021-11-18 22:05:49 +05:30
2022-08-13 15:12:31 +05:30
it 'invokes #update_project_settings' do
expect ( transfer_service ) . to have_received ( :update_project_settings )
. with ( group . projects . pluck ( :id ) )
end
2021-11-18 22:05:49 +05:30
it_behaves_like 'project namespace path is in sync with project path' do
let ( :group_full_path ) { " #{ new_parent_group . path } / #{ group . path } " }
let ( :projects_with_project_namespace ) { [ project1 , project2 ] }
end
2018-03-17 18:26:18 +05:30
end
end
context 'when transferring a group with subgroups & projects descendants' do
let! ( :project1 ) { create ( :project , :repository , :private , namespace : group ) }
let! ( :project2 ) { create ( :project , :repository , :internal , namespace : group ) }
let! ( :subgroup1 ) { create ( :group , :private , parent : group ) }
let! ( :subgroup2 ) { create ( :group , :internal , parent : group ) }
before do
TestEnv . clean_test_path
create ( :group_member , :owner , group : new_parent_group , user : user )
transfer_service . execute ( new_parent_group )
end
2019-07-07 11:18:12 +05:30
it 'updates subgroups path' do
2018-03-17 18:26:18 +05:30
new_parent_path = new_parent_group . path
group . children . each do | subgroup |
expect ( subgroup . full_path ) . to eq ( " #{ new_parent_path } / #{ group . path } / #{ subgroup . path } " )
end
end
2019-07-07 11:18:12 +05:30
it 'updates projects path' do
2018-03-17 18:26:18 +05:30
new_parent_path = new_parent_group . path
group . projects . each do | project |
2023-07-09 08:55:56 +05:30
expect ( project . full_path ) . to eq ( " #{ new_parent_path } / #{ group . path } / #{ project . path } " )
2018-03-17 18:26:18 +05:30
end
end
2019-07-07 11:18:12 +05:30
it 'creates redirect for the subgroups and projects' do
2018-05-09 12:01:36 +05:30
expect ( group . redirect_routes . count ) . to eq ( 1 )
expect ( subgroup1 . redirect_routes . count ) . to eq ( 1 )
expect ( subgroup2 . redirect_routes . count ) . to eq ( 1 )
expect ( project1 . redirect_routes . count ) . to eq ( 1 )
expect ( project2 . redirect_routes . count ) . to eq ( 1 )
2018-03-17 18:26:18 +05:30
end
2021-11-18 22:05:49 +05:30
it_behaves_like 'project namespace path is in sync with project path' do
let ( :group_full_path ) { " #{ new_parent_group . path } / #{ group . path } " }
let ( :projects_with_project_namespace ) { [ project1 , project2 ] }
end
2018-03-17 18:26:18 +05:30
end
2018-12-13 13:39:08 +05:30
context 'when transferring a group with nested groups and projects' do
2020-08-18 19:51:02 +05:30
let ( :subgroup1 ) { create ( :group , :private , parent : group ) }
2018-03-17 18:26:18 +05:30
let! ( :project1 ) { create ( :project , :repository , :private , namespace : group ) }
let! ( :nested_subgroup ) { create ( :group , :private , parent : subgroup1 ) }
let! ( :nested_project ) { create ( :project , :repository , :private , namespace : subgroup1 ) }
before do
TestEnv . clean_test_path
create ( :group_member , :owner , group : new_parent_group , user : user )
end
2020-08-18 19:51:02 +05:30
context 'updated paths' do
2021-02-22 17:27:13 +05:30
let_it_be_with_reload ( :group ) { create ( :group , :public ) }
2020-08-18 19:51:02 +05:30
before do
transfer_service . execute ( new_parent_group )
2018-03-17 18:26:18 +05:30
end
2020-08-18 19:51:02 +05:30
it 'updates subgroups path' do
new_base_path = " #{ new_parent_group . path } / #{ group . path } "
group . children . each do | children |
expect ( children . full_path ) . to eq ( " #{ new_base_path } / #{ children . path } " )
end
new_base_path = " #{ new_parent_group . path } / #{ group . path } / #{ subgroup1 . path } "
subgroup1 . children . each do | children |
expect ( children . full_path ) . to eq ( " #{ new_base_path } / #{ children . path } " )
end
2018-03-17 18:26:18 +05:30
end
2020-08-18 19:51:02 +05:30
it 'updates projects path' do
new_parent_path = " #{ new_parent_group . path } / #{ group . path } "
subgroup1 . projects . each do | project |
2023-07-09 08:55:56 +05:30
project_full_path = " #{ new_parent_path } / #{ project . namespace . path } / #{ project . path } "
2020-08-18 19:51:02 +05:30
expect ( project . full_path ) . to eq ( project_full_path )
end
end
it 'creates redirect for the subgroups and projects' do
expect ( group . redirect_routes . count ) . to eq ( 1 )
expect ( project1 . redirect_routes . count ) . to eq ( 1 )
expect ( subgroup1 . redirect_routes . count ) . to eq ( 1 )
expect ( nested_subgroup . redirect_routes . count ) . to eq ( 1 )
expect ( nested_project . redirect_routes . count ) . to eq ( 1 )
2018-03-17 18:26:18 +05:30
end
end
2020-08-18 19:51:02 +05:30
context 'resets project authorizations' do
2021-02-22 17:27:13 +05:30
let_it_be ( :old_parent_group ) { create ( :group ) }
2022-07-16 23:28:13 +05:30
let_it_be_with_refind ( :group ) { create ( :group , :private , parent : old_parent_group ) }
2021-02-22 17:27:13 +05:30
let_it_be ( :new_group_member ) { create ( :user ) }
let_it_be ( :old_group_member ) { create ( :user ) }
2021-12-11 22:18:48 +05:30
let_it_be ( :unique_subgroup_member ) { create ( :user ) }
let_it_be ( :direct_project_member ) { create ( :user ) }
2020-08-18 19:51:02 +05:30
before do
new_parent_group . add_maintainer ( new_group_member )
old_parent_group . add_maintainer ( old_group_member )
2021-12-11 22:18:48 +05:30
subgroup1 . add_developer ( unique_subgroup_member )
nested_project . add_developer ( direct_project_member )
2020-08-18 19:51:02 +05:30
group . refresh_members_authorized_projects
2021-12-11 22:18:48 +05:30
subgroup1 . refresh_members_authorized_projects
2020-08-18 19:51:02 +05:30
end
it 'removes old project authorizations' do
expect { transfer_service . execute ( new_parent_group ) } . to change {
ProjectAuthorization . where ( project_id : project1 . id , user_id : old_group_member . id ) . size
} . from ( 1 ) . to ( 0 )
end
it 'adds new project authorizations' do
expect { transfer_service . execute ( new_parent_group ) } . to change {
ProjectAuthorization . where ( project_id : project1 . id , user_id : new_group_member . id ) . size
} . from ( 0 ) . to ( 1 )
end
2022-08-13 15:12:31 +05:30
it 'performs authorizations job' do
expect ( AuthorizedProjectUpdate :: ProjectRecalculateWorker ) . to receive ( :bulk_perform_async )
2020-08-18 19:51:02 +05:30
transfer_service . execute ( new_parent_group )
end
context 'for nested projects' do
it 'removes old project authorizations' do
expect { transfer_service . execute ( new_parent_group ) } . to change {
ProjectAuthorization . where ( project_id : nested_project . id , user_id : old_group_member . id ) . size
} . from ( 1 ) . to ( 0 )
end
it 'adds new project authorizations' do
expect { transfer_service . execute ( new_parent_group ) } . to change {
ProjectAuthorization . where ( project_id : nested_project . id , user_id : new_group_member . id ) . size
} . from ( 0 ) . to ( 1 )
end
2021-12-11 22:18:48 +05:30
it 'preserves existing project authorizations for direct project members' do
expect { transfer_service . execute ( new_parent_group ) } . not_to change {
ProjectAuthorization . where ( project_id : nested_project . id , user_id : direct_project_member . id ) . count
}
end
2020-08-18 19:51:02 +05:30
end
2021-12-11 22:18:48 +05:30
context 'for nested groups with unique members' do
it 'preserves existing project authorizations' do
expect { transfer_service . execute ( new_parent_group ) } . not_to change {
ProjectAuthorization . where ( project_id : nested_project . id , user_id : unique_subgroup_member . id ) . count
}
2020-08-18 19:51:02 +05:30
end
2021-12-11 22:18:48 +05:30
end
context 'for groups with many projects' do
let_it_be ( :project_list ) { create_list ( :project , 11 , :repository , :private , namespace : group ) }
2020-08-18 19:51:02 +05:30
it 'adds new project authorizations for the user which makes a transfer' do
transfer_service . execute ( new_parent_group )
expect ( ProjectAuthorization . where ( project_id : project1 . id , user_id : user . id ) . size ) . to eq ( 1 )
expect ( ProjectAuthorization . where ( project_id : nested_project . id , user_id : user . id ) . size ) . to eq ( 1 )
end
2021-12-11 22:18:48 +05:30
it 'adds project authorizations for users in the new hierarchy' do
expect { transfer_service . execute ( new_parent_group ) } . to change {
ProjectAuthorization . where ( project_id : project_list . map { | project | project . id } , user_id : new_group_member . id ) . size
} . from ( 0 ) . to ( project_list . count )
end
it 'removes project authorizations for users in the old hierarchy' do
expect { transfer_service . execute ( new_parent_group ) } . to change {
ProjectAuthorization . where ( project_id : project_list . map { | project | project . id } , user_id : old_group_member . id ) . size
} . from ( project_list . count ) . to ( 0 )
end
2020-08-18 19:51:02 +05:30
it 'schedules authorizations job' do
2021-12-11 22:18:48 +05:30
expect ( AuthorizedProjectUpdate :: ProjectRecalculateWorker ) . to receive ( :bulk_perform_async )
2022-08-13 15:12:31 +05:30
. with ( array_including ( group . all_projects . ids . map { | id | [ id ] } ) )
2020-08-18 19:51:02 +05:30
transfer_service . execute ( new_parent_group )
end
end
2021-10-29 20:43:33 +05:30
context 'transferring groups with shared_projects' do
let_it_be_with_reload ( :shared_project ) { create ( :project , :public ) }
shared_examples_for 'drops the authorizations of ancestor members from the old hierarchy' do
it 'drops the authorizations of ancestor members from the old hierarchy' do
expect { transfer_service . execute ( new_parent_group ) } . to change {
ProjectAuthorization . where ( project : shared_project , user : old_group_member ) . size
} . from ( 1 ) . to ( 0 )
end
end
context 'when the group that has existing project share is transferred' do
before do
create ( :project_group_link , :maintainer , project : shared_project , group : group )
end
it_behaves_like 'drops the authorizations of ancestor members from the old hierarchy'
end
context 'when the group whose subgroup has an existing project share is transferred' do
let_it_be_with_reload ( :subgroup ) { create ( :group , :private , parent : group ) }
before do
create ( :project_group_link , :maintainer , project : shared_project , group : subgroup )
end
it_behaves_like 'drops the authorizations of ancestor members from the old hierarchy'
end
end
context 'when a group that has existing group share is transferred' do
let ( :shared_with_group ) { group }
let_it_be ( :member_of_shared_with_group ) { create ( :user ) }
let_it_be ( :shared_group ) { create ( :group , :private ) }
let_it_be ( :project_in_shared_group ) { create ( :project , namespace : shared_group ) }
before do
shared_with_group . add_developer ( member_of_shared_with_group )
create ( :group_group_link , :maintainer , shared_group : shared_group , shared_with_group : shared_with_group )
shared_with_group . refresh_members_authorized_projects
end
it 'retains the authorizations of direct members' do
expect { transfer_service . execute ( new_parent_group ) } . not_to change {
ProjectAuthorization . where (
project : project_in_shared_group ,
user : member_of_shared_with_group ,
access_level : Gitlab :: Access :: DEVELOPER ) . size
} . from ( 1 )
end
end
2018-03-17 18:26:18 +05:30
end
end
2021-01-03 14:25:43 +05:30
context 'when transferring a group with two factor authentication switched on' do
before do
TestEnv . clean_test_path
create ( :group_member , :owner , group : new_parent_group , user : user )
create ( :group , :private , parent : group , require_two_factor_authentication : true )
group . update! ( require_two_factor_authentication : true )
2021-06-08 01:23:25 +05:30
new_parent_group . reload # make sure traversal_ids are reloaded
2021-01-03 14:25:43 +05:30
end
it 'does not update group two factor authentication setting' do
transfer_service . execute ( new_parent_group )
expect ( group . require_two_factor_authentication ) . to eq ( true )
end
context 'when new parent disallows two factor authentication switched on for descendants' do
before do
new_parent_group . namespace_settings . update! ( allow_mfa_for_subgroups : false )
end
it 'updates group two factor authentication setting' do
transfer_service . execute ( new_parent_group )
expect ( group . require_two_factor_authentication ) . to eq ( false )
end
it 'schedules update of group two factor authentication setting for descendants' do
expect ( DisallowTwoFactorForSubgroupsWorker ) . to receive ( :perform_async ) . with ( group . id )
transfer_service . execute ( new_parent_group )
end
end
end
2018-03-17 18:26:18 +05:30
context 'when updating the group goes wrong' do
let! ( :subgroup1 ) { create ( :group , :public , parent : group ) }
let! ( :subgroup2 ) { create ( :group , :public , parent : group ) }
let ( :new_parent_group ) { create ( :group , :private ) }
let! ( :project1 ) { create ( :project , :repository , :public , namespace : group ) }
before do
allow ( group ) . to receive ( :save! ) . and_raise ( ActiveRecord :: RecordInvalid . new ( group ) )
TestEnv . clean_test_path
create ( :group_member , :owner , group : new_parent_group , user : user )
transfer_service . execute ( new_parent_group )
end
2019-07-07 11:18:12 +05:30
it 'restores group and projects visibility' do
2018-03-17 18:26:18 +05:30
subgroup1 . reload
project1 . reload
expect ( subgroup1 . public? ) . to be_truthy
expect ( project1 . public? ) . to be_truthy
end
end
2021-11-18 22:05:49 +05:30
2022-01-26 12:08:38 +05:30
context 'when group has pending builds' , :sidekiq_inline do
2021-11-18 22:05:49 +05:30
let_it_be ( :project ) { create ( :project , :public , namespace : group . reload ) }
let_it_be ( :other_project ) { create ( :project ) }
let_it_be ( :pending_build ) { create ( :ci_pending_build , project : project ) }
let_it_be ( :unrelated_pending_build ) { create ( :ci_pending_build , project : other_project ) }
before do
group . add_owner ( user )
new_parent_group . add_owner ( user )
end
it 'updates pending builds for the group' , :aggregate_failures do
transfer_service . execute ( new_parent_group )
pending_build . reload
unrelated_pending_build . reload
expect ( pending_build . namespace_id ) . to eq ( group . id )
expect ( pending_build . namespace_traversal_ids ) . to eq ( group . traversal_ids )
expect ( unrelated_pending_build . namespace_id ) . to eq ( other_project . namespace_id )
expect ( unrelated_pending_build . namespace_traversal_ids ) . to eq ( other_project . namespace . traversal_ids )
end
end
2018-03-17 18:26:18 +05:30
end
2019-07-07 11:18:12 +05:30
context 'when transferring a subgroup into root group' do
let ( :group ) { create ( :group , :public , :nested ) }
let ( :subgroup ) { create ( :group , :public , parent : group ) }
let ( :transfer_service ) { described_class . new ( subgroup , user ) }
it 'ensures there is still an owner for the transferred group' do
expect ( subgroup . owners ) . to be_empty
transfer_service . execute ( nil )
subgroup . reload
expect ( subgroup . owners ) . to match_array ( user )
end
context 'when group has explicit owner' do
let ( :another_owner ) { create ( :user ) }
let! ( :another_member ) { create ( :group_member , :owner , group : subgroup , user : another_owner ) }
it 'does not add additional owner' do
expect ( subgroup . owners ) . to match_array ( another_owner )
transfer_service . execute ( nil )
subgroup . reload
expect ( subgroup . owners ) . to match_array ( another_owner )
end
end
end
2019-12-21 20:55:43 +05:30
2019-12-26 22:10:19 +05:30
context 'when a project has container images' do
2019-12-21 20:55:43 +05:30
let ( :group ) { create ( :group , :public , :nested ) }
2019-12-26 22:10:19 +05:30
let! ( :container_repository ) { create ( :container_repository , project : project ) }
subject { transfer_service . execute ( new_parent_group ) }
2019-12-21 20:55:43 +05:30
before do
2019-12-26 22:10:19 +05:30
group . add_owner ( user )
new_parent_group . add_owner ( user )
2019-12-21 20:55:43 +05:30
end
2019-12-26 22:10:19 +05:30
context 'within group' do
let ( :project ) { create ( :project , :repository , :public , namespace : group ) }
it 'does not transfer' do
expect ( subject ) . to be false
expect ( transfer_service . error ) . to match ( / Docker images in their Container Registry / )
end
end
2019-12-21 20:55:43 +05:30
2019-12-26 22:10:19 +05:30
context 'within subgroup' do
let ( :subgroup ) { create ( :group , parent : group ) }
let ( :project ) { create ( :project , :repository , :public , namespace : subgroup ) }
it 'does not transfer' do
expect ( subject ) . to be false
expect ( transfer_service . error ) . to match ( / Docker images in their Container Registry / )
end
2019-12-21 20:55:43 +05:30
end
end
2022-06-21 17:19:12 +05:30
context 'crm' do
let ( :root_group ) { create ( :group , :public ) }
let ( :subgroup ) { create ( :group , :public , parent : root_group ) }
let ( :another_subgroup ) { create ( :group , :public , parent : root_group ) }
let ( :subsubgroup ) { create ( :group , :public , parent : subgroup ) }
let ( :root_project ) { create ( :project , group : root_group ) }
let ( :sub_project ) { create ( :project , group : subgroup ) }
let ( :another_project ) { create ( :project , group : another_subgroup ) }
let ( :subsub_project ) { create ( :project , group : subsubgroup ) }
let! ( :contacts ) { create_list ( :contact , 4 , group : root_group ) }
2023-07-09 08:55:56 +05:30
let! ( :organizations ) { create_list ( :crm_organization , 2 , group : root_group ) }
2022-06-21 17:19:12 +05:30
before do
create ( :issue_customer_relations_contact , contact : contacts [ 0 ] , issue : create ( :issue , project : root_project ) )
create ( :issue_customer_relations_contact , contact : contacts [ 1 ] , issue : create ( :issue , project : sub_project ) )
create ( :issue_customer_relations_contact , contact : contacts [ 2 ] , issue : create ( :issue , project : another_project ) )
create ( :issue_customer_relations_contact , contact : contacts [ 3 ] , issue : create ( :issue , project : subsub_project ) )
root_group . add_owner ( user )
end
context 'moving up' do
let ( :group ) { subsubgroup }
it 'retains issue contacts' do
expect { transfer_service . execute ( root_group ) }
. not_to change { CustomerRelations :: IssueContact . count }
end
2022-08-27 11:52:29 +05:30
it_behaves_like 'publishes a GroupTransferedEvent' do
let ( :target ) { root_group }
end
2022-06-21 17:19:12 +05:30
end
context 'moving down' do
let ( :group ) { subgroup }
it 'retains issue contacts' do
expect { transfer_service . execute ( another_subgroup ) }
. not_to change { CustomerRelations :: IssueContact . count }
end
2022-08-27 11:52:29 +05:30
it_behaves_like 'publishes a GroupTransferedEvent' do
let ( :target ) { another_subgroup }
end
2022-06-21 17:19:12 +05:30
end
context 'moving sideways' do
let ( :group ) { subsubgroup }
it 'retains issue contacts' do
expect { transfer_service . execute ( another_subgroup ) }
. not_to change { CustomerRelations :: IssueContact . count }
end
2022-08-27 11:52:29 +05:30
it_behaves_like 'publishes a GroupTransferedEvent' do
let ( :target ) { another_subgroup }
end
2022-06-21 17:19:12 +05:30
end
context 'moving to new root group' do
let ( :group ) { root_group }
before do
new_parent_group . add_owner ( user )
end
it 'moves all crm objects' do
expect { transfer_service . execute ( new_parent_group ) }
. to change { root_group . contacts . count } . by ( - 4 )
. and change { root_group . organizations . count } . by ( - 2 )
end
it 'retains issue contacts' do
expect { transfer_service . execute ( new_parent_group ) }
. not_to change { CustomerRelations :: IssueContact . count }
end
2022-08-27 11:52:29 +05:30
it_behaves_like 'publishes a GroupTransferedEvent' do
let ( :target ) { new_parent_group }
end
2022-06-21 17:19:12 +05:30
end
context 'moving to a subgroup within a new root group' do
let ( :group ) { root_group }
let ( :subgroup_in_new_parent_group ) { create ( :group , parent : new_parent_group ) }
context 'with permission on the root group' do
before do
new_parent_group . add_owner ( user )
end
it 'moves all crm objects' do
expect { transfer_service . execute ( subgroup_in_new_parent_group ) }
. to change { root_group . contacts . count } . by ( - 4 )
. and change { root_group . organizations . count } . by ( - 2 )
end
it 'retains issue contacts' do
expect { transfer_service . execute ( subgroup_in_new_parent_group ) }
. not_to change { CustomerRelations :: IssueContact . count }
end
2022-08-27 11:52:29 +05:30
it_behaves_like 'publishes a GroupTransferedEvent' do
let ( :target ) { subgroup_in_new_parent_group }
end
2022-06-21 17:19:12 +05:30
end
context 'with permission on the subgroup' do
before do
subgroup_in_new_parent_group . add_owner ( user )
end
it 'raises error' do
transfer_service . execute ( subgroup_in_new_parent_group )
expect ( transfer_service . error ) . to eq ( " Transfer failed: Group contains contacts/organizations and you don't have enough permissions to move them to the new root group. " )
end
2022-08-27 11:52:29 +05:30
it 'does not publish a GroupTransferedEvent' do
expect { transfer_service . execute ( subgroup_in_new_parent_group ) }
. not_to publish_event ( Groups :: GroupTransferedEvent )
end
2022-06-21 17:19:12 +05:30
end
end
end
2023-03-17 16:20:25 +05:30
context 'with namespace_commit_emails concerns' do
let_it_be ( :group , reload : true ) { create ( :group ) }
let_it_be ( :target ) { create ( :group ) }
before do
group . add_owner ( user )
target . add_owner ( user )
end
context 'when origin is a root group' do
before do
create_list ( :namespace_commit_email , 2 , namespace : group )
end
it 'deletes all namespace_commit_emails' do
expect { transfer_service . execute ( target ) }
. to change { group . namespace_commit_emails . count } . by ( - 2 )
end
it_behaves_like 'publishes a GroupTransferedEvent'
end
context 'when origin is not a root group' do
let ( :group ) { create ( :group , parent : create ( :group ) ) }
it 'does not attempt to delete namespace_commit_emails' do
expect ( Users :: NamespaceCommitEmail ) . not_to receive ( :delete_for_namespace )
transfer_service . execute ( target )
end
end
end
2018-03-17 18:26:18 +05:30
end
end