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

module MergeRequests
  module Conflicts
    class ResolveService < MergeRequests::Conflicts::BaseService
      def execute(current_user, params)
        conflicts = Gitlab::Conflict::FileCollection.new(merge_request)

        conflicts.resolve(current_user, params[:commit_message], params[:files])
      end
    end
  end
end

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe MergeRequests::Conflicts::ResolveService, feature_category: :code_review_workflow do
  include ProjectForksHelper
  let(:user) { create(:user) }
  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',
      source_project: project,
      target_branch: 'conflict-start'
    )
  end

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

  describe '#execute' do
    let(:service) { described_class.new(merge_request) }

    def blob_content(project, ref, path)
      project.repository.blob_at(ref, path).data
    end

    context 'with section params' do
      let(:params) do
        {
          files: [
            {
              old_path: 'files/ruby/popen.rb',
              new_path: 'files/ruby/popen.rb',
              sections: {
                '2f6fcd96b88b36ce98c38da085c795a27d92a3dd_14_14' => 'head'
              }
            }, {
              old_path: 'files/ruby/regex.rb',
              new_path: 'files/ruby/regex.rb',
              sections: {
                '6eb14e00385d2fb284765eb1cd8d420d33d63fc9_9_9' => 'head',
                '6eb14e00385d2fb284765eb1cd8d420d33d63fc9_21_21' => 'origin',
                '6eb14e00385d2fb284765eb1cd8d420d33d63fc9_49_49' => 'origin'
              }
            }
          ],
          commit_message: 'This is a commit message!'
        }
      end

      context 'when the source and target project are the same' do
        before do
          service.execute(user, params)
        end

        it 'creates a commit with the message' do
          expect(merge_request.source_branch_head.message).to eq(params[:commit_message])
        end

        it 'creates a commit with the correct parents' do
          expect(merge_request.source_branch_head.parents.map(&:id))
            .to eq(%w[1450cd639e0bc6721eb02800169e464f212cde06
                      824be604a34828eb682305f0d963056cfac87b2d])
        end
      end

      context 'when some files have trailing newlines' do
        let!(:source_head) do
          branch = 'conflict-resolvable'
          path = 'files/ruby/popen.rb'
          popen_content = blob_content(project, branch, path)

          project.repository.update_file(
            user,
            path,
            popen_content.chomp("\n"),
            message: 'Remove trailing newline from popen.rb',
            branch_name: branch
          )
        end

        before do
          service.execute(user, params)
        end

        it 'preserves trailing newlines from our side of the conflicts' do
          head_sha = merge_request.source_branch_head.sha
          popen_content = blob_content(project, head_sha, 'files/ruby/popen.rb')
          regex_content = blob_content(project, head_sha, 'files/ruby/regex.rb')

          expect(popen_content).not_to end_with("\n")
          expect(regex_content).to end_with("\n")
        end
      end

      context 'when the source project is a fork and does not contain the HEAD of the target branch' do
        let!(:target_head) do
          project.repository.create_file(
            user,
            'new-file-in-target',
            '',
            message: 'Add new file in target',
            branch_name: 'conflict-start')
        end

        subject do
          described_class.new(merge_request_from_fork).execute(user, params)
        end

        it 'creates a commit with the message' do
          subject

          expect(merge_request_from_fork.source_branch_head.message).to eq(params[:commit_message])
        end

        it 'creates a commit with the correct parents' do
          subject

          expect(merge_request_from_fork.source_branch_head.parents.map(&:id))
            .to eq(['404fa3fc7c2c9b5dacff102f353bdf55b1be2813', target_head])
        end
      end
    end

    context 'with content and sections params' do
      let(:popen_content) { "class Popen\nend" }

      let(:params) do
        {
          files: [
            {
              old_path: 'files/ruby/popen.rb',
              new_path: 'files/ruby/popen.rb',
              content: popen_content
            }, {
              old_path: 'files/ruby/regex.rb',
              new_path: 'files/ruby/regex.rb',
              sections: {
                '6eb14e00385d2fb284765eb1cd8d420d33d63fc9_9_9' => 'head',
                '6eb14e00385d2fb284765eb1cd8d420d33d63fc9_21_21' => 'origin',
                '6eb14e00385d2fb284765eb1cd8d420d33d63fc9_49_49' => 'origin'
              }
            }
          ],
          commit_message: 'This is a commit message!'
        }
      end

      before do
        service.execute(user, params)
      end

      it 'creates a commit with the message' do
        expect(merge_request.source_branch_head.message).to eq(params[:commit_message])
      end

      it 'creates a commit with the correct parents' do
        expect(merge_request.source_branch_head.parents.map(&:id))
          .to eq(%w[1450cd639e0bc6721eb02800169e464f212cde06
                    824be604a34828eb682305f0d963056cfac87b2d])
      end

      it 'sets the content to the content given' do
        blob = blob_content(
          merge_request.source_project,
          merge_request.source_branch_head.sha,
          'files/ruby/popen.rb'
        )

        expect(blob).to eq(popen_content)
      end
    end

    context 'when a resolution section is missing' do
      let(:invalid_params) do
        {
          files: [
            {
              old_path: 'files/ruby/popen.rb',
              new_path: 'files/ruby/popen.rb',
              content: ''
            }, {
              old_path: 'files/ruby/regex.rb',
              new_path: 'files/ruby/regex.rb',
              sections: { '6eb14e00385d2fb284765eb1cd8d420d33d63fc9_9_9' => 'head' }
            }
          ],
          commit_message: 'This is a commit message!'
        }
      end

      it 'raises a ResolutionError error' do
        expect { service.execute(user, invalid_params) }
          .to raise_error(Gitlab::Git::Conflict::Resolver::ResolutionError)
      end
    end

    context 'when the content of a file is unchanged' do
      let(:resolver) do
        MergeRequests::Conflicts::ListService.new(merge_request).conflicts.resolver
      end

      let(:regex_conflict) do
        resolver.conflict_for_path(resolver.conflicts, 'files/ruby/regex.rb', 'files/ruby/regex.rb')
      end

      let(:invalid_params) do
        {
          files: [
            {
              old_path: 'files/ruby/popen.rb',
              new_path: 'files/ruby/popen.rb',
              content: ''
            }, {
              old_path: 'files/ruby/regex.rb',
              new_path: 'files/ruby/regex.rb',
              content: regex_conflict.content
            }
          ],
          commit_message: 'This is a commit message!'
        }
      end

      it 'raises a ResolutionError error' do
        expect { service.execute(user, invalid_params) }
          .to raise_error(Gitlab::Git::Conflict::Resolver::ResolutionError)
      end
    end

    context 'when a file is missing' do
      let(:invalid_params) do
        {
          files: [
            {
              old_path: 'files/ruby/popen.rb',
              new_path: 'files/ruby/popen.rb',
              content: ''
            }
          ],
          commit_message: 'This is a commit message!'
        }
      end

      it 'raises a ResolutionError error' do
        expect { service.execute(user, invalid_params) }
          .to raise_error(Gitlab::Git::Conflict::Resolver::ResolutionError)
      end
    end
  end
end


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

module MergeRequests
  module Conflicts
    class ListService < MergeRequests::Conflicts::BaseService
      delegate :file_for_path, :to_json, to: :conflicts

      def can_be_resolved_by?(user)
        return false unless merge_request.source_project

        access = ::Gitlab::UserAccess.new(user, container: merge_request.source_project)
        access.can_push_to_branch?(merge_request.source_branch)
      end

      def can_be_resolved_in_ui?
        return @conflicts_can_be_resolved_in_ui if defined?(@conflicts_can_be_resolved_in_ui)

        return @conflicts_can_be_resolved_in_ui = false unless merge_request.cannot_be_merged?
        return @conflicts_can_be_resolved_in_ui = false unless merge_request.has_complete_diff_refs?
        return @conflicts_can_be_resolved_in_ui = false if merge_request.branch_missing?

        @conflicts_can_be_resolved_in_ui = conflicts.can_be_resolved_in_ui?
      end

      def conflicts
        @conflicts ||=
          Gitlab::Conflict::FileCollection.new(
            merge_request,
            allow_tree_conflicts: params[:allow_tree_conflicts]
          )
      end
    end
  end
end

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe MergeRequests::Conflicts::ListService, feature_category: :code_review_workflow do
  describe '#can_be_resolved_in_ui?' do
    def create_merge_request(source_branch, target_branch = 'conflict-start')
      create(:merge_request, source_branch: source_branch, target_branch: target_branch, merge_status: :unchecked) do |mr|
        mr.mark_as_unmergeable
      end
    end

    def conflicts_service(merge_request)
      described_class.new(merge_request)
    end

    it 'returns a falsey value when the MR can be merged without conflicts' do
      merge_request = create_merge_request('master')
      merge_request.mark_as_mergeable

      expect(conflicts_service(merge_request).can_be_resolved_in_ui?).to be_falsey
    end

    it 'returns a falsey value when the MR is marked as having conflicts, but has none' do
      merge_request = create_merge_request('master')

      expect(conflicts_service(merge_request).can_be_resolved_in_ui?).to be_falsey
    end

    it 'returns a falsey value when one of the MR branches is missing' do
      merge_request = create_merge_request('conflict-resolvable')
      merge_request.project.repository.rm_branch(merge_request.author, 'conflict-resolvable')

      expect(conflicts_service(merge_request).can_be_resolved_in_ui?).to be_falsey
    end

    it 'returns a falsey value when the MR does not support new diff notes' do
      merge_request = create_merge_request('conflict-resolvable')
      merge_request.merge_request_diff.update!(start_commit_sha: nil)

      expect(conflicts_service(merge_request).can_be_resolved_in_ui?).to be_falsey
    end

    it 'returns a falsey value when the conflicts contain a large file' do
      merge_request = create_merge_request('conflict-too-large')

      expect(conflicts_service(merge_request).can_be_resolved_in_ui?).to be_falsey
    end

    it 'returns a falsey value when the conflicts contain a binary file' do
      merge_request = create_merge_request('conflict-binary-file')

      expect(conflicts_service(merge_request).can_be_resolved_in_ui?).to be_falsey
    end

    it 'returns a falsey value when the conflicts contain a file edited in one branch and deleted in another' do
      merge_request = create_merge_request('conflict-missing-side')

      expect(conflicts_service(merge_request).can_be_resolved_in_ui?).to be_falsey
    end

    it 'returns a truthy value when the conflicts are resolvable in the UI' do
      merge_request = create_merge_request('conflict-resolvable')

      expect(conflicts_service(merge_request).can_be_resolved_in_ui?).to be_truthy
    end

    it 'returns a truthy value when the conflicts have to be resolved in an editor' do
      merge_request = create_merge_request('conflict-contains-conflict-markers')

      expect(conflicts_service(merge_request).can_be_resolved_in_ui?).to be_truthy
    end

    it 'returns a falsey value when the MR has a missing ref after a force push' do
      merge_request = create_merge_request('conflict-resolvable')
      service = conflicts_service(merge_request)
      allow_next_instance_of(Gitlab::GitalyClient::ConflictsService) do |instance|
        allow(instance).to receive(:list_conflict_files).and_raise(GRPC::Unknown)
      end

      expect(service.can_be_resolved_in_ui?).to be_falsey
    end

    it 'returns a falsey value when the MR has a missing revision after a force push' do
      merge_request = create_merge_request('conflict-resolvable')
      service = conflicts_service(merge_request)
      allow(merge_request).to receive_message_chain(:target_branch_head, :raw, :id).and_return(Gitlab::Git::BLANK_SHA)

      expect(service.can_be_resolved_in_ui?).to be_falsey
    end

    it 'returns a falsey value when the conflict is in a submodule revision' do
      merge_request = create_merge_request('update-gitlab-shell-v-6-0-3', 'update-gitlab-shell-v-6-0-1')

      expect(conflicts_service(merge_request).can_be_resolved_in_ui?).to be_falsey
    end
  end
