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

module AlertManagement
  module HttpIntegrations
    class DestroyService
      # @param integration [AlertManagement::HttpIntegration]
      # @param current_user [User]
      def initialize(integration, current_user)
        @integration = integration
        @current_user = current_user
      end

      def execute
        return error_no_permissions unless allowed?
        return error_legacy_prometheus unless destroy_allowed?

        if integration.destroy
          success
        else
          error(integration.errors.full_messages.to_sentence)
        end
      end

      private

      attr_reader :integration, :current_user

      def allowed?
        current_user&.can?(:admin_operations, integration)
      end

      # Prevents downtime while migrating from Integrations::Prometheus.
      # Remove with https://gitlab.com/gitlab-org/gitlab/-/issues/409734
      def destroy_allowed?
        !(integration.legacy? && integration.prometheus?)
      end

      def error(message)
        ServiceResponse.error(message: message)
      end

      def success
        ServiceResponse.success(payload: { integration: integration })
      end

      def error_no_permissions
        error(_('You have insufficient permissions to remove this HTTP integration'))
      end

      def error_legacy_prometheus
        error(_('Legacy Prometheus integrations cannot currently be removed'))
      end
    end
  end
end

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe AlertManagement::HttpIntegrations::DestroyService, feature_category: :incident_management do
  let_it_be(:user_with_permissions) { create(:user) }
  let_it_be(:user_without_permissions) { create(:user) }
  let_it_be(:project) { create(:project) }

  let!(:integration) { create(:alert_management_http_integration, project: project) }
  let(:current_user) { user_with_permissions }
  let(:params) { {} }
  let(:service) { described_class.new(integration, current_user) }

  before_all do
    project.add_maintainer(user_with_permissions)
  end

  describe '#execute' do
    shared_examples 'error response' do |message|
      it 'has an informative message' do
        expect(response).to be_error
        expect(response.message).to eq(message)
      end
    end

    subject(:response) { service.execute }

    context 'when the current_user is anonymous' do
      let(:current_user) { nil }

      it_behaves_like 'error response', 'You have insufficient permissions to remove this HTTP integration'
    end

    context 'when current_user does not have permission to create integrations' do
      let(:current_user) { user_without_permissions }

      it_behaves_like 'error response', 'You have insufficient permissions to remove this HTTP integration'
    end

    context 'when an error occurs during removal' do
      before do
        allow(integration).to receive(:destroy).and_return(false)
        integration.errors.add(:name, 'cannot be removed')
      end

      it_behaves_like 'error response', 'Name cannot be removed'
    end

    context 'when destroying a legacy Prometheus integration' do
      let_it_be(:existing_integration) { create(:alert_management_prometheus_integration, :legacy, project: project) }
      let!(:integration) { existing_integration }

      it_behaves_like 'error response', 'Legacy Prometheus integrations cannot currently be removed'
    end

    it 'successfully returns the integration' do
      expect(response).to be_success

      integration_result = response.payload[:integration]
      expect(integration_result).to be_a(::AlertManagement::HttpIntegration)
      expect(integration_result.name).to eq(integration.name)
      expect(integration_result.active).to eq(integration.active)
      expect(integration_result.token).to eq(integration.token)
      expect(integration_result.endpoint_identifier).to eq(integration.endpoint_identifier)

      expect { integration.reload }.to raise_error ActiveRecord::RecordNotFound
    end
  end
end


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

module AlertManagement
  module HttpIntegrations
    class UpdateService < BaseService
      # @param integration [AlertManagement::HttpIntegration]
      # @param current_user [User]
      # @param params [Hash]
      def initialize(integration, current_user, params)
        @integration = integration

        super(integration.project, current_user, params)
      end

      def execute
        return error_no_permissions unless allowed?

        integration.transaction do
          if integration.update(permitted_params.merge(token_params))
            @response = success(integration)

            if type_update? && too_many_integrations?(integration)
              @response = error_multiple_integrations

              raise ActiveRecord::Rollback
            end
          else
            @response = error_on_save(integration)
          end
        end

        @response
      end

      private

      attr_reader :integration

      def token_params
        return {} unless params[:regenerate_token]

        { token: nil }
      end

      def type_update?
        params[:type_identifier].present?
      end

      def error_no_permissions
        error(_('You have insufficient permissions to update this HTTP integration'))
      end
    end
  end
end

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe AlertManagement::HttpIntegrations::UpdateService, feature_category: :incident_management do
  let_it_be(:user_with_permissions) { create(:user) }
  let_it_be(:user_without_permissions) { create(:user) }
  let_it_be(:project) { create(:project) }
  let_it_be_with_reload(:integration) { create(:alert_management_http_integration, :inactive, project: project, name: 'Old Name') }

  let(:current_user) { user_with_permissions }
  let(:params) { {} }

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

  before_all do
    project.add_maintainer(user_with_permissions)
  end

  describe '#execute' do
    shared_examples 'error response' do |message|
      it 'has an informative message' do
        expect(response).to be_error
        expect(response.message).to eq(message)
      end
    end

    subject(:response) { service.execute }

    context 'when the current_user is anonymous' do
      let(:current_user) { nil }

      it_behaves_like 'error response', 'You have insufficient permissions to update this HTTP integration'
    end

    context 'when current_user does not have permission to create integrations' do
      let(:current_user) { user_without_permissions }

      it_behaves_like 'error response', 'You have insufficient permissions to update this HTTP integration'
    end

    context 'when an error occurs during update' do
      let(:params) { { name: '' } }

      it_behaves_like 'error response', "Name can't be blank"
    end

    context 'with name param' do
      let(:params) { { name: 'New Name' } }

      it 'successfully updates the integration' do
        expect(response).to be_success
        expect(response.payload[:integration].name).to eq('New Name')
      end
    end

    context 'with active param' do
      let(:params) { { active: true } }

      it 'successfully updates the integration' do
        expect(response).to be_success
        expect(response.payload[:integration]).to be_active
      end
    end

    context 'with regenerate_token flag' do
      let(:params) { { regenerate_token: true } }

      it 'successfully updates the integration' do
        previous_token = integration.token

        expect(response).to be_success
        expect(response.payload[:integration].token).not_to eq(previous_token)
      end
    end
  end
end


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

module AlertManagement
  module HttpIntegrations
    class CreateService < BaseService
      def execute
        return error_no_permissions unless allowed?

        ::AlertManagement::HttpIntegration.transaction do
          integration = project.alert_management_http_integrations.build(permitted_params)

          if integration.save
            @response = success(integration)

            if too_many_integrations?(integration)
              @response = error_multiple_integrations

              raise ActiveRecord::Rollback
            end
          else
            @response = error_on_save(integration)
          end
        end

        @response
      end

      private

      def error_no_permissions
        error(_('You have insufficient permissions to create an HTTP integration for this project'))
      end
    end
  end
end

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe AlertManagement::HttpIntegrations::CreateService, feature_category: :incident_management do
  let_it_be(:user_with_permissions) { create(:user) }
  let_it_be(:user_without_permissions) { create(:user) }
  let_it_be_with_reload(:project) { create(:project) }

  let(:current_user) { user_with_permissions }
  let(:params) { {} }

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

  before_all do
    project.add_maintainer(user_with_permissions)
  end

  describe '#execute' do
    shared_examples 'error response' do |message|
      it 'has an informative message' do
        expect(response).to be_error
        expect(response.message).to eq(message)
      end
    end

    subject(:response) { service.execute }

    context 'when the current_user is anonymous' do
      let(:current_user) { nil }

      it_behaves_like 'error response', 'You have insufficient permissions to create an HTTP integration for this project'
    end

    context 'when current_user does not have permission to create integrations' do
      let(:current_user) { user_without_permissions }

      it_behaves_like 'error response', 'You have insufficient permissions to create an HTTP integration for this project'
    end

    context 'when an error occurs during update' do
      it_behaves_like 'error response', "Name can't be blank"
    end

    context 'with valid params' do
      let(:params) { { name: 'New HTTP Integration', active: true } }

      it 'successfully creates an integration' do
        expect(response).to be_success

        integration = response.payload[:integration]
        expect(integration).to be_a(::AlertManagement::HttpIntegration)
        expect(integration.name).to eq('New HTTP Integration')
        expect(integration).to be_active
        expect(integration.token).to be_present
        expect(integration.endpoint_identifier).to be_present
      end

      context 'with an existing HTTP integration' do
        let_it_be(:http_integration) { create(:alert_management_http_integration, project: project) }

        it_behaves_like 'error response', 'Multiple integrations of a single type are not supported for this project'

        context 'when creating a different type of integration' do
          let(:params) { { type_identifier: :prometheus, name: 'Prometheus' } }

          it 'is successful' do
            expect(response).to be_success
            expect(response.payload[:integration]).to be_a(::AlertManagement::HttpIntegration)
          end
        end
      end

      context 'with an existing Prometheus integration' do
        let_it_be(:http_integration) { create(:alert_management_prometheus_integration, project: project) }

        context 'when creating a different type of integration' do
          it 'is successful' do
            expect(response).to be_success
            expect(response.payload[:integration]).to be_a(::AlertManagement::HttpIntegration)
          end
        end

        context 'when creating the same time of integration' do
          let(:params) { { type_identifier: :prometheus, name: 'Prometheus' } }

          it_behaves_like 'error response', 'Multiple integrations of a single type are not supported for this project'
        end
      end
    end
  end
end


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

module AlertManagement
  module MetricImages
    class UploadService < BaseService
      attr_reader :alert, :file, :url, :url_text, :metric

      def initialize(alert, current_user, params = {})
        super

        @alert = alert
        @file = params.fetch(:file)
        @url = params.fetch(:url, nil)
        @url_text = params.fetch(:url_text, nil)
      end

      def execute
        unless can_upload_metrics?
          return ServiceResponse.error(
            message: _("You are not authorized to upload metric images"),
            http_status: :forbidden
          )
        end

        metric = AlertManagement::MetricImage.new(
          alert: alert,
          file: file,
          url: url,
          url_text: url_text
        )

        if metric.save
          ServiceResponse.success(payload: { metric: metric, alert: alert })
        else
          ServiceResponse.error(message: metric.errors.full_messages.join(', '), http_status: :bad_request)
        end
      end

      private

      def can_upload_metrics?
        current_user&.can?(:upload_alert_management_metric_image, alert)
      end
    end
  end
