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

module Groups
  class UpdateService < Groups::BaseService
    include UpdateVisibilityLevel

    SETTINGS_PARAMS = [:allow_mfa_for_subgroups].freeze

    def execute
      reject_parent_id!
      remove_unallowed_params

      before_assignment_hook(group, params)

      if renaming_group_with_container_registry_images?
        group.errors.add(:base, container_images_error)
        return false
      end

      return false unless valid_visibility_level_change?(group, group.visibility_attribute_value(params))

      return false unless valid_share_with_group_lock_change?

      return false unless valid_path_change?

      return false unless update_shared_runners

      handle_changes

      handle_namespace_settings

      group.assign_attributes(params)

      begin
        success = group.save

        after_update if success

        success
      rescue Gitlab::UpdatePathError => e
        group.errors.add(:base, e.message)

        false
      end
    end

    private

    def valid_path_change?
      return true unless group.packages_feature_enabled?
      return true if params[:path].blank?
      return true if group.has_parent?
      return true if !group.has_parent? && group.path == params[:path]

      # we have a path change on a root group:
      # check that we don't have any npm package with a scope set to the group path
      npm_packages = ::Packages::GroupPackagesFinder.new(current_user, group, package_type: :npm, preload_pipelines: false)
                       .execute
                       .with_npm_scope(group.path)

      return true unless npm_packages.exists?

      group.errors.add(:path, s_('GroupSettings|cannot change when group contains projects with NPM packages'))
      false
    end

    def before_assignment_hook(group, params)
      @full_path_before = group.full_path
      @path_before = group.path
    end

    def renaming_group_with_container_registry_images?
      renaming? && group.has_container_repository_including_subgroups?
    end

    def renaming?
      new_path = params[:path]

      new_path && new_path != @path_before
    end

    def container_images_error
      s_("GroupSettings|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.")
    end

    def after_update
      if group.previous_changes.include?(group.visibility_level_field) && group.private?
        # don't enqueue immediately to prevent todos removal in case of a mistake
        TodosDestroyer::GroupPrivateWorker.perform_in(Todo::WAIT_FOR_DELETE, group.id)
      end

      update_two_factor_requirement_for_subgroups

      publish_event
    end

    def update_two_factor_requirement_for_subgroups
      settings = group.namespace_settings
      return if settings.allow_mfa_for_subgroups

      if settings.previous_changes.include?(:allow_mfa_for_subgroups)
        # enque in batches members update
        DisallowTwoFactorForSubgroupsWorker.perform_async(group.id)
      end
    end

    def reject_parent_id!
      params.delete(:parent_id)
    end

    # overridden in EE
    def remove_unallowed_params
      params.delete(:emails_disabled) unless can?(current_user, :set_emails_disabled, group)

      unless can?(current_user, :update_default_branch_protection, group)
        params.delete(:default_branch_protection)
        params.delete(:default_branch_protection_defaults)
      end
    end

    def handle_changes
      handle_settings_update
      handle_crm_settings_update unless params[:crm_enabled].nil?
    end

    def handle_settings_update
      settings_params = params.slice(*allowed_settings_params)
      settings_params.merge!({ default_branch_protection: params[:default_branch_protection] }.compact)
      allowed_settings_params.each { |param| params.delete(param) }

      ::NamespaceSettings::UpdateService.new(current_user, group, settings_params).execute
    end

    def handle_crm_settings_update
      crm_enabled = params.delete(:crm_enabled)
      return if group.crm_enabled? == crm_enabled

      crm_settings = group.crm_settings || group.build_crm_settings
      crm_settings.enabled = crm_enabled
      crm_settings.save
    end

    def allowed_settings_params
      SETTINGS_PARAMS
    end

    def valid_share_with_group_lock_change?
      return true unless changing_share_with_group_lock?
      return true if can?(current_user, :change_share_with_group_lock, group)

      group.errors.add(:share_with_group_lock, s_('GroupSettings|cannot be disabled when the parent group "Share with group lock" is enabled, except by the owner of the parent group'))
      false
    end

    def changing_share_with_group_lock?
      return false if params[:share_with_group_lock].nil?

      params[:share_with_group_lock] != group.share_with_group_lock
    end

    def update_shared_runners
      return true if params[:shared_runners_setting].nil?

      result = Groups::UpdateSharedRunnersService.new(group, current_user, shared_runners_setting: params.delete(:shared_runners_setting)).execute

      return true if result[:status] == :success

      group.errors.add(:update_shared_runners, result[:message])
      false
    end

    def publish_event
      return unless renaming?

      event = Groups::GroupPathChangedEvent.new(
        data: {
          group_id: group.id,
          root_namespace_id: group.root_ancestor.id,
          old_path: @full_path_before,
          new_path: group.full_path
        }
      )

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