end


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

module Captcha
  ##
  # Encapsulates logic of checking captchas.
  #
  class CaptchaVerificationService
    include Recaptcha::Adapters::ControllerMethods

    # Currently the only value that is used out of the request by the reCAPTCHA library
    # is 'remote_ip'. Therefore, we just create a struct to avoid passing the full request
    # object through all the service layer objects, and instead just rely on passing only
    # the required remote_ip value. This eliminates the need to couple the service layer
    # to the HTTP request (for the purpose of this service, at least).
    RequestStruct = Struct.new(:remote_ip)

    def initialize(spam_params:)
      @spam_params = spam_params
    end

    ##
    # Performs verification of a captcha response.
    #
    # NOTE: Currently only supports reCAPTCHA, and is not yet used in all places of the app in which
    # captchas are verified, but these can be addressed in future MRs.  See:
    # https://gitlab.com/gitlab-org/gitlab/-/issues/273480
    def execute
      return false unless spam_params.captcha_response

      @request = RequestStruct.new(spam_params.ip_address)

      Gitlab::Recaptcha.load_configurations!

      # NOTE: We could pass the model and let the recaptcha gem automatically add errors to it,
      # but we do not, for two reasons:
      #
      # 1. We want control over when the errors are added
      # 2. We want control over the wording and i18n of the message
      # 3. We want a consistent interface and behavior when adding support for other captcha
      #    libraries which may not support automatically adding errors to the model.
      verify_recaptcha(response: spam_params.captcha_response)
    end

    private

    attr_reader :spam_params

    # The recaptcha library's Recaptcha::Adapters::ControllerMethods#verify_recaptcha method requires that
    # 'request' be a readable attribute - it doesn't support passing it as an options argument.
    attr_reader :request
  end
end

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Captcha::CaptchaVerificationService, feature_category: :team_planning do
  describe '#execute' do
    let(:captcha_response) { 'abc123' }
    let(:fake_ip) { '1.2.3.4' }
    let(:spam_params) do
      ::Spam::SpamParams.new(
        captcha_response: captcha_response,
        spam_log_id: double,
        ip_address: fake_ip,
        user_agent: double,
        referer: double
      )
    end

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

    subject { service.execute }

    context 'when there is no captcha_response' do
      let(:captcha_response) { nil }

      it 'returns false' do
        expect(subject).to eq(false)
      end
    end

    context 'when there is a captcha_response' do
      before do
        expect(Gitlab::Recaptcha).to receive(:load_configurations!)
      end

      it 'returns false' do
        expect(service).to receive(:verify_recaptcha).with(response: captcha_response) { true }

        expect(subject).to eq(true)
      end

      it 'has a request method which returns an object with the ip address #remote_ip' do
        subject

        request_struct = service.send(:request)
        expect(request_struct).to respond_to(:remote_ip)
        expect(request_struct.remote_ip).to eq(fake_ip)
      end
    end
  end
end


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

module JiraImport
  class ServerUsersMapperService < UsersMapperService
    private

    def url
      "/rest/api/2/user/search?username=''&maxResults=#{MAX_USERS}&startAt=#{start_at.to_i}"
    end

    def jira_user_id(jira_user)
      jira_user['key']
    end

    def jira_user_name(jira_user)
      jira_user['name']
    end
  end
end

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe JiraImport::ServerUsersMapperService, feature_category: :integrations do
  let(:start_at) { 7 }
  let(:url) { "/rest/api/2/user/search?username=''&maxResults=50&startAt=#{start_at}" }

  let_it_be(:user_1) { create(:user, username: 'randomuser', name: 'USER-name1', email: 'uji@example.com') }
  let_it_be(:user_2) { create(:user, username: 'username-2') }
  let_it_be(:user_5) { create(:user, username: 'username-5') }
  let_it_be(:user_4) { create(:user, email: 'user-4@example.com') }
  let_it_be(:user_6) { create(:user, email: 'user-6@example.com') }
  let_it_be(:user_7) { create(:user, username: 'username-7') }
  let_it_be(:user_8) do
    create(:user).tap { |user| create(:email, user: user, email: 'user8_email@example.com') }
  end

  let_it_be(:current_user) { create(:user) }
  let_it_be(:group)        { create(:group) }
  let_it_be(:project)      { create(:project, group: group) }

  let(:jira_users) do
    [
      { 'key' => 'abcd', 'name' => 'User-Name1' }, # matched by name
      { 'key' => 'efg', 'name' => 'username-2' }, # matcher by username
      { 'key' => 'hij' }, # no match
      { 'key' => '123', 'name' => 'user-4', 'emailAddress' => 'user-4@example.com' }, # matched by email
      { 'key' => '456', 'name' => 'username5foo', 'emailAddress' => 'user-5@example.com' }, # no match
      { 'key' => '789', 'name' => 'user-6', 'emailAddress' => 'user-6@example.com' }, # matched by email, no project member
      { 'key' => 'xyz', 'name' => 'username-7', 'emailAddress' => 'user-7@example.com' }, # matched by username, no project member
      { 'key' => 'vhk', 'name' => 'user-8', 'emailAddress' => 'user8_email@example.com' }, # matched by secondary email
      { 'key' => 'uji', 'name' => 'user-9', 'emailAddress' => 'uji@example.com' } # matched by email, same as user_1
    ]
  end

  describe '#execute' do
    before do
      project.add_developer(current_user)
      project.add_developer(user_1)
      project.add_developer(user_2)
      group.add_developer(user_4)
      group.add_guest(user_8)
    end

    it_behaves_like 'mapping jira users'
  end
end


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

module JiraImport
  class CloudUsersMapperService < UsersMapperService
    private

    def url
      "/rest/api/2/users?maxResults=#{MAX_USERS}&startAt=#{start_at.to_i}"
    end

    def jira_user_id(jira_user)
      jira_user['accountId']
    end

    def jira_user_name(jira_user)
      jira_user['displayName']
    end
  end
end

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe JiraImport::CloudUsersMapperService, feature_category: :integrations do
  let(:start_at) { 7 }
  let(:url) { "/rest/api/2/users?maxResults=50&startAt=#{start_at}" }

  let_it_be(:user_1) { create(:user, username: 'randomuser', name: 'USER-name1', email: 'uji@example.com') }
  let_it_be(:user_2) { create(:user, username: 'username-2') }
  let_it_be(:user_5) { create(:user, username: 'username-5') }
  let_it_be(:user_4) { create(:user, email: 'user-4@example.com') }
  let_it_be(:user_6) { create(:user, email: 'user-6@example.com') }
  let_it_be(:user_7) { create(:user, username: 'username-7') }
  let_it_be(:user_8) do
    create(:user).tap { |user| create(:email, user: user, email: 'user8_email@example.com') }
  end

  let_it_be(:current_user) { create(:user) }
  let_it_be(:group)        { create(:group) }
  let_it_be(:project)      { create(:project, group: group) }

  let(:jira_users) do
    [
      { 'accountId' => 'abcd', 'displayName' => 'User-Name1' }, # matched by name
      { 'accountId' => 'efg', 'displayName' => 'username-2' }, # matcher by username
      { 'accountId' => 'hij' }, # no match
      { 'accountId' => '123', 'displayName' => 'user-4', 'emailAddress' => 'user-4@example.com' }, # matched by email
      { 'accountId' => '456', 'displayName' => 'username5foo', 'emailAddress' => 'user-5@example.com' }, # no match
      { 'accountId' => '789', 'displayName' => 'user-6', 'emailAddress' => 'user-6@example.com' }, # matched by email, no project member
      { 'accountId' => 'xyz', 'displayName' => 'username-7', 'emailAddress' => 'user-7@example.com' }, # matched by username, no project member
      { 'accountId' => 'vhk', 'displayName' => 'user-8', 'emailAddress' => 'user8_email@example.com' }, # matched by secondary email
      { 'accountId' => 'uji', 'displayName' => 'user-9', 'emailAddress' => 'uji@example.com' } # matched by email, same as user_1
    ]
  end

  describe '#execute' do
    before do
      project.add_developer(current_user)
      project.add_developer(user_1)
      project.add_developer(user_2)
      group.add_developer(user_4)
      group.add_guest(user_8)
    end

    it_behaves_like 'mapping jira users'
  end
end


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

module JiraImport
  class UsersImporter
    def initialize(user, project, start_at)
      @project = project
      @start_at = start_at
      @user = user
    end

    def execute
      Gitlab::JiraImport.validate_project_settings!(project, user: user)

      ServiceResponse.success(payload: mapped_users)
    rescue Timeout::Error, Errno::EINVAL, Errno::ECONNRESET, Errno::ECONNREFUSED, URI::InvalidURIError, JIRA::HTTPError, OpenSSL::SSL::SSLError => error
      Gitlab::ErrorTracking.log_exception(error, project_id: project.id)
      ServiceResponse.error(message: "There was an error when communicating to Jira")
    rescue Projects::ImportService::Error => error
      ServiceResponse.error(message: error.message)
    end

    private

    attr_reader :user, :project, :start_at

    def mapped_users
      users_mapper_service.execute
    end

    def users_mapper_service
      @users_mapper_service ||= user_mapper_service_factory
    end

    def user_mapper_service_factory
      if project.jira_integration.data_fields.deployment_server?
        ServerUsersMapperService.new(user, project, start_at)
      elsif project.jira_integration.data_fields.deployment_cloud?
        CloudUsersMapperService.new(user, project, start_at)
      else
        raise ArgumentError
      end
    end
  end
