Instruction:
Write RSpec test file for following ruby class
```ruby
# frozen_string_literal: true

module Groups
  class TransferService < Groups::BaseService
    TransferError = Class.new(StandardError)

    attr_reader :error, :new_parent_group

    def initialize(group, user, params = {})
      super
      @error = nil
    end

    def log_group_transfer_success(group, new_parent_group)
      log_transfer(group, new_parent_group, nil)
    end

    def log_group_transfer_error(group, new_parent_group, error_message)
      log_transfer(group, new_parent_group, error_message)
    end

    def execute(new_parent_group)
      @new_parent_group = new_parent_group
      ensure_allowed_transfer
      proceed_to_transfer

      log_group_transfer_success(@group, @new_parent_group)

    rescue TransferError, ActiveRecord::RecordInvalid, Gitlab::UpdatePathError => e
      @group.errors.clear
      @error = s_("TransferGroup|Transfer failed: %{error_message}") % { error_message: e.message }

      log_group_transfer_error(@group, @new_parent_group, e.message)

      false
    end

    private

    def log_transfer(group, new_namespace, error_message = nil)
      action = error_message.nil? ? "was" : "was not"

      log_payload = {
        message: "Group #{action} transferred to a new namespace",
        group_path: group.full_path,
        group_id: group.id,
        new_parent_group_path: new_parent_group&.full_path,
        new_parent_group_id: new_parent_group&.id,
        error_message: error_message
      }

      if error_message.nil?
        ::Gitlab::AppLogger.info(log_payload)
      else
        ::Gitlab::AppLogger.error(log_payload)
      end
    end

    def proceed_to_transfer
      old_root_ancestor_id = @group.root_ancestor.id
      was_root_group = @group.root?

      Gitlab::Database::QueryAnalyzers::PreventCrossDatabaseModification.temporary_ignore_tables_in_transaction(
        %w[routes redirect_routes], url: 'https://gitlab.com/gitlab-org/gitlab/-/issues/424280'
      ) do
        Group.transaction do
          update_group_attributes
          ensure_ownership
          update_integrations
          remove_issue_contacts(old_root_ancestor_id, was_root_group)
          update_crm_objects(was_root_group)
          remove_namespace_commit_emails(was_root_group)
        end
      end

      post_update_hooks(@updated_project_ids, old_root_ancestor_id)
      propagate_integrations
      update_pending_builds

      true
    end

    # Overridden in EE
    def post_update_hooks(updated_project_ids, old_root_ancestor_id)
      refresh_project_authorizations
      refresh_descendant_groups if @new_parent_group
      publish_event(old_root_ancestor_id)
    end

    # Overridden in EE
    def ensure_allowed_transfer
      raise_transfer_error(:group_is_already_root) if group_is_already_root?
      raise_transfer_error(:same_parent_as_current) if same_parent?
      raise_transfer_error(:has_subscription) if has_subscription?
      raise_transfer_error(:invalid_policies) unless valid_policies?
      raise_transfer_error(:namespace_with_same_path) if namespace_with_same_path?
      raise_transfer_error(:group_contains_images) if group_projects_contain_registry_images?
      raise_transfer_error(:cannot_transfer_to_subgroup) if transfer_to_subgroup?
      raise_transfer_error(:group_contains_namespaced_npm_packages) if group_with_namespaced_npm_packages?
      raise_transfer_error(:no_permissions_to_migrate_crm) if no_permissions_to_migrate_crm?
    end

    def no_permissions_to_migrate_crm?
      return false unless group && @new_parent_group
      return false if group.root_ancestor == @new_parent_group.root_ancestor

      return true if group.contacts.exists? && !current_user.can?(:admin_crm_contact, @new_parent_group.root_ancestor)
      return true if group.crm_organizations.exists? && !current_user.can?(:admin_crm_organization, @new_parent_group.root_ancestor)

      false
    end

    def group_with_namespaced_npm_packages?
      return false unless group.packages_feature_enabled?

      npm_packages = ::Packages::GroupPackagesFinder.new(current_user, group, package_type: :npm, preload_pipelines: false).execute
      npm_packages = npm_packages.with_npm_scope(group.root_ancestor.path)

      different_root_ancestor? && npm_packages.exists?
    end

    def different_root_ancestor?
      group.root_ancestor != new_parent_group&.root_ancestor
    end

    def group_is_already_root?
      !@new_parent_group && !@group.has_parent?
    end

    def same_parent?
      @new_parent_group && @new_parent_group.id == @group.parent_id
    end

    def has_subscription?
      @group.paid?
    end

    def transfer_to_subgroup?
      @new_parent_group && \
      @group.self_and_descendants.pluck_primary_key.include?(@new_parent_group.id)
    end

    def valid_policies?
      return false unless can?(current_user, :admin_group, @group)

      if @new_parent_group
        can?(current_user, :create_subgroup, @new_parent_group)
      else
        can?(current_user, :create_group)
      end
    end

    # rubocop: disable CodeReuse/ActiveRecord
    def namespace_with_same_path?
      Namespace.exists?(path: @group.path, parent: @new_parent_group)
    end
    # rubocop: enable CodeReuse/ActiveRecord

    def group_projects_contain_registry_images?
      @group.has_container_repository_including_subgroups?
    end

    def update_group_attributes
      if @new_parent_group && @new_parent_group.visibility_level < @group.visibility_level
        update_children_and_projects_visibility
        @group.visibility_level = @new_parent_group.visibility_level
      end

      update_two_factor_authentication if @new_parent_group

      @group.parent = @new_parent_group
      @group.clear_memoization(:self_and_ancestors_ids)
      @group.clear_memoization(:root_ancestor) if different_root_ancestor?

      inherit_group_shared_runners_settings

      @group.save!
      # #reload is called to make sure traversal_ids are reloaded
      @group.reload # rubocop:disable Cop/ActiveRecordAssociationReload
    end

    # rubocop: disable CodeReuse/ActiveRecord
    def update_children_and_projects_visibility
      descendants = @group.descendants.where("visibility_level > ?", @new_parent_group.visibility_level)

      Group
        .where(id: descendants.select(:id))
        .update_all(visibility_level: @new_parent_group.visibility_level)

      projects_to_update = @group
        .all_projects
        .where("visibility_level > ?", @new_parent_group.visibility_level)

      # Used in post_update_hooks in EE. Must use pluck (and not select)
      # here as after we perform the update below we won't be able to find
      # these records again.
      @updated_project_ids = projects_to_update.pluck(:id)

      Namespaces::ProjectNamespace
        .where(id: projects_to_update.select(:project_namespace_id))
        .update_all(visibility_level: @new_parent_group.visibility_level)

      projects_to_update
        .update_all(visibility_level: @new_parent_group.visibility_level)

      update_project_settings(@updated_project_ids)
    end

    # Overridden in EE
    def update_project_settings(updated_project_ids)
    end

    def update_two_factor_authentication
      return if namespace_parent_allows_two_factor_auth

      @group.require_two_factor_authentication = false
    end

    def refresh_descendant_groups
      return if namespace_parent_allows_two_factor_auth

      if @group.descendants.where(require_two_factor_authentication: true).any?
        DisallowTwoFactorForSubgroupsWorker.perform_async(@group.id)
      end
    end
    # rubocop: enable CodeReuse/ActiveRecord

    def namespace_parent_allows_two_factor_auth
      @new_parent_group.namespace_settings.allow_mfa_for_subgroups
    end

    def ensure_ownership
      return if @new_parent_group
      return unless @group.owners.empty?

      add_owner_on_transferred_group
    end

    # Overridden in EE
    def add_owner_on_transferred_group
      @group.add_owner(current_user)
    end

    def refresh_project_authorizations
      project_ids = Groups::ProjectsRequiringAuthorizationsRefresh::OnTransferFinder.new(@group).execute

      AuthorizedProjectUpdate::ProjectAccessChangedService.new(project_ids).execute
    end

    def raise_transfer_error(message)
      raise TransferError, localized_error_messages[message]
    end

    # Overridden in EE
    def localized_error_messages
      {
        database_not_supported: s_('TransferGroup|Database is not supported.'),
        namespace_with_same_path: s_('TransferGroup|The parent group already has a subgroup or a project with the same path.'),
        group_is_already_root: s_('TransferGroup|Group is already a root group.'),
        same_parent_as_current: s_('TransferGroup|Group is already associated to the parent group.'),
        invalid_policies: s_("TransferGroup|You don't have enough permissions."),
        group_contains_images: s_('TransferGroup|Cannot update the path because there are projects under this group that contain Docker images in their Container Registry. Please remove the images from your projects first and try again.'),
        cannot_transfer_to_subgroup: s_('TransferGroup|Cannot transfer group to one of its subgroup.'),
        group_contains_namespaced_npm_packages: s_('TransferGroup|Group contains projects with NPM packages scoped to the current root level group.'),
        no_permissions_to_migrate_crm: s_("TransferGroup|Group contains contacts/organizations and you don't have enough permissions to move them to the new root group.")
      }.freeze
    end

    def inherit_group_shared_runners_settings
      parent_setting = @group.parent&.shared_runners_setting
      return unless parent_setting

      if @group.shared_runners_setting_higher_than?(parent_setting)
        result = Groups::UpdateSharedRunnersService.new(@group, current_user, shared_runners_setting: parent_setting).execute

        raise TransferError, result[:message] unless result[:status] == :success
      end
    end

    def update_integrations
      @group.integrations.with_default_settings.delete_all
      Integration.create_from_active_default_integrations(@group, :group_id)
    end

    def propagate_integrations
      @group.integrations.with_default_settings.each do |integration|
        PropagateIntegrationWorker.perform_async(integration.id)
      end
    end

    def update_pending_builds
      ::Ci::PendingBuilds::UpdateGroupWorker.perform_async(group.id, pending_builds_params)
    end

    def pending_builds_params
      {
        namespace_traversal_ids: group.traversal_ids,
        namespace_id: group.id
      }
    end

    def update_crm_objects(was_root_group)
      return unless was_root_group

      CustomerRelations::Contact.move_to_root_group(group)
      CustomerRelations::Organization.move_to_root_group(group)
    end

    def remove_issue_contacts(old_root_ancestor_id, was_root_group)
      return if was_root_group
      return if old_root_ancestor_id == @group.root_ancestor.id

      CustomerRelations::IssueContact.delete_for_group(@group)
    end

    def publish_event(old_root_ancestor_id)
      event = ::Groups::GroupTransferedEvent.new(
        data: {
          group_id: group.id,
          old_root_namespace_id: old_root_ancestor_id,
          new_root_namespace_id: group.root_ancestor.id
        }
      )

      Gitlab::EventStore.publish(event)
    end

    def remove_namespace_commit_emails(was_root_group)
      Users::NamespaceCommitEmail.delete_for_namespace(@group) if was_root_group
    end
  end
end

Groups::TransferService.prepend_mod_with('Groups::TransferService')

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Groups::TransferService, :sidekiq_inline, feature_category: :groups_and_projects do
  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

  let_it_be(:user) { create(:user) }
  let_it_be(:new_parent_group) { create(:group, :public, :crm_enabled) }

  let!(:group_member) { create(:group_member, :owner, group: group, user: user) }
  let(:transfer_service) { described_class.new(group, user) }

  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

  context 'handling packages' do
    let_it_be(:group) { create(:group) }
    let_it_be(:new_group) { create(:group) }

    let_it_be(:project) { create(:project, namespace: group) }

    before do
      group.add_owner(user)
      new_group&.add_owner(user)
    end

    context 'with an npm package' do
      let_it_be(:npm_package) { create(:npm_package, project: project, name: "@testscope/test") }

      shared_examples 'transfer allowed' do
        it 'allows transfer' do
          transfer_service.execute(new_group)

          expect(transfer_service.error).to be nil
          expect(group.parent).to eq(new_group)
        end
      end

      it_behaves_like 'transfer allowed'

      context 'with a project within subgroup' do
        let_it_be(:root_group) { create(:group) }
        let_it_be(:group) { create(:group, parent: root_group) }
        let_it_be(:project) { create(:project, namespace: group) }

        before do
          root_group.add_owner(user)
        end

        it_behaves_like 'transfer allowed'

        context 'without a root namespace change' do
          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") }

          it 'does not allow transfer' do
            transfer_service.execute(new_group)

            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'
          end
        end

        context 'when transferring a group into a root group' do
          let_it_be(:root_group) { create(:group) }
          let_it_be(:group) { create(:group, parent: root_group) }
          let_it_be(:new_group) { nil }

          it_behaves_like 'transfer allowed'
        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

  shared_examples 'ensuring allowed transfer for a group' do
    context "when there's an exception on GitLab shell directories" do
      before do
        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
        create(:group_member, :owner, group: new_parent_group, user: user)
      end

      it 'returns false' do
        expect(transfer_service.execute(new_parent_group)).to be_falsy
      end

      it 'adds an error on group' do
        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
      let_it_be_with_reload(:group) { create(:group, :public, :nested) }

      it_behaves_like 'ensuring allowed transfer for a group'

      context 'when the group is already a root group' do
        let(:group) { create(:group, :public) }

        it 'adds an error on group' do
          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
        let_it_be(:group_member) { create(:group_member, :guest, group: group, user: user) }

        it "returns false" do
          expect(transfer_service.execute(nil)).to be_falsy
        end

        it "adds an error on group" do
          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
        let_it_be(:group) { create(:group, :public, :nested, path: 'not-unique') }

        before do
          create(:group, path: 'not-unique')
        end

        it 'returns false' do
          expect(transfer_service.execute(nil)).to be_falsy
        end

        it 'adds an error on group' do
          transfer_service.execute(nil)
          expect(transfer_service.error).to eq('Transfer failed: The parent group already has a subgroup or a project with the same path.')
        end
      end

      context 'when the group is a subgroup and the transfer is valid' do
        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) }

        before do
          transfer_service.execute(nil)
          group.reload
        end

        it 'updates group attributes' do
          expect(group.parent).to be_nil
        end

        it 'updates group children path' do
          group.children.each do |subgroup|
            expect(subgroup.full_path).to eq("#{group.path}/#{subgroup.path}")
          end
        end

        it 'updates group projects path' do
          group.projects.each do |project|
            expect(project.full_path).to eq("#{group.path}/#{project.path}")
          end
        end

        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
            let(:group_full_path) { group.path.to_s }
            let(:projects_with_project_namespace) { [project1, project2] }
          end
        end
      end
    end

    context 'when transferring a subgroup into another group' do
      let_it_be_with_reload(:group) { create(:group, :public, :nested) }

      it_behaves_like 'ensuring allowed transfer for a group'

      context 'when the new parent group is the same as the previous parent group' do
        let_it_be(:group) { create(:group, :public, :nested, parent: new_parent_group) }

        it 'returns false' do
          expect(transfer_service.execute(new_parent_group)).to be_falsy
        end

        it 'adds an error on group' do
          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
        let_it_be(:group_member) { create(:group_member, :guest, group: group, user: user) }

        it "returns false" do
          expect(transfer_service.execute(new_parent_group)).to be_falsy
        end

        it "adds an error on group" do
          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

        it 'returns false' do
          expect(transfer_service.execute(new_parent_group)).to be_falsy
        end

        it 'adds an error on group' do
          transfer_service.execute(new_parent_group)
          expect(transfer_service.error).to eq('Transfer failed: The parent group already has a subgroup or a project with the same path.')
        end
      end

      context 'when the parent group has a project with the same path' do
        let_it_be_with_reload(:group) { create(:group, :public, :nested, path: 'foo') }
        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) }

        it 'adds an error on group' do
          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.')
        end
      end

      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
          let(:group_full_path) { new_parent_group.full_path.to_s }
          let(:projects_with_project_namespace) { [project] }
        end
      end

      context 'when the group is allowed to be transferred' do
        let_it_be(:new_parent_group, reload: true) { create(:group, :public) }
        let_it_be(:new_parent_group_integration) { create(:integrations_slack, :group, group: new_parent_group, webhook: 'http://new-group.slack.com') }

        before do
          allow(PropagateIntegrationWorker).to receive(:perform_async)

          create(:group_member, :owner, group: new_parent_group, user: user)

          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) }

          it 'does not update the visibility for the group' do
            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) }

          it 'updates visibility level based on the parent group' do
            group.reload
            expect(group.private?).to be_truthy
            expect(group.visibility_level).to eq(new_parent_group.visibility_level)
          end
        end

        context 'with a group integration' do
          let(:new_created_integration) { Integration.find_by(group: group) }

          context 'with an inherited integration' do
            let_it_be(:instance_integration) { create(:integrations_slack, :instance, webhook: 'http://project.slack.com') }
            let_it_be(:group_integration) { create(:integrations_slack, :group, group: group, webhook: 'http://group.slack.com', inherit_from_id: instance_integration.id) }

            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)
              expect(Integration.count).to eq(3)
            end
          end

          context 'with a custom integration' do
            let_it_be(:group_integration) { create(:integrations_slack, :group, group: group, webhook: 'http://group.slack.com') }

            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

        it 'updates visibility for the group based on the parent group' do
          expect(group.visibility_level).to eq(new_parent_group.visibility_level)
        end

        it 'updates parent group to the new parent' do
          expect(group.parent).to eq(new_parent_group)
        end

        it 'returns the group as children of the new parent' do
          expect(new_parent_group.children.count).to eq(1)
          expect(new_parent_group.children.first).to eq(group)
        end

        it 'creates a redirect for the group' do
          expect(group.redirect_routes.count).to eq(1)
        end
      end

      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
            expect(Groups::UpdateSharedRunnersService).to receive(:new).with(group, user, { shared_runners_setting: Namespace::SR_DISABLED_AND_OVERRIDABLE }).and_call_original

            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
            expect(Groups::UpdateSharedRunnersService).to receive(:new).with(group, user, { shared_runners_setting: Namespace::SR_DISABLED_AND_UNOVERRIDABLE }).and_call_original

            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

      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

      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

        it 'updates subgroups path' do
          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

        it 'creates redirects for the subgroups' do
          expect(group.redirect_routes.count).to eq(1)
          expect(subgroup1.redirect_routes.count).to eq(1)
          expect(subgroup2.redirect_routes.count).to eq(1)
        end

        context 'when the new parent has a higher visibility than the children' do
          it 'does not update the children visibility' do
            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) }

          it 'updates children visibility to match the new parent' do
            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)
          allow(transfer_service).to receive(:update_project_settings)
          transfer_service.execute(new_parent_group)
        end

        it 'updates projects path' do
          new_parent_path = new_parent_group.path
          group.projects.each do |project|
            expect(project.full_path).to eq("#{new_parent_path}/#{group.path}/#{project.path}")
          end
        end

        it 'creates permanent redirects for the projects' do
          expect(group.redirect_routes.count).to eq(1)
          expect(project1.redirect_routes.count).to eq(1)
          expect(project2.redirect_routes.count).to eq(1)
        end

        context 'when the new parent has a higher visibility than the projects' do
          it 'does not update projects visibility' do
            expect(project1.private?).to be_truthy
            expect(project2.internal?).to be_truthy
          end

          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
        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) }
          let!(:new_parent_group) { create(:group, :private) }

          it 'updates projects visibility to match the new parent' do
            group.projects.each do |project|
              expect(project.private?).to be_truthy
            end
          end

          it 'invokes #update_project_settings' do
            expect(transfer_service).to have_received(:update_project_settings)
                                          .with(group.projects.pluck(:id))
          end

          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
        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

        it 'updates subgroups path' do
          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

        it 'updates projects path' do
          new_parent_path = new_parent_group.path
          group.projects.each do |project|
            expect(project.full_path).to eq("#{new_parent_path}/#{group.path}/#{project.path}")
          end
        end

        it 'creates redirect for the subgroups and projects' do
          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)
        end

        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
      end

      context 'when transferring a group with nested groups and projects' do
        let(:subgroup1) { create(:group, :private, parent: group) }
        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

        context 'updated paths' do
          let_it_be_with_reload(:group) { create(:group, :public) }

          before do
            transfer_service.execute(new_parent_group)
          end

          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
          end

          it 'updates projects path' do
            new_parent_path = "#{new_parent_group.path}/#{group.path}"
            subgroup1.projects.each do |project|
              project_full_path = "#{new_parent_path}/#{project.namespace.path}/#{project.path}"
              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)
          end
        end

        context 'resets project authorizations' do
          let_it_be(:old_parent_group) { create(:group) }
          let_it_be_with_refind(:group) { create(:group, :private, parent: old_parent_group) }
          let_it_be(:new_group_member) { create(:user) }
          let_it_be(:old_group_member) { create(:user) }
          let_it_be(:unique_subgroup_member) { create(:user) }
          let_it_be(:direct_project_member) { create(:user) }

          before do
            new_parent_group.add_maintainer(new_group_member)
            old_parent_group.add_maintainer(old_group_member)
            subgroup1.add_developer(unique_subgroup_member)
            nested_project.add_developer(direct_project_member)
            group.refresh_members_authorized_projects
            subgroup1.refresh_members_authorized_projects
          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

          it 'performs authorizations job' do
            expect(AuthorizedProjectUpdate::ProjectRecalculateWorker).to receive(:bulk_perform_async)

            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

            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
          end

          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
              }
            end
          end

          context 'for groups with many projects' do
            let_it_be(:project_list) { create_list(:project, 11, :repository, :private, namespace: group) }

            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

            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

            it 'schedules authorizations job' do
              expect(AuthorizedProjectUpdate::ProjectRecalculateWorker).to receive(:bulk_perform_async)
                .with(array_including(group.all_projects.ids.map { |id| [id] }))

              transfer_service.execute(new_parent_group)
            end
          end

          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
        end
      end

      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)
          new_parent_group.reload # make sure traversal_ids are reloaded
        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

      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

        it 'restores group and projects visibility' do
          subgroup1.reload
          project1.reload
          expect(subgroup1.public?).to be_truthy
          expect(project1.public?).to be_truthy
        end
      end

      context 'when group has pending builds', :sidekiq_inline do
        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
    end

    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

    context 'when a project has container images' do
      let(:group) { create(:group, :public, :nested) }
      let!(:container_repository) { create(:container_repository, project: project) }

      subject { transfer_service.execute(new_parent_group) }

      before do
        group.add_owner(user)
        new_parent_group.add_owner(user)
      end

      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

      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
      end
    end

    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) }
      let!(:crm_organizations) { create_list(:crm_organization, 2, group: root_group) }

      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

        it_behaves_like 'publishes a GroupTransferedEvent' do
          let(:target) { root_group }
        end
      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

        it_behaves_like 'publishes a GroupTransferedEvent' do
          let(:target) { another_subgroup }
        end
      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

        it_behaves_like 'publishes a GroupTransferedEvent' do
          let(:target) { another_subgroup }
        end
      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.crm_organizations.count }.by(-2)
        end

        it 'retains issue contacts' do
          expect { transfer_service.execute(new_parent_group) }
            .not_to change { CustomerRelations::IssueContact.count }
        end

        it_behaves_like 'publishes a GroupTransferedEvent' do
          let(:target) { new_parent_group }
        end
      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.crm_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

          it_behaves_like 'publishes a GroupTransferedEvent' do
            let(:target) { subgroup_in_new_parent_group }
          end
        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

          it 'does not publish a GroupTransferedEvent' do
            expect { transfer_service.execute(subgroup_in_new_parent_group) }
              .not_to publish_event(Groups::GroupTransferedEvent)
          end
        end
      end
    end

    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
  end
end


Instruction:
Write RSpec test file for following ruby class
```ruby
# frozen_string_literal: true