end

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe AlertManagement::MetricImages::UploadService, feature_category: :metrics do
  subject(:service) { described_class.new(alert, current_user, params) }

  let_it_be_with_refind(:project) { create(:project) }
  let_it_be_with_refind(:alert) { create(:alert_management_alert, project: project) }
  let_it_be_with_refind(:current_user) { create(:user) }

  let(:params) do
    {
      file: fixture_file_upload('spec/fixtures/rails_sample.jpg', 'image/jpg'),
      url: 'https://www.gitlab.com'
    }
  end

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

    shared_examples 'uploads the metric' do
      it 'uploads the metric and returns a success' do
        expect { subject }.to change(AlertManagement::MetricImage, :count).by(1)
        expect(subject.success?).to eq(true)
        expect(subject.payload).to match({ metric: instance_of(AlertManagement::MetricImage), alert: alert })
      end
    end

    shared_examples 'no metric saved, an error given' do |message|
      it 'returns an error and does not upload', :aggregate_failures do
        expect(subject.success?).to eq(false)
        expect(subject.message).to match(a_string_matching(message))
        expect(AlertManagement::MetricImage.count).to eq(0)
      end
    end

    context 'user does not have permissions' do
      it_behaves_like 'no metric saved, an error given', 'You are not authorized to upload metric images'
    end

    context 'user has permissions' do
      before_all do
        project.add_developer(current_user)
      end

      it_behaves_like 'uploads the metric'

      context 'no url given' do
        let(:params) do
          {
            file: fixture_file_upload('spec/fixtures/rails_sample.jpg', 'image/jpg')
          }
        end

        it_behaves_like 'uploads the metric'
      end

      context 'record invalid' do
        let(:params) do
          {
            file: fixture_file_upload('spec/fixtures/doc_sample.txt', 'text/plain'),
            url: 'https://www.gitlab.com'
          }
        end

        it_behaves_like 'no metric saved, an error given', /File does not have a supported extension. Only png, jpg, jpeg, gif, bmp, tiff, ico, and webp are supported/ # rubocop: disable Layout/LineLength
      end

      context 'user is guest' do
        before_all do
          project.add_guest(current_user)
        end

        it_behaves_like 'no metric saved, an error given', 'You are not authorized to upload metric images'
      end
    end
  end
end


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

module AlertManagement
  module Alerts
    class UpdateService < ::BaseProjectService
      include Gitlab::Utils::StrongMemoize

      # @param alert [AlertManagement::Alert]
      # @param current_user [User]
      # @param params [Hash] Attributes of the alert
      def initialize(alert, current_user, params)
        @alert = alert
        @param_errors = []
        @status = params.delete(:status)

        super(project: alert.project, current_user: current_user, params: params)
      end

      def execute
        return error_no_permissions unless allowed?

        filter_params
        return error_invalid_params if param_errors.any?

        # Save old assignees for system notes
        old_assignees = alert.assignees.to_a

        if alert.update(params)
          handle_changes(old_assignees: old_assignees)

          success
        else
          error(alert.errors.full_messages.to_sentence)
        end
      end

      private

      attr_reader :alert, :param_errors, :status

      def allowed?
        current_user&.can?(:update_alert_management_alert, alert)
      end

      def todo_service
        strong_memoize(:todo_service) do
          TodoService.new
        end
      end

      def success
        ServiceResponse.success(payload: { alert: alert })
      end

      def error(message)
        ServiceResponse.error(payload: { alert: alert }, message: message)
      end

      def error_no_permissions
        error(_('You have no permissions'))
      end

      def error_invalid_params
        error(param_errors.to_sentence)
      end

      def add_param_error(message)
        param_errors << message
      end

      def param_errors?
        params.empty? && status.blank?
      end

      def filter_params
        param_errors << _('Please provide attributes to update') if param_errors?

        filter_status
        filter_assignees
        filter_duplicate
      end

      def handle_changes(old_assignees:)
        handle_assignement(old_assignees) if params[:assignees]
        handle_status_change if params[:status_event]
      end

      # ----- Assignee-related behavior ------
      def filter_assignees
        return if params[:assignees].nil?

        # Always take first assignee while multiple are not currently supported
        params[:assignees] = Array(params[:assignees].first)

        param_errors << _('Assignee has no permissions') if unauthorized_assignees?
      end

      def unauthorized_assignees?
        params[:assignees]&.any? { |user| !user.can?(:read_alert_management_alert, alert) }
      end

      def handle_assignement(old_assignees)
        assign_todo(old_assignees)
        add_assignee_system_note(old_assignees)
      end

      def assign_todo(old_assignees)
        todo_service.reassigned_assignable(alert, current_user, old_assignees)
      end

      def add_assignee_system_note(old_assignees)
        SystemNoteService.change_issuable_assignees(alert, project, current_user, old_assignees)
      end

      # ------ Status-related behavior -------
      def filter_status
        return unless status

        status_event = alert.status_event_for(status)

        unless status_event
          param_errors << _('Invalid status')
          return
        end

        params[:status_event] = status_event
      end

      def handle_status_change
        add_status_change_system_note
        resolve_todos if alert.resolved?
      end

      def add_status_change_system_note
        SystemNoteService.change_alert_status(alert, current_user)
      end

      def resolve_todos
        todo_service.resolve_todos_for_target(alert, current_user)
      end

      def filter_duplicate
        # Only need to check if changing to a not-resolved status
        return if params[:status_event].blank? || params[:status_event] == :resolve
        return unless alert.resolved?

        param_errors << unresolved_alert_error if duplicate_alert?
      end

      def duplicate_alert?
        return if alert.fingerprint.blank?

        unresolved_alert.present?
      end

      def unresolved_alert
        strong_memoize(:unresolved_alert) do
          AlertManagement::Alert.find_unresolved_alert(project, alert.fingerprint)
        end
      end

      def unresolved_alert_error
        _('An %{link_start}alert%{link_end} with the same fingerprint is already open. ' \
          'To change the status of this alert, resolve the linked alert.'
         ) % unresolved_alert_url_params
      end

      def unresolved_alert_url_params
        alert_path = Gitlab::Routing.url_helpers.details_project_alert_management_path(project, unresolved_alert)

        {
          link_start: '<a href="%{url}">'.html_safe % { url: alert_path },
          link_end: '</a>'.html_safe
        }
      end
    end
  end
end

AlertManagement::Alerts::UpdateService.prepend_mod

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe AlertManagement::Alerts::UpdateService, feature_category: :incident_management do
  let_it_be(:user_with_permissions) { create(:user) }
  let_it_be(:other_user_with_permissions) { create(:user) }
  let_it_be(:user_without_permissions) { create(:user) }
  let_it_be(:project) { create(:project) }
  let_it_be(:alert, reload: true) { create(:alert_management_alert, :triggered, project: project) }

  let(:current_user) { user_with_permissions }
  let(:params) { {} }

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

  before_all do
    project.add_developer(user_with_permissions)
    project.add_developer(other_user_with_permissions)
  end

  describe '#execute' do
    shared_examples 'does not add a todo' do
      specify { expect { response }.not_to change(Todo, :count) }
    end

    shared_examples 'does not add a system note' do
      specify { expect { response }.not_to change(Note, :count) }
    end

    shared_examples 'adds a system note' do |note_matcher = nil|
      specify do
        expect { response }.to change { alert.reload.notes.count }.by(1)
        expect(alert.notes.last.note).to match(note_matcher) if note_matcher
      end
    end

    shared_examples 'error response' do |message|
      it_behaves_like 'does not add a todo'
      it_behaves_like 'does not add a system note'

      it 'has an informative message' do
        expect(response).to be_error
        expect(response.message).to eq(message)
      end
    end

    subject(:response) { service.execute }

    context 'when the current_user is nil' do
      let(:current_user) { nil }

      it_behaves_like 'error response', 'You have no permissions'
    end

    context 'when current_user does not have permission to update alerts' do
      let(:current_user) { user_without_permissions }

      it_behaves_like 'error response', 'You have no permissions'
    end

    context 'when no parameters are included' do
      it_behaves_like 'error response', 'Please provide attributes to update'
    end

    context 'when an error occurs during update' do
      let(:params) { { title: nil } }

      it_behaves_like 'error response', "Title can't be blank"
    end

    shared_examples 'title update' do
      it_behaves_like 'does not add a todo'
      it_behaves_like 'does not add a system note'

      it 'updates the attribute' do
        original_title = alert.title

        expect { response }.to change { alert.title }.from(original_title).to(expected_title)
        expect(response).to be_success
      end
    end

    context 'when a model attribute is included without assignees' do
      let(:params) { { title: 'This is an updated alert.' } }
      let(:expected_title) { params[:title] }

      it_behaves_like 'title update'
    end

    context 'when alert is resolved and another existing unresolved alert' do
      let!(:alert) { create(:alert_management_alert, :resolved, project: project) }
      let!(:existing_alert) { create(:alert_management_alert, :triggered, project: project) }

      let(:params) { { title: 'This is an updated alert.' } }
      let(:expected_title) { params[:title] }

      it_behaves_like 'title update'
    end

    context 'when assignees are included' do
      shared_examples 'adds a todo' do
        let(:assignee) { expected_assignees.first }

        specify do
          expect { response }.to change { assignee.reload.todos.count }.by(1)
          expect(assignee.todos.last.author).to eq(current_user)
        end
      end

      shared_examples 'successful assignment' do
        it_behaves_like 'adds a system note'
        it_behaves_like 'adds a todo'

        after do
          alert.assignees = []
        end

        specify do
          expect { response }.to change { alert.reload.assignees }.from([]).to(expected_assignees)
          expect(response).to be_success
        end
      end

      let(:expected_assignees) { params[:assignees] }

      context 'when the assignee is the current user' do
        let(:params) { { assignees: [current_user] } }

        it_behaves_like 'successful assignment'
      end

      context 'when the assignee has read permissions' do
        let(:params) { { assignees: [other_user_with_permissions] } }

        it_behaves_like 'successful assignment'
      end

      context 'when the assignee does not have read permissions' do
        let(:params) { { assignees: [user_without_permissions] } }

        it_behaves_like 'error response', 'Assignee has no permissions'
      end

      context 'when user is already assigned' do
        let(:params) { { assignees: [user_with_permissions] } }

        before do
          alert.assignees << user_with_permissions
        end

        it_behaves_like 'does not add a system note'
        it_behaves_like 'does not add a todo'
      end

      context 'with multiple users included' do
        let(:params) { { assignees: [user_with_permissions, user_without_permissions] } }
        let(:expected_assignees) { [user_with_permissions] }

        it_behaves_like 'successful assignment'
      end
    end

    context 'when a status is included' do
      let(:params) { { status: new_status } }
      let(:new_status) { :acknowledged }

      it 'successfully changes the status' do
        expect { response }.to change { alert.acknowledged? }.to(true)
        expect(response).to be_success
        expect(response.payload[:alert]).to eq(alert)
      end

      it_behaves_like 'adds a system note'

      context 'with unknown status' do
        let(:new_status) { :unknown_status }

        it_behaves_like 'error response', 'Invalid status'
      end

      context 'with resolving status' do
        let(:new_status) { :resolved }

        it 'changes the status' do
          expect { response }.to change { alert.resolved? }.to(true)
        end

        it "resolves the current user's related todos" do
          todo = create(:todo, :pending, target: alert, user: current_user, project: alert.project)

          expect { response }.to change { todo.reload.state }.from('pending').to('done')
        end
      end

      context 'with existing unresolved alert' do
        context 'with fingerprints' do
          let_it_be(:existing_alert) { create(:alert_management_alert, :triggered, fingerprint: alert.fingerprint, project: project) }

          it 'does not query for existing alerts' do
            expect(::AlertManagement::Alert).not_to receive(:find_unresolved_alert)

            response
          end

          context 'when status was resolved' do
            let_it_be(:alert) { create(:alert_management_alert, :resolved, :with_fingerprint, project: project) }
            let_it_be(:existing_alert) { create(:alert_management_alert, :triggered, fingerprint: alert.fingerprint, project: project) }

            let(:url) { Gitlab::Routing.url_helpers.details_project_alert_management_path(project, existing_alert) }
            let(:link) { ActionController::Base.helpers.link_to(_('alert'), url) }
            let(:message) do
              "An #{link} with the same fingerprint is already open. " \
              'To change the status of this alert, resolve the linked alert.'
            end

            it_behaves_like 'does not add a todo'
            it_behaves_like 'does not add a system note'

            it 'has an informative message' do
              expect(response).to be_error
              expect(response.message).to eq(message)
            end
          end
        end

        context 'without fingerprints' do
          let_it_be(:existing_alert) { create(:alert_management_alert, :triggered, fingerprint: alert.fingerprint, project: project) }

          it 'successfully changes the status' do
            expect { response }.to change { alert.acknowledged? }.to(true)
            expect(response).to be_success
            expect(response.payload[:alert]).to eq(alert)
          end

          it_behaves_like 'adds a system note'
        end
      end

      context 'two existing closed alerts' do
        let_it_be(:alert) { create(:alert_management_alert, :resolved, :with_fingerprint, project: project) }
        let_it_be(:existing_alert) { create(:alert_management_alert, :resolved, fingerprint: alert.fingerprint, project: project) }

        it 'successfully changes the status' do
          expect { response }.to change { alert.acknowledged? }.to(true)
          expect(response).to be_success
          expect(response.payload[:alert]).to eq(alert)
        end

        it_behaves_like 'adds a system note'
      end
    end
  end