Groups::UpdateService.prepend_mod_with('Groups::UpdateService')

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Groups::UpdateService, feature_category: :groups_and_projects do
  let!(:user) { create(:user) }
  let!(:private_group) { create(:group, :private) }
  let!(:internal_group) { create(:group, :internal) }
  let!(:public_group) { create(:group, :public) }

  describe "#execute" do
    context 'with project' do
      let!(:group) { create(:group, :public) }
      let(:project) { create(:project, namespace: group) }

      context 'located in a subgroup' do
        let(:subgroup) { create(:group, parent: group) }
        let!(:project) { create(:project, namespace: subgroup) }

        before do
          subgroup.add_owner(user)
        end

        it 'does allow a path update if there is not a root namespace change' do
          expect(update_group(subgroup, user, path: 'updated')).to be true
          expect(subgroup.errors[:path]).to be_empty
        end
      end
    end

    context "project visibility_level validation" do
      context "public group with public projects" do
        let!(:service) { described_class.new(public_group, user, visibility_level: Gitlab::VisibilityLevel::INTERNAL) }

        before do
          public_group.add_member(user, Gitlab::Access::OWNER)
          create(:project, :public, group: public_group)

          expect(TodosDestroyer::GroupPrivateWorker).not_to receive(:perform_in)
        end

        it "does not change permission level" do
          service.execute
          expect(public_group.errors.count).to eq(1)

          expect(TodosDestroyer::GroupPrivateWorker).not_to receive(:perform_in)
        end

        it "returns false if save failed" do
          allow(public_group).to receive(:save).and_return(false)

          expect(service.execute).to be_falsey
        end

        context 'when a project has container images' do
          let(:params) { { path: SecureRandom.hex } }
          let!(:container_repository) { create(:container_repository, project: project) }

          subject { described_class.new(public_group, user, params).execute }

          context 'within group' do
            let(:project) { create(:project, group: public_group) }

            context 'with path updates' do
              it 'does not allow the update' do
                expect(subject).to be false
                expect(public_group.errors[:base].first).to match(/Docker images in their Container Registry/)
              end
            end

            context 'with name updates' do
              let(:params) { { name: 'new-name' } }

              it 'allows the update' do
                expect(subject).to be true
                expect(public_group.reload.name).to eq('new-name')
              end
            end

            context 'when the path does not change' do
              let(:params) { { name: 'new-name', path: public_group.path } }

              it 'allows the update' do
                expect(subject).to be true
                expect(public_group.reload.name).to eq('new-name')
              end
            end
          end

          context 'within subgroup' do
            let(:subgroup) { create(:group, parent: public_group) }
            let(:project) { create(:project, group: subgroup) }

            it 'does not allow path updates' do
              expect(subject).to be false
              expect(public_group.errors[:base].first).to match(/Docker images in their Container Registry/)
            end
          end
        end
      end

      context "internal group with internal project" do
        let!(:service) { described_class.new(internal_group, user, visibility_level: Gitlab::VisibilityLevel::PRIVATE) }

        before do
          internal_group.add_member(user, Gitlab::Access::OWNER)
          create(:project, :internal, group: internal_group)

          expect(TodosDestroyer::GroupPrivateWorker).not_to receive(:perform_in)
        end

        it "does not change permission level" do
          service.execute
          expect(internal_group.errors.count).to eq(1)
        end
      end

      context "internal group with private project" do
        let!(:service) { described_class.new(internal_group, user, visibility_level: Gitlab::VisibilityLevel::PRIVATE) }

        before do
          internal_group.add_member(user, Gitlab::Access::OWNER)
          create(:project, :private, group: internal_group)

          expect(TodosDestroyer::GroupPrivateWorker).to receive(:perform_in)
            .with(Todo::WAIT_FOR_DELETE, internal_group.id)
        end

        it "changes permission level to private" do
          service.execute
          expect(internal_group.visibility_level)
            .to eq(Gitlab::VisibilityLevel::PRIVATE)
        end
      end
    end

    context "with parent_id user doesn't have permissions for" do
      let(:service) { described_class.new(public_group, user, parent_id: private_group.id) }

      before do
        service.execute
      end

      it 'does not update parent_id' do
        updated_group = public_group.reload

        expect(updated_group.parent_id).to be_nil
      end
    end

    context 'crm_enabled param' do
      context 'when no existing crm_settings' do
        it 'when param not present, leave crm disabled' do
          params = {}

          described_class.new(public_group, user, params).execute
          updated_group = public_group.reload

          expect(updated_group.crm_enabled?).to be_falsey
        end

        it 'when param set true, enables crm' do
          params = { crm_enabled: true }

          described_class.new(public_group, user, params).execute
          updated_group = public_group.reload

          expect(updated_group.crm_enabled?).to be_truthy
        end
      end

      context 'with existing crm_settings' do
        it 'when param set true, enables crm' do
          params = { crm_enabled: true }
          create(:crm_settings, group: public_group)

          described_class.new(public_group, user, params).execute

          updated_group = public_group.reload
          expect(updated_group.crm_enabled?).to be_truthy
        end

        it 'when param set false, disables crm' do
          params = { crm_enabled: false }
          create(:crm_settings, group: public_group, enabled: true)

          described_class.new(public_group, user, params).execute

          updated_group = public_group.reload
          expect(updated_group.crm_enabled?).to be_falsy
        end

        it 'when param not present, crm remains disabled' do
          params = {}
          create(:crm_settings, group: public_group)

          described_class.new(public_group, user, params).execute

          updated_group = public_group.reload
          expect(updated_group.crm_enabled?).to be_falsy
        end

        it 'when param not present, crm remains enabled' do
          params = {}
          create(:crm_settings, group: public_group, enabled: true)

          described_class.new(public_group, user, params).execute

          updated_group = public_group.reload
          expect(updated_group.crm_enabled?).to be_truthy
        end
      end
    end
  end

  context "unauthorized visibility_level validation" do
    let!(:service) { described_class.new(internal_group, user, visibility_level: 99) }

    before do
      internal_group.add_member(user, Gitlab::Access::MAINTAINER)
    end

    it "does not change permission level" do
      service.execute
      expect(internal_group.errors.count).to eq(1)
    end
  end

  context "path change validation" do
    let_it_be(:group) { create(:group) }
    let_it_be(:subgroup) { create(:group, parent: group) }
    let_it_be(:project) { create(:project, namespace: subgroup) }

    subject(:execute_update) { update_group(target_group, user, update_params) }

    shared_examples 'not allowing a path update' do
      let(:update_params) { { path: 'updated' } }

      it 'does not allow a path update' do
        target_group.add_maintainer(user)

        expect(execute_update).to be false
        expect(target_group.errors[:path]).to include('cannot change when group contains projects with NPM packages')
      end
    end

    shared_examples 'allowing an update' do |on:|
      let(:update_params) { { on => 'updated' } }

      it "allows an update on #{on}" do
        target_group.reload.add_maintainer(user)

        expect(execute_update).to be true
        expect(target_group.errors).to be_empty
        expect(target_group[on]).to eq('updated')
      end
    end

    context 'with namespaced npm packages' do
      let_it_be(:package) { create(:npm_package, project: project, name: "@#{group.path}/test") }

      context 'updating the root group' do
        let_it_be_with_refind(:target_group) { group }

        it_behaves_like 'not allowing a path update'
        it_behaves_like 'allowing an update', on: :name
      end

      context 'updating the subgroup' do
        let_it_be_with_refind(:target_group) { subgroup }

        it_behaves_like 'allowing an update', on: :path
        it_behaves_like 'allowing an update', on: :name
      end
    end

    context 'with scoped npm packages' do
      let_it_be(:package) { create(:npm_package, project: project, name: '@any_scope/test') }

      context 'updating the root group' do
        let_it_be_with_refind(:target_group) { group }

        it_behaves_like 'allowing an update', on: :path
        it_behaves_like 'allowing an update', on: :name
      end

      context 'updating the subgroup' do
        let_it_be_with_refind(:target_group) { subgroup }

        it_behaves_like 'allowing an update', on: :path
        it_behaves_like 'allowing an update', on: :name
      end
    end

    context 'with unscoped npm packages' do
      let_it_be(:package) { create(:npm_package, project: project, name: 'test') }

      context 'updating the root group' do
        let_it_be_with_refind(:target_group) { group }

        it_behaves_like 'allowing an update', on: :path
        it_behaves_like 'allowing an update', on: :name
      end

      context 'updating the subgroup' do
        let_it_be_with_refind(:target_group) { subgroup }

        it_behaves_like 'allowing an update', on: :path
        it_behaves_like 'allowing an update', on: :name
      end
    end
  end

  context 'when user is not group owner' do
    context 'when group is private' do
      before do
        private_group.add_maintainer(user)
      end

      it 'does not update the group to public' do
        result = described_class.new(private_group, user, visibility_level: Gitlab::VisibilityLevel::PUBLIC).execute

        expect(result).to eq(false)
        expect(private_group.errors.count).to eq(1)
        expect(private_group).to be_private
      end

      it 'does not update the group to public with tricky value' do
        result = described_class.new(private_group, user, visibility_level: Gitlab::VisibilityLevel::PUBLIC.to_s + 'r').execute

        expect(result).to eq(false)
        expect(private_group.errors.count).to eq(1)
        expect(private_group).to be_private
      end
    end

    context 'when group is public' do
      before do
        public_group.add_maintainer(user)
      end

      it 'does not update the group to private' do
        result = described_class.new(public_group, user, visibility_level: Gitlab::VisibilityLevel::PRIVATE).execute

        expect(result).to eq(false)
        expect(public_group.errors.count).to eq(1)
        expect(public_group).to be_public
      end

      it 'does not update the group to private with invalid string value' do
        result = described_class.new(public_group, user, visibility_level: 'invalid').execute

        expect(result).to eq(false)
        expect(public_group.errors.count).to eq(1)
        expect(public_group).to be_public
      end

      it 'does not update the group to private with valid string value' do
        result = described_class.new(public_group, user, visibility_level: 'private').execute

        expect(result).to eq(false)
        expect(public_group.errors.count).to eq(1)
        expect(public_group).to be_public
      end

      # See https://gitlab.com/gitlab-org/gitlab/-/issues/359910
      it 'does not update the group to private because of Active Record typecasting' do
        result = described_class.new(public_group, user, visibility_level: 'public').execute

        expect(result).to eq(true)
        expect(public_group.errors.count).to eq(0)
        expect(public_group).to be_public
      end
    end
  end

  context 'when updating #emails_disabled' do
    let(:service) { described_class.new(internal_group, user, emails_disabled: true) }

    it 'updates the attribute' do
      internal_group.add_member(user, Gitlab::Access::OWNER)

      expect { service.execute }.to change { internal_group.emails_disabled }.to(true)
    end

    it 'does not update when not group owner' do
      expect { service.execute }.not_to change { internal_group.emails_disabled }
    end
  end

  context 'updating default_branch_protection' do
    let(:service) do
      described_class.new(internal_group, user, default_branch_protection: Gitlab::Access::PROTECTION_NONE)
    end

    let(:settings) { internal_group.namespace_settings }
    let(:expected_settings) { Gitlab::Access::BranchProtection.protection_none.stringify_keys }

    context 'for users who have the ability to update default_branch_protection' do
      it 'updates default_branch_protection attribute' do
        internal_group.add_owner(user)

        expect { service.execute }.to change { internal_group.default_branch_protection }.from(Gitlab::Access::PROTECTION_FULL).to(Gitlab::Access::PROTECTION_NONE)
      end

      it 'updates default_branch_protection_defaults to match default_branch_protection' do
        internal_group.add_owner(user)

        expect { service.execute }.to change { settings.default_branch_protection_defaults  }.from({}).to(expected_settings)
      end
    end

    context 'for users who do not have the ability to update default_branch_protection' do
      it 'does not update the attribute' do
        expect { service.execute }.not_to change { internal_group.default_branch_protection }
        expect { service.execute }.not_to change { internal_group.namespace_settings.default_branch_protection_defaults }
      end
    end
  end

  context 'updating default_branch_protection_defaults' do
    let(:branch_protection) { ::Gitlab::Access::BranchProtection.protected_against_developer_pushes.stringify_keys }

    let(:service) do
      described_class.new(internal_group, user, default_branch_protection_defaults: branch_protection)
    end

    let(:settings) { internal_group.namespace_settings }
    let(:expected_settings) { branch_protection }

    context 'for users who have the ability to update default_branch_protection_defaults' do
      it 'updates default_branch_protection attribute' do
        internal_group.add_owner(user)

        expect { service.execute }.to change { internal_group.default_branch_protection_defaults }.from({}).to(expected_settings)
      end
    end

    context 'for users who do not have the ability to update default_branch_protection_defaults' do
      it 'does not update the attribute' do
        expect { service.execute }.not_to change { internal_group.default_branch_protection_defaults }
        expect { service.execute }.not_to change { internal_group.namespace_settings.default_branch_protection_defaults }
      end
    end
  end

  context 'EventStore' do
    let(:service) { described_class.new(group, user, **params) }
    let(:root_group) { create(:group, path: 'root') }
    let(:group) do
      create(:group, parent: root_group, path: 'old-path').tap { |g| g.add_owner(user) }
    end

    context 'when changing a group path' do
      let(:new_path) { SecureRandom.hex }
      let(:params) { { path: new_path } }

      it 'publishes a GroupPathChangedEvent' do
        old_path = group.full_path

        expect { service.execute }
          .to publish_event(Groups::GroupPathChangedEvent)
          .with(
            group_id: group.id,
            root_namespace_id: group.root_ancestor.id,
            old_path: old_path,
            new_path: "root/#{new_path}"
          )
      end
    end

    context 'when not changing a group path' do
      let(:params) { { name: 'very-new-name' } }

      it 'does not publish a GroupPathChangedEvent' do
        expect { service.execute }
          .not_to publish_event(Groups::GroupPathChangedEvent)
      end
    end
  end

  context 'rename group' do
    let(:new_path) { SecureRandom.hex }
    let!(:service) { described_class.new(internal_group, user, path: new_path) }

    before do
      internal_group.add_member(user, Gitlab::Access::MAINTAINER)
      create(:project, :internal, group: internal_group)
    end

    it 'returns true' do
      expect(service.execute).to eq(true)
    end
  end

  context 'for a subgroup' do
    let(:subgroup) { create(:group, :private, parent: private_group) }

    context 'when the parent group share_with_group_lock is enabled' do
      before do
        private_group.update_column(:share_with_group_lock, true)
      end

      context 'for the parent group owner' do
        it 'allows disabling share_with_group_lock' do
          private_group.add_owner(user)

          result = described_class.new(subgroup, user, share_with_group_lock: false).execute

          expect(result).to be_truthy
          expect(subgroup.reload.share_with_group_lock).to be_falsey
        end
      end

      context 'for a subgroup owner (who does not own the parent)' do
        it 'does not allow disabling share_with_group_lock' do
          subgroup_owner = create(:user)
          subgroup.add_owner(subgroup_owner)

          result = described_class.new(subgroup, subgroup_owner, share_with_group_lock: false).execute

          expect(result).to be_falsey
          expect(subgroup.errors.full_messages.first).to match(/cannot be disabled when the parent group "Share with group lock" is enabled, except by the owner of the parent group/)
          expect(subgroup.reload.share_with_group_lock).to be_truthy
        end
      end
    end
  end

  context 'change shared Runners config' do
    let(:group) { create(:group) }
    let(:project) { create(:project, shared_runners_enabled: true, group: group) }

    subject { described_class.new(group, user, shared_runners_setting: Namespace::SR_DISABLED_AND_UNOVERRIDABLE).execute }

    before do
      group.add_owner(user)
    end

    it 'calls the shared runners update service' do
      expect_any_instance_of(::Groups::UpdateSharedRunnersService).to receive(:execute).and_return({ status: :success })

      expect(subject).to be_truthy
    end

    it 'handles errors in the shared runners update service' do
      expect_any_instance_of(::Groups::UpdateSharedRunnersService).to receive(:execute).and_return({ status: :error, message: 'something happened' })

      expect(subject).to be_falsy

      expect(group.errors[:update_shared_runners].first).to eq('something happened')
    end
  end

  context 'changes allowing subgroups to establish own 2FA' do
    let(:group) { create(:group) }
    let(:params) { { allow_mfa_for_subgroups: false } }

    subject { described_class.new(group, user, params).execute }

    it 'changes settings' do
      subject

      expect(group.namespace_settings.reload.allow_mfa_for_subgroups).to eq(false)
    end

    it 'enqueues update subgroups and its members' do
      expect(DisallowTwoFactorForSubgroupsWorker).to receive(:perform_async).with(group.id)

      subject
    end
  end

  def update_group(group, user, opts)
    Groups::UpdateService.new(group, user, opts).execute
  end