module Groups
  class AutoDevopsService < Groups::BaseService
    def execute
      raise Gitlab::Access::AccessDeniedError unless can?(current_user, :admin_group, group)

      group.update(auto_devops_enabled: auto_devops_enabled)
    end

    private

    def auto_devops_enabled
      params[:auto_devops_enabled]
    end
  end
end

```

Response:
# frozen_string_literal: true
require 'spec_helper'

RSpec.describe Groups::AutoDevopsService, '#execute', feature_category: :auto_devops do
  let_it_be(:group) { create(:group) }
  let_it_be(:user) { create(:user) }

  let(:group_params) { { auto_devops_enabled: '0' } }
  let(:service) { described_class.new(group, user, group_params) }

  context 'when user does not have enough privileges' do
    it 'raises exception' do
      group.add_developer(user)

      expect do
        service.execute
      end.to raise_exception(Gitlab::Access::AccessDeniedError)
    end
  end

  context 'when user has enough privileges' do
    before do
      group.add_owner(user)
    end

    it 'updates group auto devops enabled accordingly' do
      service.execute

      expect(group.auto_devops_enabled).to eq(false)
    end

    context 'when group has projects' do
      it 'reflects changes on projects' do
        project_1 = create(:project, namespace: group)

        service.execute

        expect(project_1).not_to have_auto_devops_implicitly_enabled
      end
    end

    context 'when group has subgroups' do
      it 'reflects changes on subgroups' do
        subgroup_1 = create(:group, parent: group)

        service.execute

        expect(subgroup_1.auto_devops_enabled?).to eq(false)
      end

      context 'when subgroups have projects' do
        it 'reflects changes on projects' do
          subgroup_1 = create(:group, parent: group)
          project_1 = create(:project, namespace: subgroup_1)

          service.execute

          expect(project_1).not_to have_auto_devops_implicitly_enabled
        end
      end
    end
  end
end


Instruction:
Write RSpec test file for following ruby class
```ruby
# frozen_string_literal: true