end

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe JiraImport::UsersImporter, feature_category: :integrations do
  include JiraIntegrationHelpers

  let_it_be(:user) { create(:user) }
  let_it_be(:group) { create(:group) }
  let_it_be(:project, reload: true) { create(:project, group: group) }
  let_it_be(:start_at) { 7 }

  let(:importer) { described_class.new(user, project, start_at) }

  subject { importer.execute }

  describe '#execute' do
    let(:mapped_users) do
      [
        {
          jira_account_id: 'acc1',
          jira_display_name: 'user-name1',
          jira_email: 'sample@jira.com',
          gitlab_id: project_member.id
        },
        {
          jira_account_id: 'acc2',
          jira_display_name: 'user-name2',
          jira_email: nil,
          gitlab_id: group_member.id
        }
      ]
    end

    before do
      stub_jira_integration_test
      project.add_maintainer(user)
    end

    context 'when Jira import is not configured properly' do
      it 'returns an error' do
        expect(subject.errors).to eq(['Jira integration not configured.'])
      end
    end

    RSpec.shared_examples 'maps Jira users to GitLab users' do |users_mapper_service:|
      context 'when Jira import is configured correctly' do
        let_it_be(:jira_integration) { create(:jira_integration, project: project, active: true, url: "http://jira.example.net") }

        context 'when users mapper service raises an error' do
          let(:error) { Timeout::Error.new }

          it 'returns an error response' do
            expect_next_instance_of(users_mapper_service) do |instance|
              expect(instance).to receive(:execute).and_raise(error)
            end

            expect(Gitlab::ErrorTracking).to receive(:log_exception).with(error, project_id: project.id)
            expect(subject.error?).to be_truthy
            expect(subject.message).to include('There was an error when communicating to Jira')
          end
        end

        context 'when users mapper service returns result' do
          context 'when users mapper service returns an empty array' do
            it 'returns nil payload' do
              expect_next_instance_of(users_mapper_service) do |instance|
                expect(instance).to receive(:execute).and_return([])
              end

              expect(subject.success?).to be_truthy
              expect(subject.payload).to be_empty
            end
          end

          context 'when Jira client returns any users' do
            let_it_be(:project_member) { create(:user, email: 'sample@jira.com') }
            let_it_be(:group_member) { create(:user, name: 'user-name2') }
            let_it_be(:other_user) { create(:user) }

            before do
              project.add_developer(project_member)
              group.add_developer(group_member)
            end

            it 'returns the mapped users' do
              expect_next_instance_of(users_mapper_service) do |instance|
                expect(instance).to receive(:execute).and_return(mapped_users)
              end

              expect(subject.success?).to be_truthy
              expect(subject.payload).to eq(mapped_users)
            end
          end
        end
      end
    end

    context 'when Jira instance is of Server deployment type' do
      before do
        allow(project).to receive(:jira_integration).and_return(jira_integration)

        jira_integration.data_fields.deployment_server!
      end

      it_behaves_like 'maps Jira users to GitLab users', users_mapper_service: JiraImport::ServerUsersMapperService
    end

    context 'when Jira instance is of Cloud deployment type' do
      before do
        allow(project).to receive(:jira_integration).and_return(jira_integration)

        jira_integration.data_fields.deployment_cloud!
      end

      it_behaves_like 'maps Jira users to GitLab users', users_mapper_service: JiraImport::CloudUsersMapperService
    end
  end
end


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

module JiraImport
  class StartImportService
    attr_reader :user, :project, :jira_project_key, :users_mapping

    def initialize(user, project, jira_project_key, users_mapping)
      @user = user
      @project = project
      @jira_project_key = jira_project_key
      @users_mapping = users_mapping
    end

    def execute
      validation_response = validate
      return validation_response if validation_response&.error?

      store_users_mapping
      create_and_schedule_import
    end

    private

    def store_users_mapping
      return if users_mapping.blank?

      mapping = users_mapping.map do |map|
        next if !map[:jira_account_id] || !map[:gitlab_id]

        [map[:jira_account_id], map[:gitlab_id]]
      end.compact.to_h

      return if mapping.blank?

      Gitlab::JiraImport.cache_users_mapping(project.id, mapping)
    end

    def create_and_schedule_import
      jira_import = build_jira_import
      project.import_type = 'jira'
      project.save! && jira_import.schedule!

      ServiceResponse.success(payload: { import_data: jira_import })
    rescue StandardError => ex
      # in case project.save! raises an error
      Gitlab::ErrorTracking.track_exception(ex, project_id: project.id)
      jira_import&.do_fail!(error_message: ex.message)
      build_error_response(ex.message)
    end

    def build_jira_import
      label = create_import_label(project)
      project.jira_imports.build(
        user: user,
        label: label,
        jira_project_key: jira_project_key,
        # we do not have the jira_project_name or jira_project_xid yet so just set a mock value,
        # we will once https://gitlab.com/gitlab-org/gitlab/-/merge_requests/28190
        jira_project_name: jira_project_key,
        jira_project_xid: 0
      )
    end

    def create_import_label(project)
      label = ::Labels::CreateService.new(build_label_attrs(project)).execute(project: project)
      raise Projects::ImportService::Error, _('Failed to create import label for jira import.') if label.blank?

      label
    end

    def build_label_attrs(project)
      import_start_time = Time.zone.now
      jira_imports_for_project = project.jira_imports.by_jira_project_key(jira_project_key).size + 1
      title = "jira-import::#{jira_project_key}-#{jira_imports_for_project}"
      description = "Label for issues that were imported from Jira on #{import_start_time.strftime('%Y-%m-%d %H:%M:%S')}"
      color = ::Gitlab::Color.color_for(title).to_s
      { title: title, description: description, color: color }
    end

    def validate
      Gitlab::JiraImport.validate_project_settings!(project, user: user)

      return build_error_response(_('Unable to find Jira project to import data from.')) if jira_project_key.blank?
      return build_error_response(_('Jira import is already running.')) if import_in_progress?
    rescue Projects::ImportService::Error => e
      build_error_response(e.message)
    end

    def build_error_response(message)
      ServiceResponse.error(message: message, http_status: 400)
    end

    def import_in_progress?
      project.latest_jira_import&.in_progress?
    end
  end
end

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe JiraImport::StartImportService, feature_category: :integrations do
  include JiraIntegrationHelpers

  let_it_be(:user) { create(:user) }
  let_it_be(:project, reload: true) { create(:project) }

  let(:key) { 'KEY' }
  let(:mapping) do
    [
      { jira_account_id: 'abc', gitlab_id: 12 },
      { jira_account_id: 'def', gitlab_id: nil },
      { jira_account_id: nil, gitlab_id: 1 }
    ]
  end

  subject { described_class.new(user, project, key, mapping).execute }

  context 'when an error is returned from the project validation' do
    before do
      allow(Gitlab::JiraImport).to receive(:validate_project_settings!)
        .and_raise(Projects::ImportService::Error, 'Jira import feature is disabled.')
    end

    it_behaves_like 'responds with error', 'Jira import feature is disabled.'
  end

  context 'when project validation is ok' do
    let!(:jira_integration) { create(:jira_integration, project: project, active: true) }

    before do
      stub_jira_integration_test
      allow(Gitlab::JiraImport).to receive(:validate_project_settings!)
    end

    context 'when Jira project key is not provided' do
      let(:key) { '' }

      it_behaves_like 'responds with error', 'Unable to find Jira project to import data from.'
    end

    context 'when correct data provided' do
      let(:fake_key)  { 'some-key' }

      subject { described_class.new(user, project, fake_key, mapping).execute }

      context 'when import is already running' do
        let_it_be(:jira_import_state) { create(:jira_import_state, :started, project: project) }

        it_behaves_like 'responds with error', 'Jira import is already running.'
      end

      context 'when an error is raised while scheduling import' do
        before do
          expect_next_instance_of(JiraImportState) do |jira_impport|
            expect(jira_impport).to receive(:schedule!).and_raise(Projects::ImportService::Error, 'Unexpected failure.')
          end
        end

        it_behaves_like 'responds with error', 'Unexpected failure.'

        it 'saves the error message' do
          subject

          expect(JiraImportState.last.error_message).to eq('Unexpected failure.')
        end
      end

      context 'when everything is ok' do
        context 'with complete mapping' do
          before do
            expect(Gitlab::JiraImport).to receive(:cache_users_mapping).with(project.id, { 'abc' => 12 })
          end

          it 'returns success response' do
            expect(subject).to be_a(ServiceResponse)
            expect(subject).to be_success
          end

          it 'schedules Jira import' do
            subject

            expect(project.latest_jira_import).to be_scheduled
          end

          it 'creates Jira import data', :aggregate_failures do
            jira_import = subject.payload[:import_data]

            expect(jira_import.jira_project_xid).to eq(0)
            expect(jira_import.jira_project_name).to eq(fake_key)
            expect(jira_import.jira_project_key).to eq(fake_key)
            expect(jira_import.user).to eq(user)
          end

          it 'creates Jira import label' do
            expect { subject }.to change { Label.count }.by(1)
          end

          it 'creates Jira label title with correct number' do
            jira_import = subject.payload[:import_data]
            label_title = "jira-import::#{jira_import.jira_project_key}-1"

            expect(jira_import.label.title).to eq(label_title)
          end
        end

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

          it 'returns success response' do
            expect(Gitlab::JiraImport).not_to receive(:cache_users_mapping)

            expect(subject).to be_a(ServiceResponse)
            expect(subject).to be_success
          end
        end

        context 'when no mapping value is complete' do
          let(:mapping) do
            [
              { jira_account_id: 'def', gitlab_id: nil },
              { jira_account_id: nil, gitlab_id: 1 }
            ]
          end

          it 'returns success response' do
            expect(Gitlab::JiraImport).not_to receive(:cache_users_mapping)

            expect(subject).to be_a(ServiceResponse)
            expect(subject).to be_success
          end
        end
      end

      context 'when multiple Jira imports for same Jira project' do
        let!(:jira_imports) { create_list(:jira_import_state, 3, :finished, project: project, jira_project_key: fake_key) }

        it 'creates Jira label title with correct number' do
          jira_import = subject.payload[:import_data]
          label_title = "jira-import::#{jira_import.jira_project_key}-4"

          expect(jira_import.label.title).to eq(label_title)
        end
      end
    end
  end
end


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

module Tags
  class DestroyService < BaseService
    def execute(tag_name)
      repository = project.repository
      tag = repository.find_tag(tag_name)

      unless tag
        return error('No such tag', 404)
      end

      if repository.rm_tag(current_user, tag_name)
        ##
        # When a tag in a repository is destroyed,
        # release assets will be destroyed too.
        Releases::DestroyService
          .new(project, current_user, tag: tag_name)
          .execute

        unlock_artifacts(tag_name)

        success('Tag was removed')
      else
        error('Failed to remove tag')
      end
    rescue Gitlab::Git::PreReceiveError => ex
      error(ex.message)
    end

    def error(message, return_code = 400)
      super(message).merge(return_code: return_code)
    end

    def success(message)
      super().merge(message: message)
    end

    private

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

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Tags::DestroyService, feature_category: :source_code_management do
  let(:project) { create(:project, :repository) }
  let(:repository) { project.repository }
  let(:user) { create(:user) }
  let(:service) { described_class.new(project, user) }

  describe '#execute' do
    subject { service.execute(tag_name) }

    before do
      allow(Ci::RefDeleteUnlockArtifactsWorker).to receive(:perform_async)
    end

    it 'removes the tag' do
      expect(repository).to receive(:before_remove_tag)
      expect(service).to receive(:success)

      service.execute('v1.1.0')
    end

    it 'calls the RefDeleteUnlockArtifactsWorker' do
      expect(Ci::RefDeleteUnlockArtifactsWorker).to receive(:perform_async).with(project.id, user.id, 'refs/tags/v1.1.0')

      service.execute('v1.1.0')
    end

    context 'when there is an associated release on the tag' do
      let(:tag) { repository.tags.first }
      let(:tag_name) { tag.name }

      before do
        project.add_maintainer(user)
        create(:release, tag: tag_name, project: project)
      end

      it 'destroys the release' do
        expect { subject }.to change { project.releases.count }.by(-1)
      end
    end
  end