end


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

module Groups
  # Service class for counting and caching the number of open issues of a group.
  class OpenIssuesCountService < Groups::CountService
    PUBLIC_COUNT_KEY = 'group_public_open_issues_count'
    TOTAL_COUNT_KEY = 'group_total_open_issues_count'

    def clear_all_cache_keys
      [cache_key(PUBLIC_COUNT_KEY), cache_key(TOTAL_COUNT_KEY)].each do |key|
        Rails.cache.delete(key)
      end
    end

    private

    def cache_key_name
      public_only? ? PUBLIC_COUNT_KEY : TOTAL_COUNT_KEY
    end

    def public_only?
      !user_is_at_least_reporter?
    end

    def user_is_at_least_reporter?
      strong_memoize(:user_is_at_least_reporter) do
        group.member?(user, Gitlab::Access::REPORTER)
      end
    end

    def relation_for_count
      IssuesFinder.new(
        user,
        group_id: group.id,
        state: 'opened',
        non_archived: true,
        include_subgroups: true,
        public_only: public_only?
      ).execute
    end

    def issuable_key
      'open_issues'
    end
  end
end

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Groups::OpenIssuesCountService, :use_clean_rails_memory_store_caching, feature_category: :groups_and_projects do
  let_it_be(:group) { create(:group, :public) }
  let_it_be(:project) { create(:project, :public, namespace: group) }
  let_it_be(:user) { create(:user) }
  let_it_be(:issue) { create(:issue, :opened, project: project) }
  let_it_be(:confidential) { create(:issue, :opened, confidential: true, project: project) }
  let_it_be(:closed) { create(:issue, :closed, project: project) }

  subject { described_class.new(group, user) }

  describe '#relation_for_count' do
    before do
      allow(IssuesFinder).to receive(:new).and_call_original
    end

    it 'uses the IssuesFinder to scope issues' do
      expect(IssuesFinder)
        .to receive(:new)
        .with(user, group_id: group.id, state: 'opened', non_archived: true, include_subgroups: true, public_only: true)

      subject.count
    end
  end

  describe '#count' do
    context 'when user is nil' do
      it 'does not include confidential issues in the issue count' do
        expect(described_class.new(group).count).to eq(1)
      end
    end

    context 'when user is provided' do
      context 'when user can read confidential issues' do
        before do
          group.add_reporter(user)
        end

        it 'returns the right count with confidential issues' do
          expect(subject.count).to eq(2)
        end
      end

      context 'when user cannot read confidential issues' do
        before do
          group.add_guest(user)
        end

        it 'does not include confidential issues' do
          expect(subject.count).to eq(1)
        end
      end

      it_behaves_like 'a counter caching service with threshold'
    end
  end

  describe '#clear_all_cache_keys' do
    it 'calls `Rails.cache.delete` with the correct keys' do
      expect(Rails.cache).to receive(:delete)
        .with(['groups', 'open_issues_count_service', 1, group.id, described_class::PUBLIC_COUNT_KEY])
      expect(Rails.cache).to receive(:delete)
        .with(['groups', 'open_issues_count_service', 1, group.id, described_class::TOTAL_COUNT_KEY])

      subject.clear_all_cache_keys
    end
  end
end


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

module Groups
  class UpdateStatisticsService
    attr_reader :group, :statistics

    def initialize(group, statistics: [])
      @group = group
      @statistics = statistics
    end

    def execute
      unless group
        return ServiceResponse.error(message: 'Invalid group', http_status: 400)
      end

      namespace_statistics.refresh!(only: statistics.map(&:to_sym))

      ServiceResponse.success(message: 'Group statistics successfully updated.')
    end

    private

    def namespace_statistics
      @namespace_statistics ||= group.namespace_statistics || group.build_namespace_statistics
    end
  end
end

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Groups::UpdateStatisticsService, feature_category: :groups_and_projects do
  let_it_be(:group, reload: true) { create(:group) }

  let(:statistics) { %w[wiki_size] }

  subject(:service) { described_class.new(group, statistics: statistics) }

  describe '#execute', :aggregate_failures do
    context 'when group is nil' do
      let(:group) { nil }

      it 'does nothing' do
        expect(NamespaceStatistics).not_to receive(:new)

        result = service.execute

        expect(result).to be_error
      end
    end

    context 'with an existing group' do
      context 'when namespace statistics exists for the group' do
        it 'uses the existing statistics and refreshes them' do
          namespace_statistics = create(:namespace_statistics, namespace: group)

          expect(namespace_statistics).to receive(:refresh!).with(only: statistics.map(&:to_sym)).and_call_original

          result = service.execute

          expect(result).to be_success
        end
      end

      context 'when namespace statistics does not exist for the group' do
        it 'creates the statistics and refreshes them' do
          expect_next_instance_of(NamespaceStatistics) do |instance|
            expect(instance).to receive(:refresh!).with(only: statistics.map(&:to_sym)).and_call_original
          end

          result = nil

          expect do
            result = service.execute
          end.to change { NamespaceStatistics.count }.by(1)

          expect(result).to be_success
        end
      end
    end
  end
end


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

module Groups
  class CreateService < Groups::BaseService
    def initialize(user, params = {})
      @current_user = user
      @params = params.dup
      @chat_team = @params.delete(:create_chat_team)
      @create_event = @params.delete(:create_event)
    end

    def execute
      remove_unallowed_params
      set_visibility_level

      @group = Group.new(params.except(*::NamespaceSetting.allowed_namespace_settings_params))

      @group.build_namespace_settings
      handle_namespace_settings

      after_build_hook(@group, params)

      inherit_group_shared_runners_settings

      unless can_use_visibility_level? && can_create_group?
        return @group
      end

      @group.name ||= @group.path.dup

      if create_chat_team?
        response = ::Mattermost::CreateTeamService.new(@group, current_user).execute
        return @group if @group.errors.any?

        @group.build_chat_team(name: response['name'], team_id: response['id'])
      end

      Gitlab::Database::QueryAnalyzers::PreventCrossDatabaseModification.temporary_ignore_tables_in_transaction(
        %w[routes redirect_routes], url: 'https://gitlab.com/gitlab-org/gitlab/-/issues/424281'
      ) do
        Group.transaction do
          if @group.save
            @group.add_owner(current_user)
            Integration.create_from_active_default_integrations(@group, :group_id)
          end
        end
      end

      after_create_hook

      @group
    end

    private

    attr_reader :create_event

    def after_build_hook(group, params)
      # overridden in EE
    end

    def after_create_hook
      # overridden in EE
    end

    def remove_unallowed_params
      unless can?(current_user, :create_group_with_default_branch_protection)
        params.delete(:default_branch_protection)
        params.delete(:default_branch_protection_defaults)
      end

      params.delete(:allow_mfa_for_subgroups)
    end

    def create_chat_team?
      Gitlab.config.mattermost.enabled && @chat_team && group.chat_team.nil?
    end

    def can_create_group?
      if @group.subgroup?
        unless can?(current_user, :create_subgroup, @group.parent)
          @group.parent = nil
          @group.errors.add(:parent_id, s_('CreateGroup|You don’t have permission to create a subgroup in this group.'))

          return false
        end
      else
        unless can?(current_user, :create_group)
          @group.errors.add(:base, s_('CreateGroup|You don’t have permission to create groups.'))

          return false
        end
      end

      true
    end

    def can_use_visibility_level?
      unless Gitlab::VisibilityLevel.allowed_for?(current_user, visibility_level)
        deny_visibility_level(@group)
        return false
      end

      true
    end

    def set_visibility_level
      return if visibility_level.present?

      params[:visibility_level] = Gitlab::CurrentSettings.current_application_settings.default_group_visibility
    end

    def inherit_group_shared_runners_settings
      return unless @group.parent

      @group.shared_runners_enabled = @group.parent.shared_runners_enabled
      @group.allow_descendants_override_disabled_shared_runners = @group.parent.allow_descendants_override_disabled_shared_runners
    end
  end
end