end


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

module AlertManagement
  module Alerts
    module Todo
      class CreateService
        # @param alert [AlertManagement::Alert]
        # @param current_user [User]
        def initialize(alert, current_user)
          @alert = alert
          @current_user = current_user
        end

        def execute
          return error_no_permissions unless allowed?

          todos = TodoService.new.mark_todo(alert, current_user)
          todo = todos&.first

          return error_existing_todo unless todo

          success(todo)
        end

        private

        attr_reader :alert, :current_user

        def allowed?
          current_user&.can?(:update_alert_management_alert, alert)
        end

        def error(message)
          ServiceResponse.error(payload: { alert: alert, todo: nil }, message: message)
        end

        def success(todo)
          ServiceResponse.success(payload: { alert: alert, todo: todo })
        end

        def error_no_permissions
          error(_('You have insufficient permissions to create a Todo for this alert'))
        end

        def error_existing_todo
          error(_('You already have pending todo for this alert'))
        end
      end
    end
  end
end

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe AlertManagement::Alerts::Todo::CreateService, feature_category: :incident_management do
  let_it_be(:user) { create(:user) }
  let_it_be(:alert) { create(:alert_management_alert) }

  let(:current_user) { user }

  describe '#execute' do
    subject(:result) { described_class.new(alert, current_user).execute }

    shared_examples 'permissions error' do
      it 'returns an error', :aggregate_failures do
        expect(result.error?).to be(true)
        expect(result.message).to eq('You have insufficient permissions to create a Todo for this alert')
        expect(result.payload[:todo]).to be(nil)
        expect(result.payload[:alert]).to be(alert)
      end
    end

    context 'when the user is anonymous' do
      let(:current_user) { nil }

      it_behaves_like 'permissions error'
    end

    context 'when the user does not have permission' do
      it_behaves_like 'permissions error'
    end

    context 'when user has permission' do
      before do
        alert.project.add_developer(user)
      end

      it 'creates a todo' do
        expect { result }.to change { Todo.count }.by(1)
      end

      it 'returns the alert and todo in the payload', :aggregate_failures do
        expect(result.success?).to be(true)
        expect(result.payload[:alert][:id]).to be(alert.id)
        expect(result.payload[:todo][:id]).to be(Todo.last.id)
      end

      context 'when the user has a marked todo for the alert' do
        let_it_be(:todo_params) do
          { project: alert.project,
            target: alert,
            user: user,
            action: Todo::MARKED }
        end

        context 'when todo is pending' do
          before_all do
            create(:todo, :pending, **todo_params)
          end

          before do
            stub_feature_flags(multiple_todos: false)
          end

          it 'does not create a todo' do
            expect { result }.not_to change { Todo.count }
          end

          it 'returns an error', :aggregate_failures do
            expect(result.error?).to be(true)
            expect(result.message).to be('You already have pending todo for this alert')
            expect(result.payload[:todo]).to be(nil)
            expect(result.payload[:alert]).to be(alert)
          end
        end

        context 'when todo is done' do
          before do
            create(:todo, :done, **todo_params)
          end

          it { expect(result.success?).to be(true) }
          it { expect { result }.to change { Todo.count }.by(1) }
        end
      end
    end
  end
end


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

module Suggestions
  class OutdateService
    def execute(merge_request)
      # rubocop: disable CodeReuse/ActiveRecord
      suggestions = merge_request.suggestions.active.includes(:note)

      suggestions.find_in_batches(batch_size: 100) do |group|
        outdatable_suggestion_ids = group.select do |suggestion|
          suggestion.outdated?(cached: false)
        end.map(&:id)

        Suggestion.where(id: outdatable_suggestion_ids).update_all(outdated: true)
      end
      # rubocop: enable CodeReuse/ActiveRecord
    end
  end
end

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Suggestions::OutdateService, feature_category: :code_suggestions do
  describe '#execute' do
    let(:merge_request) { create(:merge_request) }
    let(:project) { merge_request.target_project }
    let(:user) { merge_request.author }
    let(:file_path) { 'files/ruby/popen.rb' }
    let(:branch_name) { project.default_branch }
    let(:diff_file) { suggestion.diff_file }
    let(:position) { build_position(file_path, comment_line) }
    let(:note) do
      create(
        :diff_note_on_merge_request,
        noteable: merge_request,
        position: position,
        project: project
      )
    end

    def build_position(path, line)
      Gitlab::Diff::Position.new(
        old_path: path,
        new_path: path,
        old_line: nil,
        new_line: line,
        diff_refs: merge_request.diff_refs
      )
    end

    def commit_changes(file_path, new_content)
      params = {
        file_path: file_path,
        commit_message: "Update File",
        file_content: new_content,
        start_project: project,
        start_branch: project.default_branch,
        branch_name: branch_name
      }

      Files::UpdateService.new(project, user, params).execute
    end

    def update_file_line(diff_file, change_line, content)
      new_lines = diff_file.new_blob.data.lines
      new_lines[change_line..change_line] = content
      result = commit_changes(diff_file.file_path, new_lines.join)
      newrev = result[:result]

      expect(result[:status]).to eq(:success)
      expect(newrev).to be_present

      # Ensure all memoized data is cleared in order
      # to generate the new merge_request_diff.
      MergeRequest.find(merge_request.id).reload_diff(user)

      note.reload
    end

    before do
      project.add_maintainer(user)
    end

    subject { described_class.new.execute(merge_request) }

    context 'when there is a change within multi-line suggestion range' do
      let(:comment_line) { 9 }
      let(:lines_above) { 8 } # suggesting to change lines 1..9
      let(:change_line) { 2 } # line 2 is within the range
      let!(:suggestion) do
        create(:suggestion, :content_from_repo, note: note, lines_above: lines_above)
      end

      it 'updates the outdatable suggestion record' do
        update_file_line(diff_file, change_line, "# foo\nbar\n")

        # Make sure note is still active
        expect(note.active?).to be(true)

        expect { subject }.to change { suggestion.reload.outdated }
          .from(false).to(true)
      end
    end

    context 'when there is no change within multi-line suggestion range' do
      let(:comment_line) { 9 }
      let(:lines_above) { 3 } # suggesting to change lines 6..9
      let(:change_line) { 2 } # line 2 is not within the range
      let!(:suggestion) do
        create(:suggestion, :content_from_repo, note: note, lines_above: lines_above)
      end

      subject { described_class.new.execute(merge_request) }

      it 'does not outdates suggestion record' do
        update_file_line(diff_file, change_line, "# foo\nbar\n")

        # Make sure note is still active
        expect(note.active?).to be(true)

        expect { subject }.not_to change { suggestion.reload.outdated }.from(false)
      end
    end
  end
end


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