end


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

module Tags
  class CreateService < BaseService
    def execute(tag_name, target, message)
      return error('Target is empty', 400) if target.blank?

      valid_tag = Gitlab::GitRefValidator.validate(tag_name)
      return error('Tag name invalid', 400) unless valid_tag

      repository = project.repository
      message = message&.strip

      new_tag = nil

      begin
        new_tag = repository.add_tag(current_user, tag_name, target, message)
      rescue Gitlab::Git::Repository::TagExistsError
        return error("Tag #{tag_name} already exists", 409)
      rescue Gitlab::Git::PreReceiveError => ex
        return error(ex.message)
      end

      if new_tag
        repository.expire_tags_cache

        success.merge(tag: new_tag)
      else
        error("Target #{target} is invalid", 400)
      end
    end
  end
end

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Tags::CreateService, feature_category: :source_code_management do
  let(:project) { create(:project, :repository) }
  let(:repository) { project.repository }
  let(:user) { create(:user) }
  let(:service) { described_class.new(project, user) }

  describe '#execute' do
    it 'creates the tag and returns success' do
      response = service.execute('v42.42.42', 'master', 'Foo')

      expect(response[:status]).to eq(:success)
      expect(response[:tag]).to be_a Gitlab::Git::Tag
      expect(response[:tag].name).to eq('v42.42.42')
    end

    context 'when target is invalid' do
      it 'returns an error' do
        response = service.execute('v1.1.0', 'foo', 'Foo')

        expect(response[:status]).to eq(:error)
        expect(response[:http_status]).to eq(400)
        expect(response[:message]).to eq('Target foo is invalid')
      end
    end

    context 'when tag_name is empty' do
      it 'returns an error' do
        response = service.execute('', 'foo', 'Foo')

        expect(response[:status]).to eq(:error)
        expect(response[:http_status]).to eq(400)
        expect(response[:message]).to eq('Tag name invalid')
      end
    end

    context 'when target is empty' do
      it 'returns an error' do
        response = service.execute('v1.1.0', '', 'Foo')

        expect(response[:status]).to eq(:error)
        expect(response[:http_status]).to eq(400)
        expect(response[:message]).to eq('Target is empty')
      end
    end

    context 'when tag already exists' do
      it 'returns an error' do
        expect(repository).to receive(:add_tag)
          .with(user, 'v1.1.0', 'master', 'Foo')
          .and_raise(Gitlab::Git::Repository::TagExistsError)

        response = service.execute('v1.1.0', 'master', 'Foo')

        expect(response[:status]).to eq(:error)
        expect(response[:http_status]).to eq(409)
        expect(response[:message]).to eq('Tag v1.1.0 already exists')
      end
    end

    context 'when tag name is invalid' do
      it 'returns an error' do
        response = service.execute('HEAD', 'master', 'Foo')

        expect(response[:status]).to eq(:error)
        expect(response[:http_status]).to eq(400)
        expect(response[:message]).to eq('Tag name invalid')
      end
    end

    context 'when pre-receive hook fails' do
      it 'returns an error' do
        expect(repository).to receive(:add_tag)
          .with(user, 'v1.1.0', 'master', 'Foo')
          .and_raise(Gitlab::Git::PreReceiveError, 'GitLab: something went wrong')

        response = service.execute('v1.1.0', 'master', 'Foo')

        expect(response[:status]).to eq(:error)
        expect(response[:message]).to eq('something went wrong')
      end
    end
  end
end


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

module Search
  class GlobalService
    include Search::Filter
    include Gitlab::Utils::StrongMemoize

    DEFAULT_SCOPE = 'projects'
    ALLOWED_SCOPES = %w[projects issues merge_requests milestones users].freeze

    attr_accessor :current_user, :params

    def initialize(user, params)
      @current_user = user
      @params = params.dup
    end

    def execute
      Gitlab::SearchResults.new(current_user,
                                params[:search],
                                projects,
                                order_by: params[:order_by],
                                sort: params[:sort],
                                filters: filters)
    end

    # rubocop: disable CodeReuse/ActiveRecord
    def projects
      @projects ||= ProjectsFinder.new(current_user: current_user).execute.preload(:topics, :project_topics)
    end

    def allowed_scopes
      ALLOWED_SCOPES
    end

    def scope
      strong_memoize(:scope) do
        allowed_scopes.include?(params[:scope]) ? params[:scope] : DEFAULT_SCOPE
      end
    end
  end
end

Search::GlobalService.prepend_mod_with('Search::GlobalService')

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Search::GlobalService, feature_category: :global_search do
  let_it_be(:user) { create(:user) }
  let_it_be(:internal_user) { create(:user) }

  let_it_be(:found_project)    { create(:project, :private, name: 'searchable_project') }
  let_it_be(:unfound_project)  { create(:project, :private, name: 'unfound_project') }
  let_it_be(:internal_project) { create(:project, :internal, name: 'searchable_internal_project') }
  let_it_be(:public_project)   { create(:project, :public, name: 'searchable_public_project') }
  let_it_be(:archived_project) { create(:project, :public, archived: true, name: 'archived_project') }

  before do
    found_project.add_maintainer(user)
  end

  describe '#execute' do
    context 'unauthenticated' do
      it 'returns public projects only' do
        results = described_class.new(nil, search: "searchable").execute

        expect(results.objects('projects')).to match_array [public_project]
      end
    end

    context 'authenticated' do
      it 'returns public, internal and private projects' do
        results = described_class.new(user, search: "searchable").execute

        expect(results.objects('projects')).to match_array [public_project, found_project, internal_project]
      end

      it 'returns only public & internal projects' do
        results = described_class.new(internal_user, search: "searchable").execute

        expect(results.objects('projects')).to match_array [internal_project, public_project]
      end

      it 'project name is searchable' do
        results = described_class.new(user, search: found_project.name).execute

        expect(results.objects('projects')).to match_array [found_project]
      end

      it 'does not return archived projects' do
        results = described_class.new(user, search: "archived").execute

        expect(results.objects('projects')).not_to include(archived_project)
      end

      it 'returns archived projects if the include_archived option is passed' do
        results = described_class.new(user, { include_archived: true, search: "archived" }).execute

        expect(results.objects('projects')).to include(archived_project)
      end
    end
  end

  context 'issues' do
    let(:scope) { 'issues' }

    context 'sorting' do
      let_it_be(:project) { create(:project, :public) }

      let!(:old_result) { create(:issue, project: project, title: 'sorted old', created_at: 1.month.ago) }
      let!(:new_result) { create(:issue, project: project, title: 'sorted recent', created_at: 1.day.ago) }
      let!(:very_old_result) { create(:issue, project: project, title: 'sorted very old', created_at: 1.year.ago) }

      let!(:old_updated) { create(:issue, project: project, title: 'updated old', updated_at: 1.month.ago) }
      let!(:new_updated) { create(:issue, project: project, title: 'updated recent', updated_at: 1.day.ago) }
      let!(:very_old_updated) { create(:issue, project: project, title: 'updated very old', updated_at: 1.year.ago) }

      include_examples 'search results sorted' do
        let(:results_created) { described_class.new(nil, search: 'sorted', sort: sort).execute }
        let(:results_updated) { described_class.new(nil, search: 'updated', sort: sort).execute }
      end
    end
  end

  context 'merge_request' do
    let(:scope) { 'merge_requests' }

    context 'sorting' do
      let!(:project) { create(:project, :public) }

      let!(:old_result) { create(:merge_request, :opened, source_project: project, source_branch: 'old-1', title: 'sorted old', created_at: 1.month.ago) }
      let!(:new_result) { create(:merge_request, :opened, source_project: project, source_branch: 'new-1', title: 'sorted recent', created_at: 1.day.ago) }
      let!(:very_old_result) { create(:merge_request, :opened, source_project: project, source_branch: 'very-old-1', title: 'sorted very old', created_at: 1.year.ago) }

      let!(:old_updated) { create(:merge_request, :opened, source_project: project, source_branch: 'updated-old-1', title: 'updated old', updated_at: 1.month.ago) }
      let!(:new_updated) { create(:merge_request, :opened, source_project: project, source_branch: 'updated-new-1', title: 'updated recent', updated_at: 1.day.ago) }
      let!(:very_old_updated) { create(:merge_request, :opened, source_project: project, source_branch: 'updated-very-old-1', title: 'updated very old', updated_at: 1.year.ago) }

      include_examples 'search results sorted' do
        let(:results_created) { described_class.new(nil, search: 'sorted', sort: sort).execute }
        let(:results_updated) { described_class.new(nil, search: 'updated', sort: sort).execute }
      end
    end
  end
end


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

module Search
  class SnippetService < Search::GlobalService
    def execute
      Gitlab::SnippetSearchResults.new(current_user, params[:search])
    end

    def scope
      @scope ||= 'snippet_titles'
    end
  end
end

Search::SnippetService.prepend_mod_with('Search::SnippetService')

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Search::SnippetService, feature_category: :global_search do
  let_it_be(:author) { create(:author) }
  let_it_be(:project) { create(:project, :public) }

  let_it_be(:public_snippet)   { create(:snippet, :public, title: 'Foo Bar Title') }
  let_it_be(:internal_snippet) { create(:snippet, :internal, title: 'Foo Bar Title') }
  let_it_be(:private_snippet)  { create(:snippet, :private, title: 'Foo Bar Title', author: author) }

  let_it_be(:project_public_snippet)   { create(:snippet, :public, project: project, title: 'Foo Bar Title') }
  let_it_be(:project_internal_snippet) { create(:snippet, :internal, project: project, title: 'Foo Bar Title') }
  let_it_be(:project_private_snippet)  { create(:snippet, :private, project: project, title: 'Foo Bar Title') }

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

  describe '#execute' do
    context 'unauthenticated' do
      it 'returns public snippets only' do
        search = described_class.new(nil, search: 'bar')
        results = search.execute

        expect(results.objects('snippet_titles')).to match_array [public_snippet, project_public_snippet]
      end
    end

    context 'authenticated' do
      it 'returns only public & internal snippets for regular users' do
        search = described_class.new(user, search: 'bar')
        results = search.execute

        expect(results.objects('snippet_titles')).to match_array [public_snippet, internal_snippet, project_public_snippet, project_internal_snippet]
      end

      it 'returns public, internal snippets and project private snippets for project members' do
        project.add_developer(user)
        search = described_class.new(user, search: 'bar')
        results = search.execute

        expect(results.objects('snippet_titles')).to match_array [public_snippet, internal_snippet, project_public_snippet, project_internal_snippet, project_private_snippet]
      end

      it 'returns public, internal and private snippets where user is the author' do
        search = described_class.new(author, search: 'bar')
        results = search.execute

        expect(results.objects('snippet_titles')).to match_array [public_snippet, internal_snippet, private_snippet, project_public_snippet, project_internal_snippet]
      end

      context 'when admin mode is enabled', :enable_admin_mode do
        it 'returns all snippets when user is admin' do
          admin = create(:admin)
          search = described_class.new(admin, search: 'bar')
          results = search.execute

          expect(results.objects('snippet_titles')).to match_array [public_snippet, internal_snippet, private_snippet, project_public_snippet, project_internal_snippet, project_private_snippet]
        end
      end

      context 'when admin mode is disabled' do
        it 'returns only public & internal snippets when user is admin' do
          admin = create(:admin)
          search = described_class.new(admin, search: 'bar')
          results = search.execute

          expect(results.objects('snippet_titles')).to match_array [public_snippet, internal_snippet, project_public_snippet, project_internal_snippet]
        end
      end
    end
  end

  describe '#scope' do
    it 'always scopes to snippet_titles' do
      search = described_class.new(user, search: 'bar')

      expect(search.scope).to eq 'snippet_titles'
    end
  end