Groups::CreateService.prepend_mod_with('Groups::CreateService')

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Groups::CreateService, '#execute', feature_category: :groups_and_projects do
  let!(:user) { create(:user) }
  let!(:group_params) { { path: "group_path", visibility_level: Gitlab::VisibilityLevel::PUBLIC } }

  subject { service.execute }

  shared_examples 'has sync-ed traversal_ids' do
    specify { expect(subject.reload.traversal_ids).to eq([subject.parent&.traversal_ids, subject.id].flatten.compact) }
  end

  describe 'visibility level restrictions' do
    let!(:service) { described_class.new(user, group_params) }

    context "create groups without restricted visibility level" do
      it { is_expected.to be_persisted }
    end

    context "cannot create group with restricted visibility level" do
      before do
        allow_any_instance_of(ApplicationSetting).to receive(:restricted_visibility_levels).and_return([Gitlab::VisibilityLevel::PUBLIC])
      end

      it { is_expected.not_to be_persisted }
    end
  end

  context 'when `setup_for_company:true` is passed' do
    let(:params) { group_params.merge(setup_for_company: true) }
    let(:service) { described_class.new(user, params) }
    let(:created_group) { service.execute }

    it 'creates group with the specified setup_for_company' do
      expect(created_group.setup_for_company).to eq(true)
    end
  end

  context 'creating a group with `default_branch_protection` attribute' do
    let(:params) { group_params.merge(default_branch_protection: Gitlab::Access::PROTECTION_NONE) }
    let(:service) { described_class.new(user, params) }
    let(:created_group) { service.execute }

    context 'for users who have the ability to create a group with `default_branch_protection`' do
      it 'creates group with the specified branch protection level' do
        expect(created_group.default_branch_protection).to eq(Gitlab::Access::PROTECTION_NONE)
      end
    end

    context 'for users who do not have the ability to create a group with `default_branch_protection`' do
      it 'does not create the group with the specified branch protection level' do
        allow(Ability).to receive(:allowed?).and_call_original
        allow(Ability).to receive(:allowed?).with(user, :create_group_with_default_branch_protection) { false }

        expect(created_group.default_branch_protection).not_to eq(Gitlab::Access::PROTECTION_NONE)
      end
    end
  end

  context 'creating a group with `default_branch_protection_defaults` attribute' do
    let(:branch_protection) { ::Gitlab::Access::BranchProtection.protected_against_developer_pushes.stringify_keys }
    let(:params) { group_params.merge(default_branch_protection_defaults: branch_protection) }
    let(:service) { described_class.new(user, params) }
    let(:created_group) { service.execute }

    context 'for users who have the ability to create a group with `default_branch_protection`' do
      before do
        allow(Ability).to receive(:allowed?).and_call_original
        allow(Ability).to receive(:allowed?).with(user, :update_default_branch_protection, an_instance_of(Group)).and_return(true)
      end

      it 'creates group with the specified default branch protection settings' do
        expect(created_group.default_branch_protection_defaults).to eq(branch_protection)
      end
    end

    context 'for users who do not have the ability to create a group with `default_branch_protection_defaults`' do
      it 'does not create the group with the specified default branch protection settings' do
        allow(Ability).to receive(:allowed?).and_call_original
        allow(Ability).to receive(:allowed?).with(user, :create_group_with_default_branch_protection) { false }

        expect(created_group.default_branch_protection_defaults).not_to eq(Gitlab::Access::PROTECTION_NONE)
      end
    end
  end

  context 'creating a group with `allow_mfa_for_subgroups` attribute' do
    let(:params) { group_params.merge(allow_mfa_for_subgroups: false) }
    let(:service) { described_class.new(user, params) }

    it 'creates group without error' do
      expect(service.execute).to be_persisted
    end
  end

  describe 'creating a top level group' do
    let(:service) { described_class.new(user, group_params) }

    context 'when user can create a group' do
      before do
        user.update_attribute(:can_create_group, true)
      end

      it { is_expected.to be_persisted }

      context 'with before_commit callback' do
        it_behaves_like 'has sync-ed traversal_ids'
      end
    end

    context 'when user can not create a group' do
      before do
        user.update_attribute(:can_create_group, false)
      end

      it { is_expected.not_to be_persisted }
    end
  end

  describe 'creating subgroup' do
    let!(:group) { create(:group) }
    let!(:service) { described_class.new(user, group_params.merge(parent_id: group.id)) }

    context 'as group owner' do
      before do
        group.add_owner(user)
      end

      it { is_expected.to be_persisted }

      it_behaves_like 'has sync-ed traversal_ids'
    end

    context 'as guest' do
      it 'does not save group and returns an error' do
        is_expected.not_to be_persisted

        expect(subject.errors[:parent_id].first).to eq(s_('CreateGroup|You don’t have permission to create a subgroup in this group.'))
        expect(subject.parent_id).to be_nil
      end
    end

    context 'as owner' do
      before do
        group.add_owner(user)
      end

      it { is_expected.to be_persisted }
    end

    context 'as maintainer' do
      before do
        group.add_maintainer(user)
      end

      it { is_expected.to be_persisted }
    end
  end

  describe "when visibility level is passed as a string" do
    let(:service) { described_class.new(user, group_params) }
    let(:group_params) { { path: 'group_path', visibility: 'public' } }

    it "assigns the correct visibility level" do
      group = service.execute

      expect(group.visibility_level).to eq(Gitlab::VisibilityLevel::PUBLIC)
    end
  end

  describe 'creating a mattermost team' do
    let!(:params) { group_params.merge(create_chat_team: "true") }
    let!(:service) { described_class.new(user, params) }

    before do
      stub_mattermost_setting(enabled: true)
    end

    it 'create the chat team with the group' do
      allow_any_instance_of(::Mattermost::Team).to receive(:create)
        .and_return({ 'name' => 'tanuki', 'id' => 'lskdjfwlekfjsdifjj' })

      expect { subject }.to change { ChatTeam.count }.from(0).to(1)
    end
  end

  describe 'creating a setting record' do
    let(:service) { described_class.new(user, group_params) }

    it 'create the settings record connected to the group' do
      group = subject
      expect(group.namespace_settings).to be_persisted
    end
  end

  describe 'creating a details record' do
    let(:service) { described_class.new(user, group_params) }

    it 'create the details record connected to the group' do
      group = subject
      expect(group.namespace_details).to be_persisted
    end
  end

  describe 'create service for the group' do
    let(:service) { described_class.new(user, group_params) }
    let(:created_group) { service.execute }

    context 'with an active instance-level integration' do
      let!(:instance_integration) { create(:prometheus_integration, :instance, api_url: 'https://prometheus.instance.com/') }

      it 'creates a service from the instance-level integration' do
        expect(created_group.integrations.count).to eq(1)
        expect(created_group.integrations.first.api_url).to eq(instance_integration.api_url)
        expect(created_group.integrations.first.inherit_from_id).to eq(instance_integration.id)
      end

      context 'with an active group-level integration' do
        let(:service) { described_class.new(user, group_params.merge(parent_id: group.id)) }
        let!(:group_integration) { create(:prometheus_integration, :group, group: group, api_url: 'https://prometheus.group.com/') }
        let(:group) do
          create(:group).tap do |group|
            group.add_owner(user)
          end
        end

        it 'creates a service from the group-level integration' do
          expect(created_group.integrations.count).to eq(1)
          expect(created_group.integrations.first.api_url).to eq(group_integration.api_url)
          expect(created_group.integrations.first.inherit_from_id).to eq(group_integration.id)
        end

        context 'with an active subgroup' do
          let(:service) { described_class.new(user, group_params.merge(parent_id: subgroup.id)) }
          let!(:subgroup_integration) { create(:prometheus_integration, :group, group: subgroup, api_url: 'https://prometheus.subgroup.com/') }
          let(:subgroup) do
            create(:group, parent: group).tap do |subgroup|
              subgroup.add_owner(user)
            end
          end

          it 'creates a service from the subgroup-level integration' do
            expect(created_group.integrations.count).to eq(1)
            expect(created_group.integrations.first.api_url).to eq(subgroup_integration.api_url)
            expect(created_group.integrations.first.inherit_from_id).to eq(subgroup_integration.id)
          end
        end
      end
    end
  end

  context 'shared runners configuration' do
    context 'parent group present' do
      using RSpec::Parameterized::TableSyntax

      where(:shared_runners_config, :descendants_override_disabled_shared_runners_config) do
        true  | false
        false | false
        # true  | true # invalid at the group level, leaving as comment to make explicit
        false | true
      end

      with_them do
        let!(:group) { create(:group, shared_runners_enabled: shared_runners_config, allow_descendants_override_disabled_shared_runners: descendants_override_disabled_shared_runners_config) }
        let!(:service) { described_class.new(user, group_params.merge(parent_id: group.id)) }

        before do
          group.add_owner(user)
        end

        it 'creates group following the parent config' do
          new_group = service.execute

          expect(new_group.shared_runners_enabled).to eq(shared_runners_config)
          expect(new_group.allow_descendants_override_disabled_shared_runners).to eq(descendants_override_disabled_shared_runners_config)
        end
      end
    end

    context 'root group' do
      let!(:service) { described_class.new(user) }

      it 'follows default config' do
        new_group = service.execute

        expect(new_group.shared_runners_enabled).to eq(true)
        expect(new_group.allow_descendants_override_disabled_shared_runners).to eq(false)
      end
    end
  end
end


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

module Groups
  class NestedCreateService < Groups::BaseService
    attr_reader :group_path, :visibility_level

    def initialize(user, params)
      @current_user = user
      @params = params.dup
      @group_path = @params.delete(:group_path)
      @visibility_level = @params.delete(:visibility_level) ||
        Gitlab::CurrentSettings.current_application_settings.default_group_visibility
    end

    def execute
      return unless group_path

      if namespace = namespace_or_group(group_path)
        return namespace
      end

      create_group_path
    end

    private

    def create_group_path
      group_path_segments = group_path.split('/')

      last_group = nil
      partial_path_segments = []
      while subgroup_name = group_path_segments.shift
        partial_path_segments << subgroup_name
        partial_path = partial_path_segments.join('/')

        new_params = params.reverse_merge(
          path: subgroup_name,
          name: subgroup_name,
          parent: last_group,
          visibility_level: visibility_level
        )

        last_group = namespace_or_group(partial_path) ||
          Groups::CreateService.new(current_user, new_params).execute
      end

      last_group
    end

    def namespace_or_group(group_path)
      Namespace.find_by_full_path(group_path)
    end
  end
end

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Groups::NestedCreateService, feature_category: :groups_and_projects do
  let(:user) { create(:user) }

  subject(:service) { described_class.new(user, params) }

  shared_examples 'with a visibility level' do
    it 'creates the group with correct visibility level' do
      allow(Gitlab::CurrentSettings.current_application_settings)
        .to receive(:default_group_visibility) { Gitlab::VisibilityLevel::INTERNAL }

      group = service.execute

      expect(group.visibility_level).to eq(Gitlab::VisibilityLevel::INTERNAL)
    end

    context 'adding a visibility level ' do
      it 'overwrites the visibility level' do
        service = described_class.new(user, params.merge(visibility_level: Gitlab::VisibilityLevel::PRIVATE))

        group = service.execute

        expect(group.visibility_level).to eq(Gitlab::VisibilityLevel::PRIVATE)
      end
    end
  end

  describe 'with subgroups' do
    let(:params) { { group_path: 'a-group/a-sub-group' } }

    describe "#execute" do
      it 'returns the group if it already existed' do
        parent = create(:group, path: 'a-group')
        child = create(:group, path: 'a-sub-group', parent: parent)

        parent.add_owner(user)
        child.add_owner(user)

        expect(service.execute).to eq(child)
      end

      it 'reuses a parent if it already existed' do
        parent = create(:group, path: 'a-group')
        parent.add_owner(user)

        expect(service.execute.parent).to eq(parent)
      end

      it 'creates group and subgroup in the database' do
        service.execute

        parent = Group.find_by_full_path('a-group')
        child = parent.children.find_by(path: 'a-sub-group')

        expect(parent).not_to be_nil
        expect(child).not_to be_nil
      end

      it_behaves_like 'with a visibility level'
    end
  end
end


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

module Groups
  module GroupLinks
    class DestroyService < ::Groups::BaseService
      def execute(one_or_more_links, skip_authorization: false)
        unless skip_authorization || group && can?(current_user, :admin_group_member, group)
          return error('Not Found', 404)
        end

        links = Array(one_or_more_links)

        if GroupGroupLink.delete(links)
          Gitlab::AppLogger.info(
            "GroupGroupLinks with ids: #{links.map(&:id)} have been deleted.")

          groups_to_refresh = links.map(&:shared_with_group)
          groups_to_refresh.uniq.each do |group|
            next if Feature.enabled?(:skip_group_share_unlink_auth_refresh, group.root_ancestor)

            group.refresh_members_authorized_projects(direct_members_only: true)
          end
        else
          Gitlab::AppLogger.info(
            "Failed to delete GroupGroupLinks with ids: #{links.map(&:id)}.")
        end

        links
      end
    end
  end