module Groups
  class UpdateSharedRunnersService < Groups::BaseService
    def execute
      return error('Operation not allowed', 403) unless can?(current_user, :admin_group, group)

      validate_params

      update_shared_runners
      update_pending_builds_async

      success

    rescue ActiveRecord::RecordInvalid, ArgumentError => error
      error(error.message)
    end

    private

    def validate_params
      unless Namespace::SHARED_RUNNERS_SETTINGS.include?(params[:shared_runners_setting])
        raise ArgumentError, "state must be one of: #{Namespace::SHARED_RUNNERS_SETTINGS.join(', ')}"
      end
    end

    def update_shared_runners
      case params[:shared_runners_setting]
      when Namespace::SR_DISABLED_AND_UNOVERRIDABLE
        set_shared_runners_enabled!(false)
      when Namespace::SR_DISABLED_WITH_OVERRIDE, Namespace::SR_DISABLED_AND_OVERRIDABLE
        disable_shared_runners_and_allow_override!
      when Namespace::SR_ENABLED
        set_shared_runners_enabled!(true)
      end
    end

    def update_pending_builds?
      group.previous_changes.include?('shared_runners_enabled')
    end

    def update_pending_builds_async
      return unless update_pending_builds?

      group.run_after_commit_or_now do |group|
        pending_builds_params = { instance_runners_enabled: group.shared_runners_enabled }

        ::Ci::UpdatePendingBuildService.new(group, pending_builds_params).execute
      end
    end

    def set_shared_runners_enabled!(enabled)
      group.update!(
        shared_runners_enabled: enabled,
        allow_descendants_override_disabled_shared_runners: false)

      group_ids = group.descendants
      unless group_ids.empty?
        Group.by_id(group_ids).update_all(
          shared_runners_enabled: enabled,
          allow_descendants_override_disabled_shared_runners: false)
      end

      group.all_projects.update_all(shared_runners_enabled: enabled)
    end

    def disable_shared_runners_and_allow_override!
      # enabled -> disabled_and_overridable
      if group.shared_runners_enabled?
        group.update!(
          shared_runners_enabled: false,
          allow_descendants_override_disabled_shared_runners: true)

        group_ids = group.descendants
        Group.by_id(group_ids).update_all(shared_runners_enabled: false) unless group_ids.empty?

        group.all_projects.update_all(shared_runners_enabled: false)

      # disabled_and_unoverridable -> disabled_and_overridable
      else
        group.update!(allow_descendants_override_disabled_shared_runners: true)
      end
    end
  end