end


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

module Search
  class GroupService < Search::GlobalService
    attr_accessor :group

    def initialize(user, group, params)
      super(user, params)

      @group = group
    end

    def execute
      Gitlab::GroupSearchResults.new(
        current_user,
        params[:search],
        projects,
        group: group,
        order_by: params[:order_by],
        sort: params[:sort],
        filters: filters
      )
    end

    def projects
      return Project.none unless group
      return @projects if defined? @projects

      @projects = super.inside_path(group.full_path)
    end
  end
end

Search::GroupService.prepend_mod_with('Search::GroupService')

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Search::GroupService, feature_category: :global_search do
  shared_examples_for 'group search' do
    context 'finding projects by name' do
      let(:user) { create(:user) }
      let(:term) { "Project Name" }
      let(:nested_group) { create(:group, :nested) }

      # These projects shouldn't be found
      let!(:outside_project) { create(:project, :public, name: "Outside #{term}") }
      let!(:private_project) { create(:project, :private, namespace: nested_group, name: "Private #{term}") }
      let!(:other_project)   { create(:project, :public, namespace: nested_group, name: term.reverse) }

      # These projects should be found
      let!(:project1) { create(:project, :internal, namespace: nested_group, name: "Inner #{term} 1") }
      let!(:project2) { create(:project, :internal, namespace: nested_group, name: "Inner #{term} 2") }
      let!(:project3) { create(:project, :internal, namespace: nested_group.parent, name: "Outer #{term}") }

      let(:results) { described_class.new(user, search_group, search: term).execute }

      subject { results.objects('projects') }

      context 'in parent group' do
        let(:search_group) { nested_group.parent }

        it { is_expected.to match_array([project1, project2, project3]) }
      end

      context 'in subgroup' do
        let(:search_group) { nested_group }

        it { is_expected.to match_array([project1, project2]) }
      end
    end
  end

  describe 'basic search' do
    include_examples 'group search'
  end

  context 'issues' do
    let(:scope) { 'issues' }

    context 'sorting' do
      let_it_be(:group) { create(:group) }
      let_it_be(:project) { create(:project, :public, group: group) }

      let!(:old_result) { create(:issue, project: project, title: 'sorted old', created_at: 1.month.ago) }
      let!(:new_result) { create(:issue, project: project, title: 'sorted recent', created_at: 1.day.ago) }
      let!(:very_old_result) { create(:issue, project: project, title: 'sorted very old', created_at: 1.year.ago) }

      let!(:old_updated) { create(:issue, project: project, title: 'updated old', updated_at: 1.month.ago) }
      let!(:new_updated) { create(:issue, project: project, title: 'updated recent', updated_at: 1.day.ago) }
      let!(:very_old_updated) { create(:issue, project: project, title: 'updated very old', updated_at: 1.year.ago) }

      include_examples 'search results sorted' do
        let(:results_created) { described_class.new(nil, group, search: 'sorted', sort: sort).execute }
        let(:results_updated) { described_class.new(nil, group, search: 'updated', sort: sort).execute }
      end
    end
  end

  context 'merge requests' do
    let(:scope) { 'merge_requests' }

    context 'sorting' do
      let!(:group) { create(:group) }
      let!(:project) { create(:project, :public, group: group) }

      let!(:old_result) { create(:merge_request, :opened, source_project: project, source_branch: 'old-1', title: 'sorted old', created_at: 1.month.ago) }
      let!(:new_result) { create(:merge_request, :opened, source_project: project, source_branch: 'new-1', title: 'sorted recent', created_at: 1.day.ago) }
      let!(:very_old_result) { create(:merge_request, :opened, source_project: project, source_branch: 'very-old-1', title: 'sorted very old', created_at: 1.year.ago) }

      let!(:old_updated) { create(:merge_request, :opened, source_project: project, source_branch: 'updated-old-1', title: 'updated old', updated_at: 1.month.ago) }
      let!(:new_updated) { create(:merge_request, :opened, source_project: project, source_branch: 'updated-new-1', title: 'updated recent', updated_at: 1.day.ago) }
      let!(:very_old_updated) { create(:merge_request, :opened, source_project: project, source_branch: 'updated-very-old-1', title: 'updated very old', updated_at: 1.year.ago) }

      include_examples 'search results sorted' do
        let(:results_created) { described_class.new(nil, group, search: 'sorted', sort: sort).execute }
        let(:results_updated) { described_class.new(nil, group, search: 'updated', sort: sort).execute }
      end
    end
  end
end


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