end

Groups::GroupLinks::DestroyService.prepend_mod

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Groups::GroupLinks::DestroyService, '#execute', feature_category: :groups_and_projects do
  let_it_be(:user) { create(:user) }
  let_it_be(:group) { create(:group, :private) }
  let_it_be(:shared_group) { create(:group, :private) }
  let_it_be(:project) { create(:project, group: shared_group) }
  let_it_be(:owner) { create(:user) }

  subject { described_class.new(shared_group, owner) }

  context 'when authorizing by user' do
    before do
      group.add_developer(owner)
      shared_group.add_owner(owner)
    end

    context 'single link' do
      let!(:link) { create(:group_group_link, shared_group: shared_group, shared_with_group: group) }

      it 'destroys the link' do
        expect { subject.execute(link) }.to change { shared_group.shared_with_group_links.count }.from(1).to(0)
      end

      context 'with skip_group_share_unlink_auth_refresh feature flag disabled' do
        before do
          stub_feature_flags(skip_group_share_unlink_auth_refresh: false)
        end

        it 'revokes project authorization', :sidekiq_inline do
          group.add_developer(user)

          expect { subject.execute(link) }.to(
            change { Ability.allowed?(user, :read_project, project) }.from(true).to(false))
        end
      end

      context 'with skip_group_share_unlink_auth_refresh feature flag enabled' do
        before do
          stub_feature_flags(skip_group_share_unlink_auth_refresh: true)
        end

        it 'maintains project authorization', :sidekiq_inline do
          group.add_developer(user)

          expect(Ability.allowed?(user, :read_project, project)).to be_truthy
        end
      end
    end

    context 'multiple links' do
      let_it_be(:another_group) { create(:group, :private) }
      let_it_be(:another_shared_group) { create(:group, :private) }

      let!(:links) do
        [
          create(:group_group_link, shared_group: shared_group, shared_with_group: group),
          create(:group_group_link, shared_group: shared_group, shared_with_group: another_group),
          create(:group_group_link, shared_group: another_shared_group, shared_with_group: group),
          create(:group_group_link, shared_group: another_shared_group, shared_with_group: another_group)
        ]
      end

      context 'with skip_group_share_unlink_auth_refresh feature flag disabled' do
        before do
          stub_feature_flags(skip_group_share_unlink_auth_refresh: false)
        end

        it 'updates project authorization once per group' do
          expect(GroupGroupLink).to receive(:delete).and_call_original
          expect(group).to receive(:refresh_members_authorized_projects).with(direct_members_only: true).once
          expect(another_group).to receive(:refresh_members_authorized_projects).with(direct_members_only: true).once

          subject.execute(links)
        end
      end

      context 'with skip_group_share_unlink_auth_refresh feature flag enabled' do
        before do
          stub_feature_flags(skip_group_share_unlink_auth_refresh: true)
        end

        it 'does not update project authorization once per group' do
          expect(GroupGroupLink).to receive(:delete).and_call_original
          expect(group).not_to receive(:refresh_members_authorized_projects)
          expect(another_group).not_to receive(:refresh_members_authorized_projects)

          subject.execute(links)
        end
      end
    end
  end

  context 'when skipping authorization' do
    let!(:link) { create(:group_group_link, shared_group: shared_group, shared_with_group: group) }

    context 'with provided group and owner' do
      it 'destroys the link' do
        expect do
          subject.execute(link, skip_authorization: true)
        end.to change { shared_group.shared_with_group_links.count }.from(1).to(0)
      end
    end

    context 'without providing group or owner' do
      subject { described_class.new(nil, nil) }

      it 'destroys the link' do
        expect do
          subject.execute(link, skip_authorization: true)
        end.to change { shared_group.shared_with_group_links.count }.from(1).to(0)
      end
    end
  end
end


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

module Groups
  module GroupLinks
    class UpdateService < ::Groups::BaseService
      def initialize(group_link, user = nil)
        super(group_link.shared_group, user)

        @group_link = group_link
      end

      def execute(group_link_params)
        group_link.update!(group_link_params)

        if requires_authorization_refresh?(group_link_params)
          group_link.shared_with_group.refresh_members_authorized_projects(direct_members_only: true)
        end

        group_link
      end

      private

      attr_accessor :group_link

      def requires_authorization_refresh?(params)
        params.include?(:group_access)
      end
    end
  end
end

Groups::GroupLinks::UpdateService.prepend_mod

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Groups::GroupLinks::UpdateService, '#execute', feature_category: :groups_and_projects do
  let(:user) { create(:user) }

  let_it_be(:group) { create(:group, :private) }
  let_it_be(:shared_group) { create(:group, :private) }
  let_it_be(:project) { create(:project, group: shared_group) }

  let(:group_member_user) { create(:user) }
  let!(:link) { create(:group_group_link, shared_group: shared_group, shared_with_group: group) }

  let(:expiry_date) { 1.month.from_now.to_date }
  let(:group_link_params) do
    { group_access: Gitlab::Access::GUEST,
      expires_at: expiry_date }
  end

  subject { described_class.new(link, user).execute(group_link_params) }

  before do
    group.add_developer(group_member_user)
  end

  it 'updates existing link' do
    expect(link.group_access).to eq(Gitlab::Access::DEVELOPER)
    expect(link.expires_at).to be_nil

    subject

    link.reload

    expect(link.group_access).to eq(Gitlab::Access::GUEST)
    expect(link.expires_at).to eq(expiry_date)
  end

  it 'updates project permissions', :sidekiq_inline do
    expect { subject }.to change { group_member_user.can?(:create_release, project) }.from(true).to(false)
  end

  it 'executes UserProjectAccessChangedService' do
    expect_next_instance_of(UserProjectAccessChangedService, [group_member_user.id]) do |service|
      expect(service).to receive(:execute)
    end

    subject
  end

  context 'with only param not requiring authorization refresh' do
    let(:group_link_params) { { expires_at: Date.tomorrow } }

    it 'does not execute UserProjectAccessChangedService' do
      expect(UserProjectAccessChangedService).not_to receive(:new)

      subject
    end
  end
end


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

module Groups
  module GroupLinks
    class CreateService < ::Groups::BaseService
      include GroupLinkable

      def initialize(group, shared_with_group, user, params)
        @shared_with_group = shared_with_group

        super(group, user, params)
      end

      private

      delegate :root_ancestor, to: :group

      def valid_to_create?
        can?(current_user, :admin_group_member, group) &&
          can?(current_user, :read_group, shared_with_group) &&
          sharing_allowed?
      end

      def build_link
        @link = GroupGroupLink.new(
          shared_group: group,
          shared_with_group: shared_with_group,
          group_access: params[:shared_group_access],
          expires_at: params[:expires_at]
        )
      end

      def setup_authorizations
        shared_with_group.refresh_members_authorized_projects(direct_members_only: true)
      end
    end
  end
end

Groups::GroupLinks::CreateService.prepend_mod

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Groups::GroupLinks::CreateService, '#execute', feature_category: :groups_and_projects do
  let_it_be(:shared_with_group_parent) { create(:group, :private) }
  let_it_be(:shared_with_group) { create(:group, :private, parent: shared_with_group_parent) }
  let_it_be(:shared_with_group_child) { create(:group, :private, parent: shared_with_group) }

  let_it_be(:group_parent) { create(:group, :private) }

  let(:group) { create(:group, :private, parent: group_parent) }

  let(:opts) do
    {
      shared_group_access: Gitlab::Access::DEVELOPER,
      expires_at: nil
    }
  end

  subject { described_class.new(group, shared_with_group, user, opts) }

  shared_examples_for 'not shareable' do
    it 'does not share and returns an error' do
      expect do
        result = subject.execute

        expect(result[:status]).to eq(:error)
        expect(result[:http_status]).to eq(404)
      end.not_to change { group.shared_with_group_links.count }
    end
  end

  shared_examples_for 'shareable' do
    it 'adds group to another group' do
      expect do
        result = subject.execute

        expect(result[:status]).to eq(:success)
      end.to change { group.shared_with_group_links.count }.from(0).to(1)
    end
  end

  context 'when user has proper membership to share a group' do
    let_it_be(:group_user) { create(:user) }

    let(:user) { group_user }

    before do
      shared_with_group.add_guest(group_user)
      group.add_owner(group_user)
    end

    it_behaves_like 'shareable'

    context 'when sharing outside the hierarchy is disabled' do
      let_it_be_with_refind(:group_parent) do
        create(:group, namespace_settings: create(:namespace_settings, prevent_sharing_groups_outside_hierarchy: true))
      end

      it_behaves_like 'not shareable'

      context 'when group is inside hierarchy' do
        let(:shared_with_group) { create(:group, :private, parent: group_parent) }

        it_behaves_like 'shareable'
      end
    end

    context 'project authorizations based on group hierarchies' do
      let_it_be(:child_group_user) { create(:user) }
      let_it_be(:parent_group_user) { create(:user) }

      before do
        shared_with_group_parent.add_owner(parent_group_user)
        shared_with_group.add_owner(group_user)
        shared_with_group_child.add_owner(child_group_user)
      end

      context 'project authorizations refresh' do
        it 'is executed only for the direct members of the group' do
          expect(UserProjectAccessChangedService).to receive(:new).with(contain_exactly(group_user.id))
                                                                  .and_call_original

          subject.execute
        end
      end

      context 'project authorizations' do
        let(:group_child) { create(:group, :private, parent: group) }
        let(:project_parent) { create(:project, group: group_parent) }
        let(:project) { create(:project, group: group) }
        let(:project_child) { create(:project, group: group_child) }

        context 'group user' do
          let(:user) { group_user }

          it 'create proper authorizations' do
            subject.execute

            expect(Ability.allowed?(user, :read_project, project_parent)).to be_falsey
            expect(Ability.allowed?(user, :read_project, project)).to be_truthy
            expect(Ability.allowed?(user, :read_project, project_child)).to be_truthy
          end
        end

        context 'parent group user' do
          let(:user) { parent_group_user }

          it 'create proper authorizations' do
            subject.execute

            expect(Ability.allowed?(user, :read_project, project_parent)).to be_falsey
            expect(Ability.allowed?(user, :read_project, project)).to be_falsey
            expect(Ability.allowed?(user, :read_project, project_child)).to be_falsey
          end
        end

        context 'child group user' do
          let(:user) { child_group_user }

          it 'create proper authorizations' do
            subject.execute

            expect(Ability.allowed?(user, :read_project, project_parent)).to be_falsey
            expect(Ability.allowed?(user, :read_project, project)).to be_falsey
            expect(Ability.allowed?(user, :read_project, project_child)).to be_falsey
          end
        end
      end
    end
  end

  context 'user does not have access to group' do
    let(:user) { create(:user) }

    before do
      group.add_owner(user)
    end

    it_behaves_like 'not shareable'
  end

  context 'user does not have admin access to shared group' do
    let(:user) { create(:user) }

    before do
      shared_with_group.add_guest(user)
      group.add_developer(user)
    end

    it_behaves_like 'not shareable'
  end

  context 'when group is blank' do
    let(:group_user) { create(:user) }
    let(:user) { group_user }
    let(:group) { nil }

    it 'does not share and returns an error' do
      expect do
        result = subject.execute

        expect(result[:status]).to eq(:error)
        expect(result[:http_status]).to eq(404)
      end.not_to change { shared_with_group.shared_group_links.count }
    end
  end

  context 'when shared_with_group is blank' do
    let(:group_user) { create(:user) }
    let(:user) { group_user }
    let(:shared_with_group) { nil }

    it_behaves_like 'not shareable'
  end