module Suggestions
  class ApplyService < ::BaseService
    def initialize(current_user, *suggestions, message: nil)
      @current_user = current_user
      @message = message
      @suggestion_set = Gitlab::Suggestions::SuggestionSet.new(suggestions)
    end

    def execute
      if suggestion_set.valid?
        result
      else
        error(suggestion_set.error_message)
      end
    end

    private

    attr_reader :current_user, :suggestion_set

    def result
      multi_service.execute.tap do |result|
        update_suggestions(result)
      end
    end

    def update_suggestions(result)
      return unless result[:status] == :success

      Suggestion.id_in(suggestion_set.suggestions)
        .update_all(commit_id: result[:result], applied: true)

      Gitlab::UsageDataCounters::MergeRequestActivityUniqueCounter
        .track_apply_suggestion_action(user: current_user, suggestions: suggestion_set.suggestions)
    end

    def author
      authors = suggestion_set.authors

      return unless authors.one?

      Gitlab::Git::User.from_gitlab(authors.first)
    end

    def multi_service
      params = {
        commit_message: commit_message,
        branch_name: suggestion_set.branch,
        start_branch: suggestion_set.branch,
        actions: suggestion_set.actions,
        author_name: author&.name,
        author_email: author&.email
      }

      ::Files::MultiService.new(suggestion_set.source_project, current_user, params)
    end

    def commit_message
      Gitlab::Suggestions::CommitMessage.new(current_user, suggestion_set, @message).message
    end
  end