end

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Groups::UpdateSharedRunnersService, feature_category: :groups_and_projects do
  include ReloadHelpers

  let(:user) { create(:user) }
  let(:params) { {} }
  let(:service) { described_class.new(group, user, params) }

  describe '#execute' do
    subject { service.execute }

    context 'when current_user is not the group owner' do
      let(:group) { create(:group) }

      let(:params) { { shared_runners_setting: 'enabled' } }

      before do
        group.add_maintainer(user)
      end

      it 'returns error' do
        expect(subject[:status]).to eq(:error)
        expect(subject[:message]).to eq('Operation not allowed')
        expect(subject[:http_status]).to eq(403)
      end
    end

    context 'when current_user is the group owner' do
      before do
        group.add_owner(user)
      end

      context 'enable shared Runners' do
        let(:params) { { shared_runners_setting: 'enabled' } }

        context 'when ancestor disable shared runners' do
          let(:parent) { create(:group, :shared_runners_disabled) }
          let(:group) { create(:group, :shared_runners_disabled, parent: parent) }
          let!(:project) { create(:project, shared_runners_enabled: false, group: group) }

          it 'returns an error and does not enable shared runners' do
            expect do
              expect(subject[:status]).to eq(:error)
              expect(subject[:message]).to eq('Validation failed: Shared runners enabled cannot be enabled because parent group has shared Runners disabled')

              reload_models(parent, group, project)
            end.to not_change { parent.shared_runners_enabled }
              .and not_change { group.shared_runners_enabled }
              .and not_change { project.shared_runners_enabled }
          end
        end

        context 'when updating root group' do
          let(:group) { create(:group, :shared_runners_disabled) }
          let(:sub_group) { create(:group, :shared_runners_disabled, parent: group) }
          let!(:project) { create(:project, shared_runners_enabled: false, group: sub_group) }

          it 'enables shared Runners for itself and descendants' do
            expect do
              expect(subject[:status]).to eq(:success)

              reload_models(group, sub_group, project)
            end.to change { group.shared_runners_enabled }.from(false).to(true)
              .and change { sub_group.shared_runners_enabled }.from(false).to(true)
              .and change { project.shared_runners_enabled }.from(false).to(true)
          end

          context 'when already allowing descendants to override' do
            let(:group) { create(:group, :shared_runners_disabled_and_overridable) }

            it 'enables shared Runners for itself and descendants' do
              expect do
                expect(subject[:status]).to eq(:success)

                reload_models(group, sub_group, project)
              end.to change { group.shared_runners_enabled }.from(false).to(true)
                .and change { group.allow_descendants_override_disabled_shared_runners }.from(true).to(false)
                .and change { sub_group.shared_runners_enabled }.from(false).to(true)
                .and change { project.shared_runners_enabled }.from(false).to(true)
            end
          end
        end

        context 'when group has pending builds' do
          let_it_be(:group) { create(:group, :shared_runners_disabled) }
          let_it_be(:project) { create(:project, namespace: group, shared_runners_enabled: false) }
          let_it_be(:pending_build_1) { create(:ci_pending_build, project: project, instance_runners_enabled: false) }
          let_it_be(:pending_build_2) { create(:ci_pending_build, project: project, instance_runners_enabled: false) }

          it 'updates pending builds for the group' do
            expect(::Ci::UpdatePendingBuildService).to receive(:new).and_call_original

            subject

            expect(pending_build_1.reload.instance_runners_enabled).to be_truthy
            expect(pending_build_2.reload.instance_runners_enabled).to be_truthy
          end

          context 'when shared runners is not toggled' do
            let(:params) { { shared_runners_setting: 'invalid_enabled' } }

            it 'does not update pending builds for the group' do
              expect(::Ci::UpdatePendingBuildService).not_to receive(:new)

              subject

              expect(pending_build_1.reload.instance_runners_enabled).to be_falsey
              expect(pending_build_2.reload.instance_runners_enabled).to be_falsey
            end
          end
        end
      end

      context 'disable shared Runners' do
        let!(:group) { create(:group) }
        let!(:sub_group) { create(:group, :shared_runners_disabled_and_overridable, parent: group) }
        let!(:sub_group2) { create(:group, parent: group) }
        let!(:project) { create(:project, group: group, shared_runners_enabled: true) }
        let!(:project2) { create(:project, group: sub_group2, shared_runners_enabled: true) }

        let(:params) { { shared_runners_setting: Namespace::SR_DISABLED_AND_UNOVERRIDABLE } }

        it 'disables shared Runners for all descendant groups and projects' do
          expect do
            expect(subject[:status]).to eq(:success)

            reload_models(group, sub_group, sub_group2, project, project2)
          end.to change { group.shared_runners_enabled }.from(true).to(false)
            .and not_change { group.allow_descendants_override_disabled_shared_runners }
            .and not_change { sub_group.shared_runners_enabled }
            .and change { sub_group.allow_descendants_override_disabled_shared_runners }.from(true).to(false)
            .and change { sub_group2.shared_runners_enabled }.from(true).to(false)
            .and not_change { sub_group2.allow_descendants_override_disabled_shared_runners }
            .and change { project.shared_runners_enabled }.from(true).to(false)
            .and change { project2.shared_runners_enabled }.from(true).to(false)
        end

        context 'with override on self' do
          let(:group) { create(:group, :shared_runners_disabled_and_overridable) }

          it 'disables it' do
            expect do
              expect(subject[:status]).to eq(:success)

              group.reload
            end
              .to not_change { group.shared_runners_enabled }
              .and change { group.allow_descendants_override_disabled_shared_runners }.from(true).to(false)
          end
        end

        context 'when group has pending builds' do
          let!(:pending_build_1) { create(:ci_pending_build, project: project, instance_runners_enabled: true) }
          let!(:pending_build_2) { create(:ci_pending_build, project: project, instance_runners_enabled: true) }

          it 'updates pending builds for the group' do
            expect(::Ci::UpdatePendingBuildService).to receive(:new).and_call_original

            subject

            expect(pending_build_1.reload.instance_runners_enabled).to be_falsey
            expect(pending_build_2.reload.instance_runners_enabled).to be_falsey
          end
        end
      end

      shared_examples 'allow descendants to override' do
        context 'top level group' do
          let!(:group) { create(:group, :shared_runners_disabled) }
          let!(:sub_group) { create(:group, :shared_runners_disabled, parent: group) }
          let!(:project) { create(:project, shared_runners_enabled: false, group: sub_group) }

          it 'enables allow descendants to override only for itself' do
            expect do
              expect(subject[:status]).to eq(:success)

              reload_models(group, sub_group, project)
            end.to change { group.allow_descendants_override_disabled_shared_runners }.from(false).to(true)
              .and not_change { group.shared_runners_enabled }
              .and not_change { sub_group.allow_descendants_override_disabled_shared_runners }
              .and not_change { sub_group.shared_runners_enabled }
              .and not_change { project.shared_runners_enabled }
          end
        end

        context 'when ancestor disables shared Runners but allows to override' do
          let!(:parent) { create(:group, :shared_runners_disabled_and_overridable) }
          let!(:group) { create(:group, :shared_runners_disabled, parent: parent) }
          let!(:project) { create(:project, shared_runners_enabled: false, group: group) }

          it 'enables allow descendants to override' do
            expect do
              expect(subject[:status]).to eq(:success)

              reload_models(parent, group, project)
            end
              .to not_change { parent.allow_descendants_override_disabled_shared_runners }
              .and not_change { parent.shared_runners_enabled }
              .and change { group.allow_descendants_override_disabled_shared_runners }.from(false).to(true)
              .and not_change { group.shared_runners_enabled }
              .and not_change { project.shared_runners_enabled }
          end
        end

        context 'when ancestor disables shared runners' do
          let(:parent) { create(:group, :shared_runners_disabled) }
          let(:group) { create(:group, :shared_runners_disabled, parent: parent) }
          let!(:project) { create(:project, shared_runners_enabled: false, group: group) }

          it 'returns an error and does not enable shared runners' do
            expect do
              expect(subject[:status]).to eq(:error)
              expect(subject[:message]).to eq('Validation failed: Allow descendants override disabled shared runners cannot be enabled because parent group does not allow it')

              reload_models(parent, group, project)
            end.to not_change { parent.shared_runners_enabled }
              .and not_change { group.shared_runners_enabled }
              .and not_change { project.shared_runners_enabled }
          end
        end

        context 'top level group that has shared Runners enabled' do
          let!(:group) { create(:group, shared_runners_enabled: true) }
          let!(:sub_group) { create(:group, shared_runners_enabled: true, parent: group) }
          let!(:project) { create(:project, shared_runners_enabled: true, group: sub_group) }

          it 'enables allow descendants to override & disables shared runners everywhere' do
            expect do
              expect(subject[:status]).to eq(:success)

              reload_models(group, sub_group, project)
            end
              .to change { group.shared_runners_enabled }.from(true).to(false)
              .and change { group.allow_descendants_override_disabled_shared_runners }.from(false).to(true)
              .and change { sub_group.shared_runners_enabled }.from(true).to(false)
              .and change { project.shared_runners_enabled }.from(true).to(false)
          end
        end
      end

      context "when using SR_DISABLED_AND_OVERRIDABLE" do
        let(:params) { { shared_runners_setting: Namespace::SR_DISABLED_AND_OVERRIDABLE } }

        include_examples 'allow descendants to override'
      end

      context "when using SR_DISABLED_WITH_OVERRIDE" do
        let(:params) { { shared_runners_setting: Namespace::SR_DISABLED_WITH_OVERRIDE } }

        include_examples 'allow descendants to override'
      end
    end
  end