end


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

module Groups
  module ImportExport
    class ImportService
      attr_reader :current_user, :group, :shared

      def initialize(group:, user:)
        @group = group
        @current_user = user
        @user_role = user_role
        @shared = Gitlab::ImportExport::Shared.new(@group)
        @logger = Gitlab::Import::Logger.build
      end

      def async_execute
        group_import_state = GroupImportState.safe_find_or_create_by!(group: group, user: current_user)
        jid = GroupImportWorker.with_status.perform_async(current_user.id, group.id)

        if jid.present?
          group_import_state.update!(jid: jid)
        else
          group_import_state.fail_op('Failed to schedule import job')

          false
        end
      end

      def execute
        Gitlab::Tracking.event(self.class.name, 'create', label: 'import_group_from_file')

        if valid_user_permissions? && import_file && valid_import_file? && restorers.all?(&:restore)
          notify_success

          Gitlab::Tracking.event(
            self.class.name,
            'create',
            label: 'import_access_level',
            user: current_user,
            extra: { user_role: user_role, import_type: 'import_group_from_file' }
          )

          group
        else
          notify_error!
        end

      ensure
        remove_base_tmp_dir
        remove_import_file
      end

      private

      def user_role
        # rubocop:disable CodeReuse/ActiveRecord, Style/MultilineTernaryOperator
        access_level = group.parent ?
          current_user&.group_members&.find_by(source_id: group.parent&.id)&.access_level :
          Gitlab::Access::OWNER
        Gitlab::Access.human_access(access_level)
        # rubocop:enable CodeReuse/ActiveRecord, Style/MultilineTernaryOperator
      end

      def import_file
        @import_file ||= Gitlab::ImportExport::FileImporter.import(
          importable: group,
          archive_file: nil,
          shared: shared
        )
      end

      def restorers
        [tree_restorer]
      end

      def tree_restorer
        @tree_restorer ||=
          Gitlab::ImportExport::Group::TreeRestorer.new(
            user: current_user,
            shared: shared,
            group: group
          )
      end

      def remove_import_file
        upload = group.import_export_upload

        return unless upload&.import_file&.file

        upload.remove_import_file!
        upload.save!
      end

      def valid_user_permissions?
        if current_user.can?(:admin_group, group)
          true
        else
          shared.error(::Gitlab::ImportExport::Error.permission_error(current_user, group))

          false
        end
      end

      def valid_import_file?
        return true if File.exist?(File.join(shared.export_path, 'tree/groups/_all.ndjson'))

        shared.error(::Gitlab::ImportExport::Error.incompatible_import_file_error)

        false
      end

      def notify_success
        @logger.info(
          group_id: group.id,
          group_name: group.name,
          message: 'Group Import/Export: Import succeeded'
        )
      end

      def notify_error
        @logger.error(
          group_id: group.id,
          group_name: group.name,
          message: "Group Import/Export: Errors occurred, see '#{Gitlab::ErrorTracking::Logger.file_name}' for details"
        )
      end

      def notify_error!
        notify_error

        raise Gitlab::ImportExport::Error, shared.errors.to_sentence
      end

      def remove_base_tmp_dir
        FileUtils.rm_rf(shared.base_path)
      end
    end
  end
end

Groups::ImportExport::ImportService.prepend_mod_with('Groups::ImportExport::ImportService')

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Groups::ImportExport::ImportService, feature_category: :importers do
  describe '#async_execute' do
    let_it_be(:user) { create(:user) }
    let_it_be(:group) { create(:group) }

    before do
      allow(GroupImportWorker).to receive(:with_status).and_return(GroupImportWorker)
    end

    context 'when the job can be successfully scheduled' do
      subject(:import_service) { described_class.new(group: group, user: user) }

      it 'creates group import state' do
        import_service.async_execute

        import_state = group.import_state

        expect(import_state.user).to eq(user)
        expect(import_state.group).to eq(group)
      end

      it 'enqueues an import job' do
        allow(GroupImportWorker).to receive(:with_status).and_return(GroupImportWorker)

        expect(GroupImportWorker).to receive(:perform_async).with(user.id, group.id)

        import_service.async_execute
      end

      it 'marks the group import as in progress' do
        import_service.async_execute

        expect(group.import_state.in_progress?).to eq true
      end

      it 'returns truthy' do
        expect(import_service.async_execute).to be_truthy
      end
    end

    context 'when the job cannot be scheduled' do
      subject(:import_service) { described_class.new(group: group, user: user) }

      before do
        allow(GroupImportWorker).to receive(:perform_async).and_return(nil)
      end

      it 'returns falsey' do
        expect(import_service.async_execute).to be_falsey
      end

      it 'does not mark the group import as created' do
        expect { import_service.async_execute }.not_to change { group.import_state }
      end
    end
  end

  context 'when importing a ndjson export' do
    let(:user) { create(:user) }
    let(:group) { create(:group) }
    let(:import_file) { fixture_file_upload('spec/fixtures/group_export.tar.gz') }

    let(:import_logger) { instance_double(Gitlab::Import::Logger) }

    subject(:service) { described_class.new(group: group, user: user) }

    before do
      ImportExportUpload.create!(group: group, import_file: import_file)

      allow(Gitlab::Import::Logger).to receive(:build).and_return(import_logger)
      allow(import_logger).to receive(:error)
      allow(import_logger).to receive(:info)
      allow(import_logger).to receive(:warn)
      allow(FileUtils).to receive(:rm_rf).and_call_original
    end

    context 'when user has correct permissions' do
      before do
        group.add_owner(user)
      end

      it 'imports group structure successfully' do
        expect(service.execute).to be_truthy
      end

      it 'tracks the event' do
        service.execute

        expect_snowplow_event(
          category: 'Groups::ImportExport::ImportService',
          action: 'create',
          label: 'import_group_from_file'
        )

        expect_snowplow_event(
          category: 'Groups::ImportExport::ImportService',
          action: 'create',
          label: 'import_access_level',
          user: user,
          extra: { user_role: 'Owner', import_type: 'import_group_from_file' }
        )
      end

      it 'removes import file' do
        service.execute

        expect(group.import_export_upload.import_file.file).to be_nil
      end

      it 'removes tmp files' do
        shared = Gitlab::ImportExport::Shared.new(group)
        allow(Gitlab::ImportExport::Shared).to receive(:new).and_return(shared)

        service.execute

        expect(FileUtils).to have_received(:rm_rf).with(shared.base_path)
        expect(Dir.exist?(shared.base_path)).to eq(false)
      end

      it 'logs the import success' do
        expect(import_logger).to receive(:info).with(
          group_id: group.id,
          group_name: group.name,
          message: 'Group Import/Export: Import succeeded'
        ).once

        service.execute
      end
    end

    context 'when user does not have correct permissions' do
      it 'logs the error and raises an exception' do
        expect(import_logger).to receive(:error).with(
          group_id: group.id,
          group_name: group.name,
          message: a_string_including('Errors occurred')
        )

        expect { service.execute }.to raise_error(Gitlab::ImportExport::Error)
      end

      it 'tracks the error' do
        shared = Gitlab::ImportExport::Shared.new(group)
        allow(Gitlab::ImportExport::Shared).to receive(:new).and_return(shared)

        expect(shared).to receive(:error) do |param|
          expect(param.message).to include 'does not have required permissions for'
        end

        expect { service.execute }.to raise_error(Gitlab::ImportExport::Error)
      end
    end

    context 'when there are errors with the import file' do
      let(:import_file) { fixture_file_upload('spec/fixtures/symlink_export.tar.gz') }

      it 'logs the error and raises an exception' do
        expect(import_logger).to receive(:error).with(
          group_id: group.id,
          group_name: group.name,
          message: a_string_including('Errors occurred')
        ).once

        expect { service.execute }.to raise_error(Gitlab::ImportExport::Error)
      end
    end

    context 'when there are errors with the sub-relations' do
      let(:import_file) { fixture_file_upload('spec/fixtures/group_export_invalid_subrelations.tar.gz') }

      before do
        group.add_owner(user)
      end

      it 'successfully imports the group' do
        expect(service.execute).to be_truthy
      end

      it 'logs the import success' do
        allow(Gitlab::Import::Logger).to receive(:build).and_return(import_logger)

        expect(import_logger).to receive(:info).with(
          group_id: group.id,
          group_name: group.name,
          message: 'Group Import/Export: Import succeeded'
        )

        service.execute

        expect_snowplow_event(
          category: 'Groups::ImportExport::ImportService',
          action: 'create',
          label: 'import_access_level',
          user: user,
          extra: { user_role: 'Owner', import_type: 'import_group_from_file' }
        )
      end
    end
  end

  context 'when importing a json export' do
    let(:user) { create(:user) }
    let(:group) { create(:group) }
    let(:import_file) { fixture_file_upload('spec/fixtures/legacy_group_export.tar.gz') }

    let(:import_logger) { instance_double(Gitlab::Import::Logger) }

    subject(:service) { described_class.new(group: group, user: user) }

    before do
      group.add_owner(user)
      ImportExportUpload.create!(group: group, import_file: import_file)

      allow(Gitlab::Import::Logger).to receive(:build).and_return(import_logger)
      allow(import_logger).to receive(:error)
      allow(import_logger).to receive(:warn)
      allow(import_logger).to receive(:info)
    end

    it 'logs the error and raises an exception' do
      expect(import_logger).to receive(:error).with(
        group_id: group.id,
        group_name: group.name,
        message: a_string_including('Errors occurred')
      ).once

      expect { service.execute }.to raise_error(Gitlab::ImportExport::Error)
    end

    it 'tracks the error' do
      shared = Gitlab::ImportExport::Shared.new(group)
      allow(Gitlab::ImportExport::Shared).to receive(:new).and_return(shared)

      expect(shared).to receive(:error) do |param|
        expect(param.message).to include 'The import file is incompatible'
      end

      expect { service.execute }.to raise_error(Gitlab::ImportExport::Error)
    end
  end