end

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Suggestions::ApplyService, feature_category: :code_suggestions do
  include ProjectForksHelper

  def build_position(**optional_args)
    args = { old_path: "files/ruby/popen.rb",
             new_path: "files/ruby/popen.rb",
             old_line: nil,
             new_line: 9,
             diff_refs: merge_request.diff_refs,
             **optional_args }

    Gitlab::Diff::Position.new(args)
  end

  def create_suggestion(args)
    position_args = args.slice(:old_path, :new_path, :old_line, :new_line)
    content_args = args.slice(:from_content, :to_content)

    position = build_position(**position_args)

    diff_note = create(
      :diff_note_on_merge_request,
      noteable: merge_request,
      position: position,
      project: project
    )

    suggestion_args = { note: diff_note }.merge(content_args)

    create(:suggestion, :content_from_repo, suggestion_args)
  end

  def apply(suggestions, custom_message = nil)
    result = apply_service.new(user, *suggestions, message: custom_message).execute

    suggestions.map(&:reload)

    expect(result[:status]).to eq(:success)
  end

  shared_examples 'successfully creates commit and updates suggestions' do
    it 'updates the files with the new content' do
      apply(suggestions)

      suggestions.each do |suggestion|
        path = suggestion.diff_file.file_path
        blob = project.repository.blob_at_branch(merge_request.source_branch, path)

        expect(blob.data).to eq(expected_content_by_path[path.to_sym])
      end
    end

    it 'updates suggestion applied and commit_id columns' do
      expect(suggestions.map(&:applied)).to all(be false)
      expect(suggestions.map(&:commit_id)).to all(be nil)

      apply(suggestions)

      expect(suggestions.map(&:applied)).to all(be true)
      expect(suggestions.map(&:commit_id)).to all(be_present)
    end

    it 'created commit has users email and name' do
      apply(suggestions)

      commit = project.repository.commit
      author = suggestions.first.note.author

      expect(user.commit_email).not_to eq(user.email)
      expect(commit.author_email).to eq(author.commit_email_or_default)
      expect(commit.committer_email).to eq(user.commit_email)
      expect(commit.author_name).to eq(author.name)
      expect(commit.committer_name).to eq(user.name)
    end

    it 'tracks apply suggestion event' do
      expect(Gitlab::UsageDataCounters::MergeRequestActivityUniqueCounter)
        .to receive(:track_apply_suggestion_action)
        .with(user: user, suggestions: suggestions)

      apply(suggestions)
    end

    context 'when a custom suggestion commit message' do
      before do
        project.update!(suggestion_commit_message: message)

        apply(suggestions)
      end

      context 'is not specified' do
        let(:message) { '' }

        it 'uses the default commit message' do
          expect(project.repository.commit.message).to(
            match(/\AApply #{suggestions.size} suggestion\(s\) to \d+ file\(s\)\z/)
          )
        end
      end

      context 'is specified' do
        let(:message) do
          'refactor: %{project_name} %{branch_name} %{username}'
        end

        it 'generates a custom commit message' do
          expect(project.repository.commit.message).to(
            eq("refactor: Project_1 master test.user")
          )
        end
      end
    end

    context 'with a user suggested commit message' do
      let(:message) { "i'm a custom commit message!" }

      it "uses the user's commit message" do
        apply(suggestions, message)

        expect(project.repository.commit.message).to(eq(message))
      end
    end
  end

  subject(:apply_service) { described_class }

  let_it_be(:user) do
    create(:user, :commit_email, name: 'Test User', username: 'test.user')
  end

  let(:project) do
    create(:project, :repository, path: 'project-1', name: 'Project_1')
  end

  let(:merge_request) do
    create(:merge_request,
      source_project: project, target_project: project, source_branch: 'master')
  end

  let(:position) { build_position }

  let(:diff_note) do
    create(:diff_note_on_merge_request,
      noteable: merge_request, position: position, project: project)
  end

  let(:suggestion) do
    create(:suggestion, :content_from_repo,
      note: diff_note, to_content: "      raise RuntimeError, 'Explosion'\n      # explosion?\n")
  end

  let(:suggestion2) do
    create_suggestion(
      to_content: "      *** SUGGESTION CHANGE ***\n",
      new_line: 15)
  end

  let(:suggestion3) do
    create_suggestion(
      to_content: "      *** ANOTHER SUGGESTION CHANGE ***\n",
      old_path: "files/ruby/regex.rb",
      new_path: "files/ruby/regex.rb",
      new_line: 22)
  end

  let(:suggestions) { [suggestion, suggestion2, suggestion3] }

  context 'patch is appliable' do
    let(:popen_content) do
      <<-CONTENT.strip_heredoc
        require 'fileutils'
        require 'open3'

        module Popen
          extend self

          def popen(cmd, path=nil)
            unless cmd.is_a?(Array)
              raise RuntimeError, 'Explosion'
              # explosion?
            end

            path ||= Dir.pwd

            vars = {
              *** SUGGESTION CHANGE ***
            }

            options = {
              chdir: path
            }

            unless File.directory?(path)
              FileUtils.mkdir_p(path)
            end

            @cmd_output = ""
            @cmd_status = 0

            Open3.popen3(vars, *cmd, options) do |stdin, stdout, stderr, wait_thr|
              @cmd_output << stdout.read
              @cmd_output << stderr.read
              @cmd_status = wait_thr.value.exitstatus
            end

            return @cmd_output, @cmd_status
          end
        end
      CONTENT
    end

    let(:regex_content) do
      <<-CONTENT.strip_heredoc
        module Gitlab
          module Regex
            extend self

            def username_regex
              default_regex
            end

            def project_name_regex
              /\\A[a-zA-Z0-9][a-zA-Z0-9_\\-\\. ]*\\z/
            end

            def name_regex
              /\\A[a-zA-Z0-9_\\-\\. ]*\\z/
            end

            def path_regex
              default_regex
            end

            def archive_formats_regex
              *** ANOTHER SUGGESTION CHANGE ***
            end

            def git_reference_regex
              # Valid git ref regex, see:
              # https://www.kernel.org/pub/software/scm/git/docs/git-check-ref-format.html
              %r{
                (?!
                   (?# doesn't begins with)
                   \\/|                    (?# rule #6)
                   (?# doesn't contain)
                   .*(?:
                      [\\\/.]\\\.|            (?# rule #1,3)
                      \\/\\/|               (?# rule #6)
                      @\\{|                (?# rule #8)
                      \\\\                  (?# rule #9)
                   )
                )
                [^\\000-\\040\\177~^:?*\\[]+  (?# rule #4-5)
                (?# doesn't end with)
                (?<!\\.lock)               (?# rule #1)
                (?<![\\/.])                (?# rule #6-7)
              }x
            end

            protected

            def default_regex
              /\\A[.?]?[a-zA-Z0-9][a-zA-Z0-9_\\-\\.]*(?<!\\.git)\\z/
            end
          end
        end
      CONTENT
    end

    let(:expected_content_by_path) do
      {
        "files/ruby/popen.rb": popen_content,
        "files/ruby/regex.rb": regex_content
      }
    end

    context 'non-fork project' do
      before do
        project.add_maintainer(user)
      end

      it_behaves_like 'successfully creates commit and updates suggestions'

      context 'when it fails to apply because a file was changed' do
        before do
          params = {
            file_path: suggestion3.diff_file.file_path,
            start_branch: suggestion3.branch,
            branch_name: suggestion3.branch,
            commit_message: 'Update file',
            file_content: 'New content'
          }

          # Reload the suggestion so it's memoized values get reset after the
          # file was changed.
          suggestion3.reload

          Files::UpdateService.new(project, user, params).execute
        end

        it 'returns error message' do
          result = apply_service.new(user, suggestion, suggestion3, suggestion2).execute

          expect(result).to eq(message: 'A file has been changed.', status: :error)
        end
      end

      context 'when HEAD from position is different from source branch HEAD on repo' do
        it 'returns error message' do
          allow(suggestion).to receive(:appliable?).and_return(true)
          allow(suggestion.position).to receive(:head_sha).and_return('old-sha')
          allow(suggestion.noteable).to receive(:source_branch_sha).and_return('new-sha')

          result = apply_service.new(user, suggestion).execute

          expect(result).to eq(message: 'A file has been changed.', status: :error)
        end
      end

      context 'single suggestion' do
        let(:author) { suggestions.first.note.author }
        let(:commit) { project.repository.commit }

        context 'author of suggestion applies suggestion' do
          before do
            suggestion.note.update!(author_id: user.id)

            apply(suggestions)
          end

          it 'created commit by same author and committer' do
            expect(user.commit_email).to eq(author.commit_email_or_default)
            expect(author).to eq(user)
            expect(commit.author_email).to eq(author.commit_email_or_default)
            expect(commit.committer_email).to eq(user.commit_email)
            expect(commit.author_name).to eq(author.name)
            expect(commit.committer_name).to eq(user.name)
          end
        end

        context 'another user applies suggestion' do
          before do
            apply(suggestions)
          end

          it 'created commit has authors info and commiters info' do
            expect(user.commit_email).not_to eq(user.email)
            expect(author).not_to eq(user)
            expect(commit.author_email).to eq(author.commit_email_or_default)
            expect(commit.committer_email).to eq(user.commit_email)
            expect(commit.author_name).to eq(author.name)
            expect(commit.committer_name).to eq(user.name)
          end
        end
      end

      context 'multiple suggestions' do
        let(:author_emails) { suggestions.map { |s| s.note.author.commit_email_or_default } }
        let(:first_author) { suggestion.note.author }
        let(:commit) { project.repository.commit }

        context 'when all the same author' do
          before do
            apply(suggestions)
          end

          it 'uses first authors information' do
            expect(author_emails).to include(first_author.commit_email_or_default).exactly(3)
            expect(commit.author_email).to eq(first_author.commit_email_or_default)
          end
        end

        context 'when all different authors' do
          before do
            suggestion2.note.update!(author_id: create(:user).id)
            suggestion3.note.update!(author_id: create(:user).id)
            apply(suggestions)
          end

          it 'uses committers information' do
            expect(commit.author_email).to eq(user.commit_email)
            expect(commit.committer_email).to eq(user.commit_email)
          end
        end
      end

      context 'multiple suggestions applied sequentially' do
        def apply_suggestion(suggestion)
          suggestion.reload
          merge_request.reload
          merge_request.clear_memoized_shas

          result = apply_service.new(user, suggestion).execute
          suggestion.reload
          expect(result[:status]).to eq(:success)

          refresh = MergeRequests::RefreshService.new(project: project, current_user: user)
          refresh.execute(
            merge_request.diff_head_sha,
            suggestion.commit_id,
            merge_request.source_branch_ref
          )

          result
        end

        def fetch_raw_diff(suggestion)
          project.reload.commit(suggestion.commit_id)
            .diffs.diff_files.first.diff.diff
        end

        it 'applies multiple suggestions in subsequent versions correctly' do
          suggestion1 = create_suggestion(
            from_content: "\n",
            to_content: "# v1 change\n",
            old_line: nil,
            new_line: 13)

          suggestion2 = create_suggestion(
            from_content: "      @cmd_output << stderr.read\n",
            to_content: "# v2 change\n",
            old_line: 24,
            new_line: 31)

          apply_suggestion(suggestion1)
          apply_suggestion(suggestion2)

          suggestion1_diff = fetch_raw_diff(suggestion1)
          suggestion2_diff = fetch_raw_diff(suggestion2)

          expected_suggestion1_diff = <<-CONTENT.strip_heredoc
            @@ -10,7 +10,7 @@ module Popen
                 end

                 path ||= Dir.pwd
            -
            +# v1 change
                 vars = {
                   "PWD" => path
                 }
          CONTENT
          expected_suggestion2_diff = <<-CONTENT.strip_heredoc
            @@ -28,7 +28,7 @@ module Popen

                 Open3.popen3(vars, *cmd, options) do |stdin, stdout, stderr, wait_thr|
                   @cmd_output << stdout.read
            -      @cmd_output << stderr.read
            +# v2 change
                   @cmd_status = wait_thr.value.exitstatus
                 end
          CONTENT
          expect(suggestion1_diff.strip).to eq(expected_suggestion1_diff.strip)
          expect(suggestion2_diff.strip).to eq(expected_suggestion2_diff.strip)
        end
      end

      context 'multi-line suggestion' do
        let(:popen_content) do
          <<~CONTENT.strip_heredoc
            require 'fileutils'
            require 'open3'

            module Popen
              extend self

            # multi
            # line

                vars = {
                  "PWD" => path
                }

                options = {
                  chdir: path
                }

                unless File.directory?(path)
                  FileUtils.mkdir_p(path)
                end

                @cmd_output = ""
                @cmd_status = 0

                Open3.popen3(vars, *cmd, options) do |stdin, stdout, stderr, wait_thr|
                  @cmd_output << stdout.read
                  @cmd_output << stderr.read
                  @cmd_status = wait_thr.value.exitstatus
                end

                return @cmd_output, @cmd_status
              end
            end
          CONTENT
        end

        let(:expected_content_by_path) do
          {
            "files/ruby/popen.rb": popen_content
          }
        end

        let(:suggestion) do
          create(:suggestion, :content_from_repo,
            note: diff_note, lines_above: 2, lines_below: 3, to_content: "# multi\n# line\n")
        end

        let(:suggestions) { [suggestion] }

        it_behaves_like 'successfully creates commit and updates suggestions'
      end

      context 'remove an empty line suggestion' do
        let(:popen_content) do
          <<~CONTENT.strip_heredoc
            require 'fileutils'
            require 'open3'

            module Popen
              extend self

              def popen(cmd, path=nil)
                unless cmd.is_a?(Array)
                  raise RuntimeError, "System commands must be given as an array of strings"
                end

                path ||= Dir.pwd
                vars = {
                  "PWD" => path
                }

                options = {
                  chdir: path
                }

                unless File.directory?(path)
                  FileUtils.mkdir_p(path)
                end

                @cmd_output = ""
                @cmd_status = 0

                Open3.popen3(vars, *cmd, options) do |stdin, stdout, stderr, wait_thr|
                  @cmd_output << stdout.read
                  @cmd_output << stderr.read
                  @cmd_status = wait_thr.value.exitstatus
                end

                return @cmd_output, @cmd_status
              end
            end
          CONTENT
        end

        let(:expected_content_by_path) do
          {
            "files/ruby/popen.rb": popen_content
          }
        end

        let(:suggestion) do
          create_suggestion(to_content: "", new_line: 13)
        end

        let(:suggestions) { [suggestion] }

        it_behaves_like 'successfully creates commit and updates suggestions'
      end
    end

    context 'fork-project' do
      let(:project) { create(:project, :public, :repository) }

      let(:forked_project) do
        fork_project_with_submodules(project, user)
      end

      let(:merge_request) do
        create(
          :merge_request,
          source_branch: 'conflict-resolvable-fork',
          source_project: forked_project,
          target_branch: 'conflict-start',
          target_project: project
        )
      end

      let!(:diff_note) do
        create(
          :diff_note_on_merge_request,
          noteable: merge_request,
          position: position,
          project: project
        )
      end

      before do
        project.add_maintainer(user)
      end

      it 'updates file in the source project' do
        expect(Files::MultiService).to receive(:new)
          .with(merge_request.source_project, user, anything)
          .and_call_original

        apply_service.new(user, suggestion).execute
      end
    end
  end

  context 'no permission' do
    let(:merge_request) do
      create(:merge_request, source_project: project, target_project: project)
    end

    let(:diff_note) do
      create(:diff_note_on_merge_request,
        noteable: merge_request, position: position, project: project)
    end

    context 'user cannot write in project repo' do
      before do
        project.add_reporter(user)
      end

      it 'returns error' do
        result = apply_service.new(user, suggestion).execute

        expect(result).to eq(
          message: "You are not allowed to push into this branch",
          status: :error
        )
      end
    end
  end

  context 'patch is not appliable' do
    let(:merge_request) do
      create(:merge_request, source_project: project, target_project: project)
    end

    let(:diff_note) do
      create(:diff_note_on_merge_request,
        noteable: merge_request, position: position, project: project)
    end

    before do
      project.add_maintainer(user)
    end

    shared_examples_for 'service not tracking apply suggestion event' do
      it 'does not track apply suggestion event' do
        expect(Gitlab::UsageDataCounters::MergeRequestActivityUniqueCounter)
          .not_to receive(:track_apply_suggestion_action)

        result
      end
    end

    context 'diff file was not found' do
      let(:result) { apply_service.new(user, suggestion).execute }

      before do
        expect(suggestion.note).to receive(:latest_diff_file).and_return(nil)
      end

      it 'returns error message' do
        expect(result).to eq(message: 'A file was not found.', status: :error)
      end

      it_behaves_like 'service not tracking apply suggestion event'
    end

    context 'when not all suggestions belong to the same branch' do
      let(:merge_request2) do
        create(
          :merge_request,
          :conflict,
          source_project: project,
          target_project: project
        )
      end

      let(:position2) do
        Gitlab::Diff::Position.new(
          old_path: "files/ruby/popen.rb",
          new_path: "files/ruby/popen.rb",
          old_line: nil,
          new_line: 15,
          diff_refs: merge_request2.diff_refs
        )
      end

      let(:diff_note2) do
        create(
          :diff_note_on_merge_request,
          noteable: merge_request2,
          position: position2,
          project: project
        )
      end

      let(:other_branch_suggestion) { create(:suggestion, note: diff_note2) }
      let(:result) { apply_service.new(user, suggestion, other_branch_suggestion).execute }

      it 'renders error message' do
        expect(result).to eq(
          message: 'Suggestions must all be on the same branch.',
          status: :error
        )
      end

      it_behaves_like 'service not tracking apply suggestion event'
    end

    context 'suggestion is not appliable' do
      let(:inapplicable_reason) { "Can't apply this suggestion." }
      let(:result) { apply_service.new(user, suggestion).execute }

      before do
        expect(suggestion).to receive(:appliable?).and_return(false)
        expect(suggestion).to receive(:inapplicable_reason).and_return(inapplicable_reason)
      end

      it 'returns error message' do
        expect(result).to eq(message: inapplicable_reason, status: :error)
      end

      it_behaves_like 'service not tracking apply suggestion event'
    end

    context 'lines of suggestions overlap' do
      let(:suggestion) do
        create_suggestion(
          to_content: "      raise RuntimeError, 'Explosion'\n      # explosion?\n")
      end

      let(:overlapping_suggestion) do
        create_suggestion(to_content: "I Overlap!")
      end

      let(:result) { apply_service.new(user, suggestion, overlapping_suggestion).execute }

      it 'returns error message' do
        expect(result).to eq(
          message: 'Suggestions are not applicable as their lines cannot overlap.',
          status: :error
        )
      end

      it_behaves_like 'service not tracking apply suggestion event'
    end
  end
end


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

module Suggestions
  class CreateService
    def initialize(note)
      @note = note
    end

    def execute
      return unless @note.supports_suggestion?

      suggestions = Gitlab::Diff::SuggestionsParser.parse(
        @note.note,
        project: @note.project,
        position: @note.position
      )

      rows = suggestions.map.with_index do |suggestion, index|
        creation_params = suggestion.to_hash.slice(
          :from_content,
          :to_content,
          :lines_above,
          :lines_below
        )

        creation_params.merge!(note_id: @note.id, relative_order: index)
      end

      rows.in_groups_of(100, false) do |rows|
        ApplicationRecord.legacy_bulk_insert('suggestions', rows) # rubocop:disable Gitlab/BulkInsert
      end

      Gitlab::UsageDataCounters::MergeRequestActivityUniqueCounter.track_add_suggestion_action(note: @note)
    end
  end
end

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Suggestions::CreateService, feature_category: :code_suggestions do
  let(:project_with_repo) { create(:project, :repository) }
  let(:merge_request) do
    create(
      :merge_request,
      source_project: project_with_repo,
      target_project: project_with_repo
    )
  end

  def build_position(args = {})
    default_args = { old_path: "files/ruby/popen.rb",
                     new_path: "files/ruby/popen.rb",
                     old_line: nil,
                     new_line: 14,
                     diff_refs: merge_request.diff_refs }

    Gitlab::Diff::Position.new(default_args.merge(args))
  end

  let(:position) { build_position }

  let(:markdown) do
    <<-MARKDOWN.strip_heredoc
        ```suggestion
          foo
          bar
        ```

        ```
          nothing
        ```

        ```suggestion
          xpto
          baz
        ```

        ```thing
          this is not a suggestion, it's a thing
        ```

        ```suggestion:-3+2
          # multi-line suggestion 1
        ```

        ```suggestion:-5
          # multi-line suggestion 1
        ```
    MARKDOWN
  end

  subject { described_class.new(note) }

  shared_examples_for 'service not tracking add suggestion event' do
    it 'does not track add suggestion event' do
      expect(Gitlab::UsageDataCounters::MergeRequestActivityUniqueCounter)
        .not_to receive(:track_add_suggestion_action)

      subject.execute
    end
  end

  describe '#execute' do
    context 'should not try to parse suggestions' do
      context 'when not a diff note for merge requests' do
        let(:note) do
          create(:diff_note_on_commit, project: project_with_repo, note: markdown)
        end

        it 'does not try to parse suggestions' do
          expect(Gitlab::Diff::SuggestionsParser).not_to receive(:parse)

          subject.execute
        end

        it_behaves_like 'service not tracking add suggestion event'
      end

      context 'when diff note is not for text' do
        let(:note) do
          create(
            :diff_note_on_merge_request,
            project: project_with_repo,
            noteable: merge_request,
            position: position,
            note: markdown
          )
        end

        before do
          allow(note).to receive(:on_text?) { false }
        end

        it 'does not try to parse suggestions' do
          expect(Gitlab::Diff::SuggestionsParser).not_to receive(:parse)

          subject.execute
        end

        it_behaves_like 'service not tracking add suggestion event'
      end
    end

    context 'when diff file is not found' do
      let(:note) do
        create(
          :diff_note_on_merge_request,
          project: project_with_repo,
          noteable: merge_request,
          position: position,
          note: markdown
        )
      end

      before do
        expect_next_instance_of(DiffNote) do |diff_note|
          expect(diff_note).to receive(:latest_diff_file).once { nil }
        end
      end

      it 'creates no suggestion' do
        expect { subject.execute }.not_to change(Suggestion, :count)
      end

      it_behaves_like 'service not tracking add suggestion event'
    end

    context 'should create suggestions' do
      let(:note) do
        create(
          :diff_note_on_merge_request,
          project: project_with_repo,
          noteable: merge_request,
          position: position,
          note: markdown
        )
      end

      let(:expected_suggestions) do
        Gitlab::Diff::SuggestionsParser.parse(
          markdown,
          project: note.project,
          position: note.position
        )
      end

      it 'persists suggestion records' do
        expect { subject.execute }.to change { note.suggestions.count }
          .from(0).to(expected_suggestions.size)
      end

      it 'persists suggestions data correctly' do
        subject.execute

        suggestions = note.suggestions.order(:relative_order)

        suggestions.zip(expected_suggestions) do |suggestion, expected_suggestion|
          expected_data = expected_suggestion.to_hash

          expect(suggestion.from_content).to eq(expected_data[:from_content])
          expect(suggestion.to_content).to eq(expected_data[:to_content])
          expect(suggestion.lines_above).to eq(expected_data[:lines_above])
          expect(suggestion.lines_below).to eq(expected_data[:lines_below])
        end
      end

      it 'tracks add suggestion event' do
        expect(Gitlab::UsageDataCounters::MergeRequestActivityUniqueCounter)
          .to receive(:track_add_suggestion_action)
          .with(note: note)

        subject.execute
      end

      context 'outdated position note' do
        let!(:outdated_diff) { merge_request.merge_request_diff }
        let!(:latest_diff) { merge_request.create_merge_request_diff }
        let(:outdated_position) { build_position(diff_refs: outdated_diff.diff_refs) }
        let(:position) { build_position(diff_refs: latest_diff.diff_refs) }

        it 'uses the correct position when creating the suggestion' do
          expect(Gitlab::Diff::SuggestionsParser).to receive(:parse)
            .with(note.note, project: note.project, position: note.position)
            .and_call_original

          subject.execute
        end
      end

      context 'when a patch removes an empty line' do
        let(:markdown) do
          <<-MARKDOWN.strip_heredoc
              ```suggestion
              ```
          MARKDOWN
        end

        let(:position) { build_position(new_line: 13) }

        it 'creates an appliable suggestion' do
          subject.execute

          suggestion = note.suggestions.last

          expect(suggestion).to be_appliable
          expect(suggestion.from_content).to eq("\n")
          expect(suggestion.to_content).to eq("")
        end
      end
    end
  end
end


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

module Mattermost
  class CreateTeamService < ::BaseService
    def initialize(group, current_user)
      @group = group
      @current_user = current_user
    end

    def execute
      # The user that creates the team will be Team Admin
      ::Mattermost::Team.new(current_user).create(**@group.mattermost_team_params)
    rescue ::Mattermost::ClientError => e
      @group.errors.add(:mattermost_team, e.message)
    end
  end
end

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Mattermost::CreateTeamService, feature_category: :integrations do
  let(:user) { create(:user) }
  let(:group) { create(:group) }

  subject { described_class.new(group, user) }

  it 'creates a team' do
    expect_next_instance_of(::Mattermost::Team) do |instance|
      expect(instance).to receive(:create).with(name: anything, display_name: anything, type: anything)
    end

    subject.execute
  end

  it 'adds an error if a team could not be created' do
    expect_next_instance_of(::Mattermost::Team) do |instance|
      expect(instance).to receive(:create).and_raise(::Mattermost::ClientError, 'client error')
    end

    subject.execute

    expect(group.errors).to be_present
  end
end


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

module Branches
  class ValidateNewService < BaseService
    def initialize(project)
      @project = project
    end

    def execute(branch_name, force: false)
      return error('Branch name is invalid') unless valid_name?(branch_name)

      if branch_exist?(branch_name) && !force
        return error('Branch already exists')
      end

      success
    rescue Gitlab::Git::PreReceiveError => ex
      error(ex.message)
    end

    private

    def valid_name?(branch_name)
      Gitlab::GitRefValidator.validate(branch_name)
    end

    def branch_exist?(branch_name)
      project.repository.branch_exists?(branch_name)
    end
  end
end

Branches::ValidateNewService.prepend_mod

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Branches::ValidateNewService, feature_category: :source_code_management do
  let(:project) { create(:project, :repository) }

  subject(:service) { described_class.new(project) }

  describe '#execute' do
    context 'validation' do
      it 'returns error with an invalid branch name' do
        result = service.execute('refs/heads/invalid_branch')

        expect(result[:status]).to eq(:error)
        expect(result[:message]).to eq('Branch name is invalid')
      end

      it 'returns success with a valid branch name' do
        result = service.execute('valid_branch_name')

        expect(result[:status]).to eq(:success)
      end
    end

    context 'branch exist' do
      it 'returns error when branch exists' do
        result = service.execute('master')

        expect(result[:status]).to eq(:error)
        expect(result[:message]).to eq('Branch already exists')
      end

      it 'returns success when branch name is available' do
        result = service.execute('valid_branch_name')

        expect(result[:status]).to eq(:success)
      end
    end
  end
end


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

module Branches
  class DeleteMergedService < BaseService
    def async_execute
      DeleteMergedBranchesWorker.perform_async(project.id, current_user.id)
    end

    def execute
      raise Gitlab::Access::AccessDeniedError unless can?(current_user, :push_code, project)

      branches = project.repository.merged_branch_names
      # Prevent deletion of branches relevant to open merge requests
      branches -= merge_request_branch_names
      # Prevent deletion of protected branches
      branches = branches.reject { |branch| ProtectedBranch.protected?(project, branch) }

      branches.each do |branch|
        ::Branches::DeleteService.new(project, current_user).execute(branch)
      end
    end

    private

    # rubocop: disable CodeReuse/ActiveRecord
    def merge_request_branch_names
      # reorder(nil) is necessary for SELECT DISTINCT because default scope adds an ORDER BY
      source_names = project.origin_merge_requests.opened.reorder(nil).distinct.pluck(:source_branch)
      target_names = project.merge_requests.opened.reorder(nil).distinct.pluck(:target_branch)
      (source_names + target_names).uniq
    end
    # rubocop: enable CodeReuse/ActiveRecord
  end
end

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Branches::DeleteMergedService, feature_category: :source_code_management do
  include ProjectForksHelper

  subject(:service) { described_class.new(project, project.first_owner) }

  let(:project) { create(:project, :repository) }

  describe '#execute' do
    it 'deletes a branch that was merged' do
      service.execute

      expect(project.repository.branch_names).not_to include('improve/awesome')
    end

    it 'keeps branch that is unmerged' do
      service.execute

      expect(project.repository.branch_names).to include('feature')
    end

    it 'keeps "master"' do
      service.execute

      expect(project.repository.branch_names).to include('master')
    end

    it 'keeps protected branches' do
      create(:protected_branch, project: project, name: 'improve/awesome')

      service.execute

      expect(project.repository.branch_names).to include('improve/awesome')
    end

    it 'keeps wildcard protected branches' do
      create(:protected_branch, project: project, name: 'improve/*')

      service.execute

      expect(project.repository.branch_names).to include('improve/awesome')
    end

    it 'ignores protected tags' do
      create(:protected_tag, project: project, name: 'improve/*')

      service.execute

      expect(project.repository.branch_names).not_to include('improve/awesome')
    end

    context 'user without rights' do
      let(:user) { create(:user) }

      it 'cannot execute' do
        expect { described_class.new(project, user).execute }.to raise_error(Gitlab::Access::AccessDeniedError)
      end
    end

    context 'open merge requests' do
      it 'does not delete branches from open merge requests' do
        forked_project = fork_project(project)
        create(:merge_request, :opened, source_project: project, target_project: project, source_branch: 'branch-merged', target_branch: 'master')
        create(:merge_request, :opened, source_project: forked_project, target_project: project, target_branch: 'improve/awesome', source_branch: 'master')

        service.execute

        expect(project.repository.branch_names).to include('branch-merged')
        expect(project.repository.branch_names).to include('improve/awesome')
      end
    end
  end

  describe '#async_execute' do
    it 'calls DeleteMergedBranchesWorker async' do
      expect(DeleteMergedBranchesWorker).to receive(:perform_async)

      service.async_execute
    end
  end
end


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

module Branches
  class DivergingCommitCountsService
    def initialize(repository)
      @repository = repository
      @cache = Gitlab::RepositoryCache.new(repository)
    end

    def call(branch)
      diverging_commit_counts(branch)
    end

    private

    attr_reader :repository, :cache

    delegate :raw_repository, to: :repository

    def diverging_commit_counts(branch)
      @root_ref_hash ||= raw_repository.commit(repository.root_ref).id
      cache.fetch(:"diverging_commit_counts_#{branch.name}") do
        number_commits_behind, number_commits_ahead =
          raw_repository.diverging_commit_count(
            @root_ref_hash,
            branch.dereferenced_target.sha)

        { behind: number_commits_behind, ahead: number_commits_ahead }
      end
    end
  end
end

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Branches::DivergingCommitCountsService, feature_category: :source_code_management do
  let(:project) { create(:project, :repository) }
  let(:repository) { project.repository }

  describe '#call' do
    let(:diverged_branch) { repository.find_branch('fix') }
    let(:root_ref_sha) { repository.raw_repository.commit(repository.root_ref).id }
    let(:diverged_branch_sha) { diverged_branch.dereferenced_target.sha }

    let(:service) { described_class.new(repository) }

    it 'returns the commit counts behind and ahead of default branch' do
      result = service.call(diverged_branch)

      expect(result).to eq(behind: 29, ahead: 2)
    end

    it 'calls diverging_commit_count without max count' do
      expect(repository.raw_repository)
        .to receive(:diverging_commit_count)
        .with(root_ref_sha, diverged_branch_sha)
        .and_return([29, 2])

      service.call(diverged_branch)
    end
  end
end


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

module Branches
  class CreateService < BaseService
    def initialize(project, user = nil, params = {})
      super(project, user, params)

      @errors = []
    end

    def execute(branch_name, ref, create_default_branch_if_empty: true)
      create_default_branch if create_default_branch_if_empty && project.empty_repo?

      result = branch_validation_service.execute(branch_name)

      return result if result[:status] == :error

      create_branch(branch_name, ref)
    end

    def bulk_create(branches)
      reset_errors

      created_branches =
        branches
          .then { |branches| only_valid_branches(branches) }
          .then { |branches| create_branches(branches) }
          .then { |branches| expire_branches_cache(branches) }

      return error(errors) if errors.present?

      success(branches: created_branches)
    end

    private

    attr_reader :errors

    def reset_errors
      @errors = []
    end

    def only_valid_branches(branches)
      branches.select do |branch_name, _ref|
        result = branch_validation_service.execute(branch_name)

        if result[:status] == :error
          errors << result[:message]
          next
        end

        true
      end
    end

    def create_branches(branches)
      branches.filter_map do |branch_name, ref|
        result = create_branch(branch_name, ref, expire_cache: false)

        if result[:status] == :error
          errors << result[:message]
          next
        end

        result[:branch]
      end
    end

    def expire_branches_cache(branches)
      repository.expire_branches_cache if branches.present?

      branches
    end

    def create_branch(branch_name, ref, expire_cache: true)
      new_branch = repository.add_branch(current_user, branch_name, ref, expire_cache: expire_cache)

      if new_branch
        success(branch: new_branch)
      else
        error("Failed to create branch '#{branch_name}': invalid reference name '#{ref}'")
      end
    rescue Gitlab::Git::CommandError => e
      error("Failed to create branch '#{branch_name}': #{e}")
    rescue Gitlab::Git::PreReceiveError => e
      Gitlab::ErrorTracking.log_exception(e, pre_receive_message: e.raw_message, branch_name: branch_name, ref: ref)
      error(e.message)
    end

    def create_default_branch
      project.repository.create_file(
        current_user,
        '/README.md',
        '',
        message: 'Add README.md',
        branch_name: project.default_branch_or_main
      )
    end

    def branch_validation_service
      @branch_validation_service ||= ::Branches::ValidateNewService.new(project)
    end
  end
end

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Branches::CreateService, :use_clean_rails_redis_caching, feature_category: :source_code_management do
  subject(:service) { described_class.new(project, user) }

  let_it_be(:project) { create(:project_empty_repo) }
  let_it_be(:user) { create(:user) }

  describe '#bulk_create' do
    subject { service.bulk_create(branches) }

    let_it_be(:project) { create(:project, :custom_repo, files: { 'foo/a.txt' => 'foo' }) }

    let(:branches) { { 'branch' => 'master', 'another_branch' => 'master' } }

    it 'creates two branches' do
      expect(subject[:status]).to eq(:success)
      expect(subject[:branches].map(&:name)).to match_array(%w[branch another_branch])

      expect(project.repository.branch_exists?('branch')).to be_truthy
      expect(project.repository.branch_exists?('another_branch')).to be_truthy
    end

    context 'when branches are empty' do
      let(:branches) { {} }

      it 'is successful' do
        expect(subject[:status]).to eq(:success)
        expect(subject[:branches]).to eq([])
      end
    end

    context 'when incorrect reference is provided' do
      let(:branches) { { 'new-feature' => 'unknown' } }

      before do
        allow(project.repository).to receive(:add_branch).and_return(false)
      end

      it 'returns an error with a reference name' do
        err_msg = 'Failed to create branch \'new-feature\': invalid reference name \'unknown\''

        expect(subject[:status]).to eq(:error)
        expect(subject[:message]).to match_array([err_msg])
      end
    end

    context 'when branch already exists' do
      let(:branches) { { 'master' => 'master' } }

      it 'returns an error' do
        expect(subject[:status]).to eq(:error)
        expect(subject[:message]).to match_array(['Branch already exists'])
      end
    end

    context 'when PreReceiveError exception' do
      let(:branches) { { 'error' => 'master' } }

      it 'logs and returns an error if there is a PreReceiveError exception' do
        error_message = 'pre receive error'
        raw_message = "GitLab: #{error_message}"
        pre_receive_error = Gitlab::Git::PreReceiveError.new(raw_message)

        allow(project.repository).to receive(:add_branch).and_raise(pre_receive_error)

        expect(Gitlab::ErrorTracking).to receive(:log_exception).with(
          pre_receive_error,
          pre_receive_message: raw_message,
          branch_name: 'error',
          ref: 'master'
        )

        expect(subject[:status]).to eq(:error)
        expect(subject[:message]).to match_array([error_message])
      end
    end

    context 'when multiple errors occur' do
      let(:branches) { { 'master' => 'master', '' => 'master', 'failed_branch' => 'master' } }

      it 'returns all errors' do
        allow(project.repository).to receive(:add_branch).with(
          user,
          'failed_branch',
          'master',
          expire_cache: false
        ).and_return(false)

        expect(subject[:status]).to eq(:error)
        expect(subject[:message]).to match_array(
          [
            'Branch already exists',
            'Branch name is invalid',
            "Failed to create branch 'failed_branch': invalid reference name 'master'"
          ]
        )
      end
    end

    context 'without N+1 for Redis cache' do
      let(:branches) { { 'branch1' => 'master', 'branch2' => 'master', 'branch3' => 'master' } }

      it 'does not trigger Redis recreation' do
        project.repository.expire_branches_cache

        control = RedisCommands::Recorder.new(pattern: ':branch_names:') { subject }

        expect(control).not_to exceed_redis_command_calls_limit(:sadd, 1)
      end
    end

    context 'without N+1 branch cache expiration' do
      let(:branches) { { 'branch_1' => 'master', 'branch_2' => 'master', 'branch_3' => 'master' } }

      it 'triggers branch cache expiration only once' do
        expect(project.repository).to receive(:expire_branches_cache).once

        subject
      end

      context 'when branches were not added' do
        let(:branches) { { 'master' => 'master' } }

        it 'does not trigger branch expiration' do
          expect(project.repository).not_to receive(:expire_branches_cache)

          subject
        end
      end
    end
  end

  describe '#execute' do
    context 'when repository is empty' do
      it 'creates master branch' do
        result = service.execute('my-feature', 'master')

        expect(result[:status]).to eq(:success)
        expect(result[:branch].name).to eq('my-feature')
        expect(project.repository.branch_exists?('master')).to be_truthy
      end

      it 'creates another-feature branch' do
        service.execute('another-feature', 'master')

        expect(project.repository.branch_exists?('another-feature')).to be_truthy
      end
    end

    context 'when branch already exists' do
      it 'returns an error' do
        result = service.execute('master', 'master')

        expect(result[:status]).to eq(:error)
        expect(result[:message]).to eq('Branch already exists')
      end
    end

    context 'when incorrect reference is provided' do
      before do
        allow(project.repository).to receive(:add_branch).and_return(false)
      end

      it 'returns an error with a reference name' do
        err_msg = 'Failed to create branch \'new-feature\': invalid reference name \'unknown\''
        result = service.execute('new-feature', 'unknown')

        expect(result[:status]).to eq(:error)
        expect(result[:message]).to eq(err_msg)
      end
    end

    it 'logs and returns an error if there is a PreReceiveError exception' do
      error_message = 'pre receive error'
      raw_message = "GitLab: #{error_message}"
      pre_receive_error = Gitlab::Git::PreReceiveError.new(raw_message)

      allow(project.repository).to receive(:add_branch).and_raise(pre_receive_error)

      expect(Gitlab::ErrorTracking).to receive(:log_exception).with(
        pre_receive_error,
        pre_receive_message: raw_message,
        branch_name: 'new-feature',
        ref: 'unknown'
      )

      result = service.execute('new-feature', 'unknown')

      expect(result[:status]).to eq(:error)
      expect(result[:message]).to eq(error_message)
    end
  end
end


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

module Branches
  class DeleteService < BaseService
    def execute(branch_name)
      repository = project.repository
      branch = repository.find_branch(branch_name)

      unless current_user.can?(:push_code, project)
        return ServiceResponse.error(
          message: 'You dont have push access to repo',
          http_status: 405)
      end

      unless branch
        return ServiceResponse.error(
          message: 'No such branch',
          http_status: 404)
      end

      if repository.rm_branch(current_user, branch_name)
        unlock_artifacts(branch_name)
        ServiceResponse.success(message: 'Branch was deleted')
      else
        ServiceResponse.error(
          message: 'Failed to remove branch',
          http_status: 400)
      end
    rescue Gitlab::Git::PreReceiveError, Gitlab::Git::CommandError => ex
      ServiceResponse.error(message: ex.message, http_status: 400)
    end

    private

    def unlock_artifacts(branch_name)
      Ci::RefDeleteUnlockArtifactsWorker.perform_async(project.id, current_user.id, "#{::Gitlab::Git::BRANCH_REF_PREFIX}#{branch_name}")
    end
  end
end

Branches::DeleteService.prepend_mod

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Branches::DeleteService, feature_category: :source_code_management do
  let(:project) { create(:project, :repository) }
  let(:repository) { project.repository }
  let(:user) { create(:user) }

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

  shared_examples 'a deleted branch' do |branch_name|
    before do
      allow(Ci::RefDeleteUnlockArtifactsWorker).to receive(:perform_async)
    end

    it 'removes the branch' do
      expect(branch_exists?(branch_name)).to be true

      result = service.execute(branch_name)

      expect(result.status).to eq :success
      expect(branch_exists?(branch_name)).to be false
    end

    it 'calls the RefDeleteUnlockArtifactsWorker' do
      expect(Ci::RefDeleteUnlockArtifactsWorker).to receive(:perform_async).with(project.id, user.id, "refs/heads/#{branch_name}")

      service.execute(branch_name)
    end
  end

  describe '#execute' do
    context 'when user has access to push to repository' do
      before do
        project.add_developer(user)
      end

      it_behaves_like 'a deleted branch', 'feature'

      context 'when Gitlab::Git::CommandError is raised' do
        before do
          allow(repository).to receive(:rm_branch) do
            raise Gitlab::Git::CommandError, 'Could not update patch'
          end
        end

        it 'handles and returns error' do
          result = service.execute('feature')

          expect(result.status).to eq(:error)
          expect(result.message).to eq('Could not update patch')
        end
      end
    end

    context 'when user does not have access to push to repository' do
      it 'does not remove branch' do
        expect(branch_exists?('feature')).to be true

        result = service.execute('feature')

        expect(result.status).to eq :error
        expect(result.message).to eq 'You dont have push access to repo'
        expect(branch_exists?('feature')).to be true
      end
    end
  end

  def branch_exists?(branch_name)
    repository.ref_exists?("refs/heads/#{branch_name}")
  end
end


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

module NamespaceSettings
  class UpdateService
    include ::Gitlab::Allowable

    attr_reader :current_user, :group, :settings_params

    def initialize(current_user, group, settings)
      @current_user = current_user
      @group = group
      @settings_params = settings
    end

    def execute
      validate_resource_access_token_creation_allowed_param

      validate_settings_param_for_root_group(
        param_key: :prevent_sharing_groups_outside_hierarchy,
        user_policy: :change_prevent_sharing_groups_outside_hierarchy
      )
      validate_settings_param_for_root_group(
        param_key: :new_user_signups_cap,
        user_policy: :change_new_user_signups_cap
      )
      validate_settings_param_for_root_group(
        param_key: :default_branch_protection,
        user_policy: :update_default_branch_protection
      )
      validate_settings_param_for_root_group(
        param_key: :default_branch_protection_defaults,
        user_policy: :update_default_branch_protection
      )

      handle_default_branch_protection unless settings_params[:default_branch_protection].blank?

      if group.namespace_settings
        group.namespace_settings.attributes = settings_params
      else
        group.build_namespace_settings(settings_params)
      end
    end

    private

    def handle_default_branch_protection
      # We are migrating default_branch_protection from an integer
      # column to a jsonb column. While completing the rest of the
      # work, we want to start translating the updates sent to the
      # existing column into the json. Eventually, we will be updating
      # the jsonb column directly and deprecating the original update
      # path. Until then, we want to sync up both columns.
      protection = Gitlab::Access::BranchProtection.new(settings_params.delete(:default_branch_protection).to_i)
      settings_params[:default_branch_protection_defaults] = protection.to_hash
    end

    def validate_resource_access_token_creation_allowed_param
      return if settings_params[:resource_access_token_creation_allowed].nil?

      unless can?(current_user, :admin_group, group)
        settings_params.delete(:resource_access_token_creation_allowed)
        group.namespace_settings.errors.add(:resource_access_token_creation_allowed, _('can only be changed by a group admin.'))
      end
    end

    def validate_settings_param_for_root_group(param_key:, user_policy:)
      return if settings_params[param_key].nil?

      unless can?(current_user, user_policy, group)
        settings_params.delete(param_key)
        group.namespace_settings.errors.add(param_key, _('can only be changed by a group admin.'))
      end

      unless group.root?
        settings_params.delete(param_key)
        group.namespace_settings.errors.add(param_key, _('only available on top-level groups.'))
      end
    end
  end
end

NamespaceSettings::UpdateService.prepend_mod_with('NamespaceSettings::UpdateService')

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe NamespaceSettings::UpdateService, feature_category: :groups_and_projects do
  let(:user) { create(:user) }
  let(:group) { create(:group) }
  let(:settings) { {} }

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

  describe "#execute" do
    context "group has no namespace_settings" do
      before do
        group.namespace_settings.destroy!
      end

      it "builds out a new namespace_settings record" do
        expect do
          service.execute
        end.to change { NamespaceSetting.count }.by(1)
      end
    end

    context "group has a namespace_settings" do
      before do
        service.execute
      end

      it "doesn't create a new namespace_setting record" do
        expect do
          service.execute
        end.not_to change { NamespaceSetting.count }
      end
    end

    context "updating :default_branch_name" do
      let(:example_branch_name) { "example_branch_name" }
      let(:settings) { { default_branch_name: example_branch_name } }

      it "changes settings" do
        expect { service.execute }
          .to change { group.namespace_settings.default_branch_name }
                .from(nil).to(example_branch_name)
      end
    end

    context 'when default_branch_protection is updated' do
      let(:namespace_settings) { group.namespace_settings }
      let(:expected) { ::Gitlab::Access::BranchProtection.protected_against_developer_pushes.stringify_keys }
      let(:settings) { { default_branch_protection: ::Gitlab::Access::PROTECTION_DEV_CAN_MERGE } }

      before do
        group.add_owner(user)
      end

      it "updates default_branch_protection_defaults from the default_branch_protection param" do
        expect { service.execute }
          .to change { namespace_settings.default_branch_protection_defaults }
                .from({}).to(expected)
      end
    end

    context 'when default_branch_protection_defaults is updated' do
      let(:namespace_settings) { group.namespace_settings }
      let(:branch_protection) { ::Gitlab::Access::BranchProtection.protected_against_developer_pushes.stringify_keys }
      let(:expected) { branch_protection }
      let(:settings) { { default_branch_protection_defaults: branch_protection } }

      context 'when the user has the ability to update' do
        before do
          allow(Ability).to receive(:allowed?).with(user, :update_default_branch_protection, group).and_return(true)
        end

        context 'when group is root' do
          before do
            allow(group).to receive(:root?).and_return(true)
          end

          it "updates default_branch_protection_defaults from the default_branch_protection param" do
            expect { service.execute }
              .to change { namespace_settings.default_branch_protection_defaults }
                    .from({}).to(expected)
          end
        end

        context 'when group is not root' do
          before do
            allow(group).to receive(:root?).and_return(false)
          end

          it "does not update default_branch_protection_defaults and adds an error to the namespace_settings", :aggregate_failures do
            expect { service.execute }.not_to change { namespace_settings.default_branch_protection_defaults }
            expect(group.namespace_settings.errors[:default_branch_protection_defaults]).to include('only available on top-level groups.')
          end
        end
      end

      context 'when the user does not have the ability to update' do
        before do
          allow(Ability).to receive(:allowed?).with(user, :update_default_branch_protection, group).and_return(false)
        end

        it "does not update default_branch_protection_defaults and adds an error to the namespace_settings", :aggregate_failures do
          expect { service.execute }.not_to change { namespace_settings.default_branch_protection_defaults }
          expect(group.namespace_settings.errors[:default_branch_protection_defaults]).to include('can only be changed by a group admin.')
        end
      end
    end

    context "updating :resource_access_token_creation_allowed" do
      let(:settings) { { resource_access_token_creation_allowed: false } }

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

        it "changes settings" do
          expect { service.execute }
            .to change { group.namespace_settings.resource_access_token_creation_allowed }
                  .from(true).to(false)
        end
      end

      context 'when user is not a group owner' do
        before do
          group.add_developer(user)
        end

        it "does not change settings" do
          expect { service.execute }.not_to change { group.namespace_settings.resource_access_token_creation_allowed }
        end

        it 'returns the group owner error' do
          service.execute
          expect(group.namespace_settings.errors.messages[:resource_access_token_creation_allowed]).to include('can only be changed by a group admin.')
        end
      end
    end

    describe 'validating settings param for root group' do
      using RSpec::Parameterized::TableSyntax

      where(:setting_key, :setting_changes_from, :setting_changes_to) do
        :prevent_sharing_groups_outside_hierarchy | false | true
        :new_user_signups_cap | nil | 100
      end

      with_them do
        let(:settings) do
          { setting_key => setting_changes_to }
        end

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

          it 'does not change settings' do
            expect { service.execute }.not_to change { group.namespace_settings.public_send(setting_key) }
          end

          it 'returns the group owner error' do
            service.execute

            expect(group.namespace_settings.errors.messages[setting_key]).to include('can only be changed by a group admin.')
          end
        end

        context 'with a subgroup' do
          let(:subgroup) { create(:group, parent: group) }

          before do
            group.add_owner(user)
          end

          it 'does not change settings' do
            service = described_class.new(user, subgroup, settings)

            expect { service.execute }.not_to change { group.namespace_settings.public_send(setting_key) }

            expect(subgroup.namespace_settings.errors.messages[setting_key]).to include('only available on top-level groups.')
          end
        end

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

          it 'changes settings' do
            expect { service.execute }
              .to change { group.namespace_settings.public_send(setting_key) }
                    .from(setting_changes_from).to(setting_changes_to)
          end
        end
      end
    end
  end
end