end


Instruction:
Write RSpec test file for following ruby class
```ruby
# frozen_string_literal: true

module Groups
  class DestroyService < Groups::BaseService
    DestroyError = Class.new(StandardError)

    def async_execute
      job_id = GroupDestroyWorker.perform_async(group.id, current_user.id)
      Gitlab::AppLogger.info("User #{current_user.id} scheduled a deletion of group ID #{group.id} with job ID #{job_id}")
    end

    # rubocop: disable CodeReuse/ActiveRecord
    def execute
      # TODO - add a policy check here https://gitlab.com/gitlab-org/gitlab/-/issues/353082
      raise DestroyError, "You can't delete this group because you're blocked." if current_user.blocked?

      group.projects.includes(:project_feature).each do |project|
        # Execute the destruction of the models immediately to ensure atomic cleanup.
        success = ::Projects::DestroyService.new(project, current_user).execute

        raise DestroyError, "Project #{project.id} can't be deleted" unless success
      end

      # reload the relation to prevent triggering destroy hooks on the projects again
      group.projects.reset

      group.children.each do |group|
        # This needs to be synchronous since the namespace gets destroyed below
        DestroyService.new(group, current_user).execute
      end

      group.chat_team&.remove_mattermost_team(current_user)

      user_ids_for_project_authorizations_refresh = obtain_user_ids_for_project_authorizations_refresh

      destroy_associated_users

      group.destroy

      if user_ids_for_project_authorizations_refresh.present?
        UserProjectAccessChangedService
          .new(user_ids_for_project_authorizations_refresh)
          .execute
      end

      publish_event

      group
    end
    # rubocop: enable CodeReuse/ActiveRecord

    private

    def any_groups_shared_with_this_group?
      group.shared_group_links.any?
    end

    def any_projects_shared_with_this_group?
      group.project_group_links.any?
    end

    # Destroying a group automatically destroys all project authorizations directly
    # associated with the group and descendents. However, project authorizations
    # for projects and groups this group is shared with are not. Without a manual
    # refresh, the project authorization records of these users to shared projects
    # and projects within the shared groups will never be removed, causing
    # inconsistencies with access permissions.
    #
    # This method retrieves the user IDs that need to be refreshed. If only
    # groups are shared with this group, only direct members need to be refreshed.
    # If projects are also shared with the group, direct members *and* shared
    # members of other groups need to be refreshed.
    # `Group#user_ids_for_project_authorizations` returns both direct and shared
    # members' user IDs.
    def obtain_user_ids_for_project_authorizations_refresh
      return unless any_projects_shared_with_this_group? || any_groups_shared_with_this_group?
      return group.user_ids_for_project_authorizations if any_projects_shared_with_this_group?

      group.users_ids_of_direct_members
    end

    # rubocop:disable CodeReuse/ActiveRecord
    def destroy_associated_users
      current_user_id = current_user.id
      bot_ids = users_to_destroy

      group.run_after_commit do
        bot_ids.each do |user_id|
          DeleteUserWorker.perform_async(current_user_id, user_id, skip_authorization: true)
        end
      end
    end
    # rubocop:enable CodeReuse/ActiveRecord

    # rubocop:disable CodeReuse/ActiveRecord
    def users_to_destroy
      group.members_and_requesters.joins(:user)
        .merge(User.project_bot)
        .allow_cross_joins_across_databases(url: 'https://gitlab.com/gitlab-org/gitlab/-/issues/422405')
        .pluck(:user_id)
    end
    # rubocop:enable CodeReuse/ActiveRecord

    def publish_event
      event = Groups::GroupDeletedEvent.new(
        data: {
          group_id: group.id,
          root_namespace_id: group.root_ancestor.id
        }
      )

      Gitlab::EventStore.publish(event)
    end
  end
