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

# RepositoryArchiveCleanUpService removes cached repository archives
# that are generated on-the-fly by Gitaly. These files are stored in the
# following form (as defined in lib/gitlab/git/repository.rb) and served
# by GitLab Workhorse:
#
# /path/to/repository/downloads/project-N/sha/@v2/archive.format
#
# Legacy paths omit the @v2 prefix.
#
# For example:
#
# /var/opt/gitlab/gitlab-rails/shared/cache/archive/project-1/master/@v2/archive.zip
class RepositoryArchiveCleanUpService
  LAST_MODIFIED_TIME_IN_MINUTES = 120

  # For `/path/project-N/sha/@v2/archive.zip`, `find /path -maxdepth 4` will find this file
  MAX_ARCHIVE_DEPTH = 4

  attr_reader :mmin, :path

  def initialize(mmin = LAST_MODIFIED_TIME_IN_MINUTES)
    @mmin = mmin
    @path = Gitlab.config.gitlab.repository_downloads_path
  end

  def execute
    Gitlab::Metrics.measure(:repository_archive_clean_up) do
      next unless File.directory?(path)

      clean_up_old_archives
      clean_up_empty_directories
    end
  end

  private

  def clean_up_old_archives
    run(%W[find #{path} -mindepth 1 -maxdepth #{MAX_ARCHIVE_DEPTH} -type f \( -name \*.tar -o -name \*.bz2 -o -name \*.tar.gz -o -name \*.zip \) -mmin +#{mmin} -delete])
  end

  def clean_up_empty_directories
    (1...MAX_ARCHIVE_DEPTH).reverse_each { |depth| clean_up_empty_directories_with_depth(depth) }
  end

  def clean_up_empty_directories_with_depth(depth)
    run(%W[find #{path} -mindepth #{depth} -maxdepth #{depth} -type d -empty -delete])
  end

  def run(cmd)
    Gitlab::Popen.popen(cmd)
  end
end

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe RepositoryArchiveCleanUpService, feature_category: :source_code_management do
  subject(:service) { described_class.new }

  describe '#execute (new archive locations)' do
    let(:sha) { "0" * 40 }

    it 'removes outdated archives and directories in a new-style path' do
      in_directory_with_files("project-#{non_existing_record_id}/#{sha}", %w[tar tar.bz2 tar.gz zip], 3.hours) do |dirname, files|
        service.execute

        files.each { |filename| expect(File.exist?(filename)).to be_falsy }
        expect(File.directory?(dirname)).to be_falsy
        expect(File.directory?(File.dirname(dirname))).to be_falsy
      end
    end

    it 'removes outdated archives and directories in a versioned path' do
      in_directory_with_files("project-#{non_existing_record_id}/#{sha}/@v2", %w[tar tar.bz2 tar.gz zip], 3.hours) do |dirname, files|
        service.execute

        files.each { |filename| expect(File.exist?(filename)).to be_falsy }
        expect(File.directory?(dirname)).to be_falsy
        expect(File.directory?(File.dirname(dirname))).to be_falsy
      end
    end

    it 'does not remove directories when they contain outdated non-archives' do
      in_directory_with_files("project-#{non_existing_record_id}/#{sha}", %w[tar conf rb], 2.hours) do |dirname, files|
        service.execute

        expect(File.directory?(dirname)).to be_truthy
      end
    end

    it 'does not remove in-date archives in a new-style path' do
      in_directory_with_files("project-#{non_existing_record_id}/#{sha}", %w[tar tar.bz2 tar.gz zip], 1.hour) do |dirname, files|
        service.execute

        files.each { |filename| expect(File.exist?(filename)).to be_truthy }
      end
    end
  end

  describe '#execute (legacy archive locations)' do
    context 'when the downloads directory does not exist' do
      it 'does not remove any archives' do
        path = '/invalid/path/'
        stub_repository_downloads_path(path)

        allow(File).to receive(:directory?).and_call_original
        expect(File).to receive(:directory?).with(path).and_return(false)

        expect(service).not_to receive(:clean_up_old_archives)
        expect(service).not_to receive(:clean_up_empty_directories)

        service.execute
      end
    end

    context 'when the downloads directory exists' do
      shared_examples 'invalid archive files' do |dirname, extensions, mtime|
        it 'does not remove files and directory' do
          in_directory_with_files(dirname, extensions, mtime) do |dir, files|
            service.execute

            files.each { |file| expect(File.exist?(file)).to eq true }
            expect(File.directory?(dir)).to eq true
          end
        end
      end

      it 'removes files older than 2 hours that matches valid archive extensions' do
        # In macOS, the the `mmin` parameter for `find` rounds up, so add a full
        # minute to ensure these files are deemed old.
        in_directory_with_files('sample.git', %w[tar tar.bz2 tar.gz zip], 121.minutes) do |dir, files|
          service.execute

          files.each { |file| expect(File.exist?(file)).to eq false }
          expect(File.directory?(dir)).to eq false
        end
      end

      context 'with files older than 2 hours that does not matches valid archive extensions' do
        it_behaves_like 'invalid archive files', 'sample.git', %w[conf rb], 121.minutes
      end

      context 'with files older than 2 hours inside invalid directories' do
        it_behaves_like 'invalid archive files', 'john/t/doe/sample.git', %w[conf rb tar tar.gz], 121.minutes
      end

      context 'with files newer than 2 hours that matches valid archive extensions' do
        it_behaves_like 'invalid archive files', 'sample.git', %w[tar tar.bz2 tar.gz zip], 1.hour
      end

      context 'with files newer than 2 hours that does not matches valid archive extensions' do
        it_behaves_like 'invalid archive files', 'sample.git', %w[conf rb], 1.hour
      end

      context 'with files newer than 2 hours inside invalid directories' do
        it_behaves_like 'invalid archive files', 'sample.git', %w[conf rb tar tar.gz], 1.hour
      end
    end
  end

  def in_directory_with_files(dirname, extensions, mtime)
    Dir.mktmpdir do |tmpdir|
      stub_repository_downloads_path(tmpdir)
      dir = File.join(tmpdir, dirname)
      files = create_temporary_files(dir, extensions, mtime)

      yield(dir, files)
    end
  end

  def stub_repository_downloads_path(path)
    allow(Gitlab.config.gitlab).to receive(:repository_downloads_path).and_return(path)
  end

  def create_temporary_files(dir, extensions, mtime)
    FileUtils.mkdir_p(dir)
    FileUtils.touch(extensions.map { |ext| File.join(dir, "sample.#{ext}") }, mtime: Time.now.utc - mtime)
  end
end


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

class ImportExportCleanUpService
  LAST_MODIFIED_TIME_IN_MINUTES = 1440
  DIR_DEPTH = 5

  attr_reader :mmin, :path

  def initialize(mmin = LAST_MODIFIED_TIME_IN_MINUTES)
    @mmin = mmin
    @path = Gitlab::ImportExport.storage_path
  end

  def execute
    Gitlab::Metrics.measure(:import_export_clean_up) do
      execute_cleanup
    end
  end

  private

  def execute_cleanup
    clean_up_export_object_files
  ensure
    # We don't want a failure in cleaning up object storage from
    # blocking us from cleaning up temporary storage.
    clean_up_export_files if File.directory?(path)
  end

  def clean_up_export_files
    old_directories do |dir|
      FileUtils.remove_entry(dir)

      logger.info(
        message: 'Removed Import/Export tmp directory',
        dir_path: dir
      )
    end
  end

  def clean_up_export_object_files
    ImportExportUpload.with_export_file.updated_before(mmin.minutes.ago).each do |upload|
      upload.remove_export_file!
      upload.save!

      logger.info(
        message: 'Removed Import/Export export_file',
        project_id: upload.project_id,
        group_id: upload.group_id
      )
    end
  end

  def old_directories
    IO.popen(directories_cmd) do |find|
      find.each_line(chomp: true) do |directory|
        yield directory
      end
    end
  end

  def directories_cmd
    %W[find #{path} -mindepth #{DIR_DEPTH} -maxdepth #{DIR_DEPTH} -type d -not -path #{path} -mmin +#{mmin}]
  end

  def logger
    @logger ||= Gitlab::Import::Logger.build
  end
end

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe ImportExportCleanUpService, feature_category: :importers do
  describe '#execute' do
    let(:service) { described_class.new }

    let(:tmp_import_export_folder) { 'tmp/gitlab_exports' }

    before do
      allow_next_instance_of(Gitlab::Import::Logger) do |logger|
        allow(logger).to receive(:info)
      end
    end

    context 'when the import/export tmp storage directory does not exist' do
      it 'does not remove any archives' do
        path = '/invalid/path/'
        stub_repository_downloads_path(path)

        expect(service).not_to receive(:clean_up_export_files)

        service.execute
      end
    end

    context 'when the import/export tmp storage directory exists' do
      shared_examples 'removes old tmp files' do |subdir|
        it 'removes old files and logs' do
          expect_next_instance_of(Gitlab::Import::Logger) do |logger|
            expect(logger)
              .to receive(:info)
              .with(
                message: 'Removed Import/Export tmp directory',
                dir_path: anything
              )
          end

          validate_cleanup(subdir: subdir, mtime: 2.days.ago, expected: false)
        end

        it 'does not remove new files or logs' do
          expect(Gitlab::Import::Logger).not_to receive(:new)

          validate_cleanup(subdir: subdir, mtime: 2.hours.ago, expected: true)
        end
      end

      include_examples 'removes old tmp files', '@hashed'
      include_examples 'removes old tmp files', '@groups'
    end

    context 'with uploader exports' do
      it 'removes old files and logs' do
        upload = create(
          :import_export_upload,
          updated_at: 2.days.ago,
          export_file: fixture_file_upload('spec/fixtures/project_export.tar.gz')
        )

        expect_next_instance_of(Gitlab::Import::Logger) do |logger|
          expect(logger)
            .to receive(:info)
            .with(
              message: 'Removed Import/Export export_file',
              project_id: upload.project_id,
              group_id: upload.group_id
            )
        end

        expect { service.execute }.to change { upload.reload.export_file.file.nil? }.to(true)

        expect(ImportExportUpload.where(export_file: nil)).to include(upload)
      end

      it 'does not remove new files or logs' do
        upload = create(
          :import_export_upload,
          updated_at: 1.hour.ago,
          export_file: fixture_file_upload('spec/fixtures/project_export.tar.gz')
        )

        expect(Gitlab::Import::Logger).not_to receive(:new)

        expect { service.execute }.not_to change { upload.reload.export_file.file.nil? }

        expect(ImportExportUpload.where.not(export_file: nil)).to include(upload)
      end
    end

    def validate_cleanup(subdir:, mtime:, expected:)
      in_directory_with_files(mtime: mtime, subdir: subdir) do |dir, files|
        service.execute

        files.each { |file| expect(File.exist?(file)).to eq(expected) }
        expect(File.directory?(dir)).to eq(expected)
      end
    end

    def in_directory_with_files(mtime:, subdir:)
      Dir.mktmpdir do |tmpdir|
        stub_repository_downloads_path(tmpdir)
        hashed = Digest::SHA2.hexdigest(subdir)
        subdir_path = [subdir, hashed[0..1], hashed[2..3], hashed, hashed[4..10]]
        dir = File.join(tmpdir, tmp_import_export_folder, *[subdir_path])

        FileUtils.mkdir_p(dir)
        File.utime(mtime.to_i, mtime.to_i, dir)

        files = FileUtils.touch(file_list(dir) + [dir], mtime: mtime.to_time)

        yield(dir, files)
      end
    end

    def stub_repository_downloads_path(path)
      new_shared_settings = Settings.shared.merge('path' => path)
      allow(Settings).to receive(:shared).and_return(new_shared_settings)
    end

    def file_list(dir)
      Array.new(5) do |num|
        File.join(dir, "random-#{num}.tar.gz")
      end
    end
  end
end


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

# Finds the correct checkbox in the passed in markdown/html and toggles it's state,
# returning the updated markdown/html.
# We don't care if the text has changed above or below the specific checkbox, as long
# the checkbox still exists at exactly the same line number and the text is equal.
# If successful, new values are available in `updated_markdown` and `updated_markdown_html`
class TaskListToggleService
  attr_reader :updated_markdown, :updated_markdown_html

  def initialize(markdown, markdown_html, line_source:, line_number:, toggle_as_checked:)
    @markdown = markdown
    @markdown_html = markdown_html
    @line_source = line_source
    @line_number = line_number
    @toggle_as_checked = toggle_as_checked

    @updated_markdown, @updated_markdown_html = nil
  end

  def execute
    return false unless markdown && markdown_html

    toggle_markdown && toggle_markdown_html
  end

  private

  attr_reader :markdown, :markdown_html, :toggle_as_checked
  attr_reader :line_source, :line_number

  def toggle_markdown
    source_lines      = markdown.split("\n")
    source_line_index = line_number - 1
    markdown_task     = source_lines[source_line_index]

    # The source in the DB could be using either \n or \r\n line endings
    return unless markdown_task.chomp == line_source
    return unless source_checkbox = Taskable::ITEM_PATTERN.match(markdown_task)

    currently_checked = TaskList::Item.new(source_checkbox[2]).complete?

    # Check `toggle_as_checked` to make sure we don't accidentally replace
    # any `[ ]` or `[x]` in the middle of the text
    if currently_checked
      markdown_task.sub!(Taskable::COMPLETE_PATTERN, '[ ]') unless toggle_as_checked
    elsif toggle_as_checked
      markdown_task.sub!(Taskable::INCOMPLETE_PATTERN, '[x]')
    end

    source_lines[source_line_index] = markdown_task
    @updated_markdown = source_lines.join("\n")
  end

  def toggle_markdown_html
    html          = Nokogiri::HTML.fragment(markdown_html)
    html_checkbox = get_html_checkbox(html)
    return unless html_checkbox

    if toggle_as_checked
      html_checkbox[:checked] = 'checked'
    else
      html_checkbox.remove_attribute('checked')
    end

    @updated_markdown_html = html.to_html
  end

  # When using CommonMark, we should be able to use the embedded `sourcepos` attribute to
  # target the exact line in the DOM.
  def get_html_checkbox(html)
    html.css(".task-list-item[data-sourcepos^='#{line_number}:'] input.task-list-item-checkbox").first
  end
end

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe TaskListToggleService, feature_category: :team_planning do
  let(:markdown) do
    <<-EOT.strip_heredoc
      * [ ] Task 1
      * [x] Task 2

      A paragraph

      1. [X] Item 1
         - [ ] Sub-item 1

      - [ ] loose list

        with an embedded paragraph

      + [ ] No-break space (U+00A0)

      2) [ ] Another item
    EOT
  end

  let(:markdown_html) do
    <<-EOT.strip_heredoc
      <ul data-sourcepos="1:1-3:0" class="task-list" dir="auto">
        <li data-sourcepos="1:1-1:12" class="task-list-item">
          <input type="checkbox" class="task-list-item-checkbox" disabled> Task 1
        </li>
        <li data-sourcepos="2:1-3:0" class="task-list-item">
          <input type="checkbox" class="task-list-item-checkbox" disabled checked> Task 2
        </li>
      </ul>
      <p data-sourcepos="4:1-4:11" dir="auto">A paragraph</p>
      <ol data-sourcepos="6:1-8:0" class="task-list" dir="auto">
        <li data-sourcepos="6:1-8:0" class="task-list-item">
          <input type="checkbox" class="task-list-item-checkbox" checked="" disabled=""> Item 1
          <ul data-sourcepos="7:4-8:0" class="task-list">
            <li data-sourcepos="7:4-8:0" class="task-list-item">
              <input type="checkbox" class="task-list-item-checkbox" disabled=""> Sub-item 1
            </li>
          </ul>
        </li>
      </ol>
      <ul data-sourcepos="9:1-12:0" class="task-list" dir="auto">
        <li data-sourcepos="9:1-12:0" class="task-list-item">
          <p data-sourcepos="9:3-9:16"><input type="checkbox" class="task-list-item-checkbox" disabled=""> loose list</p>
          <p data-sourcepos="11:3-11:28">with an embedded paragraph</p>
        </li>
      </ul>
      <ul data-sourcepos="13:1-13:21" class="task-list" dir="auto">
        <li data-sourcepos="13:1-13:21" class="task-list-item">
          <input type="checkbox" class="task-list-item-checkbox" disabled=""> No-break space (U+00A0)
        </li>
      </ul>
      <ol start="2" data-sourcepos="15:1-15:19" class="task-list" dir="auto">
        <li data-sourcepos="15:1-15:19" class="task-list-item">
          <input type="checkbox" class="task-list-item-checkbox" disabled> Another item
        </li>
      </ol>
    EOT
  end

  it 'checks Task 1' do
    toggler = described_class.new(
      markdown, markdown_html,
      toggle_as_checked: true,
      line_source: '* [ ] Task 1',
      line_number: 1
    )

    expect(toggler.execute).to be_truthy
    expect(toggler.updated_markdown.lines[0]).to eq "* [x] Task 1\n"
    expect(toggler.updated_markdown_html).to include('disabled checked> Task 1')
  end

  it 'unchecks Item 1' do
    toggler = described_class.new(
      markdown, markdown_html,
      toggle_as_checked: false,
      line_source: '1. [X] Item 1',
      line_number: 6
    )

    expect(toggler.execute).to be_truthy
    expect(toggler.updated_markdown.lines[5]).to eq "1. [ ] Item 1\n"
    expect(toggler.updated_markdown_html).to include('disabled> Item 1')
  end

  it 'checks task in loose list' do
    toggler = described_class.new(
      markdown, markdown_html,
      toggle_as_checked: true,
      line_source: '- [ ] loose list',
      line_number: 9
    )

    expect(toggler.execute).to be_truthy
    expect(toggler.updated_markdown.lines[8]).to eq "- [x] loose list\n"
    expect(toggler.updated_markdown_html).to include('disabled checked> loose list')
  end

  it 'checks task with no-break space' do
    toggler = described_class.new(
      markdown, markdown_html,
      toggle_as_checked: true,
      line_source: '+ [ ] No-break space (U+00A0)',
      line_number: 13
    )

    expect(toggler.execute).to be_truthy
    expect(toggler.updated_markdown.lines[12]).to eq "+ [x] No-break space (U+00A0)\n"
    expect(toggler.updated_markdown_html).to include('disabled checked> No-break space (U+00A0)')
  end

  it 'checks Another item' do
    toggler = described_class.new(
      markdown, markdown_html,
      toggle_as_checked: true,
      line_source: '2) [ ] Another item',
      line_number: 15
    )

    expect(toggler.execute).to be_truthy
    expect(toggler.updated_markdown.lines[14]).to eq "2) [x] Another item"
    expect(toggler.updated_markdown_html).to include('disabled checked> Another item')
  end

  it 'returns false if line_source does not match the text' do
    toggler = described_class.new(
      markdown, markdown_html,
      toggle_as_checked: false,
      line_source: '* [x] Task Added',
      line_number: 2
    )

    expect(toggler.execute).to be_falsey
  end

  it 'tolerates \r\n line endings' do
    rn_markdown = markdown.gsub("\n", "\r\n")
    toggler = described_class.new(
      rn_markdown,
      markdown_html,
      toggle_as_checked: true,
      line_source: '* [ ] Task 1',
      line_number: 1
    )

    expect(toggler.execute).to be_truthy
    expect(toggler.updated_markdown.lines[0]).to eq "* [x] Task 1\r\n"
    expect(toggler.updated_markdown_html).to include('disabled checked> Task 1')
  end

  it 'returns false if markdown is nil' do
    toggler = described_class.new(
      nil,
      markdown_html,
      toggle_as_checked: false,
      line_source: '* [x] Task Added',
      line_number: 2
    )

    expect(toggler.execute).to be_falsey
  end

  it 'returns false if markdown_html is nil' do
    toggler = described_class.new(
      markdown,
      nil,
      toggle_as_checked: false,
      line_source: '* [x] Task Added',
      line_number: 2
    )

    expect(toggler.execute).to be_falsey
  end

  it 'properly handles tasks in a blockquote' do
    markdown =
      <<-EOT.strip_heredoc
      > > * [ ] Task 1
      > * [x] Task 2
      EOT

    markdown_html = parse_markdown(markdown)
    toggler = described_class.new(
      markdown,
      markdown_html,
      toggle_as_checked: true,
      line_source: '> > * [ ] Task 1',
      line_number: 1
    )

    expect(toggler.execute).to be_truthy
    expect(toggler.updated_markdown.lines[0]).to eq "> > * [x] Task 1\n"
    expect(toggler.updated_markdown_html).to include('disabled checked> Task 1')
  end

  it 'properly handles a GitLab blockquote' do
    markdown =
      <<-EOT.strip_heredoc
      >>>
      gitlab blockquote
      >>>

      * [ ] Task 1
      * [x] Task 2
      EOT

    markdown_html = parse_markdown(markdown)
    toggler = described_class.new(
      markdown,
      markdown_html,
      toggle_as_checked: true,
      line_source: '* [ ] Task 1',
      line_number: 5
    )

    expect(toggler.execute).to be_truthy
    expect(toggler.updated_markdown.lines[4]).to eq "* [x] Task 1\n"
    expect(toggler.updated_markdown_html).to include('disabled checked> Task 1')
  end

  context 'when clicking an embedded subtask' do
    it 'properly handles it inside an unordered list' do
      markdown =
        <<-EOT.strip_heredoc
      - - [ ] Task 1
        - [x] Task 2
        EOT

      markdown_html = parse_markdown(markdown)
      toggler = described_class.new(
        markdown,
        markdown_html,
        toggle_as_checked: true,
        line_source: '- - [ ] Task 1',
        line_number: 1
      )

      expect(toggler.execute).to be_truthy
      expect(toggler.updated_markdown.lines[0]).to eq "- - [x] Task 1\n"
      expect(toggler.updated_markdown_html).to include('disabled checked> Task 1')
    end

    it 'properly handles it inside an ordered list' do
      markdown =
        <<-EOT.strip_heredoc
      1. - [ ] Task 1
         - [x] Task 2
        EOT

      markdown_html = parse_markdown(markdown)
      toggler = described_class.new(
        markdown,
        markdown_html,
        toggle_as_checked: true,
        line_source: '1. - [ ] Task 1',
        line_number: 1
      )

      expect(toggler.execute).to be_truthy
      expect(toggler.updated_markdown.lines[0]).to eq "1. - [x] Task 1\n"
      expect(toggler.updated_markdown_html).to include('disabled checked> Task 1')
    end
  end

  def parse_markdown(markdown)
    Banzai::Pipeline::FullPipeline.call(markdown, project: nil)[:output].to_html
  end
end


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

class UpdateContainerRegistryInfoService
  def execute
    registry_config = Gitlab.config.registry
    return unless registry_config.enabled && registry_config.api_url.presence

    # registry_info will query the /v2 route of the registry API. This route
    # requires authentication, but not authorization (the response has no body,
    # only headers that show the version of the registry). There might be no
    # associated user when running this (e.g. from a rake task or a cron job),
    # so we need to generate a valid JWT token with no access permissions to
    # authenticate as a trusted client.
    token = Auth::ContainerRegistryAuthenticationService.access_token({})
    client = ContainerRegistry::Client.new(registry_config.api_url, token: token)
    info = client.registry_info

    gitlab_api_client = ContainerRegistry::GitlabApiClient.new(registry_config.api_url, token: token)
    if gitlab_api_client.supports_gitlab_api?
      info[:features] ||= []
      info[:features] << ContainerRegistry::GitlabApiClient::REGISTRY_GITLAB_V1_API_FEATURE
    end

    Gitlab::CurrentSettings.update!(
      container_registry_vendor: info[:vendor] || '',
      container_registry_version: info[:version] || '',
      container_registry_features: info[:features] || [],
      container_registry_db_enabled: info[:db_enabled] || false
    )
  end
end

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe UpdateContainerRegistryInfoService, feature_category: :container_registry do
  let_it_be(:application_settings) { Gitlab::CurrentSettings }
  let_it_be(:api_url) { 'http://registry.gitlab' }

  describe '#execute' do
    before do
      stub_access_token
      stub_container_registry_config(enabled: true, api_url: api_url)
    end

    subject { described_class.new.execute }

    shared_examples 'invalid config' do
      it 'does not update the application settings' do
        expect(application_settings).not_to receive(:update!)

        subject
      end

      it 'does not raise an error' do
        expect { subject }.not_to raise_error
      end
    end

    context 'when container registry is disabled' do
      before do
        stub_container_registry_config(enabled: false)
      end

      it_behaves_like 'invalid config'
    end

    context 'when container registry api_url is blank' do
      before do
        stub_container_registry_config(api_url: '')
      end

      it_behaves_like 'invalid config'
    end

    context 'when creating a registry client instance' do
      let(:token) { 'foo' }
      let(:client) { ContainerRegistry::Client.new(api_url, token: token) }

      before do
        stub_registry_info({})
        stub_supports_gitlab_api(false)
      end

      it 'uses a token with no access permissions' do
        expect(Auth::ContainerRegistryAuthenticationService)
          .to receive(:access_token).with({}).and_return(token)
        expect(ContainerRegistry::Client)
          .to receive(:new).with(api_url, token: token).and_return(client)

        subject
      end
    end

    context 'when unabled to detect the container registry type' do
      it 'sets the application settings to their defaults' do
        stub_registry_info({})
        stub_supports_gitlab_api(false)

        subject

        application_settings.reload
        expect(application_settings.container_registry_vendor).to be_blank
        expect(application_settings.container_registry_version).to be_blank
        expect(application_settings.container_registry_features).to eq([])
        expect(application_settings.container_registry_db_enabled).to be_falsey
      end
    end

    context 'when able to detect the container registry type' do
      context 'when using the GitLab container registry' do
        it 'updates application settings accordingly' do
          stub_registry_info(vendor: 'gitlab', version: '2.9.1-gitlab', features: %w[a b c], db_enabled: true)
          stub_supports_gitlab_api(true)

          subject

          application_settings.reload
          expect(application_settings.container_registry_vendor).to eq('gitlab')
          expect(application_settings.container_registry_version).to eq('2.9.1-gitlab')
          expect(application_settings.container_registry_features)
            .to match_array(%W[a b c #{ContainerRegistry::GitlabApiClient::REGISTRY_GITLAB_V1_API_FEATURE}])
          expect(application_settings.container_registry_db_enabled).to be_truthy
        end
      end

      context 'when using a third-party container registry' do
        it 'updates application settings accordingly' do
          stub_registry_info(vendor: 'other', version: nil, features: nil, db_enabled: false)
          stub_supports_gitlab_api(false)

          subject

          application_settings.reload
          expect(application_settings.container_registry_vendor).to eq('other')
          expect(application_settings.container_registry_version).to be_blank
          expect(application_settings.container_registry_features).to eq([])
          expect(application_settings.container_registry_db_enabled).to be_falsey
        end
      end
    end
  end

  def stub_access_token
    allow(Auth::ContainerRegistryAuthenticationService)
      .to receive(:access_token).with({}).and_return('foo')
  end

  def stub_registry_info(output)
    allow_next_instance_of(ContainerRegistry::Client) do |client|
      allow(client).to receive(:registry_info).and_return(output)
    end
  end

  def stub_supports_gitlab_api(output)
    allow_next_instance_of(ContainerRegistry::GitlabApiClient) do |client|
      allow(client).to receive(:supports_gitlab_api?).and_return(output)
    end
  end
end


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

# Base class, scoped by container (project or group).
#
# New or existing services which only require a project or group container
# should subclass BaseProjectService or BaseGroupService.
#
# If you require a different but specific, non-polymorphic container
# consider creating a new subclass, and update the related comment at
# the top of the original BaseService.
class BaseContainerService
  include BaseServiceUtility
  include ::Gitlab::Utils::StrongMemoize

  attr_accessor :project, :group
  attr_reader :container, :current_user, :params

  def initialize(container:, current_user: nil, params: {})
    @container = container
    @current_user = current_user
    @params = params.dup

    handle_container_type(container)
  end

  def project_container?
    container.is_a?(::Project)
  end

  def group_container?
    container.is_a?(::Group)
  end

  def namespace_container?
    container.is_a?(::Namespace)
  end

  def project_group
    project&.group
  end
  strong_memoize_attr :project_group

  private

  def handle_container_type(container)
    case container
    when Project
      @project = container
    when Group
      @group = container
    when Namespaces::ProjectNamespace
      @project = container.project
    end
  end
end

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe BaseContainerService, feature_category: :container_registry do
  let(:project) { Project.new }
  let(:user) { User.new }

  describe '#initialize' do
    it 'accepts container and current_user' do
      subject = described_class.new(container: project, current_user: user)

      expect(subject.container).to eq(project)
      expect(subject.current_user).to eq(user)
    end

    it 'treats current_user as optional' do
      subject = described_class.new(container: project)

      expect(subject.current_user).to be_nil
    end
  end
end


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

class GravatarService
  def execute(email, size = nil, scale = 2, username: nil)
    return if Gitlab::FIPS.enabled?
    return unless Gitlab::CurrentSettings.gravatar_enabled?

    identifier = email.presence || username.presence
    return unless identifier

    hash = Digest::MD5.hexdigest(identifier.strip.downcase)
    size = Groups::GroupMembersHelper::AVATAR_SIZE unless size && size > 0

    sprintf gravatar_url,
      hash: hash,
      size: size * scale,
      email: ERB::Util.url_encode(email&.strip || ''),
      username: ERB::Util.url_encode(username&.strip || '')
  end

  def gitlab_config
    Gitlab.config.gitlab
  end

  def gravatar_config
    Gitlab.config.gravatar
  end

  def gravatar_url
    if gitlab_config.https
      gravatar_config.ssl_url
    else
      gravatar_config.plain_url
    end
  end
end

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe GravatarService, feature_category: :user_profile do
  describe '#execute' do
    let(:url) { 'http://example.com/avatar?hash=%{hash}&size=%{size}&email=%{email}&username=%{username}' }

    before do
      allow(Gitlab.config.gravatar).to receive(:plain_url).and_return(url)
    end

    it 'replaces the placeholders' do
      avatar_url = described_class.new.execute('user@example.com', 100, 2, username: 'user')

      expect(avatar_url).to include("hash=#{Digest::MD5.hexdigest('user@example.com')}")
      expect(avatar_url).to include("size=200")
      expect(avatar_url).to include("email=user%40example.com")
      expect(avatar_url).to include("username=user")
    end
  end
end


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

class AccessTokenValidationService
  # Results:
  VALID = :valid
  EXPIRED = :expired
  REVOKED = :revoked
  INSUFFICIENT_SCOPE = :insufficient_scope
  IMPERSONATION_DISABLED = :impersonation_disabled

  attr_reader :token, :request

  def initialize(token, request: nil)
    @token = token
    @request = request
  end

  def validate(scopes: [])
    if token.expired?
      EXPIRED

    elsif token.revoked?
      REVOKED

    elsif !self.include_any_scope?(scopes)
      INSUFFICIENT_SCOPE

    elsif token.respond_to?(:impersonation) &&
        token.impersonation &&
        !Gitlab.config.gitlab.impersonation_enabled
      IMPERSONATION_DISABLED

    else
      VALID
    end
  end

  # True if the token's scope contains any of the passed scopes.
  def include_any_scope?(required_scopes)
    if required_scopes.blank?
      true
    else
      # We're comparing each required_scope against all token scopes, which would
      # take quadratic time. This consideration is irrelevant here because of the
      # small number of records involved.
      # https://gitlab.com/gitlab-org/gitlab-foss/merge_requests/12300/#note_33689006
      token_scopes = token.scopes.map(&:to_sym)

      required_scopes.any? do |scope|
        scope = API::Scope.new(scope) unless scope.is_a?(API::Scope)
        scope.sufficient?(token_scopes, request)
      end
    end
  end
end

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe AccessTokenValidationService, feature_category: :system_access do
  describe ".include_any_scope?" do
    let(:request) { double("request") }

    it "returns true if the required scope is present in the token's scopes" do
      token = double("token", scopes: [:api, :read_user])
      scopes = [:api]

      expect(described_class.new(token, request: request).include_any_scope?(scopes)).to be(true)
    end

    it "returns true if more than one of the required scopes is present in the token's scopes" do
      token = double("token", scopes: [:api, :read_user, :other_scope])
      scopes = [:api, :other_scope]

      expect(described_class.new(token, request: request).include_any_scope?(scopes)).to be(true)
    end

    it "returns true if the list of required scopes is an exact match for the token's scopes" do
      token = double("token", scopes: [:api, :read_user, :other_scope])
      scopes = [:api, :read_user, :other_scope]

      expect(described_class.new(token, request: request).include_any_scope?(scopes)).to be(true)
    end

    it "returns true if the list of required scopes contains all of the token's scopes, in addition to others" do
      token = double("token", scopes: [:api, :read_user])
      scopes = [:api, :read_user, :other_scope]

      expect(described_class.new(token, request: request).include_any_scope?(scopes)).to be(true)
    end

    it 'returns true if the list of required scopes is blank' do
      token = double("token", scopes: [])
      scopes = []

      expect(described_class.new(token, request: request).include_any_scope?(scopes)).to be(true)
    end

    it "returns false if there are no scopes in common between the required scopes and the token scopes" do
      token = double("token", scopes: [:api, :read_user])
      scopes = [:other_scope]

      expect(described_class.new(token, request: request).include_any_scope?(scopes)).to be(false)
    end

    context "conditions" do
      it "ignores any scopes whose `if` condition returns false" do
        token = double("token", scopes: [:api, :read_user])
        scopes = [API::Scope.new(:api, if: ->(_) { false })]

        expect(described_class.new(token, request: request).include_any_scope?(scopes)).to be(false)
      end

      it "does not ignore scopes whose `if` condition is not set" do
        token = double("token", scopes: [:api, :read_user])
        scopes = [API::Scope.new(:api, if: ->(_) { false }), :read_user]

        expect(described_class.new(token, request: request).include_any_scope?(scopes)).to be(true)
      end

      it "does not ignore scopes whose `if` condition returns true" do
        token = double("token", scopes: [:api, :read_user])
        scopes = [API::Scope.new(:api, if: ->(_) { true }), API::Scope.new(:read_user, if: ->(_) { false })]

        expect(described_class.new(token, request: request).include_any_scope?(scopes)).to be(true)
      end
    end
  end
end


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

class AuditEventService
  include AuditEventSaveType

  # Instantiates a new service
  #
  # @param [User, token String] author the entity who authors the change
  # @param [User, Project, Group] entity the scope which audit event belongs to
  #   This param is also used to determine the visibility of the audit event.
  #   - Project: events are visible at Project and Instance level
  #   - Group: events are visible at Group and Instance level
  #   - User: events are visible at Instance level
  # @param [Hash] details extra data of audit event
  # @param [Symbol] save_type the type to save the event
  #   Can be selected from the following, :database, :stream, :database_and_stream .
  # @params [DateTime] created_at the time the action occured
  #
  # @return [AuditEventService]
  def initialize(author, entity, details = {}, save_type = :database_and_stream, created_at = DateTime.current)
    @author = build_author(author)
    @entity = entity
    @details = details
    @ip_address = resolve_ip_address(@author)
    @save_type = save_type
    @created_at = created_at
  end

  # Builds the @details attribute for authentication
  #
  # This uses the @author as the target object being audited
  #
  # @return [AuditEventService]
  def for_authentication
    mark_as_authentication_event!

    @details = {
      with: @details[:with],
      target_id: @author.id,
      target_type: 'User',
      target_details: @author.name
    }

    self
  end

  # Writes event to a file and creates an event record in DB
  #
  # @return [AuditEvent] persisted if saves and non-persisted if fails
  def security_event
    log_security_event_to_file
    log_authentication_event_to_database
    log_security_event_to_database
  end

  # Writes event to a file
  def log_security_event_to_file
    file_logger.info(base_payload.merge(formatted_details))
  end

  private

  attr_reader :ip_address

  def build_author(author)
    case author
    when User
      author.impersonated? ? Gitlab::Audit::ImpersonatedAuthor.new(author) : author
    else
      Gitlab::Audit::UnauthenticatedAuthor.new(name: author)
    end
  end

  def resolve_ip_address(author)
    Gitlab::RequestContext.instance.client_ip ||
      author.current_sign_in_ip
  end

  def base_payload
    {
      author_id: @author.id,
      author_name: @author.name,
      entity_id: @entity.id,
      entity_type: @entity.class.name,
      created_at: @created_at
    }
  end

  def authentication_event_payload
    {
      # @author can be a User or various Gitlab::Audit authors.
      # Only capture real users for successful authentication events.
      user: author_if_user,
      user_name: @author.name,
      ip_address: ip_address,
      result: AuthenticationEvent.results[:success],
      provider: @details[:with]
    }
  end

  def author_if_user
    @author if @author.is_a?(User)
  end

  def file_logger
    @file_logger ||= Gitlab::AuditJsonLogger.build
  end

  def formatted_details
    @details.merge(@details.slice(:from, :to).transform_values(&:to_s))
  end

  def mark_as_authentication_event!
    @authentication_event = true
  end

  def authentication_event?
    @authentication_event
  end

  def log_security_event_to_database
    return if Gitlab::Database.read_only?

    event = build_event
    save_or_track event
    event
  end

  def build_event
    AuditEvent.new(base_payload.merge(details: @details))
  end

  def stream_event_to_external_destinations(_event)
    # Defined in EE
  end

  def log_authentication_event_to_database
    return unless Gitlab::Database.read_write? && authentication_event?

    event = AuthenticationEvent.new(authentication_event_payload)
    save_or_track event

    event
  end

  def save_or_track(event)
    event.save! if should_save_database?(@save_type)
    stream_event_to_external_destinations(event) if should_save_stream?(@save_type)
  rescue StandardError => e
    Gitlab::ErrorTracking.track_and_raise_for_dev_exception(e, audit_event_type: event.class.to_s)
  end
end

AuditEventService.prepend_mod_with('AuditEventService')

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe AuditEventService, :with_license, feature_category: :audit_events do
  let_it_be(:project) { create(:project) }
  let_it_be(:user) { create(:user, :with_sign_ins) }
  let_it_be(:project_member) { create(:project_member, user: user) }

  let(:service) { described_class.new(user, project, { action: :destroy }) }
  let(:logger) { instance_double(Gitlab::AuditJsonLogger) }

  describe '#security_event' do
    it 'creates an event and logs to a file' do
      expect(service).to receive(:file_logger).and_return(logger)
      expect(logger).to receive(:info).with({ author_id: user.id,
                                              author_name: user.name,
                                              entity_id: project.id,
                                              entity_type: "Project",
                                              action: :destroy,
                                              created_at: anything })

      expect { service.security_event }.to change(AuditEvent, :count).by(1)
    end

    it 'formats from and to fields' do
      service = described_class.new(
        user, project,
        {
          from: true,
          to: false,
          action: :create,
          target_id: 1
        })
      expect(service).to receive(:file_logger).and_return(logger)
      expect(logger).to receive(:info).with({ author_id: user.id,
                                              author_name: user.name,
                                              entity_type: 'Project',
                                              entity_id: project.id,
                                              from: 'true',
                                              to: 'false',
                                              action: :create,
                                              target_id: 1,
                                              created_at: anything })

      expect { service.security_event }.to change(AuditEvent, :count).by(1)

      details = AuditEvent.last.details
      expect(details[:from]).to be true
      expect(details[:to]).to be false
      expect(details[:action]).to eq(:create)
      expect(details[:target_id]).to eq(1)
    end

    context 'when defining created_at manually' do
      let(:service) { described_class.new(user, project, { action: :destroy }, :database, 3.weeks.ago) }

      it 'is overridden successfully' do
        freeze_time do
          expect(service).to receive(:file_logger).and_return(logger)
          expect(logger).to receive(:info).with({ author_id: user.id,
                                                  author_name: user.name,
                                                  entity_id: project.id,
                                                  entity_type: "Project",
                                                  action: :destroy,
                                                  created_at: 3.weeks.ago })

          expect { service.security_event }.to change(AuditEvent, :count).by(1)
          expect(AuditEvent.last.created_at).to eq(3.weeks.ago)
        end
      end
    end

    context 'authentication event' do
      let(:audit_service) { described_class.new(user, user, with: 'standard') }

      it 'creates an authentication event' do
        expect(AuthenticationEvent).to receive(:new).with(
          {
            user: user,
            user_name: user.name,
            ip_address: user.current_sign_in_ip,
            result: AuthenticationEvent.results[:success],
            provider: 'standard'
          }
        ).and_call_original

        audit_service.for_authentication.security_event
      end

      it 'tracks exceptions when the event cannot be created' do
        allow_next_instance_of(AuditEvent) do |event|
          allow(event).to receive(:valid?).and_return(false)
        end

        expect(Gitlab::ErrorTracking).to(
          receive(:track_and_raise_for_dev_exception)
        )

        audit_service.for_authentication.security_event
      end

      context 'with IP address', :request_store do
        using RSpec::Parameterized::TableSyntax

        where(:from_context, :from_author_sign_in, :output) do
          '192.168.0.2' | '192.168.0.3' | '192.168.0.2'
          nil           | '192.168.0.3' | '192.168.0.3'
        end

        with_them do
          let(:user) { create(:user, current_sign_in_ip: from_author_sign_in) }
          let(:audit_service) { described_class.new(user, user, with: 'standard') }

          before do
            allow(Gitlab::RequestContext.instance).to receive(:client_ip).and_return(from_context)
          end

          specify do
            expect(AuthenticationEvent).to receive(:new).with(hash_including(ip_address: output)).and_call_original

            audit_service.for_authentication.security_event
          end
        end
      end
    end
  end

  describe '#log_security_event_to_file' do
    it 'logs security event to file' do
      expect(service).to receive(:file_logger).and_return(logger)
      expect(logger).to receive(:info).with({ author_id: user.id,
                                              author_name: user.name,
                                              entity_type: 'Project',
                                              entity_id: project.id,
                                              action: :destroy,
                                              created_at: anything })

      service.log_security_event_to_file
    end
  end
end


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

# SystemNoteService
#
# Used for creating system notes (e.g., when a user references a merge request
# from an issue, an issue's assignee changes, an issue is closed, etc.)
module SystemNoteService
  extend self

  # Called when commits are added to a merge request
  #
  # noteable         - Noteable object
  # project          - Project owning noteable
  # author           - User performing the change
  # new_commits      - Array of Commits added since last push
  # existing_commits - Array of Commits added in a previous push
  # oldrev           - Optional String SHA of a previous Commit
  #
  # Returns the created Note object
  def add_commits(noteable, project, author, new_commits, existing_commits = [], oldrev = nil)
    ::SystemNotes::CommitService.new(noteable: noteable, project: project, author: author).add_commits(new_commits, existing_commits, oldrev)
  end

  # Called when a commit was tagged
  #
  # noteable  - Noteable object
  # project   - Project owning noteable
  # author    - User performing the tag
  # tag_name  - The created tag name
  #
  # Returns the created Note object
  def tag_commit(noteable, project, author, tag_name)
    ::SystemNotes::CommitService.new(noteable: noteable, project: project, author: author).tag_commit(tag_name)
  end

  def change_assignee(noteable, project, author, assignee)
    ::SystemNotes::IssuablesService.new(noteable: noteable, project: project, author: author).change_assignee(assignee)
  end

  def change_issuable_assignees(issuable, project, author, old_assignees)
    ::SystemNotes::IssuablesService.new(noteable: issuable, project: project, author: author).change_issuable_assignees(old_assignees)
  end

  def change_issuable_reviewers(issuable, project, author, old_reviewers)
    ::SystemNotes::IssuablesService.new(noteable: issuable, project: project, author: author).change_issuable_reviewers(old_reviewers)
  end

  def change_issuable_contacts(issuable, project, author, added_count, removed_count)
    ::SystemNotes::IssuablesService.new(noteable: issuable, project: project, author: author).change_issuable_contacts(added_count, removed_count)
  end

  def relate_issuable(noteable, noteable_ref, user)
    ::SystemNotes::IssuablesService.new(noteable: noteable, project: noteable.project, author: user).relate_issuable(noteable_ref)
  end

  def unrelate_issuable(noteable, noteable_ref, user)
    ::SystemNotes::IssuablesService.new(noteable: noteable, project: noteable.project, author: user).unrelate_issuable(noteable_ref)
  end

  # Called when the due_date or start_date of a Noteable is changed
  #
  # noteable  - Noteable object
  # project   - Project owning noteable
  # author    - User performing the change
  # due_date  - Due date being assigned, or nil
  #
  # Example Note text:
  #
  #   "removed due date"
  #
  #   "changed due date to September 20, 2018 and changed start date to September 25, 2018"
  #
  # Returns the created Note object
  def change_start_date_or_due_date(noteable, project, author, changed_dates)
    ::SystemNotes::TimeTrackingService.new(
      noteable: noteable,
      project: project,
      author: author
    ).change_start_date_or_due_date(changed_dates)
  end

  # Called when the estimated time of a Noteable is changed
  #
  # noteable      - Noteable object
  # project       - Project owning noteable
  # author        - User performing the change
  # time_estimate - Estimated time
  #
  # Example Note text:
  #
  #   "removed time estimate"
  #
  #   "changed time estimate to 3d 5h"
  #
  # Returns the created Note object
  def change_time_estimate(noteable, project, author)
    ::SystemNotes::TimeTrackingService.new(noteable: noteable, project: project, author: author).change_time_estimate
  end

  # Called when the spent time of a Noteable is changed
  #
  # noteable   - Noteable object
  # project    - Project owning noteable
  # author     - User performing the change
  # time_spent - Spent time
  #
  # Example Note text:
  #
  #   "removed time spent"
  #
  #   "added 2h 30m of time spent"
  #
  # Returns the created Note object
  def change_time_spent(noteable, project, author)
    ::SystemNotes::TimeTrackingService.new(noteable: noteable, project: project, author: author).change_time_spent
  end

  # Called when a timelog is added to an issuable
  #
  # issuable   - Issuable object (Issue, WorkItem or MergeRequest)
  # project    - Project owning the issuable
  # author     - User performing the change
  # timelog    - Created timelog
  #
  # Example Note text:
  #
  #   "subtracted 1h 15m of time spent"
  #
  #   "added 2h 30m of time spent"
  #
  # Returns the created Note object
  def created_timelog(issuable, project, author, timelog)
    ::SystemNotes::TimeTrackingService.new(noteable: issuable, project: project, author: author).created_timelog(timelog)
  end

  # Called when a timelog is removed from a Noteable
  #
  # noteable  - Noteable object
  # project   - Project owning the noteable
  # author    - User performing the change
  # timelog   - The removed timelog
  #
  # Example Note text:
  #   "deleted 2h 30m of time spent from 22-03-2022"
  #
  # Returns the created Note object
  def remove_timelog(noteable, project, author, timelog)
    ::SystemNotes::TimeTrackingService.new(noteable: noteable, project: project, author: author).remove_timelog(timelog)
  end

  def close_after_error_tracking_resolve(issue, project, author)
    ::SystemNotes::IssuablesService.new(noteable: issue, project: project, author: author).close_after_error_tracking_resolve
  end

  def change_status(noteable, project, author, status, source = nil)
    ::SystemNotes::IssuablesService.new(noteable: noteable, project: project, author: author).change_status(status, source)
  end

  # Called when 'merge when pipeline succeeds' is executed
  def merge_when_pipeline_succeeds(noteable, project, author, sha)
    ::SystemNotes::MergeRequestsService.new(noteable: noteable, project: project, author: author).merge_when_pipeline_succeeds(sha)
  end

  # Called when 'merge when pipeline succeeds' is canceled
  def cancel_merge_when_pipeline_succeeds(noteable, project, author)
    ::SystemNotes::MergeRequestsService.new(noteable: noteable, project: project, author: author).cancel_merge_when_pipeline_succeeds
  end

  # Called when 'merge when pipeline succeeds' is aborted
  def abort_merge_when_pipeline_succeeds(noteable, project, author, reason)
    ::SystemNotes::MergeRequestsService.new(noteable: noteable, project: project, author: author).abort_merge_when_pipeline_succeeds(reason)
  end

  def handle_merge_request_draft(noteable, project, author)
    ::SystemNotes::MergeRequestsService.new(noteable: noteable, project: project, author: author).handle_merge_request_draft
  end

  def add_merge_request_draft_from_commit(noteable, project, author, commit)
    ::SystemNotes::MergeRequestsService.new(noteable: noteable, project: project, author: author).add_merge_request_draft_from_commit(commit)
  end

  def resolve_all_discussions(merge_request, project, author)
    ::SystemNotes::MergeRequestsService.new(noteable: merge_request, project: project, author: author).resolve_all_discussions
  end

  def discussion_continued_in_issue(discussion, project, author, issue)
    ::SystemNotes::MergeRequestsService.new(project: project, author: author).discussion_continued_in_issue(discussion, issue)
  end

  def diff_discussion_outdated(discussion, project, author, change_position)
    ::SystemNotes::MergeRequestsService.new(project: project, author: author).diff_discussion_outdated(discussion, change_position)
  end

  def change_title(noteable, project, author, old_title)
    ::SystemNotes::IssuablesService.new(noteable: noteable, project: project, author: author).change_title(old_title)
  end

  def change_description(noteable, project, author)
    ::SystemNotes::IssuablesService.new(noteable: noteable, project: project, author: author).change_description
  end

  def change_issue_confidentiality(issue, project, author)
    ::SystemNotes::IssuablesService.new(noteable: issue, project: project, author: author).change_issue_confidentiality
  end

  # Called when a branch in Noteable is changed
  #
  # noteable    - Noteable object
  # project     - Project owning noteable
  # author      - User performing the change
  # branch_type - 'source' or 'target'
  # event_type  - the source of event: 'update' or 'delete'
  # old_branch  - old branch name
  # new_branch  - new branch name
  #
  # Example Note text is based on event_type:
  #
  #   update: "changed target branch from `Old` to `New`"
  #   delete: "deleted the `Old` branch. This merge request now targets the `New` branch"
  #
  # Returns the created Note object
  def change_branch(noteable, project, author, branch_type, event_type, old_branch, new_branch)
    ::SystemNotes::MergeRequestsService.new(noteable: noteable, project: project, author: author)
      .change_branch(branch_type, event_type, old_branch, new_branch)
  end

  # Called when a branch in Noteable is added or deleted
  #
  # noteable    - Noteable object
  # project     - Project owning noteable
  # author      - User performing the change
  # branch_type - :source or :target
  # branch      - branch name
  # presence    - :add or :delete
  #
  # Example Note text:
  #
  #   "restored target branch `feature`"
  #
  # Returns the created Note object
  def change_branch_presence(noteable, project, author, branch_type, branch, presence)
    ::SystemNotes::MergeRequestsService.new(noteable: noteable, project: project, author: author).change_branch_presence(branch_type, branch, presence)
  end

  # Called when a branch is created from the 'new branch' button on a issue
  # Example note text:
  #
  #   "created branch `201-issue-branch-button`"
  def new_issue_branch(issue, project, author, branch, branch_project: nil)
    ::SystemNotes::MergeRequestsService.new(noteable: issue, project: project, author: author).new_issue_branch(branch, branch_project: branch_project)
  end

  def new_merge_request(issue, project, author, merge_request)
    ::SystemNotes::MergeRequestsService.new(noteable: issue, project: project, author: author).new_merge_request(merge_request)
  end

  def cross_reference(mentioned, mentioned_in, author)
    ::SystemNotes::IssuablesService.new(noteable: mentioned, author: author).cross_reference(mentioned_in)
  end

  def cross_reference_exists?(mentioned, mentioned_in)
    ::SystemNotes::IssuablesService.new(noteable: mentioned).cross_reference_exists?(mentioned_in)
  end

  def change_task_status(noteable, project, author, new_task)
    ::SystemNotes::IssuablesService.new(noteable: noteable, project: project, author: author).change_task_status(new_task)
  end

  def noteable_moved(noteable, project, noteable_ref, author, direction:)
    ::SystemNotes::IssuablesService.new(noteable: noteable, project: project, author: author).noteable_moved(noteable_ref, direction)
  end

  def noteable_cloned(noteable, project, noteable_ref, author, direction:, created_at: nil)
    ::SystemNotes::IssuablesService.new(noteable: noteable, project: project, author: author).noteable_cloned(noteable_ref, direction, created_at: created_at)
  end

  def mark_duplicate_issue(noteable, project, author, canonical_issue)
    ::SystemNotes::IssuablesService.new(noteable: noteable, project: project, author: author).mark_duplicate_issue(canonical_issue)
  end

  def mark_canonical_issue_of_duplicate(noteable, project, author, duplicate_issue)
    ::SystemNotes::IssuablesService.new(noteable: noteable, project: project, author: author).mark_canonical_issue_of_duplicate(duplicate_issue)
  end

  def add_email_participants(noteable, project, author, body)
    ::SystemNotes::IssuablesService.new(noteable: noteable, project: project, author: author).add_email_participants(body)
  end

  def discussion_lock(issuable, author)
    ::SystemNotes::IssuablesService.new(noteable: issuable, project: issuable.project, author: author).discussion_lock
  end

  def cross_reference_disallowed?(mentioned, mentioned_in)
    ::SystemNotes::IssuablesService.new(noteable: mentioned).cross_reference_disallowed?(mentioned_in)
  end

  def relate_work_item(noteable, work_item, user)
    ::SystemNotes::IssuablesService
      .new(noteable: noteable, project: noteable.project, author: user)
      .hierarchy_changed(work_item, 'relate')
  end

  def unrelate_work_item(noteable, work_item, user)
    ::SystemNotes::IssuablesService
      .new(noteable: noteable, project: noteable.project, author: user)
      .hierarchy_changed(work_item, 'unrelate')
  end

  def zoom_link_added(issue, project, author)
    ::SystemNotes::ZoomService.new(noteable: issue, project: project, author: author).zoom_link_added
  end

  def zoom_link_removed(issue, project, author)
    ::SystemNotes::ZoomService.new(noteable: issue, project: project, author: author).zoom_link_removed
  end

  def auto_resolve_prometheus_alert(noteable, project, author)
    ::SystemNotes::IssuablesService.new(noteable: noteable, project: project, author: author).auto_resolve_prometheus_alert
  end

  # Parameters:
  #   - version [DesignManagement::Version]
  #
  # Example Note text:
  #
  #   "added [1 designs](link-to-version)"
  #   "changed [2 designs](link-to-version)"
  #
  # Returns [Array<Note>]: the created Note objects
  def design_version_added(version)
    ::SystemNotes::DesignManagementService.new(noteable: version.issue, project: version.issue.project, author: version.author).design_version_added(version)
  end

  # Called when a new discussion is created on a design
  #
  # discussion_note - DiscussionNote
  #
  # Example Note text:
  #
  #   "started a discussion on screen.png"
  #
  # Returns the created Note object
  def design_discussion_added(discussion_note)
    design = discussion_note.noteable

    ::SystemNotes::DesignManagementService.new(noteable: design.issue, project: design.project, author: discussion_note.author).design_discussion_added(discussion_note)
  end

  # Called when the merge request is approved by user
  #
  # noteable - Noteable object
  # user     - User performing approve
  #
  # Example Note text:
  #
  #   "approved this merge request"
  #
  # Returns the created Note object
  def approve_mr(noteable, user)
    merge_requests_service(noteable, noteable.project, user).approve_mr
  end

  def unapprove_mr(noteable, user)
    merge_requests_service(noteable, noteable.project, user).unapprove_mr
  end

  def change_alert_status(alert, author, reason = nil)
    ::SystemNotes::AlertManagementService.new(noteable: alert, project: alert.project, author: author).change_alert_status(reason)
  end

  def new_alert_issue(alert, issue, author)
    ::SystemNotes::AlertManagementService.new(noteable: alert, project: alert.project, author: author).new_alert_issue(issue)
  end

  def create_new_alert(alert, monitoring_tool)
    ::SystemNotes::AlertManagementService.new(noteable: alert, project: alert.project).create_new_alert(monitoring_tool)
  end

  def change_incident_severity(incident, author)
    ::SystemNotes::IncidentService.new(noteable: incident, project: incident.project, author: author).change_incident_severity
  end

  def change_incident_status(incident, author, reason = nil)
    ::SystemNotes::IncidentService.new(noteable: incident, project: incident.project, author: author).change_incident_status(reason)
  end

  def log_resolving_alert(alert, monitoring_tool)
    ::SystemNotes::AlertManagementService.new(noteable: alert, project: alert.project).log_resolving_alert(monitoring_tool)
  end

  def change_issue_type(issue, author, previous_type)
    ::SystemNotes::IssuablesService.new(noteable: issue, project: issue.project, author: author).change_issue_type(previous_type)
  end

  def add_timeline_event(timeline_event)
    incidents_service(timeline_event.incident).add_timeline_event(timeline_event)
  end

  def edit_timeline_event(timeline_event, author, was_changed:)
    incidents_service(timeline_event.incident).edit_timeline_event(timeline_event, author, was_changed: was_changed)
  end

  def delete_timeline_event(noteable, author)
    incidents_service(noteable).delete_timeline_event(author)
  end

  private

  def merge_requests_service(noteable, project, author)
    ::SystemNotes::MergeRequestsService.new(noteable: noteable, project: project, author: author)
  end

  def incidents_service(incident)
    ::SystemNotes::IncidentsService.new(noteable: incident)
  end
end

SystemNoteService.prepend_mod_with('SystemNoteService')

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe SystemNoteService, feature_category: :shared do
  include Gitlab::Routing
  include RepoHelpers
  include AssetsHelpers
  include DesignManagementTestHelpers

  let_it_be(:group)    { create(:group) }
  let_it_be(:project)  { create(:project, :repository, group: group) }
  let_it_be(:author)   { create(:user) }

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

  describe '.add_commits' do
    let(:new_commits) { double }
    let(:old_commits) { double }
    let(:oldrev)      { double }

    it 'calls CommitService' do
      expect_next_instance_of(::SystemNotes::CommitService) do |service|
        expect(service).to receive(:add_commits).with(new_commits, old_commits, oldrev)
      end

      described_class.add_commits(noteable, project, author, new_commits, old_commits, oldrev)
    end
  end

  describe '.tag_commit' do
    let(:tag_name) { double }

    it 'calls CommitService' do
      expect_next_instance_of(::SystemNotes::CommitService) do |service|
        expect(service).to receive(:tag_commit).with(tag_name)
      end

      described_class.tag_commit(noteable, project, author, tag_name)
    end
  end

  describe '.change_assignee' do
    let(:assignee) { double }

    it 'calls IssuableService' do
      expect_next_instance_of(::SystemNotes::IssuablesService) do |service|
        expect(service).to receive(:change_assignee).with(assignee)
      end

      described_class.change_assignee(noteable, project, author, assignee)
    end
  end

  describe '.change_issuable_assignees' do
    let(:assignees) { [double, double] }

    it 'calls IssuableService' do
      expect_next_instance_of(::SystemNotes::IssuablesService) do |service|
        expect(service).to receive(:change_issuable_assignees).with(assignees)
      end

      described_class.change_issuable_assignees(noteable, project, author, assignees)
    end
  end

  describe '.change_issuable_reviewers' do
    let(:reviewers) { [double, double] }

    it 'calls IssuableService' do
      expect_next_instance_of(::SystemNotes::IssuablesService) do |service|
        expect(service).to receive(:change_issuable_reviewers).with(reviewers)
      end

      described_class.change_issuable_reviewers(noteable, project, author, reviewers)
    end
  end

  describe '.change_issuable_contacts' do
    let(:added_count) { 5 }
    let(:removed_count) { 3 }

    it 'calls IssuableService' do
      expect_next_instance_of(::SystemNotes::IssuablesService) do |service|
        expect(service).to receive(:change_issuable_contacts).with(added_count, removed_count)
      end

      described_class.change_issuable_contacts(noteable, project, author, added_count, removed_count)
    end
  end

  describe '.close_after_error_tracking_resolve' do
    it 'calls IssuableService' do
      expect_next_instance_of(::SystemNotes::IssuablesService) do |service|
        expect(service).to receive(:close_after_error_tracking_resolve)
      end

      described_class.close_after_error_tracking_resolve(noteable, project, author)
    end
  end

  describe '.relate_issuable' do
    let(:noteable_ref) { double }
    let(:noteable) { double }

    before do
      allow(noteable).to receive(:project).and_return(double)
    end

    it 'calls IssuableService' do
      expect_next_instance_of(::SystemNotes::IssuablesService) do |service|
        expect(service).to receive(:relate_issuable).with(noteable_ref)
      end

      described_class.relate_issuable(noteable, noteable_ref, double)
    end
  end

  describe '.unrelate_issuable' do
    let(:noteable_ref) { double }
    let(:noteable) { double }

    before do
      allow(noteable).to receive(:project).and_return(double)
    end

    it 'calls IssuableService' do
      expect_next_instance_of(::SystemNotes::IssuablesService) do |service|
        expect(service).to receive(:unrelate_issuable).with(noteable_ref)
      end

      described_class.unrelate_issuable(noteable, noteable_ref, double)
    end
  end

  describe '.change_start_date_or_due_date' do
    let(:changed_dates) { double }

    it 'calls TimeTrackingService' do
      expect_next_instance_of(::SystemNotes::TimeTrackingService) do |service|
        expect(service).to receive(:change_start_date_or_due_date).with(changed_dates)
      end

      described_class.change_start_date_or_due_date(noteable, project, author, changed_dates)
    end
  end

  describe '.change_status' do
    let(:status) { double }
    let(:source) { double }

    it 'calls IssuableService' do
      expect_next_instance_of(::SystemNotes::IssuablesService) do |service|
        expect(service).to receive(:change_status).with(status, source)
      end

      described_class.change_status(noteable, project, author, status, source)
    end
  end

  describe '.merge_when_pipeline_succeeds' do
    it 'calls MergeRequestsService' do
      sha = double

      expect_next_instance_of(::SystemNotes::MergeRequestsService) do |service|
        expect(service).to receive(:merge_when_pipeline_succeeds).with(sha)
      end

      described_class.merge_when_pipeline_succeeds(noteable, project, author, sha)
    end
  end

  describe '.cancel_merge_when_pipeline_succeeds' do
    it 'calls MergeRequestsService' do
      expect_next_instance_of(::SystemNotes::MergeRequestsService) do |service|
        expect(service).to receive(:cancel_merge_when_pipeline_succeeds)
      end

      described_class.cancel_merge_when_pipeline_succeeds(noteable, project, author)
    end
  end

  describe '.abort_merge_when_pipeline_succeeds' do
    it 'calls MergeRequestsService' do
      reason = double

      expect_next_instance_of(::SystemNotes::MergeRequestsService) do |service|
        expect(service).to receive(:abort_merge_when_pipeline_succeeds).with(reason)
      end

      described_class.abort_merge_when_pipeline_succeeds(noteable, project, author, reason)
    end
  end

  describe '.change_title' do
    let(:title) { double }

    it 'calls IssuableService' do
      expect_next_instance_of(::SystemNotes::IssuablesService) do |service|
        expect(service).to receive(:change_title).with(title)
      end

      described_class.change_title(noteable, project, author, title)
    end
  end

  describe '.change_description' do
    it 'calls IssuableService' do
      expect_next_instance_of(::SystemNotes::IssuablesService) do |service|
        expect(service).to receive(:change_description)
      end

      described_class.change_description(noteable, project, author)
    end
  end

  describe '.change_issue_confidentiality' do
    it 'calls IssuableService' do
      expect_next_instance_of(::SystemNotes::IssuablesService) do |service|
        expect(service).to receive(:change_issue_confidentiality)
      end

      described_class.change_issue_confidentiality(noteable, project, author)
    end
  end

  describe '.change_branch' do
    it 'calls MergeRequestsService' do
      old_branch = double('old_branch')
      new_branch = double('new_branch')
      branch_type = double('branch_type')
      event_type = double('event_type')

      expect_next_instance_of(::SystemNotes::MergeRequestsService) do |service|
        expect(service).to receive(:change_branch).with(branch_type, event_type, old_branch, new_branch)
      end

      described_class.change_branch(noteable, project, author, branch_type, event_type, old_branch, new_branch)
    end
  end

  describe '.change_branch_presence' do
    it 'calls MergeRequestsService' do
      presence = double
      branch = double
      branch_type = double

      expect_next_instance_of(::SystemNotes::MergeRequestsService) do |service|
        expect(service).to receive(:change_branch_presence).with(branch_type, branch, presence)
      end

      described_class.change_branch_presence(noteable, project, author, branch_type, branch, presence)
    end
  end

  describe '.new_issue_branch' do
    it 'calls MergeRequestsService' do
      branch = double
      branch_project = double

      expect_next_instance_of(::SystemNotes::MergeRequestsService) do |service|
        expect(service).to receive(:new_issue_branch).with(branch, branch_project: branch_project)
      end

      described_class.new_issue_branch(noteable, project, author, branch, branch_project: branch_project)
    end
  end

  describe '.new_merge_request' do
    it 'calls MergeRequestsService' do
      merge_request = double

      expect_next_instance_of(::SystemNotes::MergeRequestsService) do |service|
        expect(service).to receive(:new_merge_request).with(merge_request)
      end

      described_class.new_merge_request(noteable, project, author, merge_request)
    end
  end

  describe '.zoom_link_added' do
    it 'calls ZoomService' do
      expect_next_instance_of(::SystemNotes::ZoomService) do |service|
        expect(service).to receive(:zoom_link_added)
      end

      described_class.zoom_link_added(noteable, project, author)
    end
  end

  describe '.zoom_link_removed' do
    it 'calls ZoomService' do
      expect_next_instance_of(::SystemNotes::ZoomService) do |service|
        expect(service).to receive(:zoom_link_removed)
      end

      described_class.zoom_link_removed(noteable, project, author)
    end
  end

  describe '.cross_reference' do
    let(:mentioned_in) { double }

    it 'calls IssuableService' do
      expect_next_instance_of(::SystemNotes::IssuablesService) do |service|
        expect(service).to receive(:cross_reference).with(mentioned_in)
      end

      described_class.cross_reference(double, mentioned_in, double)
    end
  end

  describe '.cross_reference_disallowed?' do
    let(:mentioned_in) { double }

    it 'calls IssuableService' do
      expect_next_instance_of(::SystemNotes::IssuablesService) do |service|
        expect(service).to receive(:cross_reference_disallowed?).with(mentioned_in)
      end

      described_class.cross_reference_disallowed?(double, mentioned_in)
    end
  end

  describe '.cross_reference_exists?' do
    let(:mentioned_in) { double }

    it 'calls IssuableService' do
      expect_next_instance_of(::SystemNotes::IssuablesService) do |service|
        expect(service).to receive(:cross_reference_exists?).with(mentioned_in)
      end

      described_class.cross_reference_exists?(double, mentioned_in)
    end
  end

  describe '.noteable_moved' do
    let(:noteable_ref) { double }
    let(:direction) { double }

    it 'calls IssuableService' do
      expect_next_instance_of(::SystemNotes::IssuablesService) do |service|
        expect(service).to receive(:noteable_moved).with(noteable_ref, direction)
      end

      described_class.noteable_moved(double, double, noteable_ref, double, direction: direction)
    end
  end

  describe '.noteable_cloned' do
    let(:noteable_ref) { double }
    let(:direction) { double }
    let(:created_at) { double }

    it 'calls IssuableService' do
      expect_next_instance_of(::SystemNotes::IssuablesService) do |service|
        expect(service).to receive(:noteable_cloned).with(noteable_ref, direction, created_at: created_at)
      end

      described_class.noteable_cloned(double, double, noteable_ref, double, direction: direction, created_at: created_at)
    end
  end

  describe '.change_time_estimate' do
    it 'calls TimeTrackingService' do
      expect_next_instance_of(::SystemNotes::TimeTrackingService) do |service|
        expect(service).to receive(:change_time_estimate)
      end

      described_class.change_time_estimate(noteable, project, author)
    end
  end

  describe '.discussion_continued_in_issue' do
    let(:discussion) { create(:diff_note_on_merge_request, project: project).to_discussion }
    let(:merge_request) { discussion.noteable }
    let(:issue) { create(:issue, project: project) }

    def reloaded_merge_request
      MergeRequest.find(merge_request.id)
    end

    subject { described_class.discussion_continued_in_issue(discussion, project, author, issue) }

    it_behaves_like 'a system note' do
      let(:expected_noteable) { discussion.first_note.noteable }
      let(:action)              { 'discussion' }
    end

    it 'creates a new note in the discussion' do
      # we need to completely rebuild the merge request object, or the `@discussions` on the merge request are not reloaded.
      expect { subject }.to change { reloaded_merge_request.discussions.first.notes.size }.by(1)
    end

    it 'mentions the created issue in the system note' do
      expect(subject.note).to include(issue.to_reference)
    end
  end

  describe '.change_time_spent' do
    it 'calls TimeTrackingService' do
      expect_next_instance_of(::SystemNotes::TimeTrackingService) do |service|
        expect(service).to receive(:change_time_spent)
      end

      described_class.change_time_spent(noteable, project, author)
    end
  end

  describe '.created_timelog' do
    let(:issue) { create(:issue, project: project) }
    let(:timelog) { create(:timelog, user: author, issue: issue, time_spent: 1800) }

    it 'calls TimeTrackingService' do
      expect_next_instance_of(::SystemNotes::TimeTrackingService) do |service|
        expect(service).to receive(:created_timelog)
      end

      described_class.created_timelog(noteable, project, author, timelog)
    end
  end

  describe '.remove_timelog' do
    let(:issue) { create(:issue, project: project) }
    let(:timelog) { create(:timelog, user: author, issue: issue, time_spent: 1800) }

    it 'calls TimeTrackingService' do
      expect_next_instance_of(::SystemNotes::TimeTrackingService) do |service|
        expect(service).to receive(:remove_timelog)
      end

      described_class.remove_timelog(noteable, project, author, timelog)
    end
  end

  describe '.handle_merge_request_draft' do
    it 'calls MergeRequestsService' do
      expect_next_instance_of(::SystemNotes::MergeRequestsService) do |service|
        expect(service).to receive(:handle_merge_request_draft)
      end

      described_class.handle_merge_request_draft(noteable, project, author)
    end
  end

  describe '.add_merge_request_draft_from_commit' do
    it 'calls MergeRequestsService' do
      commit = double

      expect_next_instance_of(::SystemNotes::MergeRequestsService) do |service|
        expect(service).to receive(:add_merge_request_draft_from_commit).with(commit)
      end

      described_class.add_merge_request_draft_from_commit(noteable, project, author, commit)
    end
  end

  describe '.change_task_status' do
    let(:new_task) { double }

    it 'calls IssuableService' do
      expect_next_instance_of(::SystemNotes::IssuablesService) do |service|
        expect(service).to receive(:change_task_status).with(new_task)
      end

      described_class.change_task_status(noteable, project, author, new_task)
    end
  end

  describe '.resolve_all_discussions' do
    it 'calls MergeRequestsService' do
      expect_next_instance_of(::SystemNotes::MergeRequestsService) do |service|
        expect(service).to receive(:resolve_all_discussions)
      end

      described_class.resolve_all_discussions(noteable, project, author)
    end
  end

  describe '.diff_discussion_outdated' do
    it 'calls MergeRequestsService' do
      discussion = double
      change_position = double

      expect_next_instance_of(::SystemNotes::MergeRequestsService) do |service|
        expect(service).to receive(:diff_discussion_outdated).with(discussion, change_position)
      end

      described_class.diff_discussion_outdated(discussion, project, author, change_position)
    end
  end

  describe '.mark_duplicate_issue' do
    let(:canonical_issue) { double }

    it 'calls IssuableService' do
      expect_next_instance_of(::SystemNotes::IssuablesService) do |service|
        expect(service).to receive(:mark_duplicate_issue).with(canonical_issue)
      end

      described_class.mark_duplicate_issue(noteable, project, author, canonical_issue)
    end
  end

  describe '.mark_canonical_issue_of_duplicate' do
    let(:duplicate_issue) { double }

    it 'calls IssuableService' do
      expect_next_instance_of(::SystemNotes::IssuablesService) do |service|
        expect(service).to receive(:mark_canonical_issue_of_duplicate).with(duplicate_issue)
      end

      described_class.mark_canonical_issue_of_duplicate(noteable, project, author, duplicate_issue)
    end
  end

  describe '.discussion_lock' do
    let(:issuable) { double }

    before do
      allow(issuable).to receive(:project).and_return(double)
    end

    it 'calls IssuableService' do
      expect_next_instance_of(::SystemNotes::IssuablesService) do |service|
        expect(service).to receive(:discussion_lock)
      end

      described_class.discussion_lock(issuable, double)
    end
  end

  describe '.auto_resolve_prometheus_alert' do
    it 'calls IssuableService' do
      expect_next_instance_of(::SystemNotes::IssuablesService) do |service|
        expect(service).to receive(:auto_resolve_prometheus_alert)
      end

      described_class.auto_resolve_prometheus_alert(noteable, project, author)
    end
  end

  describe '.design_version_added' do
    let(:version) { create(:design_version) }

    it 'calls DesignManagementService' do
      expect_next_instance_of(SystemNotes::DesignManagementService) do |service|
        expect(service).to receive(:design_version_added).with(version)
      end

      described_class.design_version_added(version)
    end
  end

  describe '.design_discussion_added' do
    let(:discussion_note) { create(:diff_note_on_design) }

    it 'calls DesignManagementService' do
      expect_next_instance_of(SystemNotes::DesignManagementService) do |service|
        expect(service).to receive(:design_discussion_added).with(discussion_note)
      end

      described_class.design_discussion_added(discussion_note)
    end
  end

  describe '.approve_mr' do
    it 'calls MergeRequestsService' do
      expect_next_instance_of(::SystemNotes::MergeRequestsService) do |service|
        expect(service).to receive(:approve_mr)
      end

      described_class.approve_mr(noteable, author)
    end
  end

  describe '.unapprove_mr' do
    it 'calls MergeRequestsService' do
      expect_next_instance_of(::SystemNotes::MergeRequestsService) do |service|
        expect(service).to receive(:unapprove_mr)
      end

      described_class.unapprove_mr(noteable, author)
    end
  end

  describe '.change_alert_status' do
    let(:alert) { build(:alert_management_alert) }

    context 'with status change reason' do
      let(:reason) { 'reason for status change' }

      it 'calls AlertManagementService' do
        expect_next_instance_of(SystemNotes::AlertManagementService) do |service|
          expect(service).to receive(:change_alert_status).with(reason)
        end

        described_class.change_alert_status(alert, author, reason)
      end
    end

    context 'without status change reason' do
      it 'calls AlertManagementService' do
        expect_next_instance_of(SystemNotes::AlertManagementService) do |service|
          expect(service).to receive(:change_alert_status).with(nil)
        end

        described_class.change_alert_status(alert, author)
      end
    end
  end

  describe '.new_alert_issue' do
    let(:alert) { build(:alert_management_alert, :with_incident) }

    it 'calls AlertManagementService' do
      expect_next_instance_of(SystemNotes::AlertManagementService) do |service|
        expect(service).to receive(:new_alert_issue).with(alert.issue)
      end

      described_class.new_alert_issue(alert, alert.issue, author)
    end
  end

  describe '.create_new_alert' do
    let(:alert) { build(:alert_management_alert) }
    let(:monitoring_tool) { 'Prometheus' }

    it 'calls AlertManagementService' do
      expect_next_instance_of(SystemNotes::AlertManagementService) do |service|
        expect(service).to receive(:create_new_alert).with(monitoring_tool)
      end

      described_class.create_new_alert(alert, monitoring_tool)
    end
  end

  describe '.change_incident_severity' do
    let(:incident) { build(:incident) }

    it 'calls IncidentService' do
      expect_next_instance_of(SystemNotes::IncidentService) do |service|
        expect(service).to receive(:change_incident_severity)
      end

      described_class.change_incident_severity(incident, author)
    end
  end

  describe '.change_incident_status' do
    let(:incident) { instance_double('Issue', project: project) }

    context 'with status change reason' do
      let(:reason) { 'reason for status change' }

      it 'calls IncidentService' do
        expect_next_instance_of(SystemNotes::IncidentService) do |service|
          expect(service).to receive(:change_incident_status).with(reason)
        end

        described_class.change_incident_status(incident, author, reason)
      end
    end

    context 'without status change reason' do
      it 'calls IncidentService' do
        expect_next_instance_of(SystemNotes::IncidentService) do |service|
          expect(service).to receive(:change_incident_status).with(nil)
        end

        described_class.change_incident_status(incident, author)
      end
    end
  end

  describe '.log_resolving_alert' do
    let(:alert) { build(:alert_management_alert) }
    let(:monitoring_tool) { 'Prometheus' }

    it 'calls AlertManagementService' do
      expect_next_instance_of(SystemNotes::AlertManagementService) do |service|
        expect(service).to receive(:log_resolving_alert).with(monitoring_tool)
      end

      described_class.log_resolving_alert(alert, monitoring_tool)
    end
  end

  describe '.change_issue_type' do
    let(:incident) { build(:incident) }

    it 'calls IssuableService' do
      expect_next_instance_of(::SystemNotes::IssuablesService) do |service|
        expect(service).to receive(:change_issue_type).with('issue')
      end

      described_class.change_issue_type(incident, author, 'issue')
    end
  end

  describe '.add_timeline_event' do
    let(:timeline_event) { instance_double('IncidentManagement::TimelineEvent', incident: noteable, project: project) }

    it 'calls IncidentsService' do
      expect_next_instance_of(::SystemNotes::IncidentsService) do |service|
        expect(service).to receive(:add_timeline_event).with(timeline_event)
      end

      described_class.add_timeline_event(timeline_event)
    end
  end

  describe '.edit_timeline_event' do
    let(:timeline_event) { instance_double('IncidentManagement::TimelineEvent', incident: noteable, project: project) }

    it 'calls IncidentsService' do
      expect_next_instance_of(::SystemNotes::IncidentsService) do |service|
        expect(service).to receive(:edit_timeline_event).with(timeline_event, author, was_changed: :occurred_at)
      end

      described_class.edit_timeline_event(timeline_event, author, was_changed: :occurred_at)
    end
  end

  describe '.delete_timeline_event' do
    it 'calls IncidentsService' do
      expect_next_instance_of(::SystemNotes::IncidentsService) do |service|
        expect(service).to receive(:delete_timeline_event).with(author)
      end

      described_class.delete_timeline_event(noteable, author)
    end
  end

  describe '.relate_work_item' do
    let(:work_item) { double('work_item', issue_type: :task) }
    let(:noteable) { double }

    before do
      allow(noteable).to receive(:project).and_return(double)
    end

    it 'calls IssuableService' do
      expect_next_instance_of(::SystemNotes::IssuablesService) do |service|
        expect(service).to receive(:hierarchy_changed).with(work_item, 'relate')
      end

      described_class.relate_work_item(noteable, work_item, double)
    end
  end

  describe '.unrelate_wotk_item' do
    let(:work_item) { double('work_item', issue_type: :task) }
    let(:noteable) { double }

    before do
      allow(noteable).to receive(:project).and_return(double)
    end

    it 'calls IssuableService' do
      expect_next_instance_of(::SystemNotes::IssuablesService) do |service|
        expect(service).to receive(:hierarchy_changed).with(work_item, 'unrelate')
      end

      described_class.unrelate_work_item(noteable, work_item, double)
    end
  end
end


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

class BulkPushEventPayloadService
  def initialize(event, push_data)
    @event = event
    @push_data = push_data
  end

  def execute
    @event.build_push_event_payload(
      action: @push_data[:action],
      commit_count: 0,
      ref_count: @push_data[:ref_count],
      ref_type: @push_data[:ref_type]
    )

    @event.push_event_payload.tap(&:save!)
  end
end

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe BulkPushEventPayloadService, feature_category: :source_code_management do
  let(:event) { create(:push_event) }

  let(:push_data) do
    {
      action: :created,
      ref_count: 4,
      ref_type: :branch
    }
  end

  subject { described_class.new(event, push_data) }

  it 'creates a PushEventPayload' do
    push_event_payload = subject.execute

    expect(push_event_payload).to be_persisted
    expect(push_event_payload.action).to eq(push_data[:action].to_s)
    expect(push_event_payload.commit_count).to eq(0)
    expect(push_event_payload.ref_count).to eq(push_data[:ref_count])
    expect(push_event_payload.ref_type).to eq(push_data[:ref_type].to_s)
  end
end