end


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

module Groups
  module ImportExport
    class ExportService
      def initialize(group:, user:, params: {})
        @group = group
        @current_user = user
        @params = params
        @shared = @params[:shared] || Gitlab::ImportExport::Shared.new(@group)
        @logger = Gitlab::Export::Logger.build
      end

      def async_execute
        GroupExportWorker.perform_async(current_user.id, group.id, params)
      end

      def execute
        validate_user_permissions

        remove_existing_export! if group.export_file_exists?

        save!
      ensure
        remove_archive_tmp_dir
      end

      private

      attr_reader :group, :current_user, :params
      attr_accessor :shared

      def validate_user_permissions
        unless current_user.can?(:admin_group, group)
          shared.error(::Gitlab::ImportExport::Error.permission_error(current_user, group))

          notify_error!
        end
      end

      def remove_existing_export!
        import_export_upload = group.import_export_upload

        import_export_upload.remove_export_file!
        import_export_upload.save
      end

      def save!
        # We cannot include the file_saver with the other savers because
        # it removes the tmp dir. This means that if we want to add new savers
        # in EE the data won't be available.
        if save_exporters && file_saver.save
          notify_success
        else
          notify_error!
        end
      end

      def save_exporters
        log_info('Group export started')

        savers.all? do |exporter|
          log_info("#{exporter.class.name} saver started")

          exporter.save
        end
      end

      def savers
        [version_saver, tree_exporter]
      end

      def tree_exporter
        Gitlab::ImportExport::Group::TreeSaver.new(
          group: group,
          current_user: current_user,
          shared: shared,
          params: params
        )
      end

      def version_saver
        Gitlab::ImportExport::VersionSaver.new(shared: shared)
      end

      def file_saver
        Gitlab::ImportExport::Saver.new(exportable: group, shared: shared)
      end

      def remove_archive_tmp_dir
        FileUtils.rm_rf(shared.archive_path) if shared&.archive_path
      end

      def notify_error!
        notify_error

        raise Gitlab::ImportExport::Error, shared.errors.to_sentence
      end

      def log_info(message)
        @logger.info(
          message: message,
          group_id: group.id,
          group_name: group.name
        )
      end

      def notify_success
        log_info('Group Export succeeded')

        notification_service.group_was_exported(group, current_user)
      end

      def notify_error
        @logger.error(
          message: 'Group Export failed',
          group_id: group.id,
          group_name: group.name,
          errors: shared.errors.join(', ')
        )

        notification_service.group_was_not_exported(group, current_user, shared.errors)
      end

      def notification_service
        @notification_service ||= NotificationService.new
      end
    end
  end
end

Groups::ImportExport::ExportService.prepend_mod_with('Groups::ImportExport::ExportService')

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Groups::ImportExport::ExportService, feature_category: :importers do
  describe '#async_execute' do
    let(:user) { create(:user) }
    let(:group) { create(:group) }

    context 'when the job can be successfully scheduled' do
      let(:export_service) { described_class.new(group: group, user: user) }

      it 'enqueues an export job' do
        allow(GroupExportWorker).to receive(:perform_async).with(user.id, group.id, {})

        export_service.async_execute
      end

      it 'returns truthy' do
        expect(export_service.async_execute).to be_present
      end
    end

    context 'when the job cannot be scheduled' do
      let(:export_service) { described_class.new(group: group, user: user) }

      before do
        allow(GroupExportWorker).to receive(:perform_async).and_return(nil)
      end

      it 'returns falsey' do
        expect(export_service.async_execute).to be_falsey
      end
    end
  end

  describe '#execute' do
    let!(:user) { create(:user) }
    let(:group) { create(:group) }
    let(:shared) { Gitlab::ImportExport::Shared.new(group) }
    let(:archive_path) { shared.archive_path }
    let(:service) { described_class.new(group: group, user: user, params: { shared: shared }) }

    before do
      group.add_owner(user)
    end

    after do
      FileUtils.rm_rf(archive_path)
    end

    it 'saves the version' do
      expect(Gitlab::ImportExport::VersionSaver).to receive(:new).and_call_original

      service.execute
    end

    it 'saves the models using ndjson tree saver' do
      expect(Gitlab::ImportExport::Group::TreeSaver).to receive(:new).and_call_original

      service.execute
    end

    it 'compresses and removes tmp files' do
      expect(group.import_export_upload).to be_nil
      expect(Gitlab::ImportExport::Saver).to receive(:new).and_call_original

      service.execute

      expect(Dir.exist?(shared.archive_path)).to eq false
      expect(File.exist?(group.import_export_upload.export_file.path)).to eq true
    end

    it 'notifies the user' do
      expect_next_instance_of(NotificationService) do |instance|
        expect(instance).to receive(:group_was_exported)
      end

      service.execute
    end

    context 'when saver succeeds' do
      it 'saves the group in the file system' do
        service.execute

        expect(group.import_export_upload.export_file.file).not_to be_nil
        expect(File.directory?(archive_path)).to eq(false)
        expect(File.exist?(shared.archive_path)).to eq(false)
      end
    end

    context 'when user does not have admin_group permission' do
      let!(:another_user) { create(:user) }
      let(:service) { described_class.new(group: group, user: another_user, params: { shared: shared }) }

      let(:expected_message) do
        "User with ID: %s does not have required permissions for Group: %s with ID: %s" %
          [another_user.id, group.name, group.id]
      end

      it 'fails' do
        expect { service.execute }.to raise_error(Gitlab::ImportExport::Error).with_message(expected_message)
      end

      it 'logs the error' do
        expect_next_instance_of(Gitlab::Export::Logger) do |logger|
          expect(logger).to receive(:error).with(
            group_id: group.id,
            group_name: group.name,
            errors: expected_message,
            message: 'Group Export failed'
          )
        end

        expect { service.execute }.to raise_error(Gitlab::ImportExport::Error)
      end

      it 'tracks the error' do
        expect(shared).to receive(:error) { |param| expect(param.message).to eq expected_message }

        expect { service.execute }.to raise_error(Gitlab::ImportExport::Error)
      end
    end

    context 'when export fails' do
      context 'when file saver fails' do
        before do
          allow_next_instance_of(Gitlab::ImportExport::Saver) do |saver|
            allow(saver).to receive(:save).and_return(false)
          end
        end

        it 'removes the remaining exported data' do
          expect { service.execute }.to raise_error(Gitlab::ImportExport::Error)

          expect(group.import_export_upload).to be_nil
          expect(Dir.exist?(shared.archive_path)).to eq(false)
        end

        it 'notifies the user about failed group export' do
          expect_next_instance_of(NotificationService) do |instance|
            expect(instance).to receive(:group_was_not_exported)
          end

          expect { service.execute }.to raise_error(Gitlab::ImportExport::Error)
        end
      end

      context 'when file compression fails' do
        before do
          allow(service).to receive_message_chain(:tree_exporter, :save).and_return(false)
        end

        it 'removes the remaining exported data' do
          allow_next_instance_of(Gitlab::ImportExport::Saver) do |saver|
            allow(saver).to receive(:compress_and_save).and_return(false)
          end

          expect { service.execute }.to raise_error(Gitlab::ImportExport::Error)

          expect(group.import_export_upload).to be_nil
          expect(Dir.exist?(shared.archive_path)).to eq(false)
        end

        it 'notifies logger' do
          allow(service).to receive_message_chain(:tree_exporter, :save).and_return(false)

          expect(service.instance_variable_get(:@logger)).to receive(:error)

          expect { service.execute }.to raise_error(Gitlab::ImportExport::Error)
        end
      end
    end

    context 'when there is an existing export file' do
      subject(:export_service) { described_class.new(group: group, user: user) }

      let(:import_export_upload) do
        create(
          :import_export_upload,
          group: group,
          export_file: fixture_file_upload('spec/fixtures/group_export.tar.gz')
        )
      end

      it 'removes it' do
        existing_file = import_export_upload.export_file

        expect { export_service.execute }.to change { existing_file.file }.to(be_nil)
      end
    end
  end
end


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

module Groups
  module DeployTokens
    class RevokeService < BaseService
      attr_accessor :token

      def execute
        @token = group.deploy_tokens.find(params[:id])
        @token.revoke!
      end
    end
  end
end

Groups::DeployTokens::RevokeService.prepend_mod

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Groups::DeployTokens::RevokeService, feature_category: :deployment_management do
  let_it_be(:entity) { create(:group) }
  let_it_be(:deploy_token) { create(:deploy_token, :group, groups: [entity]) }
  let_it_be(:user) { create(:user) }
  let_it_be(:deploy_token_params) { { id: deploy_token.id } }

  describe '#execute' do
    subject { described_class.new(entity, user, deploy_token_params).execute }

    it "revokes a group deploy token" do
      expect(deploy_token.revoked).to eq(false)

      expect { subject }.to change { deploy_token.reload.revoked }.to eq(true)
    end

    context 'invalid token id' do
      let(:deploy_token_params) { { token_id: non_existing_record_id } }

      it 'raises an error' do
        expect { subject }.to raise_error(ActiveRecord::RecordNotFound)
      end
    end
  end
end


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

module Groups
  module DeployTokens
    class DestroyService < BaseService
      include DeployTokenMethods

      def execute
        destroy_deploy_token(@group, params)
      end
    end
  end
end

Groups::DeployTokens::DestroyService.prepend_mod

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Groups::DeployTokens::DestroyService, feature_category: :deployment_management do
  it_behaves_like 'a deploy token deletion service' do
    let_it_be(:entity) { create(:group) }
    let_it_be(:deploy_token_class) { GroupDeployToken }
    let_it_be(:deploy_token) { create(:deploy_token, :group, groups: [entity]) }
  end
end


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

module Groups
  module DeployTokens
    class CreateService < BaseService
      include DeployTokenMethods

      def execute
        deploy_token = create_deploy_token_for(@group, current_user, params)

        create_deploy_token_payload_for(deploy_token)
      end
    end
  end
end

Groups::DeployTokens::CreateService.prepend_mod

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Groups::DeployTokens::CreateService, feature_category: :deployment_management do
  it_behaves_like 'a deploy token creation service' do
    let(:entity) { create(:group) }
    let(:deploy_token_class) { GroupDeployToken }
  end