end

Groups::DestroyService.prepend_mod_with('Groups::DestroyService')

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Groups::DestroyService, feature_category: :groups_and_projects do
  let!(:user)         { create(:user) }
  let!(:group)        { create(:group) }
  let!(:nested_group) { create(:group, parent: group) }
  let!(:project)      { create(:project, :repository, :legacy_storage, namespace: group) }
  let!(:notification_setting) { create(:notification_setting, source: group) }
  let(:remove_path)  { group.path + "+#{group.id}+deleted" }
  let(:removed_repo) { Gitlab::Git::Repository.new(project.repository_storage, remove_path, nil, nil) }

  before do
    group.add_member(user, Gitlab::Access::OWNER)
  end

  def destroy_group(group, user, async)
    if async
      Groups::DestroyService.new(group, user).async_execute
    else
      Groups::DestroyService.new(group, user).execute
    end
  end

  shared_examples 'group destruction' do |async|
    context 'database records', :sidekiq_might_not_need_inline do
      before do
        destroy_group(group, user, async)
      end

      it { expect(Group.unscoped.all).not_to include(group) }
      it { expect(Group.unscoped.all).not_to include(nested_group) }
      it { expect(Project.unscoped.all).not_to include(project) }
      it { expect(NotificationSetting.unscoped.all).not_to include(notification_setting) }
    end

    context 'bot tokens', :sidekiq_inline do
      it 'initiates group bot removal', :aggregate_failures do
        bot = create(:user, :project_bot)
        group.add_developer(bot)
        create(:personal_access_token, user: bot)

        destroy_group(group, user, async)

        expect(
          Users::GhostUserMigration.where(user: bot, initiator_user: user)
        ).to be_exists
      end
    end

    context 'mattermost team', :sidekiq_might_not_need_inline do
      let!(:chat_team) { create(:chat_team, namespace: group) }

      it 'destroys the team too' do
        expect_next_instance_of(::Mattermost::Team) do |instance|
          expect(instance).to receive(:destroy)
        end

        destroy_group(group, user, async)
      end
    end

    context 'file system', :sidekiq_might_not_need_inline do
      context 'Sidekiq inline' do
        before do
          # Run sidekiq immediately to check that renamed dir will be removed
          perform_enqueued_jobs { destroy_group(group, user, async) }
        end

        it 'verifies that paths have been deleted' do
          expect(removed_repo).not_to exist
        end
      end
    end

    context 'event store', :sidekiq_might_not_need_inline do
      it 'publishes a GroupDeletedEvent' do
        expect { destroy_group(group, user, async) }
          .to publish_event(Groups::GroupDeletedEvent)
          .with(
            group_id: group.id,
            root_namespace_id: group.root_ancestor.id
          )
      end
    end
  end

  describe 'asynchronous delete' do
    it_behaves_like 'group destruction', true

    context 'Sidekiq fake' do
      before do
        # Don't run Sidekiq to verify that group and projects are not actually destroyed
        Sidekiq::Testing.fake! { destroy_group(group, user, true) }
      end

      it 'verifies original paths and projects still exist' do
        expect(removed_repo).not_to exist
        expect(Project.unscoped.count).to eq(1)
        expect(Group.unscoped.count).to eq(2)
      end
    end
  end

  describe 'synchronous delete' do
    it_behaves_like 'group destruction', false
  end

  context 'projects in pending_delete' do
    before do
      project.pending_delete = true
      project.save!
    end

    it_behaves_like 'group destruction', false
  end

  context 'repository removal status is taken into account' do
    it 'raises exception' do
      expect_next_instance_of(::Projects::DestroyService) do |destroy_service|
        expect(destroy_service).to receive(:execute).and_return(false)
      end

      expect { destroy_group(group, user, false) }
        .to raise_error(Groups::DestroyService::DestroyError, "Project #{project.id} can't be deleted")
    end
  end

  context 'when group owner is blocked' do
    before do
      user.block!
    end

    it 'returns a more descriptive error message' do
      expect { destroy_group(group, user, false) }
      .to raise_error(Groups::DestroyService::DestroyError, "You can't delete this group because you're blocked.")
    end
  end

  describe 'repository removal' do
    before do
      destroy_group(group, user, false)
    end

    context 'legacy storage' do
      let!(:project) { create(:project, :legacy_storage, :empty_repo, namespace: group) }

      it 'removes repository' do
        expect(project.repository.raw).not_to exist
      end
    end

    context 'hashed storage' do
      let!(:project) { create(:project, :empty_repo, namespace: group) }

      it 'removes repository' do
        expect(project.repository.raw).not_to exist
      end
    end
  end

  describe 'authorization updates', :sidekiq_inline do
    context 'for solo groups' do
      context 'group is deleted' do
        it 'updates project authorization' do
          expect { destroy_group(group, user, false) }.to(
            change { user.can?(:read_project, project) }.from(true).to(false))
        end

        it 'does not make use of a specific service to update project_authorizations records' do
          expect(UserProjectAccessChangedService)
            .not_to receive(:new).with(group.user_ids_for_project_authorizations)

          destroy_group(group, user, false)
        end
      end
    end

    context 'for shared groups within different hierarchies' do
      let(:group1) { create(:group, :private) }
      let(:group2) { create(:group, :private) }

      let(:group1_user) { create(:user) }
      let(:group2_user) { create(:user) }

      before do
        group1.add_member(group1_user, Gitlab::Access::OWNER)
        group2.add_member(group2_user, Gitlab::Access::OWNER)
      end

      context 'when a project is shared with a group' do
        let!(:group1_project) { create(:project, :private, group: group1) }

        before do
          create(:project_group_link, project: group1_project, group: group2)
        end

        context 'and the shared group is deleted' do
          it 'updates project authorizations so group2 users no longer have access', :aggregate_failures do
            expect(group1_user.can?(:read_project, group1_project)).to eq(true)
            expect(group2_user.can?(:read_project, group1_project)).to eq(true)

            destroy_group(group2, group2_user, false)

            expect(group1_user.can?(:read_project, group1_project)).to eq(true)
            expect(group2_user.can?(:read_project, group1_project)).to eq(false)
          end

          it 'calls the service to update project authorizations only with necessary user ids' do
            expect(UserProjectAccessChangedService)
              .to receive(:new).with(array_including(group2_user.id)).and_call_original

            destroy_group(group2, group2_user, false)
          end
        end

        context 'and the group is shared with another group' do
          let(:group3) { create(:group, :private) }
          let(:group3_user) { create(:user) }

          before do
            group3.add_member(group3_user, Gitlab::Access::OWNER)

            create(:group_group_link, shared_group: group2, shared_with_group: group3)
            group3.refresh_members_authorized_projects
          end

          it 'updates project authorizations so group2 and group3 users no longer have access', :aggregate_failures do
            expect(group1_user.can?(:read_project, group1_project)).to eq(true)
            expect(group2_user.can?(:read_project, group1_project)).to eq(true)
            expect(group3_user.can?(:read_project, group1_project)).to eq(true)

            destroy_group(group2, group2_user, false)

            expect(group1_user.can?(:read_project, group1_project)).to eq(true)
            expect(group2_user.can?(:read_project, group1_project)).to eq(false)
            expect(group3_user.can?(:read_project, group1_project)).to eq(false)
          end

          it 'calls the service to update project authorizations only with necessary user ids' do
            expect(UserProjectAccessChangedService)
              .to receive(:new).with(array_including(group2_user.id, group3_user.id)).and_call_original

            destroy_group(group2, group2_user, false)
          end
        end
      end

      context 'when a group is shared with a group' do
        let!(:group2_project) { create(:project, :private, group: group2) }

        before do
          create(:group_group_link, shared_group: group2, shared_with_group: group1)
          group1.refresh_members_authorized_projects
        end

        context 'and the shared group is deleted' do
          it 'updates project authorizations since the project has been deleted with the group', :aggregate_failures do
            expect(group1_user.can?(:read_project, group2_project)).to eq(true)
            expect(group2_user.can?(:read_project, group2_project)).to eq(true)

            destroy_group(group2, group2_user, false)

            expect(group1_user.can?(:read_project, group2_project)).to eq(false)
            expect(group2_user.can?(:read_project, group2_project)).to eq(false)
          end

          it 'does not call the service to update project authorizations' do
            expect(UserProjectAccessChangedService).not_to receive(:new)

            destroy_group(group2, group2_user, false)
          end
        end

        context 'the shared_with group is deleted' do
          let!(:group2_subgroup) { create(:group, :private, parent: group2) }
          let!(:group2_subgroup_project) { create(:project, :private, group: group2_subgroup) }

          it 'updates project authorizations so users of both groups lose access', :aggregate_failures do
            expect(group1_user.can?(:read_project, group2_project)).to eq(true)
            expect(group2_user.can?(:read_project, group2_project)).to eq(true)
            expect(group1_user.can?(:read_project, group2_subgroup_project)).to eq(true)
            expect(group2_user.can?(:read_project, group2_subgroup_project)).to eq(true)

            destroy_group(group1, group1_user, false)

            expect(group1_user.can?(:read_project, group2_project)).to eq(false)
            expect(group2_user.can?(:read_project, group2_project)).to eq(true)
            expect(group1_user.can?(:read_project, group2_subgroup_project)).to eq(false)
            expect(group2_user.can?(:read_project, group2_subgroup_project)).to eq(true)
          end

          it 'calls the service to update project authorizations only with necessary user ids' do
            expect(UserProjectAccessChangedService)
              .to receive(:new).with([group1_user.id]).and_call_original

            destroy_group(group1, group1_user, false)
          end
        end
      end
    end

    context 'for shared groups in the same group hierarchy' do
      let(:shared_group) { group }
      let(:shared_with_group) { nested_group }
      let!(:shared_with_group_user) { create(:user) }

      before do
        shared_with_group.add_member(shared_with_group_user, Gitlab::Access::MAINTAINER)

        create(:group_group_link, shared_group: shared_group, shared_with_group: shared_with_group)
        shared_with_group.refresh_members_authorized_projects
      end

      context 'the shared group is deleted' do
        it 'updates project authorization' do
          expect { destroy_group(shared_group, user, false) }.to(
            change { shared_with_group_user.can?(:read_project, project) }.from(true).to(false))
        end

        it 'does not make use of a specific service to update project authorizations' do
          # Due to the recursive nature of `Groups::DestroyService`, `UserProjectAccessChangedService`
          # will still be executed for the nested group as they fall under the same hierarchy
          # and hence we need to account for this scenario.
          expect(UserProjectAccessChangedService)
            .to receive(:new).with(shared_with_group.users_ids_of_direct_members).and_call_original

          expect(UserProjectAccessChangedService)
            .not_to receive(:new).with(shared_group.users_ids_of_direct_members)

          destroy_group(shared_group, user, false)
        end
      end

      context 'the shared_with group is deleted' do
        it 'updates project authorization' do
          expect { destroy_group(shared_with_group, user, false) }.to(
            change { shared_with_group_user.can?(:read_project, project) }.from(true).to(false))
        end

        it 'makes use of a specific service to update project authorizations' do
          expect(UserProjectAccessChangedService)
            .to receive(:new).with(shared_with_group.users_ids_of_direct_members).and_call_original

          destroy_group(shared_with_group, user, false)
        end
      end
    end
  end
end