module Issuable
  class DestroyLabelLinksService
    BATCH_SIZE = 100

    def initialize(target_id, target_type)
      @target_id = target_id
      @target_type = target_type
    end

    def execute
      inner_query =
        LabelLink
          .select(:id)
          .for_target(target_id, target_type)
          .limit(BATCH_SIZE)

      delete_query = <<~SQL
      DELETE FROM "#{LabelLink.table_name}"
      WHERE id IN (#{inner_query.to_sql})
      SQL

      loop do
        result = LabelLink.connection.execute(delete_query)

        break if result.cmd_tuples == 0
      end
    end

    private

    attr_reader :target_id, :target_type
  end
end

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Issuable::DestroyLabelLinksService, feature_category: :team_planning do
  describe '#execute' do
    context 'when target is an Issue' do
      let_it_be(:target) { create(:issue) }

      it_behaves_like 'service deleting label links of an issuable'
    end

    context 'when target is a MergeRequest' do
      let_it_be(:target) { create(:merge_request) }

      it_behaves_like 'service deleting label links of an issuable'
    end
  end
end


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

module Issuable
  class CommonSystemNotesService < ::BaseProjectService
    attr_reader :issuable

    def execute(issuable, old_labels: [], old_milestone: nil, is_update: true)
      @issuable = issuable

      # We disable touch so that created system notes do not update
      # the noteable's updated_at field
      ApplicationRecord.no_touching do
        if is_update
          if issuable.previous_changes.include?('title')
            create_title_change_note(issuable.previous_changes['title'].first)
          end

          handle_description_change_note

          handle_time_tracking_note if issuable.is_a?(TimeTrackable)
          create_discussion_lock_note if issuable.previous_changes.include?('discussion_locked')
        end

        handle_start_date_or_due_date_change_note
        create_milestone_change_event(old_milestone) if issuable.previous_changes.include?('milestone_id')
        create_labels_note(old_labels) if old_labels && issuable.labels != old_labels
      end
    end

    private

    def handle_start_date_or_due_date_change_note
      # Type check needed as some issuables do their own date change handling for date fields other than due_date
      change_date_fields = issuable.is_a?(Issue) ? %w[due_date start_date] : %w[due_date]
      changed_dates = issuable.previous_changes.slice(*change_date_fields)
      create_start_date_or_due_date_note(changed_dates)
    end

    def handle_time_tracking_note
      if issuable.previous_changes.include?('time_estimate')
        create_time_estimate_note
      end

      if issuable.time_spent?
        create_time_spent_note
      end
    end

    def handle_description_change_note
      if issuable.previous_changes.include?('description')
        if issuable.tasks? && issuable.updated_tasks.any?
          create_task_status_note
        else
          # TODO: Show this note if non-task content was modified.
          # https://gitlab.com/gitlab-org/gitlab-foss/issues/33577
          create_description_change_note
        end
      end
    end

    def create_draft_note(old_title)
      return unless issuable.is_a?(MergeRequest)

      if MergeRequest.draft?(old_title) != issuable.draft?
        SystemNoteService.handle_merge_request_draft(issuable, issuable.project, current_user)
      end
    end

    def create_labels_note(old_labels)
      added_labels = issuable.labels - old_labels
      removed_labels = old_labels - issuable.labels

      ResourceEvents::ChangeLabelsService
        .new(issuable, current_user)
        .execute(added_labels: added_labels, removed_labels: removed_labels)
    end

    def create_title_change_note(old_title)
      create_draft_note(old_title)

      if issuable.draftless_title_changed(old_title)
        SystemNoteService.change_title(issuable, issuable.project, current_user, old_title)
      end
    end

    def create_description_change_note
      SystemNoteService.change_description(issuable, issuable.project, current_user)
    end

    def create_task_status_note
      issuable.updated_tasks.each do |task|
        SystemNoteService.change_task_status(issuable, issuable.project, current_user, task)
      end
    end

    def create_time_estimate_note
      SystemNoteService.change_time_estimate(issuable, issuable.project, current_user)
    end

    def create_time_spent_note
      SystemNoteService.change_time_spent(issuable, issuable.project, issuable.time_spent_user)
    end

    def create_milestone_change_event(old_milestone)
      ResourceEvents::ChangeMilestoneService.new(issuable, current_user, old_milestone: old_milestone)
        .execute
    end

    def create_start_date_or_due_date_note(changed_dates)
      return if changed_dates.blank?

      SystemNoteService.change_start_date_or_due_date(issuable, issuable.project, current_user, changed_dates)
    end

    def create_discussion_lock_note
      SystemNoteService.discussion_lock(issuable, current_user)
    end
  end
end

Issuable::CommonSystemNotesService.prepend_mod_with('Issuable::CommonSystemNotesService')

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Issuable::CommonSystemNotesService, feature_category: :team_planning do
  let_it_be(:project) { create(:project) }
  let_it_be(:user) { create(:user) }

  let(:issuable) { create(:issue, project: project) }

  shared_examples 'system note for issuable date changes' do
    it 'creates a system note for due_date set' do
      issuable.update!(due_date: Date.today)

      expect { subject }.to change(issuable.notes, :count).from(0).to(1)
      expect(issuable.notes.last.note).to match('changed due date to')
    end

    it 'creates a system note for start_date set' do
      issuable.update!(start_date: Date.today)

      expect { subject }.to change(issuable.notes, :count).from(0).to(1)
      expect(issuable.notes.last.note).to match('changed start date to')
    end

    it 'creates a note when both start and due date are changed' do
      issuable.update!(start_date: Date.today, due_date: 1.week.from_now)

      expect { subject }.to change { issuable.notes.count }.from(0).to(1)
      expect(issuable.notes.last.note).to match(/changed start date to.+and changed due date to/)
    end

    it 'does not call SystemNoteService if no dates are changed' do
      issuable.update!(title: 'new title')

      expect(SystemNoteService).not_to receive(:change_start_date_or_due_date)

      subject
    end
  end

  context 'on issuable update' do
    it_behaves_like 'system note creation', { title: 'New title' }, 'changed title'
    it_behaves_like 'system note creation', { description: 'New description' }, 'changed the description'
    it_behaves_like 'system note creation', { discussion_locked: true }, 'locked the discussion in this issue'
    it_behaves_like 'system note creation', { time_estimate: 5 }, 'changed time estimate'

    context 'when new label is added' do
      let(:label) { create(:label, project: project) }

      before do
        issuable.labels << label
        issuable.save!
      end

      it 'creates a resource label event' do
        described_class.new(project: project, current_user: user).execute(issuable, old_labels: [])
        event = issuable.reload.resource_label_events.last

        expect(event).not_to be_nil
        expect(event.label_id).to eq label.id
        expect(event.user_id).to eq user.id
      end
    end

    context 'with merge requests Draft note' do
      context 'adding Draft note' do
        let(:issuable) { create(:merge_request, title: "merge request") }

        it_behaves_like 'system note creation', { title: "Draft: merge request" }, 'marked this merge request as **draft**'

        context 'and changing title' do
          before do
            issuable.update_attribute(:title, "Draft: changed title")
          end

          it_behaves_like 'draft notes creation', 'draft'
        end
      end

      context 'removing Draft note' do
        let(:issuable) { create(:merge_request, title: "Draft: merge request") }

        it_behaves_like 'system note creation', { title: "merge request" }, 'marked this merge request as **ready**'

        context 'and changing title' do
          before do
            issuable.update_attribute(:title, "changed title")
          end

          it_behaves_like 'draft notes creation', 'ready'
        end
      end
    end

    context 'when changing dates' do
      it_behaves_like 'system note for issuable date changes' do
        subject { described_class.new(project: project, current_user: user).execute(issuable) }
      end
    end
  end

  context 'on issuable create' do
    let(:issuable) { build(:issue, project: project) }

    subject { described_class.new(project: project, current_user: user).execute(issuable, old_labels: [], is_update: false) }

    it 'does not create system note for title and description' do
      issuable.save!

      expect { subject }.not_to change { issuable.notes.count }
    end

    it 'creates a resource label event for labels added' do
      label = create(:label, project: project)

      issuable.labels << label
      issuable.save!

      expect { subject }.to change { issuable.resource_label_events.count }.from(0).to(1)

      event = issuable.reload.resource_label_events.last

      expect(event).not_to be_nil
      expect(event.label_id).to eq label.id
      expect(event.user_id).to eq user.id
    end

    context 'when changing milestones' do
      let_it_be(:milestone) { create(:milestone, project: project) }
      let_it_be(:issuable) { create(:issue, project: project, milestone: milestone) }

      it 'does not create a system note for milestone set' do
        expect { subject }.not_to change { issuable.notes.count }
      end

      it 'creates a milestone change event' do
        expect { subject }.to change { ResourceMilestoneEvent.count }.from(0).to(1)
      end
    end

    context 'when changing dates' do
      it_behaves_like 'system note for issuable date changes'
    end
  end
end


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

module Issuable
  class DestroyService < IssuableBaseService
    # TODO: this is to be removed once we get to rename the IssuableBaseService project param to container
    def initialize(container:, current_user: nil, params: {})
      super(container: container, current_user: current_user, params: params)
    end

    def execute(issuable)
      before_destroy(issuable)
      after_destroy(issuable) if issuable.destroy
    end

    private

    # overriden in EE
    def before_destroy(issuable); end

    def after_destroy(issuable)
      delete_associated_records(issuable)
      issuable.update_project_counter_caches
      issuable.assignees.each(&:invalidate_cache_counts)
    end

    def group_for(issuable)
      issuable.resource_parent.group
    end

    def delete_associated_records(issuable)
      actor = group_for(issuable)

      delete_todos(actor, issuable)
      delete_label_links(actor, issuable)
    end

    def delete_todos(actor, issuable)
      issuable.run_after_commit_or_now do
        TodosDestroyer::DestroyedIssuableWorker.perform_async(issuable.id, issuable.class.name)
      end
    end

    def delete_label_links(actor, issuable)
      issuable.run_after_commit_or_now do
        Issuable::LabelLinksDestroyWorker.perform_async(issuable.id, issuable.class.name)
      end
    end
  end
end

Issuable::DestroyService.prepend_mod_with('Issuable::DestroyService')

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Issuable::DestroyService, feature_category: :team_planning do
  let(:user) { create(:user) }
  let(:group) { create(:group, :public) }
  let(:project) { create(:project, :public, group: group) }

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

  describe '#execute' do
    context 'when issuable is an issue' do
      let!(:issue) { create(:issue, project: project, author: user, assignees: [user]) }

      it 'destroys the issue' do
        expect { service.execute(issue) }.to change { project.issues.count }.by(-1)
      end

      it 'updates open issues count cache' do
        expect_any_instance_of(Projects::OpenIssuesCountService).to receive(:refresh_cache)

        service.execute(issue)
      end

      it 'invalidates the issues count cache for the assignees' do
        expect_any_instance_of(User).to receive(:invalidate_cache_counts).once
        service.execute(issue)
      end

      it_behaves_like 'service deleting todos' do
        let(:issuable) { issue }
      end

      it_behaves_like 'service deleting label links' do
        let(:issuable) { issue }
      end
    end

    context 'when issuable is a merge request' do
      let!(:merge_request) { create(:merge_request, target_project: project, source_project: project, author: user, assignees: [user]) }

      it 'destroys the merge request' do
        expect { service.execute(merge_request) }.to change { project.merge_requests.count }.by(-1)
      end

      it 'updates open merge requests count cache' do
        expect_any_instance_of(Projects::OpenMergeRequestsCountService).to receive(:refresh_cache)

        service.execute(merge_request)
      end

      it 'invalidates the merge request caches for the MR assignee' do
        expect_any_instance_of(User).to receive(:invalidate_cache_counts).once
        service.execute(merge_request)
      end

      it_behaves_like 'service deleting todos' do
        let(:issuable) { merge_request }
      end

      it_behaves_like 'service deleting label links' do
        let(:issuable) { merge_request }
      end
    end
  end
end


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

# This service return notes grouped by discussion ID and paginated per discussion.
# System notes also have a discussion ID assigned including Synthetic system notes.
module Issuable
  class DiscussionsListService
    include Gitlab::Utils::StrongMemoize

    attr_reader :current_user, :issuable, :params

    def initialize(current_user, issuable, params = {})
      @current_user = current_user
      @issuable = issuable
      @params = params.dup
    end

    def execute
      return Note.none unless can_read_issuable_notes?

      notes = NotesFinder.new(current_user, params.merge({ target: issuable, project: issuable.project }))
                .execute.with_web_entity_associations.inc_relations_for_view(issuable).fresh

      if paginator
        paginated_discussions_by_type = paginator.records.group_by(&:table_name)

        notes = if paginated_discussions_by_type['notes'].present?
                  notes.id_in(paginated_discussions_by_type['notes'].flat_map(&:ids))
                else
                  notes.none
                end
      end

      if params[:notes_filter] != UserPreference::NOTES_FILTERS[:only_comments]
        notes = ResourceEvents::MergeIntoNotesService.new(
          issuable, current_user, paginated_notes: paginated_discussions_by_type
        ).execute(notes)
      end

      # Here we assume all notes belong to the same project as the work item
      project = notes.first&.project
      notes = ::Preloaders::Projects::NotesPreloader.new(project, current_user).call(notes)

      # we need to check the permission on every note, because some system notes for instance can have references to
      # resources that some user do not have read access, so those notes are filtered out from the list of notes.
      # see Note#all_referenced_mentionables_allowed?
      notes = notes.select { |n| n.readable_by?(current_user) }

      Discussion.build_collection(notes, issuable)
    end

    def paginator
      return if params[:per_page].blank?

      strong_memoize(:paginator) do
        issuable
          .discussion_root_note_ids(notes_filter: params[:notes_filter])
          .keyset_paginate(cursor: params[:cursor], per_page: params[:per_page].to_i)
      end
    end

    def can_read_issuable_notes?
      return Ability.allowed?(current_user, :read_security_resource, issuable) if issuable.is_a?(Vulnerability)

      Ability.allowed?(current_user, :"read_#{issuable.to_ability_name}", issuable) &&
        Ability.allowed?(current_user, :read_note, issuable)
    end
  end
end

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Issuable::DiscussionsListService, feature_category: :team_planning do
  let_it_be(:current_user) { create(:user) }
  let_it_be(:group) { create(:group, :private) }
  let_it_be(:project) { create(:project, :repository, :private, group: group) }
  let_it_be(:milestone) { create(:milestone, project: project) }
  let_it_be(:label) { create(:label, project: project) }
  let_it_be(:label_2) { create(:label, project: project) }

  let(:finder_params_for_issuable) { {} }

  subject(:discussions_service) { described_class.new(current_user, issuable, finder_params_for_issuable) }

  describe 'fetching notes for issue' do
    let_it_be(:issuable) { create(:issue, project: project) }

    it_behaves_like 'listing issuable discussions', :guest, 1, 7

    context 'without notes widget' do
      let_it_be(:issuable) { create(:work_item, project: project) }

      before do
        WorkItems::Type.default_by_type(:issue).widget_definitions.find_by_widget_type(:notes).update!(disabled: true)
      end

      it "returns no notes" do
        expect(discussions_service.execute).to be_empty
      end
    end

    context 'when issue exists at the group level' do
      let_it_be(:issuable) { create(:issue, :group_level, namespace: group) }

      it_behaves_like 'listing issuable discussions', :guest, 1, 7
    end
  end

  describe 'fetching notes for merge requests' do
    let_it_be(:issuable) { create(:merge_request, source_project: project, target_project: project) }

    it_behaves_like 'listing issuable discussions', :reporter, 0, 6
  end
end


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

# This follows the rules specified in the specs.
# See spec/requests/api/graphql/mutations/merge_requests/set_assignees_spec.rb

module Issuable
  class ProcessAssignees
    def initialize(assignee_ids:, add_assignee_ids:, remove_assignee_ids:, existing_assignee_ids: nil, extra_assignee_ids: nil)
      @assignee_ids = assignee_ids&.map(&:to_i)
      @add_assignee_ids = add_assignee_ids&.map(&:to_i)
      @remove_assignee_ids = remove_assignee_ids&.map(&:to_i)
      @existing_assignee_ids = existing_assignee_ids&.map(&:to_i) || []
      @extra_assignee_ids = extra_assignee_ids&.map(&:to_i) || []
    end

    def execute
      updated_new_assignees = new_assignee_ids

      if add_assignee_ids.blank? && remove_assignee_ids.blank?
        updated_new_assignees = assignee_ids if assignee_ids
      else
        updated_new_assignees |= add_assignee_ids if add_assignee_ids
        updated_new_assignees -= remove_assignee_ids if remove_assignee_ids
      end

      updated_new_assignees.uniq
    end

    private

    attr_accessor :assignee_ids, :add_assignee_ids, :remove_assignee_ids, :existing_assignee_ids, :extra_assignee_ids

    def new_assignee_ids
      existing_assignee_ids | extra_assignee_ids
    end
  end
end

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Issuable::ProcessAssignees, feature_category: :team_planning do
  describe '#execute' do
    it 'returns assignee_ids when add_assignee_ids and remove_assignee_ids are not specified' do
      process = described_class.new(
        assignee_ids: %w[5 7 9],
        add_assignee_ids: nil,
        remove_assignee_ids: nil,
        existing_assignee_ids: %w[1 3 9],
        extra_assignee_ids: %w[2 5 12]
      )
      result = process.execute

      expect(result).to contain_exactly(5, 7, 9)
    end

    it 'combines other ids when assignee_ids is nil' do
      process = described_class.new(
        assignee_ids: nil,
        add_assignee_ids: nil,
        remove_assignee_ids: nil,
        existing_assignee_ids: %w[1 3 11],
        extra_assignee_ids: %w[2 5 12]
      )
      result = process.execute

      expect(result).to contain_exactly(1, 2, 3, 5, 11, 12)
    end

    it 'combines other ids when both add_assignee_ids and remove_assignee_ids are not empty' do
      process = described_class.new(
        assignee_ids: %w[5 7 9],
        add_assignee_ids: %w[2 4 6],
        remove_assignee_ids: %w[4 7 11],
        existing_assignee_ids: %w[1 3 11],
        extra_assignee_ids: %w[2 5 12]
      )
      result = process.execute

      expect(result).to contain_exactly(1, 2, 3, 5, 6, 12)
    end

    it 'combines other ids when remove_assignee_ids is not empty' do
      process = described_class.new(
        assignee_ids: %w[5 7 9],
        add_assignee_ids: nil,
        remove_assignee_ids: %w[4 7 11],
        existing_assignee_ids: %w[1 3 11],
        extra_assignee_ids: %w[2 5 12]
      )
      result = process.execute

      expect(result).to contain_exactly(1, 2, 3, 5, 12)
    end

    it 'combines other ids when add_assignee_ids is not empty' do
      process = described_class.new(
        assignee_ids: %w[5 7 9],
        add_assignee_ids: %w[2 4 6],
        remove_assignee_ids: nil,
        existing_assignee_ids: %w[1 3 11],
        extra_assignee_ids: %w[2 5 12]
      )
      result = process.execute

      expect(result).to contain_exactly(1, 2, 4, 3, 5, 6, 11, 12)
    end

    it 'combines ids when existing_assignee_ids and extra_assignee_ids are omitted' do
      process = described_class.new(
        assignee_ids: %w[5 7 9],
        add_assignee_ids: %w[2 4 6],
        remove_assignee_ids: %w[4 7 11]
      )
      result = process.execute

      expect(result.sort).to eq([2, 6].sort)
    end

    it 'handles mixed string and integer arrays' do
      process = described_class.new(
        assignee_ids: %w[5 7 9],
        add_assignee_ids: [2, 4, 6],
        remove_assignee_ids: %w[4 7 11],
        existing_assignee_ids: [1, 3, 11],
        extra_assignee_ids: %w[2 5 12]
      )
      result = process.execute

      expect(result).to contain_exactly(1, 2, 3, 5, 6, 12)
    end
  end
end


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

module Issuable
  class BulkUpdateService
    include Gitlab::Allowable

    attr_accessor :parent, :current_user, :params

    def initialize(parent, user = nil, params = {})
      @parent = parent
      @current_user = user
      @params = params.dup
    end

    def execute(type)
      model_ids = ids_from_params(params.delete(:issuable_ids))
      set_update_params(type)
      updated_issuables = update_issuables(type, model_ids)

      if updated_issuables.present? && requires_count_cache_reset?(type)
        schedule_group_issues_count_reset(updated_issuables)
      end

      response_success(payload: { count: updated_issuables.size })
    rescue ArgumentError => e
      response_error(e.message, 422)
    end

    private

    def ids_from_params(issuable_ids)
      return issuable_ids if issuable_ids.is_a?(Array)

      issuable_ids.split(',')
    end

    def set_update_params(type)
      params.slice!(*permitted_attrs(type))

      if params[:assignee_ids] == [IssuableFinder::Params::NONE.to_s]
        params[:assignee_ids] = []
      end
    end

    def permitted_attrs(type)
      attrs = %i[state_event milestone_id add_label_ids remove_label_ids subscription_event]

      if type == 'issue'
        attrs.push(:assignee_ids, :confidential)
      elsif type == 'merge_request'
        attrs.push(:assignee_ids)
      else
        attrs.push(:assignee_id)
      end
    end

    def update_issuables(type, ids)
      model_class = type.classify.constantize
      update_class = type.classify.pluralize.constantize::UpdateService
      items = find_issuables(parent, model_class, ids)
      authorized_issuables = []

      items.each do |issuable|
        next unless can?(current_user, :"update_#{type}", issuable)

        authorized_issuables << issuable
        update_class.new(
          **update_class.constructor_container_arg(issuable.issuing_parent),
          current_user: current_user,
          params: dup_params
        ).execute(issuable)
      end

      authorized_issuables
    end

    def find_issuables(parent, model_class, ids)
      issuables = model_class.id_in(ids)

      case parent
      when Project
        issuables = issuables.of_projects(parent)
      when Group
        issuables = issuables.of_projects(parent.all_projects)
      else
        raise ArgumentError, _('A parent must be provided when bulk updating issuables')
      end

      issuables.includes_for_bulk_update
    end

    # Duplicates params and its top-level values
    # We cannot use deep_dup because ActiveRecord objects will result
    # to new records with no id assigned
    def dup_params
      dup = HashWithIndifferentAccess.new

      params.each do |key, value|
        dup[key] = value.is_a?(ActiveRecord::Base) ? value : value.dup
      end

      dup
    end

    def response_success(message: nil, payload: nil)
      ServiceResponse.success(message: message, payload: payload)
    end

    def response_error(message, http_status)
      ServiceResponse.error(message: message, http_status: http_status)
    end

    def requires_count_cache_reset?(type)
      type.to_sym == :issue && params.include?(:state_event)
    end

    def schedule_group_issues_count_reset(updated_issuables)
      group_ids = updated_issuables.map(&:project).map(&:namespace_id)
      return if group_ids.empty?

      Issuables::ClearGroupsIssueCounterWorker.perform_async(group_ids)
    end
  end
end

Issuable::BulkUpdateService.prepend_mod_with('Issuable::BulkUpdateService')

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Issuable::BulkUpdateService, feature_category: :team_planning do
  let_it_be(:user)    { create(:user) }
  let_it_be(:project) { create(:project, :repository, namespace: user.namespace) }

  def bulk_update(issuables, extra_params = {})
    bulk_update_params = extra_params
      .reverse_merge(issuable_ids: Array(issuables).map(&:id).join(','))

    type = Array(issuables).first.model_name.param_key
    Issuable::BulkUpdateService.new(parent, user, bulk_update_params).execute(type)
  end

  shared_examples 'updates milestones' do
    it 'succeeds' do
      result = bulk_update(issuables, milestone_id: milestone.id)

      expect(result.success?).to be_truthy
      expect(result.payload[:count]).to eq(issuables.count)
    end

    it 'updates the issuables milestone' do
      bulk_update(issuables, milestone_id: milestone.id)

      issuables.each do |issuable|
        expect(issuable.reload.milestone).to eq(milestone)
      end
    end
  end

  shared_examples 'updates confidentiality' do
    it 'succeeds' do
      result = bulk_update(issuables, confidential: true)

      expect(result.success?).to be_truthy
      expect(result.payload[:count]).to eq(issuables.count)
    end

    it 'updates the issuables confidentiality' do
      bulk_update(issuables, confidential: true)

      issuables.each do |issuable|
        expect(issuable.reload.confidential).to be(true)
      end
    end
  end

  shared_examples 'updating labels' do
    def create_issue_with_labels(labels)
      create(:labeled_issue, project: project, labels: labels)
    end

    let(:issue_all_labels) { create_issue_with_labels([bug, regression, merge_requests]) }
    let(:issue_bug_and_regression) { create_issue_with_labels([bug, regression]) }
    let(:issue_bug_and_merge_requests) { create_issue_with_labels([bug, merge_requests]) }
    let(:issue_no_labels) { create(:issue, project: project) }
    let(:issues) { [issue_all_labels, issue_bug_and_regression, issue_bug_and_merge_requests, issue_no_labels] }

    let(:add_labels) { [] }
    let(:remove_labels) { [] }

    let(:bulk_update_params) do
      {
        add_label_ids: add_labels.map(&:id),
        remove_label_ids: remove_labels.map(&:id)
      }
    end

    before do
      bulk_update(issues, bulk_update_params)
    end

    context 'when add_label_ids are passed' do
      let(:issues) { [issue_all_labels, issue_bug_and_merge_requests, issue_no_labels] }
      let(:add_labels) { [bug, regression, merge_requests] }

      it 'adds those label IDs to all issues passed' do
        expect(issues.map(&:reload).map(&:label_ids)).to all(include(*add_labels.map(&:id)))
      end

      it 'does not update issues not passed in' do
        expect(issue_bug_and_regression.label_ids).to contain_exactly(bug.id, regression.id)
      end
    end

    context 'when remove_label_ids are passed' do
      let(:issues) { [issue_all_labels, issue_bug_and_merge_requests, issue_no_labels] }
      let(:remove_labels) { [bug, regression, merge_requests] }

      it 'removes those label IDs from all issues passed' do
        expect(issues.map(&:reload).map(&:label_ids)).to all(be_empty)
      end

      it 'does not update issues not passed in' do
        expect(issue_bug_and_regression.label_ids).to contain_exactly(bug.id, regression.id)
      end
    end

    context 'when add_label_ids and remove_label_ids are passed' do
      let(:issues) { [issue_all_labels, issue_bug_and_merge_requests, issue_no_labels] }
      let(:add_labels) { [bug] }
      let(:remove_labels) { [merge_requests] }

      it 'adds the label IDs to all issues passed' do
        expect(issues.map(&:reload).map(&:label_ids)).to all(include(bug.id))
      end

      it 'removes the label IDs from all issues passed' do
        expect(issues.map(&:reload).flat_map(&:label_ids)).not_to include(merge_requests.id)
      end

      it 'does not update issues not passed in' do
        expect(issue_bug_and_regression.label_ids).to contain_exactly(bug.id, regression.id)
      end
    end
  end

  shared_examples 'scheduling cached group count clear' do
    it 'schedules worker' do
      expect(Issuables::ClearGroupsIssueCounterWorker).to receive(:perform_async)

      bulk_update(issuables, params)
    end
  end

  shared_examples 'not scheduling cached group count clear' do
    it 'does not schedule worker' do
      expect(Issuables::ClearGroupsIssueCounterWorker).not_to receive(:perform_async)

      bulk_update(issuables, params)
    end
  end

  shared_examples 'bulk update service' do
    it 'result count only includes authorized issuables' do
      all_issues = issues + [create(:issue, project: create(:project, :private))]
      result = bulk_update(all_issues, { assignee_ids: [user.id] })

      expect(result[:count]).to eq(issues.count)
    end

    context 'when issuable_ids are passed as an array' do
      it 'updates assignees' do
        expect do
          described_class.new(
            parent,
            user,
            { issuable_ids: issues.map(&:id), assignee_ids: [user.id] }
          ).execute('issue')

          issues.each(&:reset)
        end.to change { issues.flat_map(&:assignee_ids) }.from([]).to([user.id] * 2)
      end
    end
  end

  context 'with issuables at a project level' do
    let_it_be_with_reload(:issues) { create_list(:issue, 2, project: project) }

    let(:parent) { project }

    it_behaves_like 'bulk update service'

    context 'with unpermitted attributes' do
      let(:label) { create(:label, project: project) }

      it 'does not update the issues' do
        bulk_update(issues, label_ids: [label.id])

        expect(issues.map(&:reload).map(&:label_ids)).not_to include(label.id)
      end
    end

    context 'when issuable update service raises an ArgumentError' do
      before do
        allow_next_instance_of(Issues::UpdateService) do |update_service|
          allow(update_service).to receive(:execute).and_raise(ArgumentError, 'update error')
        end
      end

      it 'returns an error response' do
        result = bulk_update(issues, add_label_ids: [])

        expect(result).to be_error
        expect(result.errors).to contain_exactly('update error')
        expect(result.http_status).to eq(422)
      end
    end

    describe 'close issues' do
      it 'succeeds and returns the correct number of issues updated' do
        result = bulk_update(issues, state_event: 'close')

        expect(result.success?).to be_truthy
        expect(result.payload[:count]).to eq(issues.count)
      end

      it 'closes all the issues passed' do
        bulk_update(issues, state_event: 'close')

        expect(project.issues.opened).to be_empty
        expect(project.issues.closed).not_to be_empty
      end

      it_behaves_like 'scheduling cached group count clear' do
        let(:issuables) { issues }
        let(:params) { { state_event: 'close' } }
      end
    end

    describe 'reopen issues' do
      let_it_be_with_reload(:closed_issues) { create_list(:closed_issue, 2, project: project) }

      it 'succeeds and returns the correct number of issues updated' do
        result = bulk_update(closed_issues, state_event: 'reopen')

        expect(result.success?).to be_truthy
        expect(result.payload[:count]).to eq(closed_issues.count)
      end

      it 'reopens all the issues passed' do
        bulk_update(closed_issues, state_event: 'reopen')

        expect(project.issues.closed).to be_empty
        expect(project.issues.opened).not_to be_empty
      end

      it_behaves_like 'scheduling cached group count clear' do
        let(:issuables) { closed_issues }
        let(:params) { { state_event: 'reopen' } }
      end
    end

    describe 'updating merge request assignee' do
      let(:merge_request) { create(:merge_request, target_project: project, source_project: project, assignees: [user]) }

      context 'when the new assignee ID is a valid user' do
        it 'succeeds' do
          new_assignee = create(:user)
          project.add_developer(new_assignee)

          result = bulk_update(merge_request, assignee_ids: [user.id, new_assignee.id])

          expect(result.success?).to be_truthy
          expect(result.payload[:count]).to eq(1)
        end

        it 'updates the assignee to the user ID passed' do
          assignee = create(:user)
          project.add_developer(assignee)

          expect { bulk_update(merge_request, assignee_ids: [assignee.id]) }
            .to change { merge_request.reload.assignee_ids }.from([user.id]).to([assignee.id])
        end
      end

      context "when the new assignee ID is #{IssuableFinder::Params::NONE}" do
        it 'unassigns the issues' do
          expect { bulk_update(merge_request, assignee_ids: [IssuableFinder::Params::NONE]) }
            .to change { merge_request.reload.assignee_ids }.to([])
        end
      end

      context 'when the new assignee IDs array is empty' do
        it 'removes all assignees' do
          expect { bulk_update(merge_request, assignee_ids: []) }
            .to change(merge_request.assignees, :count).by(-1)
        end
      end
    end

    describe 'updating issue assignee' do
      let(:issue) { create(:issue, project: project, assignees: [user]) }

      context 'when the new assignee ID is a valid user' do
        it 'succeeds' do
          new_assignee = create(:user)
          project.add_developer(new_assignee)

          result = bulk_update(issue, assignee_ids: [new_assignee.id])

          expect(result.success?).to be_truthy
          expect(result.payload[:count]).to eq(1)
        end

        it 'updates the assignee to the user ID passed' do
          assignee = create(:user)
          project.add_developer(assignee)
          expect { bulk_update(issue, assignee_ids: [assignee.id]) }
            .to change { issue.reload.assignees.first }.from(user).to(assignee)
        end
      end

      context "when the new assignee ID is #{IssuableFinder::Params::NONE}" do
        it "unassigns the issues" do
          expect { bulk_update(issue, assignee_ids: [IssuableFinder::Params::NONE.to_s]) }
            .to change { issue.reload.assignees.count }.from(1).to(0)
        end
      end

      context 'when the new assignee IDs array is empty' do
        it 'removes all assignees' do
          expect { bulk_update(issue, assignee_ids: []) }
            .to change(issue.assignees, :count).by(-1)
        end
      end
    end

    describe 'updating milestones' do
      let(:issuables) { [create(:issue, project: project)] }
      let(:milestone) { create(:milestone, project: project) }

      it_behaves_like 'updates milestones'

      it_behaves_like 'not scheduling cached group count clear' do
        let(:params) { { milestone_id: milestone.id } }
      end
    end

    describe 'updating confidentiality' do
      let(:issuables) { create_list(:issue, 2, project: project) }

      it_behaves_like 'updates confidentiality'

      it_behaves_like 'not scheduling cached group count clear' do
        let(:params) { { confidential: true } }
      end
    end

    describe 'updating labels' do
      let(:bug) { create(:label, project: project) }
      let(:regression) { create(:label, project: project) }
      let(:merge_requests) { create(:label, project: project) }

      it_behaves_like 'updating labels'
    end

    describe 'subscribe to issues' do
      let(:issues) { create_list(:issue, 2, project: project) }

      it 'subscribes the given user' do
        bulk_update(issues, subscription_event: 'subscribe')

        expect(issues).to all(be_subscribed(user, project))
      end
    end

    describe 'unsubscribe from issues' do
      let(:issues) do
        create_list(:closed_issue, 2, project: project) do |issue|
          issue.subscriptions.create!(user: user, project: project, subscribed: true)
        end
      end

      it 'unsubscribes the given user' do
        bulk_update(issues, subscription_event: 'unsubscribe')

        issues.each do |issue|
          expect(issue).not_to be_subscribed(user, project)
        end
      end
    end

    describe 'updating issues from external project' do
      it 'updates only issues that belong to the parent project' do
        issue1 = create(:issue, project: project)
        issue2 = create(:issue, project: create(:project))
        result = bulk_update([issue1, issue2], assignee_ids: [user.id])

        expect(result.success?).to be_truthy
        expect(result.payload[:count]).to eq(1)

        expect(issue1.reload.assignees).to eq([user])
        expect(issue2.reload.assignees).to be_empty
      end
    end
  end

  context 'with issuables at a group level' do
    let_it_be(:group) { create(:group) }

    let(:parent) { group }

    before do
      group.add_reporter(user)
    end

    it_behaves_like 'bulk update service' do
      let_it_be_with_reload(:issues) { create_list(:issue, 2, project: create(:project, group: group)) }
    end

    describe 'updating milestones' do
      let(:milestone) { create(:milestone, group: group) }
      let(:project)   { create(:project, :repository, group: group) }

      before do
        group.add_maintainer(user)
      end

      context 'when issues' do
        let(:issue1)    { create(:issue, project: project) }
        let(:issue2)    { create(:issue, project: project) }
        let(:issuables) { [issue1, issue2] }

        it_behaves_like 'updates milestones'
      end

      context 'when merge requests' do
        let(:merge_request1) { create(:merge_request, source_project: project, source_branch: 'branch-1') }
        let(:merge_request2) { create(:merge_request, source_project: project, source_branch: 'branch-2') }
        let(:issuables)      { [merge_request1, merge_request2] }

        it_behaves_like 'updates milestones'
      end
    end

    describe 'updating confidentiality' do
      let_it_be(:project) { create(:project, :repository, group: group) }

      before do
        group.add_maintainer(user)
      end

      context 'with issues' do
        let(:issuables) { create_list(:issue, 2, project: project) }

        it_behaves_like 'updates confidentiality'
      end

      context 'with merge requests' do
        let(:issuables) { [create(:merge_request, source_project: project, target_project: project)] }

        it 'does not throw an error' do
          result = bulk_update(issuables, confidential: true)

          expect(result.success?).to be_truthy
        end
      end
    end

    describe 'updating labels' do
      let(:project)        { create(:project, :repository, group: group) }
      let(:bug)            { create(:group_label, group: group) }
      let(:regression)     { create(:group_label, group: group) }
      let(:merge_requests) { create(:group_label, group: group) }

      it_behaves_like 'updating labels'
    end

    describe 'with issues from external group' do
      it 'updates issues that belong to the parent group or descendants' do
        issue1 = create(:issue, project: create(:project, group: group))
        issue2 = create(:issue, project: create(:project, group: create(:group)))
        issue3 = create(:issue, project: create(:project, group: create(:group, parent: group)))
        milestone = create(:milestone, group: group)
        result = bulk_update([issue1, issue2, issue3], milestone_id: milestone.id)

        expect(result.success?).to be_truthy
        expect(result.payload[:count]).to eq(2)

        expect(issue1.reload.milestone).to eq(milestone)
        expect(issue2.reload.milestone).to be_nil
        expect(issue3.reload.milestone).to eq(milestone)
      end
    end
  end

  context 'when no parent is provided' do
    it 'returns an unscoped update error' do
      result = described_class.new(nil, user, { assignee_ids: [user.id], issuable_ids: [] }).execute('issue')

      expect(result).to be_error
      expect(result.errors).to contain_exactly(_('A parent must be provided when bulk updating issuables'))
    end
  end
end