end


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

module PersonalAccessTokens
  class LastUsedService
    def initialize(personal_access_token)
      @personal_access_token = personal_access_token
    end

    def execute
      # Needed to avoid calling service on Oauth tokens
      return unless @personal_access_token.has_attribute?(:last_used_at)

      # We _only_ want to update last_used_at and not also updated_at (which
      # would be updated when using #touch).
      @personal_access_token.update_column(:last_used_at, Time.zone.now) if update?
    end

    private

    def update?
      return false if ::Gitlab::Database.read_only?

      last_used = @personal_access_token.last_used_at

      return true if last_used.nil?

      last_used <= 10.minutes.ago
    end
  end
end

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe PersonalAccessTokens::LastUsedService, feature_category: :system_access do
  describe '#execute' do
    subject { described_class.new(personal_access_token).execute }

    context 'when the personal access token was used 10 minutes ago', :freeze_time do
      let(:personal_access_token) { create(:personal_access_token, last_used_at: 10.minutes.ago) }

      it 'updates the last_used_at timestamp' do
        expect { subject }.to change { personal_access_token.last_used_at }
      end

      it 'does not run on read-only GitLab instances' do
        allow(::Gitlab::Database).to receive(:read_only?).and_return(true)

        expect { subject }.not_to change { personal_access_token.last_used_at }
      end
    end

    context 'when the personal access token was used less than 10 minutes ago', :freeze_time do
      let(:personal_access_token) { create(:personal_access_token, last_used_at: (10.minutes - 1.second).ago) }

      it 'does not update the last_used_at timestamp' do
        expect { subject }.not_to change { personal_access_token.last_used_at }
      end
    end

    context 'when the last_used_at timestamp is nil' do
      let_it_be(:personal_access_token) { create(:personal_access_token, last_used_at: nil) }

      it 'updates the last_used_at timestamp' do
        expect { subject }.to change { personal_access_token.last_used_at }
      end
    end

    context 'when not a personal access token' do
      let_it_be(:personal_access_token) { create(:oauth_access_token) }

      it 'does not execute' do
        expect(subject).to be_nil
      end
    end
  end
end


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

module PersonalAccessTokens
  class RotateService
    EXPIRATION_PERIOD = 1.week

    def initialize(current_user, token)
      @current_user = current_user
      @token = token
    end

    def execute(params = {})
      return error_response(message: _('token already revoked')) if token.revoked?

      response = ServiceResponse.success

      PersonalAccessToken.transaction do
        unless token.revoke!
          response = error_response
          raise ActiveRecord::Rollback
        end

        target_user = token.user
        new_token = target_user.personal_access_tokens.create(create_token_params(token, params))

        if new_token.persisted?
          response = error_response unless update_bot_membership(target_user, new_token.expires_at)
          response = success_response(new_token)
        else
          response = error_response(message: new_token.errors.full_messages.to_sentence)

          raise ActiveRecord::Rollback
        end
      end

      response
    end

    private

    attr_reader :current_user, :token

    def expires_at(params)
      return params[:expires_at] if params[:expires_at]

      params[:expires_at] || EXPIRATION_PERIOD.from_now.to_date
    end

    def success_response(new_token)
      ServiceResponse.success(payload: { personal_access_token: new_token })
    end

    def error_response(message: _('failed to revoke token'))
      ServiceResponse.error(message: message)
    end

    def create_token_params(token, params)
      {  name: token.name,
         previous_personal_access_token_id: token.id,
         impersonation: token.impersonation,
         scopes: token.scopes,
         expires_at: expires_at(params) }
    end

    def update_bot_membership(target_user, expires_at)
      return unless target_user.project_bot?

      target_user.members.first.update(expires_at: expires_at)
    end
  end
end

PersonalAccessTokens::RotateService.prepend_mod

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe PersonalAccessTokens::RotateService, feature_category: :system_access do
  describe '#execute' do
    let_it_be(:token, reload: true) { create(:personal_access_token) }

    subject(:response) { described_class.new(token.user, token).execute }

    it "rotates user's own token", :freeze_time do
      expect(response).to be_success

      new_token = response.payload[:personal_access_token]

      expect(new_token.token).not_to eq(token.token)
      expect(new_token.expires_at).to eq(Date.today + 1.week)
      expect(new_token.user).to eq(token.user)
    end

    it 'revokes the previous token' do
      expect { response }.to change { token.reload.revoked? }.from(false).to(true)

      new_token = response.payload[:personal_access_token]
      expect(new_token).not_to be_revoked
    end

    it 'saves the previous token as previous PAT attribute' do
      response

      new_token = response.payload[:personal_access_token]
      expect(new_token.previous_personal_access_token).to eql(token)
    end

    context 'when token user has a membership' do
      context 'when its not a bot user' do
        let_it_be(:user_membership) do
          create(:project_member, :developer, user: token.user, project: create(:project))
        end

        it 'does not update membership expires at' do
          expect { response }.not_to change { user_membership.reload.expires_at }
        end
      end

      context 'when its a bot user' do
        let_it_be(:bot_user) { create(:user, :project_bot) }
        let_it_be(:bot_user_membership) do
          create(:project_member, :developer, user: bot_user, project: create(:project))
        end

        let_it_be(:token, reload: true) { create(:personal_access_token, user: bot_user) }

        it 'updates membership expires at' do
          response

          new_token = response.payload[:personal_access_token]
          expect(bot_user_membership.reload.expires_at).to eq(new_token.expires_at)
        end
      end
    end

    context 'when user tries to rotate already revoked token' do
      let_it_be(:token, reload: true) { create(:personal_access_token, :revoked) }

      it 'returns an error' do
        expect { response }.not_to change { token.reload.revoked? }.from(true)
        expect(response).to be_error
        expect(response.message).to eq('token already revoked')
      end
    end

    context 'when revoking previous token fails' do
      it 'returns an error' do
        expect(token).to receive(:revoke!).and_return(false)

        expect(response).to be_error
      end
    end

    context 'when creating the new token fails' do
      let(:error_message) { 'boom!' }

      before do
        allow_next_instance_of(PersonalAccessToken) do |token|
          allow(token).to receive_message_chain(:errors, :full_messages, :to_sentence).and_return(error_message)
          allow(token).to receive_message_chain(:errors, :clear)
          allow(token).to receive_message_chain(:errors, :empty?).and_return(false)
        end
      end

      it 'returns an error' do
        expect(response).to be_error
        expect(response.message).to eq(error_message)
      end

      it 'reverts the changes' do
        expect { response }.not_to change { token.reload.revoked? }.from(false)
      end
    end
  end
end


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

module PersonalAccessTokens
  class RevokeService < BaseService
    attr_reader :token, :current_user, :group

    VALID_SOURCES = %i[self secret_detection].freeze

    def initialize(current_user = nil, token: nil, group: nil, source: nil)
      @current_user = current_user
      @token = token
      @group = group
      @source = source

      @source = :self if @current_user && !@source

      raise ArgumentError unless VALID_SOURCES.include?(@source)
    end

    def execute
      return ServiceResponse.error(message: 'Not permitted to revoke') unless revocation_permitted?

      if token.revoke!
        log_event
        notification_service.access_token_revoked(token.user, token.name, @source)
        ServiceResponse.success(message: success_message)
      else
        ServiceResponse.error(message: error_message)
      end
    end

    private

    def error_message
      _("Could not revoke personal access token %{personal_access_token_name}.") % { personal_access_token_name: token.name }
    end

    def success_message
      _("Revoked personal access token %{personal_access_token_name}!") % { personal_access_token_name: token.name }
    end

    def revocation_permitted?
      case @source
      when :self
        Ability.allowed?(current_user, :revoke_token, token)
      when :secret_detection
        true
      else
        false
      end
    end

    def log_event
      Gitlab::AppLogger.info(
        class: self.class.name,
        message: "PAT Revoked",
        revoked_by: current_user&.username || @source,
        revoked_for: token.user.username,
        token_id: token.id)
    end
  end
end

PersonalAccessTokens::RevokeService.prepend_mod_with('PersonalAccessTokens::RevokeService')

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe PersonalAccessTokens::RevokeService, feature_category: :system_access do
  shared_examples_for 'a successfully revoked token' do
    it { expect(subject.success?).to be true }
    it { expect(service.token.revoked?).to be true }

    it 'logs the event' do
      expect(Gitlab::AppLogger).to receive(:info).with(
        class: described_class.to_s,
        message: 'PAT Revoked',
        revoked_by: revoked_by,
        revoked_for: token.user.username,
        token_id: token.id)

      subject
    end
  end

  shared_examples_for 'an unsuccessfully revoked token' do
    it { expect(subject.success?).to be false }
    it { expect(service.token.revoked?).to be false }
  end

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

    let(:service) { described_class.new(current_user, token: token) }

    context 'when current_user is an administrator' do
      context 'when admin mode is enabled', :enable_admin_mode do
        let_it_be(:current_user) { create(:admin) }
        let_it_be(:token) { create(:personal_access_token) }

        it_behaves_like 'a successfully revoked token' do
          let(:revoked_by) { current_user.username }
        end
      end

      context 'when admin mode is disabled' do
        let_it_be(:current_user) { create(:admin) }
        let_it_be(:token) { create(:personal_access_token) }

        it_behaves_like 'an unsuccessfully revoked token'
      end
    end

    context 'when current_user is not an administrator' do
      let_it_be(:current_user) { create(:user) }

      context 'token belongs to a different user' do
        let_it_be(:token) { create(:personal_access_token) }

        it_behaves_like 'an unsuccessfully revoked token'
      end

      context 'token belongs to current_user' do
        let_it_be(:token) { create(:personal_access_token, user: current_user) }

        it_behaves_like 'a successfully revoked token' do
          let(:revoked_by) { current_user.username }
        end
      end
    end

    context 'when source' do
      let(:service) { described_class.new(nil, token: token, source: source) }

      let_it_be(:current_user) { nil }

      context 'when source is valid' do
        let_it_be(:source) { :secret_detection }
        let_it_be(:token) { create(:personal_access_token) }

        it_behaves_like 'a successfully revoked token' do
          let(:revoked_by) { :secret_detection }
        end
      end

      context 'when source is invalid' do
        let_it_be(:source) { :external_request }
        let_it_be(:token) { create(:personal_access_token) }

        it 'raises ArgumentError' do
          expect { subject }.to raise_error ArgumentError
        end
      end

      context 'when source is missing' do
        let_it_be(:source) { nil }
        let_it_be(:token) { create(:personal_access_token) }

        it 'raises ArgumentError' do
          expect { subject }.to raise_error ArgumentError
        end
      end
    end
  end
end