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

module Snippets
  class RepositoryValidationService
    attr_reader :current_user, :snippet, :repository

    RepositoryValidationError = Class.new(StandardError)

    def initialize(user, snippet)
      @current_user = user
      @snippet = snippet
      @repository = snippet.repository
    end

    def execute
      if snippet.nil?
        return service_response_error('No snippet found.', 404)
      end

      check_branch_count!
      check_branch_name_default!
      check_tag_count!
      check_file_count!
      check_size!

      ServiceResponse.success(message: 'Valid snippet repository.')
    rescue RepositoryValidationError => e
      ServiceResponse.error(message: "Error: #{e.message}", http_status: 400)
    end

    private

    def check_branch_count!
      return if repository.branch_count == 1

      raise RepositoryValidationError, _('Repository has more than one branch.')
    end

    def check_branch_name_default!
      branches = repository.branch_names

      return if branches.first == snippet.default_branch

      raise RepositoryValidationError, _('Repository has an invalid default branch name.')
    end

    def check_tag_count!
      return if repository.tag_count == 0

      raise RepositoryValidationError, _('Repository has tags.')
    end

    def check_file_count!
      file_count = repository.ls_files(snippet.default_branch).size
      limit = Snippet.max_file_limit

      if file_count > limit
        raise RepositoryValidationError, _('Repository files count over the limit')
      end

      if file_count == 0
        raise RepositoryValidationError, _('Repository must contain at least 1 file.')
      end
    end

    def check_size!
      return unless snippet.repository_size_checker.above_size_limit?

      raise RepositoryValidationError, _('Repository size is above the limit.')
    end
  end
end

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Snippets::RepositoryValidationService, feature_category: :source_code_management do
  describe '#execute' do
    let_it_be(:user)    { create(:user) }
    let_it_be(:snippet) { create(:personal_snippet, :empty_repo, author: user) }

    let(:repository)    { snippet.repository }
    let(:service)       { described_class.new(user, snippet) }

    subject { service.execute }

    before do
      allow(repository).to receive(:branch_count).and_return(1)
      allow(repository).to receive(:ls_files).and_return(['foo'])
      allow(repository).to receive(:branch_names).and_return(['master'])
    end

    it 'returns error when the repository has more than one branch' do
      allow(repository).to receive(:branch_count).and_return(2)

      expect(subject).to be_error
      expect(subject.message).to match /Repository has more than one branch/
    end

    it 'returns error when existing branch name is not the default one' do
      allow(repository).to receive(:branch_names).and_return(['foo'])

      expect(subject).to be_error
      expect(subject.message).to match /Repository has an invalid default branch name/
    end

    it 'returns error when the repository has tags' do
      allow(repository).to receive(:tag_count).and_return(1)

      expect(subject).to be_error
      expect(subject.message).to match /Repository has tags/
    end

    it 'returns error when the repository has more file than the limit' do
      limit = Snippet.max_file_limit + 1
      files = Array.new(limit) { FFaker::Filesystem.file_name }
      allow(repository).to receive(:ls_files).and_return(files)

      expect(subject).to be_error
      expect(subject.message).to match /Repository files count over the limit/
    end

    it 'returns error when the repository has no files' do
      allow(repository).to receive(:ls_files).and_return([])

      expect(subject).to be_error
      expect(subject.message).to match /Repository must contain at least 1 file/
    end

    it 'returns error when the repository size is over the limit' do
      expect_next_instance_of(Gitlab::RepositorySizeChecker) do |checker|
        expect(checker).to receive(:above_size_limit?).and_return(true)
      end

      expect(subject).to be_error
      expect(subject.message).to match /Repository size is above the limit/
    end

    it 'returns success when no validation errors are raised' do
      expect(subject).to be_success
    end
  end
end


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

module Snippets
  # Tries to schedule a move for every snippet with repositories on the source shard
  class ScheduleBulkRepositoryShardMovesService
    include ScheduleBulkRepositoryShardMovesMethods
    extend ::Gitlab::Utils::Override

    private

    override :repository_klass
    def repository_klass
      SnippetRepository
    end

    override :container_klass
    def container_klass
      Snippet
    end

    override :container_column
    def container_column
      :snippet_id
    end

    override :schedule_bulk_worker_klass
    def self.schedule_bulk_worker_klass
      ::Snippets::ScheduleBulkRepositoryShardMovesWorker
    end
  end
end

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Snippets::ScheduleBulkRepositoryShardMovesService, feature_category: :source_code_management do
  it_behaves_like 'moves repository shard in bulk' do
    let_it_be_with_reload(:container) { create(:snippet, :repository) }

    let(:move_service_klass) { Snippets::RepositoryStorageMove }
    let(:bulk_worker_klass) { ::Snippets::ScheduleBulkRepositoryShardMovesWorker }
  end
end


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

module Snippets
  class DestroyService
    include Gitlab::Allowable

    attr_reader :current_user, :snippet

    DestroyError = Class.new(StandardError)

    def initialize(user, snippet)
      @current_user = user
      @snippet = snippet
    end

    def execute
      if snippet.nil?
        return service_response_error('No snippet found.', 404)
      end

      unless user_can_delete_snippet?
        return service_response_error(
          "You don't have access to delete this snippet.",
          403
        )
      end

      attempt_destroy!

      ServiceResponse.success(message: 'Snippet was deleted.')
    rescue DestroyError
      service_response_error('Failed to remove snippet repository.', 400)
    rescue StandardError
      service_response_error('Failed to remove snippet.', 400)
    end

    private

    def attempt_destroy!
      result = Repositories::DestroyService.new(snippet.repository).execute

      raise DestroyError if result[:status] == :error

      snippet.destroy!
    end

    def user_can_delete_snippet?
      can?(current_user, :admin_snippet, snippet)
    end

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

Snippets::DestroyService.prepend_mod_with('Snippets::DestroyService')

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Snippets::DestroyService, feature_category: :source_code_management do
  let_it_be(:project) { create(:project) }
  let_it_be(:user) { create(:user) }
  let_it_be(:other_user) { create(:user) }

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

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

      it 'returns a ServiceResponse error' do
        expect(subject).to be_error
      end
    end

    shared_examples 'a successful destroy' do
      it 'deletes the snippet' do
        expect { subject }.to change { Snippet.count }.by(-1)
      end

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

    shared_examples 'an unsuccessful destroy' do
      it 'does not delete the snippet' do
        expect { subject }.not_to change { Snippet.count }
      end

      it 'returns ServiceResponse error' do
        expect(subject).to be_error
      end
    end

    shared_examples 'deletes the snippet repository' do
      it 'removes the snippet repository' do
        expect(snippet.repository.exists?).to be_truthy
        expect_next_instance_of(Repositories::DestroyService) do |instance|
          expect(instance).to receive(:execute).and_call_original
        end

        expect(subject).to be_success
      end

      context 'when the repository deletion service raises an error' do
        before do
          allow_next_instance_of(Repositories::DestroyService) do |instance|
            allow(instance).to receive(:execute).and_return({ status: :error })
          end
        end

        it_behaves_like 'an unsuccessful destroy'
      end

      context 'when a destroy error is raised' do
        before do
          allow(snippet).to receive(:destroy!).and_raise(ActiveRecord::ActiveRecordError)
        end

        it_behaves_like 'an unsuccessful destroy'
      end

      context 'when repository is nil' do
        it 'does not schedule anything and return success' do
          allow(snippet).to receive(:repository).and_return(nil)

          expect_next_instance_of(Repositories::DestroyService) do |instance|
            expect(instance).to receive(:execute).and_call_original
          end

          expect(subject).to be_success
        end
      end
    end

    context 'when ProjectSnippet' do
      let!(:snippet) { create(:project_snippet, :repository, project: project, author: author) }

      context 'when user is able to admin_project_snippet' do
        let(:author) { user }

        before do
          project.add_developer(user)
        end

        it_behaves_like 'a successful destroy'
        it_behaves_like 'deletes the snippet repository'

        context 'project statistics' do
          before do
            snippet.statistics.refresh!
          end

          it 'updates stats after deletion' do
            expect(project.reload.statistics.snippets_size).not_to be_zero

            subject

            expect(project.reload.statistics.snippets_size).to be_zero
          end

          it 'schedules a namespace statistics update' do
            expect(Namespaces::ScheduleAggregationWorker).to receive(:perform_async).with(project.namespace_id).once

            subject
          end
        end
      end

      context 'when user is not able to admin_project_snippet' do
        let(:author) { other_user }

        it_behaves_like 'an unsuccessful destroy'
      end
    end

    context 'when PersonalSnippet' do
      let!(:snippet) { create(:personal_snippet, :repository, author: author) }

      context 'when user is able to admin_personal_snippet' do
        let(:author) { user }

        it_behaves_like 'a successful destroy'
        it_behaves_like 'deletes the snippet repository'

        it 'schedules a namespace statistics update' do
          expect(Namespaces::ScheduleAggregationWorker).to receive(:perform_async).with(author.namespace_id)

          subject
        end
      end

      context 'when user is not able to admin_personal_snippet' do
        let(:author) { other_user }

        it_behaves_like 'an unsuccessful destroy'
      end
    end

    context 'when the repository does not exist' do
      let(:snippet) { create(:personal_snippet, author: user) }

      it 'does not schedule anything and return success' do
        expect(snippet.repository).not_to be_nil
        expect(snippet.repository.exists?).to be_falsey

        expect_next_instance_of(Repositories::DestroyService) do |instance|
          expect(instance).to receive(:execute).and_call_original
        end

        expect(subject).to be_success
      end
    end
  end
end


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

module Snippets
  class UpdateService < Snippets::BaseService
    COMMITTABLE_ATTRIBUTES = %w[file_name content].freeze

    UpdateError = Class.new(StandardError)

    def initialize(project:, current_user: nil, params: {}, perform_spam_check: false)
      super(project: project, current_user: current_user, params: params)
      @perform_spam_check = perform_spam_check
    end

    def execute(snippet)
      return invalid_params_error(snippet) unless valid_params?

      if visibility_changed?(snippet) && !visibility_allowed?(visibility_level)
        return forbidden_visibility_error(snippet)
      end

      update_snippet_attributes(snippet)

      files = snippet.all_files.map { |f| { path: f } } + file_paths_to_commit

      if perform_spam_check
        snippet.check_for_spam(user: current_user, action: :update, extra_features: { files: files })
      end

      if save_and_commit(snippet)
        Gitlab::UsageDataCounters::SnippetCounter.count(:update)

        ServiceResponse.success(payload: { snippet: snippet })
      else
        snippet_error_response(snippet, 400)
      end
    end

    private

    attr_reader :perform_spam_check

    def visibility_changed?(snippet)
      visibility_level && visibility_level.to_i != snippet.visibility_level
    end

    def update_snippet_attributes(snippet)
      # We can remove the following condition once
      # https://gitlab.com/gitlab-org/gitlab/-/issues/217801
      # is implemented.
      # Once we can perform different operations through this service
      # we won't need to keep track of the `content` and `file_name` fields
      #
      # If the repository does not exist we don't need to update `params`
      # because we need to commit the information from the database
      if snippet_actions.any? && snippet.repository_exists?
        params[:content] = snippet_actions[0].content if snippet_actions[0].content
        params[:file_name] = snippet_actions[0].file_path
      end

      snippet.assign_attributes(params)
    end

    def save_and_commit(snippet)
      return false unless snippet.save

      # If the updated attributes does not need to update
      # the repository we can just return
      return true unless committable_attributes?

      unless snippet.repository_exists?
        create_repository_for(snippet)
        create_first_commit_using_db_data(snippet)
      end

      create_commit(snippet)

      true
    rescue StandardError => e
      # Restore old attributes but re-assign changes so they're not lost
      unless snippet.previous_changes.empty?
        snippet.previous_changes.each { |attr, value| snippet[attr] = value[0] }
        snippet.save

        snippet.assign_attributes(params)
      end

      add_snippet_repository_error(snippet: snippet, error: e)

      Gitlab::ErrorTracking.log_exception(e, service: 'Snippets::UpdateService')

      # If the commit action failed we remove it because
      # we don't want to leave empty repositories
      # around, to allow cloning them.
      delete_repository(snippet) if repository_empty?(snippet)

      false
    end

    def create_repository_for(snippet)
      snippet.create_repository

      raise CreateRepositoryError, 'Repository could not be created' unless snippet.repository_exists?
    end

    # If the user provides `snippet_actions` and the repository
    # does not exist, we need to commit first the snippet info stored
    # in the database.  Mostly because the content inside `snippet_actions`
    # would assume that the file is already in the repository.
    def create_first_commit_using_db_data(snippet)
      return if snippet_actions.empty?

      attrs = commit_attrs(snippet, INITIAL_COMMIT_MSG)
      actions = [{ file_path: snippet.file_name, content: snippet.content }]

      snippet.snippet_repository.multi_files_action(current_user, actions, **attrs)
    end

    def create_commit(snippet)
      raise UpdateError unless snippet.snippet_repository

      attrs = commit_attrs(snippet, UPDATE_COMMIT_MSG)

      snippet.snippet_repository.multi_files_action(current_user, files_to_commit(snippet), **attrs)
    end

    # Because we are removing repositories we don't want to remove
    # any existing repository with data. Therefore, we cannot
    # rely on cached methods for that check in order to avoid losing
    # data.
    def repository_empty?(snippet)
      snippet.repository._uncached_exists? && !snippet.repository._uncached_has_visible_content?
    end

    def committable_attributes?
      (params.stringify_keys.keys & COMMITTABLE_ATTRIBUTES).present? || snippet_actions.any?
    end

    def build_actions_from_params(snippet)
      file_name_on_repo = snippet.file_name_on_repo

      [{ previous_path: file_name_on_repo,
         file_path: params[:file_name] || file_name_on_repo,
         content: params[:content] }]
    end
  end
end

Snippets::UpdateService.prepend_mod_with('Snippets::UpdateService')

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Snippets::UpdateService, feature_category: :source_code_management do
  describe '#execute', :aggregate_failures do
    let_it_be(:user) { create(:user) }
    let_it_be(:admin) { create :user, admin: true }

    let(:action) { :update }
    let(:visibility_level) { Gitlab::VisibilityLevel::PRIVATE }
    let(:base_opts) do
      {
        title: 'Test snippet',
        file_name: 'snippet.rb',
        content: 'puts "hello world"',
        visibility_level: visibility_level
      }
    end

    let(:extra_opts) { {} }
    let(:options) { base_opts.merge(extra_opts) }
    let(:updater) { user }

    let(:service) { described_class.new(project: project, current_user: updater, params: options, perform_spam_check: true) }

    subject { service.execute(snippet) }

    shared_examples 'a service that updates a snippet' do
      it 'updates a snippet with the provided attributes' do
        expect { subject }.to change { snippet.title }.from(snippet.title).to(options[:title])
          .and change { snippet.file_name }.from(snippet.file_name).to(options[:file_name])
          .and change { snippet.content }.from(snippet.content).to(options[:content])
      end
    end

    shared_examples 'public visibility level restrictions apply' do
      let(:visibility_level) { Gitlab::VisibilityLevel::PUBLIC }

      before do
        stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC])
      end

      context 'when user is not an admin' do
        it 'responds with an error' do
          expect(subject).to be_error
        end

        it 'does not update snippet to public visibility' do
          original_visibility = snippet.visibility_level

          expect(subject.message).to match('has been restricted')
          expect(snippet.visibility_level).to eq(original_visibility)
        end
      end

      context 'when user is an admin' do
        let(:updater) { admin }

        it 'responds with success' do
          expect(subject).to be_success
        end

        it 'updates the snippet to public visibility' do
          old_visibility = snippet.visibility_level

          expect(subject.payload[:snippet]).not_to be_nil
          expect(snippet.visibility_level).not_to eq(old_visibility)
          expect(snippet.visibility_level).to eq(Gitlab::VisibilityLevel::PUBLIC)
        end
      end

      context 'when visibility level is passed as a string' do
        before do
          options[:visibility] = 'internal'
          options.delete(:visibility_level)
        end

        it 'assigns the correct visibility level' do
          expect(subject).to be_success
          expect(snippet.visibility_level).to eq(Gitlab::VisibilityLevel::INTERNAL)
        end
      end
    end

    shared_examples 'snippet update data is tracked' do
      let(:counter) { Gitlab::UsageDataCounters::SnippetCounter }

      it 'increments count when create succeeds' do
        expect { subject }.to change { counter.read(:update) }.by 1
      end

      context 'when update fails' do
        let(:extra_opts) { { title: '' } }

        it 'does not increment count' do
          expect { subject }.not_to change { counter.read(:update) }
        end
      end
    end

    shared_examples 'creates repository and creates file' do
      context 'when file_name and content params are used' do
        it 'creates repository' do
          expect(snippet.repository).not_to exist

          subject

          expect(snippet.repository).to exist
        end

        it 'commits the files to the repository' do
          subject

          expect(snippet.blobs.count).to eq 1

          blob = snippet.repository.blob_at('master', options[:file_name])

          expect(blob.data).to eq options[:content]
        end

        context 'when the repository creation fails' do
          before do
            allow(snippet).to receive(:repository_exists?).and_return(false)
          end

          it 'raise an error' do
            expect(subject).to be_error
            expect(subject.payload[:snippet].errors[:repository].to_sentence).to eq 'Error updating the snippet - Repository could not be created'
          end

          it 'does not try to commit file' do
            expect(service).not_to receive(:create_commit)

            subject
          end
        end
      end

      context 'when snippet_actions param is used' do
        let(:file_path) { 'CHANGELOG' }
        let(:created_file_path) { 'New file' }
        let(:content) { 'foobar' }
        let(:snippet_actions) { [{ action: :move, previous_path: snippet.file_name, file_path: file_path }, { action: :create, file_path: created_file_path, content: content }] }
        let(:base_opts) do
          {
            snippet_actions: snippet_actions
          }
        end

        it 'performs operation without raising errors' do
          db_content = snippet.content

          expect(subject).to be_success

          new_blob = snippet.repository.blob_at('master', file_path)
          created_file = snippet.repository.blob_at('master', created_file_path)

          expect(new_blob.data).to eq db_content
          expect(created_file.data).to eq content
        end

        context 'when the repository is not created' do
          it 'keeps snippet database data' do
            old_file_name = snippet.file_name
            old_file_content = snippet.content

            expect_next_instance_of(described_class) do |instance|
              expect(instance).to receive(:create_repository_for).and_raise(StandardError)
            end

            snippet = subject.payload[:snippet]

            expect(subject).to be_error
            expect(snippet.file_name).to eq(old_file_name)
            expect(snippet.content).to eq(old_file_content)
          end
        end
      end
    end

    shared_examples 'commit operation fails' do
      let_it_be(:gitlab_shell) { Gitlab::Shell.new }

      before do
        allow(service).to receive(:create_commit).and_raise(SnippetRepository::CommitError)
      end

      it 'returns error' do
        response = subject

        expect(response).to be_error
        expect(response.payload[:snippet].errors[:repository].to_sentence).to eq 'Error updating the snippet'
      end

      context 'when repository is empty' do
        before do
          allow(service).to receive(:repository_empty?).and_return(true)
        end

        it 'destroys the created repository in disk' do
          subject

          expect(gitlab_shell.repository_exists?(snippet.repository.storage, "#{snippet.disk_path}.git")).to be_falsey
        end

        it 'destroys the SnippetRepository object' do
          subject

          expect(snippet.reload.snippet_repository).to be_nil
        end

        it 'expires the repository exists method cache' do
          response = subject

          expect(response).to be_error
          expect(response.payload[:snippet].repository_exists?).to be_falsey
        end
      end

      context 'when repository is not empty' do
        before do
          allow(service).to receive(:repository_empty?).and_return(false)
        end

        it 'does not destroy the repository' do
          subject

          expect(gitlab_shell.repository_exists?(snippet.repository.storage, "#{snippet.disk_path}.git")).to be_truthy
        end

        it 'does not destroy the snippet repository' do
          subject

          expect(snippet.reload.snippet_repository).not_to be_nil
        end

        it 'expires the repository exists method cache' do
          response = subject

          expect(response).to be_error
          expect(response.payload[:snippet].repository_exists?).to be_truthy
        end
      end

      context 'with snippet modifications' do
        let(:option_keys) { options.stringify_keys.keys }

        it 'rolls back any snippet modifications' do
          orig_attrs = snippet.attributes.select { |k, v| k.in?(option_keys) }

          subject

          persisted_attrs = snippet.reload.attributes.select { |k, v| k.in?(option_keys) }
          expect(orig_attrs).to eq persisted_attrs
        end

        it 'keeps any snippet modifications' do
          subject

          instance_attrs = snippet.attributes.select { |k, v| k.in?(option_keys) }
          expect(options.stringify_keys).to eq instance_attrs
        end
      end
    end

    shared_examples 'updates repository content' do
      it 'commit the files to the repository' do
        blob = snippet.blobs.first
        options[:file_name] = blob.path + '_new'

        expect(blob.data).not_to eq(options[:content])

        subject

        blob = snippet.blobs.first

        expect(blob.path).to eq(options[:file_name])
        expect(blob.data).to eq(options[:content])
      end

      context 'when an error is raised' do
        let(:error) { SnippetRepository::CommitError.new('foobar') }

        before do
          allow(snippet.snippet_repository).to receive(:multi_files_action).and_raise(error)
        end

        it 'logs the error' do
          expect(Gitlab::ErrorTracking).to receive(:log_exception).with(error, service: 'Snippets::UpdateService')

          subject
        end

        it 'returns error with generic error message' do
          response = subject

          expect(response).to be_error
          expect(response.payload[:snippet].errors[:repository].to_sentence).to eq 'Error updating the snippet'
        end
      end

      it 'returns error if snippet does not have a snippet_repository' do
        allow(snippet).to receive(:snippet_repository).and_return(nil)
        allow(snippet).to receive(:track_snippet_repository).and_return(nil)

        expect(subject).to be_error
      end

      context 'when the repository does not exist' do
        it 'does not try to commit file' do
          allow(snippet).to receive(:repository_exists?).and_return(false)

          expect(service).not_to receive(:create_commit)

          subject
        end
      end
    end

    shared_examples 'committable attributes' do
      context 'when file_name is updated' do
        let(:extra_opts) { { file_name: 'snippet.rb' } }

        it 'commits to repository' do
          expect(service).to receive(:create_commit)
          expect(subject).to be_success
        end
      end

      context 'when content is updated' do
        let(:extra_opts) { { content: 'puts "hello world"' } }

        it 'commits to repository' do
          expect(service).to receive(:create_commit)
          expect(subject).to be_success
        end
      end

      context 'when content or file_name is not updated' do
        let(:options) { { title: 'Test snippet' } }

        it 'does not perform any commit' do
          expect(service).not_to receive(:create_commit)
          expect(subject).to be_success
        end
      end
    end

    shared_examples 'when snippet_actions param is present' do
      let(:file_path) { 'CHANGELOG' }
      let(:content) { 'snippet_content' }
      let(:new_title) { 'New title' }
      let(:snippet_actions) { [{ action: 'update', previous_path: file_path, file_path: file_path, content: content }] }
      let(:base_opts) do
        {
          title: new_title,
          snippet_actions: snippet_actions
        }
      end

      it 'updates a snippet with the provided attributes' do
        file_path = 'foo'
        snippet_actions[0][:action] = 'move'
        snippet_actions[0][:file_path] = file_path

        response = subject
        snippet = response.payload[:snippet]

        expect(response).to be_success
        expect(snippet.title).to eq(new_title)
        expect(snippet.file_name).to eq(file_path)
        expect(snippet.content).to eq(content)
      end

      it 'commits the files to the repository' do
        subject

        blob = snippet.repository.blob_at('master', file_path)

        expect(blob.data).to eq content
      end

      context 'when content or file_name params are present' do
        let(:extra_opts) { { content: 'foo', file_name: 'path' } }

        it 'raises a validation error' do
          response = subject
          snippet = response.payload[:snippet]

          expect(response).to be_error
          expect(snippet.errors.full_messages_for(:content)).to eq ['Content and snippet files cannot be used together']
          expect(snippet.errors.full_messages_for(:file_name)).to eq ['File name and snippet files cannot be used together']
        end
      end

      context 'when snippet_file content is not present' do
        let(:snippet_actions) { [{ action: :move, previous_path: file_path, file_path: 'new_file_path' }] }

        it 'does not update snippet content' do
          content = snippet.content

          expect(subject).to be_success

          expect(snippet.reload.content).to eq content
        end
      end

      context 'when snippet_actions param is invalid' do
        let(:snippet_actions) { [{ action: 'invalid_action' }] }

        it 'raises a validation error' do
          snippet = subject.payload[:snippet]

          expect(subject).to be_error
          expect(snippet.errors.full_messages_for(:snippet_actions)).to eq ['Snippet actions have invalid data']
        end
      end

      context 'when an error is raised committing the file' do
        it 'keeps any snippet modifications' do
          expect_next_instance_of(described_class) do |instance|
            expect(instance).to receive(:create_commit).and_raise(StandardError)
          end

          snippet = subject.payload[:snippet]

          expect(subject).to be_error
          expect(snippet.title).to eq(new_title)
          expect(snippet.file_name).to eq(file_path)
          expect(snippet.content).to eq(content)
        end
      end

      context 'commit actions' do
        let(:new_path) { 'created_new_file' }
        let(:base_opts) { { snippet_actions: snippet_actions } }

        shared_examples 'returns an error' do |error_msg|
          specify do
            response = subject

            expect(response).to be_error
            expect(response.message).to eq error_msg
          end
        end

        context 'update action' do
          let(:snippet_actions) { [{ action: :update, file_path: file_path, content: content }] }

          it 'updates the file content' do
            expect(subject).to be_success

            blob = blob(file_path)

            expect(blob.data).to eq content
          end

          context 'when previous_path is present' do
            let(:snippet_actions) { [{ action: :update, previous_path: file_path, file_path: file_path, content: content }] }

            it 'updates the file content' do
              expect(subject).to be_success

              blob = blob(file_path)

              expect(blob.data).to eq content
            end
          end

          context 'when content is not present' do
            let(:snippet_actions) { [{ action: :update, file_path: file_path }] }

            it_behaves_like 'returns an error', 'Snippet actions have invalid data'
          end

          context 'when file_path does not exist' do
            let(:snippet_actions) { [{ action: :update, file_path: 'makeup_name', content: content }] }

            it_behaves_like 'returns an error', 'Repository Error updating the snippet'
          end
        end

        context 'move action' do
          context 'when file_path and previous_path are the same' do
            let(:snippet_actions) { [{ action: :move, previous_path: file_path, file_path: file_path }] }

            it_behaves_like 'returns an error', 'Snippet actions have invalid data'
          end

          context 'when file_path and previous_path are different' do
            let(:snippet_actions) { [{ action: :move, previous_path: file_path, file_path: new_path }] }

            it 'renames the file' do
              old_blob = blob(file_path)

              expect(subject).to be_success

              blob = blob(new_path)

              expect(blob).to be_present
              expect(blob.data).to eq old_blob.data
            end
          end

          context 'when previous_path does not exist' do
            let(:snippet_actions) { [{ action: :move, previous_path: 'makeup_name', file_path: new_path }] }

            it_behaves_like 'returns an error', 'Repository Error updating the snippet'
          end

          context 'when user wants to rename the file and update content' do
            let(:snippet_actions) { [{ action: :move, previous_path: file_path, file_path: new_path, content: content }] }

            it 'performs both operations' do
              expect(subject).to be_success

              blob = blob(new_path)

              expect(blob).to be_present
              expect(blob.data).to eq content
            end
          end

          context 'when the file_path is not present' do
            let(:snippet_actions) { [{ action: :move, previous_path: file_path }] }

            it 'generates the name for the renamed file' do
              old_blob = blob(file_path)

              expect(blob('snippetfile1.txt')).to be_nil
              expect(subject).to be_success

              new_blob = blob('snippetfile1.txt')

              expect(new_blob).to be_present
              expect(new_blob.data).to eq old_blob.data
            end
          end
        end

        context 'delete action' do
          let(:snippet_actions) { [{ action: :delete, file_path: file_path }] }

          shared_examples 'deletes the file' do
            specify do
              old_blob = blob(file_path)
              expect(old_blob).to be_present

              expect(subject).to be_success
              expect(blob(file_path)).to be_nil
            end
          end

          it_behaves_like 'deletes the file'

          context 'when previous_path is present and same as file_path' do
            let(:snippet_actions) { [{ action: :delete, previous_path: file_path, file_path: file_path }] }

            it_behaves_like 'deletes the file'
          end

          context 'when previous_path is present and is different from file_path' do
            let(:snippet_actions) { [{ action: :delete, previous_path: 'foo', file_path: file_path }] }

            it_behaves_like 'deletes the file'
          end

          context 'when content is present' do
            let(:snippet_actions) { [{ action: :delete, file_path: file_path, content: 'foo' }] }

            it_behaves_like 'deletes the file'
          end

          context 'when file_path does not exist' do
            let(:snippet_actions) { [{ action: :delete, file_path: 'makeup_name' }] }

            it_behaves_like 'returns an error', 'Repository Error updating the snippet'
          end
        end

        context 'create action' do
          let(:snippet_actions) { [{ action: :create, file_path: new_path, content: content }] }

          it 'creates the file' do
            expect(subject).to be_success

            blob = blob(new_path)
            expect(blob).to be_present
            expect(blob.data).to eq content
          end

          context 'when content is not present' do
            let(:snippet_actions) { [{ action: :create, file_path: new_path }] }

            it_behaves_like 'returns an error', 'Snippet actions have invalid data'
          end

          context 'when file_path is not present or empty' do
            let(:snippet_actions) { [{ action: :create, content: content }, { action: :create, file_path: '', content: content }] }

            it 'generates the file path for the files' do
              expect(blob('snippetfile1.txt')).to be_nil
              expect(blob('snippetfile2.txt')).to be_nil

              expect(subject).to be_success

              expect(blob('snippetfile1.txt').data).to eq content
              expect(blob('snippetfile2.txt').data).to eq content
            end
          end

          context 'when file_path already exists in the repository' do
            let(:snippet_actions) { [{ action: :create, file_path: file_path, content: content }] }

            it_behaves_like 'returns an error', 'Repository Error updating the snippet'
          end

          context 'when previous_path is present' do
            let(:snippet_actions) { [{ action: :create, previous_path: 'foo', file_path: new_path, content: content }] }

            it 'creates the file' do
              expect(subject).to be_success

              blob = blob(new_path)
              expect(blob).to be_present
              expect(blob.data).to eq content
            end
          end
        end

        context 'combination of actions' do
          let(:delete_file_path) { 'CHANGELOG' }
          let(:create_file_path) { 'created_new_file' }
          let(:update_file_path) { 'LICENSE' }
          let(:move_previous_path) { 'VERSION' }
          let(:move_file_path) { 'VERSION_new' }

          let(:snippet_actions) do
            [
              { action: :create, file_path: create_file_path, content: content },
              { action: :update, file_path: update_file_path, content: content },
              { action: :delete, file_path: delete_file_path },
              { action: :move, previous_path: move_previous_path, file_path: move_file_path, content: content }
            ]
          end

          it 'performs all operations' do
            expect(subject).to be_success

            expect(blob(delete_file_path)).to be_nil

            created_blob = blob(create_file_path)
            expect(created_blob.data).to eq content

            updated_blob = blob(update_file_path)
            expect(updated_blob.data).to eq content

            expect(blob(move_previous_path)).to be_nil

            moved_blob = blob(move_file_path)
            expect(moved_blob.data).to eq content
          end
        end

        def blob(path)
          snippet.repository.blob_at('master', path)
        end
      end
    end

    shared_examples 'only file_name is present' do
      let(:base_opts) do
        {
          file_name: file_name
        }
      end

      shared_examples 'content is not updated' do
        specify do
          existing_content = snippet.blobs.first.data
          response = subject
          snippet = response.payload[:snippet]

          blob = snippet.repository.blob_at('master', file_name)

          expect(blob).not_to be_nil
          expect(response).to be_success
          expect(blob.data).to eq existing_content
        end
      end

      context 'when renaming the file_name' do
        let(:file_name) { 'new_file_name' }

        it_behaves_like 'content is not updated'
      end

      context 'when file_name does not change' do
        let(:file_name) { snippet.blobs.first.path }

        it_behaves_like 'content is not updated'
      end
    end

    shared_examples 'only content is present' do
      let(:content) { 'new_content' }
      let(:base_opts) do
        {
          content: content
        }
      end

      it 'updates the content' do
        response = subject
        snippet = response.payload[:snippet]

        blob = snippet.repository.blob_at('master', snippet.blobs.first.path)

        expect(blob).not_to be_nil
        expect(response).to be_success
        expect(blob.data).to eq content
      end
    end

    context 'when Project Snippet' do
      let_it_be(:project) { create(:project) }

      let!(:snippet) { create(:project_snippet, :repository, author: user, project: project) }

      before do
        project.add_developer(user)
      end

      it_behaves_like 'a service that updates a snippet'
      it_behaves_like 'public visibility level restrictions apply'
      it_behaves_like 'snippet update data is tracked'
      it_behaves_like 'updates repository content'
      it_behaves_like 'commit operation fails'
      it_behaves_like 'committable attributes'
      it_behaves_like 'when snippet_actions param is present'
      it_behaves_like 'only file_name is present'
      it_behaves_like 'only content is present'
      it_behaves_like 'invalid params error response'
      it_behaves_like 'checking spam'

      context 'when snippet does not have a repository' do
        let!(:snippet) { create(:project_snippet, author: user, project: project) }

        it_behaves_like 'creates repository and creates file'
        it_behaves_like 'commit operation fails'
      end
    end

    context 'when PersonalSnippet' do
      let(:project) { nil }
      let!(:snippet) { create(:personal_snippet, :repository, author: user) }

      it_behaves_like 'a service that updates a snippet'
      it_behaves_like 'public visibility level restrictions apply'
      it_behaves_like 'snippet update data is tracked'
      it_behaves_like 'updates repository content'
      it_behaves_like 'commit operation fails'
      it_behaves_like 'committable attributes'
      it_behaves_like 'when snippet_actions param is present'
      it_behaves_like 'only file_name is present'
      it_behaves_like 'only content is present'
      it_behaves_like 'invalid params error response'
      it_behaves_like 'checking spam'

      context 'when snippet does not have a repository' do
        let!(:snippet) { create(:personal_snippet, author: user, project: project) }

        it_behaves_like 'creates repository and creates file'
        it_behaves_like 'commit operation fails'
      end
    end
  end
end


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

module Snippets
  class BulkDestroyService
    include Gitlab::Allowable

    attr_reader :current_user, :snippets

    DeleteRepositoryError = Class.new(StandardError)
    SnippetAccessError = Class.new(StandardError)

    def initialize(user, snippets)
      @current_user = user
      @snippets = snippets
    end

    def execute(skip_authorization: false)
      return ServiceResponse.success(message: 'No snippets found.') if snippets.empty?

      user_can_delete_snippets! unless skip_authorization
      attempt_delete_repositories!
      snippets.destroy_all # rubocop: disable Cop/DestroyAll

      ServiceResponse.success(message: 'Snippets were deleted.')
    rescue SnippetAccessError
      service_response_error("You don't have access to delete these snippets.", 403)
    rescue DeleteRepositoryError
      service_response_error('Failed to delete snippet repositories.', 400)
    rescue StandardError
      # In case the delete operation fails
      service_response_error('Failed to remove snippets.', 400)
    end

    private

    def user_can_delete_snippets!
      allowed = DeclarativePolicy.user_scope do
        snippets.find_each.all? { |snippet| user_can_delete_snippet?(snippet) }
      end

      raise SnippetAccessError unless allowed
    end

    def user_can_delete_snippet?(snippet)
      can?(current_user, :admin_snippet, snippet)
    end

    def attempt_delete_repositories!
      snippets.each do |snippet|
        result = Repositories::DestroyService.new(snippet.repository).execute

        raise DeleteRepositoryError if result[:status] == :error
      end
    end

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

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Snippets::BulkDestroyService, feature_category: :source_code_management do
  let_it_be(:project) { create(:project) }

  let(:user) { create(:user) }
  let!(:personal_snippet) { create(:personal_snippet, :repository, author: user) }
  let!(:project_snippet) { create(:project_snippet, :repository, project: project, author: user) }
  let(:snippets) { user.snippets }
  let(:gitlab_shell) { Gitlab::Shell.new }
  let(:service_user) { user }

  before do
    project.add_developer(user)
  end

  subject { described_class.new(service_user, snippets) }

  describe '#execute' do
    it 'deletes the snippets in bulk' do
      response = nil

      expect(Repositories::DestroyService).to receive(:new).with(personal_snippet.repository).and_call_original
      expect(Repositories::DestroyService).to receive(:new).with(project_snippet.repository).and_call_original

      aggregate_failures do
        expect do
          response = subject.execute
        end.to change(Snippet, :count).by(-2)

        expect(response).to be_success
        expect(repository_exists?(personal_snippet)).to be_falsey
        expect(repository_exists?(project_snippet)).to be_falsey
      end
    end

    context 'when snippets is empty' do
      let(:snippets) { Snippet.none }

      it 'returns a ServiceResponse success response' do
        response = subject.execute

        expect(response).to be_success
        expect(response.message).to eq 'No snippets found.'
      end
    end

    shared_examples 'error is raised' do
      it 'returns error' do
        response = subject.execute

        aggregate_failures do
          expect(response).to be_error
          expect(response.message).to eq error_message
        end
      end

      it 'no record is deleted' do
        expect do
          subject.execute
        end.not_to change(Snippet, :count)
      end
    end

    context 'when user does not have access to remove the snippet' do
      let(:service_user) { create(:user) }

      it_behaves_like 'error is raised' do
        let(:error_message) { "You don't have access to delete these snippets." }
      end

      context 'when skip_authorization option is passed' do
        subject { described_class.new(service_user, snippets).execute(skip_authorization: true) }

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

        it 'deletes all the snippets that belong to the user' do
          expect { subject }.to change(Snippet, :count).by(-2)
        end
      end
    end

    context 'when an error is raised deleting the repository' do
      before do
        allow_next_instance_of(Repositories::DestroyService) do |instance|
          allow(instance).to receive(:execute).and_return({ status: :error })
        end
      end

      it_behaves_like 'error is raised' do
        let(:error_message) { 'Failed to delete snippet repositories.' }
      end
    end

    context 'when an error is raised deleting the records' do
      before do
        allow(snippets).to receive(:destroy_all).and_raise(ActiveRecord::ActiveRecordError)
      end

      it_behaves_like 'error is raised' do
        let(:error_message) { 'Failed to remove snippets.' }
      end
    end

    context 'when snippet does not have a repository attached' do
      let!(:snippet_without_repo) { create(:personal_snippet, author: user) }

      it 'returns success' do
        response = nil

        expect(Repositories::DestroyService).to receive(:new).with(personal_snippet.repository).and_call_original
        expect(Repositories::DestroyService).to receive(:new).with(project_snippet.repository).and_call_original
        expect(Repositories::DestroyService).to receive(:new).with(snippet_without_repo.repository).and_call_original

        expect do
          response = subject.execute
        end.to change(Snippet, :count).by(-3)

        expect(response).to be_success
      end
    end
  end

  def repository_exists?(snippet, path = snippet.disk_path + ".git")
    gitlab_shell.repository_exists?(snippet.snippet_repository.shard_name, path)
  end
end


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

module Snippets
  class UpdateStatisticsService
    attr_reader :snippet

    def initialize(snippet)
      @snippet = snippet
    end

    def execute
      unless snippet.repository_exists?
        return ServiceResponse.error(message: 'Invalid snippet repository', http_status: 400)
      end

      snippet.repository.expire_statistics_caches
      statistics.refresh!

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

    private

    def statistics
      @statistics ||= snippet.statistics || snippet.build_statistics
    end
  end
end

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Snippets::UpdateStatisticsService, feature_category: :source_code_management do
  describe '#execute' do
    subject { described_class.new(snippet).execute }

    shared_examples 'updates statistics' do
      it 'returns a successful response' do
        expect(subject).to be_success
      end

      it 'expires statistics cache' do
        expect(snippet.repository).to receive(:expire_statistics_caches)

        subject
      end

      context 'when snippet statistics does not exist' do
        it 'creates snippet statistics' do
          snippet.statistics.delete
          snippet.reload

          expect do
            subject
          end.to change(SnippetStatistics, :count).by(1)

          expect(snippet.statistics.commit_count).not_to be_zero
          expect(snippet.statistics.file_count).not_to be_zero
          expect(snippet.statistics.repository_size).not_to be_zero
        end
      end

      context 'when snippet statistics exists' do
        it 'updates snippet statistics' do
          expect(snippet.statistics.commit_count).to be_zero
          expect(snippet.statistics.file_count).to be_zero
          expect(snippet.statistics.repository_size).to be_zero

          subject

          expect(snippet.statistics.commit_count).not_to be_zero
          expect(snippet.statistics.file_count).not_to be_zero
          expect(snippet.statistics.repository_size).not_to be_zero
        end
      end

      context 'when snippet does not have a repository' do
        it 'returns an error response' do
          expect(snippet).to receive(:repository_exists?).and_return(false)

          expect(subject).to be_error
        end
      end

      it 'schedules a namespace storage statistics update' do
        expect(Namespaces::ScheduleAggregationWorker)
            .to receive(:perform_async).once

        subject
      end
    end

    context 'with PersonalSnippet' do
      let!(:snippet) { create(:personal_snippet, :repository) }

      it_behaves_like 'updates statistics'
    end

    context 'with ProjectSnippet' do
      let!(:snippet) { create(:project_snippet, :repository) }
      let(:project_statistics) { snippet.project.statistics }

      it_behaves_like 'updates statistics'

      it 'updates projects statistics "snippets_size"' do
        expect(project_statistics.snippets_size).to be_zero

        subject

        expect(snippet.reload.statistics.repository_size).to eq project_statistics.reload.snippets_size
      end
    end
  end
end


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

# Service for calculating visible Snippet counts via one query
# for the given user or project.
#
# Authorisation level checks will be included, ensuring the correct
# counts will be returned for the given user (if any).
#
# Basic usage:
#
#   user = User.find(1)
#
#   Snippets::CountService.new(user, author: user).execute
#   #=> {
#     are_public: 1,
#     are_internal: 1,
#     are_private: 1,
#     all: 3
#   }
#
# Counts can be scoped to a project:
#
#   user = User.find(1)
#   project = Project.find(1)
#
#   Snippets::CountService.new(user, project: project).execute
#   #=> {
#     are_public: 1,
#     are_internal: 1,
#     are_private: 0,
#     all: 2
#   }
#
# Either a project or an author *must* be supplied.
module Snippets
  class CountService
    def initialize(current_user, author: nil, project: nil)
      if !author && !project
        raise(
          ArgumentError, 'Must provide either an author or a project'
        )
      end

      @snippets_finder = SnippetsFinder.new(current_user, author: author, project: project)
    end

    def execute
      counts = snippet_counts
      return {} unless counts

      counts.slice(
        :are_public,
        :are_private,
        :are_internal,
        :are_public_or_internal,
        :total
      )
    end

    private

    # rubocop: disable CodeReuse/ActiveRecord
    def snippet_counts
      @snippets_finder.execute
        .reorder(nil)
        .select("
          count(case when snippets.visibility_level=#{Snippet::PUBLIC} and snippets.secret is FALSE then 1 else null end) as are_public,
          count(case when snippets.visibility_level=#{Snippet::INTERNAL} then 1 else null end) as are_internal,
          count(case when snippets.visibility_level=#{Snippet::PRIVATE} then 1 else null end) as are_private,
          count(case when visibility_level=#{Snippet::PUBLIC} OR visibility_level=#{Snippet::INTERNAL} then 1 else null end) as are_public_or_internal,
          count(*) as total
        ")
        .take
    end
    # rubocop: enable CodeReuse/ActiveRecord
  end
end

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Snippets::CountService, feature_category: :source_code_management do
  let_it_be(:user) { create(:user) }
  let_it_be(:project) { create(:project, :public) }

  describe '#new' do
    it 'raises an error if no author or project' do
      expect { described_class.new(user) }.to raise_error(ArgumentError)
    end

    it 'uses the SnippetsFinder to scope snippets by user' do
      expect(SnippetsFinder)
        .to receive(:new)
        .with(user, author: user, project: nil)

      described_class.new(user, author: user)
    end

    it 'allows scoping to project' do
      expect(SnippetsFinder)
        .to receive(:new)
        .with(user, author: nil, project: project)

      described_class.new(user, project: project)
    end
  end

  describe '#execute' do
    subject { described_class.new(user, author: user).execute }

    it 'returns a hash of counts' do
      expect(subject).to match({
        are_public: 0,
        are_internal: 0,
        are_private: 0,
        are_public_or_internal: 0,
        total: 0
      })
    end

    it 'only counts snippets the user has access to' do
      create(:personal_snippet, :private, author: user)
      create(:project_snippet, :private, author: user)
      create(:project_snippet, :private, author: create(:user))

      expect(subject).to match({
        are_public: 0,
        are_internal: 0,
        are_private: 1,
        are_public_or_internal: 0,
        total: 1
      })
    end

    it 'returns an empty hash if select returns nil' do
      allow_next_instance_of(described_class) do |instance|
        allow(instance).to receive(:snippet_counts).and_return(nil)
      end

      expect(subject).to match({})
    end
  end
end


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

module Snippets
  class CreateService < Snippets::BaseService
    def initialize(project:, current_user: nil, params: {}, perform_spam_check: true)
      super(project: project, current_user: current_user, params: params)
      @perform_spam_check = perform_spam_check
    end

    def execute
      @snippet = build_from_params

      return invalid_params_error(@snippet) unless valid_params?

      unless visibility_allowed?(snippet.visibility_level)
        return forbidden_visibility_error(snippet)
      end

      @snippet.author = current_user

      if perform_spam_check
        @snippet.check_for_spam(user: current_user, action: :create, extra_features: { files: file_paths_to_commit })
      end

      if save_and_commit
        UserAgentDetailService.new(spammable: @snippet, perform_spam_check: perform_spam_check).create
        Gitlab::UsageDataCounters::SnippetCounter.count(:create)

        move_temporary_files

        ServiceResponse.success(payload: { snippet: @snippet })
      else
        snippet_error_response(@snippet, 400)
      end
    end

    private

    attr_reader :snippet, :perform_spam_check

    def build_from_params
      if project
        project.snippets.build(create_params)
      else
        PersonalSnippet.new(create_params)
      end
    end

    # If the snippet_actions param is present
    # we need to fill content and file_name from
    # the model
    def create_params
      return params if snippet_actions.empty?

      params.merge(content: snippet_actions[0].content, file_name: snippet_actions[0].file_path)
    end

    def save_and_commit
      snippet_saved = @snippet.save

      if snippet_saved
        create_repository
        create_commit
      end

      snippet_saved
    rescue StandardError => e # Rescuing all because we can receive Creation exceptions, GRPC exceptions, Git exceptions, ...
      Gitlab::ErrorTracking.log_exception(e, service: 'Snippets::CreateService')

      # If the commit action failed we need to remove the repository if exists
      delete_repository(@snippet) if @snippet.repository_exists?

      # If the snippet was created, we need to remove it as we
      # would do like if it had had any validation error
      # and reassign a dupe so we don't return the deleted snippet
      if @snippet.persisted?
        @snippet.delete
        @snippet = @snippet.dup
      end

      add_snippet_repository_error(snippet: @snippet, error: e)

      false
    end

    def create_repository
      @snippet.create_repository

      raise CreateRepositoryError, 'Repository could not be created' unless @snippet.repository_exists?
    end

    def create_commit
      attrs = commit_attrs(@snippet, INITIAL_COMMIT_MSG)

      @snippet.snippet_repository.multi_files_action(current_user, files_to_commit(@snippet), **attrs)
    end

    def move_temporary_files
      return unless @snippet.is_a?(PersonalSnippet)

      uploaded_assets.each do |file|
        FileMover.new(file, from_model: current_user, to_model: @snippet).execute
      end
    end

    def build_actions_from_params(_snippet)
      [{ file_path: params[:file_name], content: params[:content] }]
    end

    def restricted_files_actions
      :create
    end
  end
end

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Snippets::CreateService, feature_category: :source_code_management do
  describe '#execute' do
    let_it_be(:user) { create(:user) }
    let_it_be(:admin) { create(:user, :admin) }

    let(:action) { :create }
    let(:opts) { base_opts.merge(extra_opts) }
    let(:base_opts) do
      {
        title: 'Test snippet',
        file_name: 'snippet.rb',
        content: 'puts "hello world"',
        visibility_level: Gitlab::VisibilityLevel::PRIVATE
      }
    end

    let(:extra_opts) { {} }
    let(:creator) { admin }

    subject { described_class.new(project: project, current_user: creator, params: opts).execute }

    let(:snippet) { subject.payload[:snippet] }

    shared_examples 'a service that creates a snippet' do
      it 'creates a snippet with the provided attributes' do
        expect(snippet.title).to eq(opts[:title])
        expect(snippet.file_name).to eq(opts[:file_name])
        expect(snippet.content).to eq(opts[:content])
        expect(snippet.visibility_level).to eq(opts[:visibility_level])
      end
    end

    shared_examples 'public visibility level restrictions apply' do
      let(:extra_opts) { { visibility_level: Gitlab::VisibilityLevel::PUBLIC } }

      before do
        stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC])
      end

      context 'when user is not an admin' do
        let(:creator) { user }

        it 'responds with an error' do
          expect(subject).to be_error
        end

        it 'does not create a public snippet' do
          expect(subject.message).to match('has been restricted')
        end
      end

      context 'when user is an admin' do
        it 'responds with success' do
          expect(subject).to be_success
        end

        it 'creates a public snippet' do
          expect(snippet.visibility_level).to eq(Gitlab::VisibilityLevel::PUBLIC)
        end
      end

      describe 'when visibility level is passed as a string' do
        let(:extra_opts) { { visibility: 'internal' } }

        before do
          base_opts.delete(:visibility_level)
        end

        it 'assigns the correct visibility level' do
          expect(subject).to be_success
          expect(snippet.visibility_level).to eq(Gitlab::VisibilityLevel::INTERNAL)
        end
      end
    end

    shared_examples 'snippet create data is tracked' do
      let(:counter) { Gitlab::UsageDataCounters::SnippetCounter }

      it 'increments count when create succeeds' do
        expect { subject }.to change { counter.read(:create) }.by 1
      end

      context 'when create fails' do
        let(:opts) { {} }

        it 'does not increment count' do
          expect { subject }.not_to change { counter.read(:create) }
        end
      end
    end

    shared_examples 'an error service response when save fails' do
      let(:extra_opts) { { content: nil } }

      it 'responds with an error' do
        expect(subject).to be_error
      end

      it 'does not create the snippet' do
        expect { subject }.not_to change { Snippet.count }
      end
    end

    shared_examples 'creates repository and files' do
      it 'creates repository' do
        subject

        expect(snippet.repository.exists?).to be_truthy
      end

      it 'commits the files to the repository' do
        subject

        blob = snippet.repository.blob_at('master', base_opts[:file_name])

        expect(blob.data).to eq base_opts[:content]
      end

      context 'when repository creation action fails' do
        before do
          allow_next_instance_of(Snippet) do |instance|
            allow(instance).to receive(:create_repository).and_return(nil)
          end
        end

        it 'does not create the snippet' do
          expect { subject }.not_to change { Snippet.count }
        end

        it 'returns a generic creation error' do
          expect(snippet.errors[:repository]).to eq ['Error creating the snippet - Repository could not be created']
        end

        it 'does not return a snippet with an id' do
          expect(snippet.id).to be_nil
        end
      end

      context 'when repository creation fails with invalid file name' do
        let(:extra_opts) { { file_name: 'invalid://file/name/here' } }

        it 'returns an appropriate error' do
          expect(snippet.errors[:repository]).to eq ['Error creating the snippet - Invalid file name']
        end
      end

      context 'when the commit action fails' do
        let(:error) { SnippetRepository::CommitError.new('foobar') }

        before do
          allow_next_instance_of(SnippetRepository) do |instance|
            allow(instance).to receive(:multi_files_action).and_raise(error)
          end
        end

        it 'does not create the snippet' do
          expect { subject }.not_to change { Snippet.count }
        end

        it 'destroys the created repository' do
          expect_next_instance_of(Repository) do |instance|
            expect(instance).to receive(:remove).and_call_original
          end

          subject
        end

        it 'destroys the snippet_repository' do
          subject

          expect(SnippetRepository.count).to be_zero
        end

        it 'logs the error' do
          expect(Gitlab::ErrorTracking).to receive(:log_exception).with(error, service: 'Snippets::CreateService')

          subject
        end

        it 'returns a generic error' do
          expect(subject).to be_error
          expect(snippet.errors[:repository]).to eq ['Error creating the snippet']
        end
      end

      context 'when snippet creation fails' do
        let(:extra_opts) { { content: nil } }

        it 'does not create repository' do
          expect do
            subject
          end.not_to change(Snippet, :count)

          expect(snippet.repository_exists?).to be_falsey
        end
      end
    end

    shared_examples 'after_save callback to store_mentions' do |mentionable_class|
      context 'when mentionable attributes change' do
        let(:extra_opts) { { description: "Description with #{user.to_reference}" } }

        it 'saves mentions' do
          expect_next_instance_of(mentionable_class) do |instance|
            expect(instance).to receive(:store_mentions!).and_call_original
          end
          expect(snippet.user_mentions.count).to eq 1
        end
      end

      context 'when mentionable attributes do not change' do
        it 'does not call store_mentions' do
          expect_next_instance_of(mentionable_class) do |instance|
            expect(instance).not_to receive(:store_mentions!)
          end
          expect(snippet.user_mentions.count).to eq 0
        end
      end

      context 'when save fails' do
        it 'does not call store_mentions' do
          base_opts.delete(:title)

          expect_next_instance_of(mentionable_class) do |instance|
            expect(instance).not_to receive(:store_mentions!)
          end
          expect(snippet.valid?).to be false
        end
      end
    end

    shared_examples 'when snippet_actions param is present' do
      let(:file_path) { 'snippet_file_path.rb' }
      let(:content) { 'snippet_content' }
      let(:snippet_actions) { [{ action: 'create', file_path: file_path, content: content }] }
      let(:base_opts) do
        {
          title: 'Test snippet',
          visibility_level: Gitlab::VisibilityLevel::PRIVATE,
          snippet_actions: snippet_actions
        }
      end

      it 'creates a snippet with the provided attributes' do
        expect(snippet.title).to eq(opts[:title])
        expect(snippet.visibility_level).to eq(opts[:visibility_level])
        expect(snippet.file_name).to eq(file_path)
        expect(snippet.content).to eq(content)
      end

      it 'commit the files to the repository' do
        expect(subject).to be_success

        blob = snippet.repository.blob_at('master', file_path)

        expect(blob.data).to eq content
      end

      context 'when content or file_name params are present' do
        let(:extra_opts) { { content: 'foo', file_name: 'path' } }

        it 'a validation error is raised' do
          expect(subject).to be_error
          expect(snippet.errors.full_messages_for(:content)).to eq ['Content and snippet files cannot be used together']
          expect(snippet.errors.full_messages_for(:file_name)).to eq ['File name and snippet files cannot be used together']
          expect(snippet.repository.exists?).to be_falsey
        end
      end

      context 'when snippet_actions param is invalid' do
        let(:snippet_actions) { [{ action: 'invalid_action', file_path: 'snippet_file_path.rb', content: 'snippet_content' }] }

        it 'a validation error is raised' do
          expect(subject).to be_error
          expect(snippet.errors.full_messages_for(:snippet_actions)).to eq ['Snippet actions have invalid data']
          expect(snippet.repository.exists?).to be_falsey
        end
      end

      context 'when snippet_actions contain an action different from "create"' do
        let(:snippet_actions) { [{ action: 'delete', file_path: 'snippet_file_path.rb' }] }

        it 'a validation error is raised' do
          expect(subject).to be_error
          expect(snippet.errors.full_messages_for(:snippet_actions)).to eq ['Snippet actions have invalid data']
          expect(snippet.repository.exists?).to be_falsey
        end
      end

      context 'when "create" operation does not have file_path or is empty' do
        let(:snippet_actions) { [{ action: 'create', content: content }, { action: 'create', content: content, file_path: '' }] }

        it 'generates the file path for the files' do
          expect(subject).to be_success
          expect(snippet.repository.blob_at('master', 'snippetfile1.txt').data).to eq content
          expect(snippet.repository.blob_at('master', 'snippetfile2.txt').data).to eq content
        end
      end
    end

    context 'when ProjectSnippet' do
      let_it_be(:project) { create(:project) }

      before do
        project.add_developer(user)
      end

      it_behaves_like 'a service that creates a snippet'
      it_behaves_like 'public visibility level restrictions apply'
      it_behaves_like 'checking spam'
      it_behaves_like 'snippet create data is tracked'
      it_behaves_like 'an error service response when save fails'
      it_behaves_like 'creates repository and files'
      it_behaves_like 'after_save callback to store_mentions', ProjectSnippet
      it_behaves_like 'when snippet_actions param is present'
      it_behaves_like 'invalid params error response'

      context 'when uploaded files are passed to the service' do
        let(:extra_opts) { { files: ['foo'] } }

        it 'does not move uploaded files to the snippet' do
          expect_next_instance_of(described_class) do |instance|
            expect(instance).to receive(:move_temporary_files).and_call_original
          end

          expect_any_instance_of(FileMover).not_to receive(:execute)

          subject
        end
      end
    end

    context 'when PersonalSnippet' do
      let(:project) { nil }

      it_behaves_like 'a service that creates a snippet'
      it_behaves_like 'public visibility level restrictions apply'
      it_behaves_like 'checking spam'
      it_behaves_like 'snippet create data is tracked'
      it_behaves_like 'an error service response when save fails'
      it_behaves_like 'creates repository and files'
      it_behaves_like 'after_save callback to store_mentions', PersonalSnippet
      it_behaves_like 'when snippet_actions param is present'
      it_behaves_like 'invalid params error response'

      context 'when the snippet description contains files' do
        include FileMoverHelpers

        let(:title) { 'Title' }
        let(:picture_secret) { SecureRandom.hex }
        let(:text_secret) { SecureRandom.hex }
        let(:picture_file) { "/-/system/user/#{creator.id}/#{picture_secret}/picture.jpg" }
        let(:text_file) { "/-/system/user/#{creator.id}/#{text_secret}/text.txt" }
        let(:files) { [picture_file, text_file] }
        let(:description) do
          "Description with picture: ![picture](/uploads#{picture_file}) and "\
          "text: [text.txt](/uploads#{text_file})"
        end

        before do
          allow(FileUtils).to receive(:mkdir_p)
          allow(FileUtils).to receive(:move)
        end

        let(:extra_opts) { { description: description, title: title, files: files } }

        it 'stores the snippet description correctly' do
          stub_file_mover(text_file)
          stub_file_mover(picture_file)

          snippet = subject.payload[:snippet]

          expected_description = "Description with picture: "\
            "![picture](/uploads/-/system/personal_snippet/#{snippet.id}/#{picture_secret}/picture.jpg) and "\
            "text: [text.txt](/uploads/-/system/personal_snippet/#{snippet.id}/#{text_secret}/text.txt)"

          expect(snippet.description).to eq(expected_description)
        end

        context 'when there is a validation error' do
          let(:title) { nil }

          it 'does not move uploaded files to the snippet' do
            expect_any_instance_of(described_class).not_to receive(:move_temporary_files)

            subject
          end
        end
      end
    end
  end
end


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

module Database
  class ConsistencyFixService
    def initialize(source_model:, target_model:, sync_event_class:, source_sort_key:, target_sort_key:)
      @source_model = source_model
      @target_model = target_model
      @sync_event_class = sync_event_class
      @source_sort_key = source_sort_key
      @target_sort_key = target_sort_key
    end

    attr_accessor :source_model, :target_model, :sync_event_class, :source_sort_key, :target_sort_key

    def execute(ids:)
      ids.each do |id|
        if source_object(id) && target_object(id)
          create_sync_event_for(id)
        elsif target_object(id)
          target_object(id).destroy!
        end
      end
      sync_event_class.enqueue_worker
    end

    private

    # rubocop: disable CodeReuse/ActiveRecord
    def source_object(id)
      source_model.find_by(source_sort_key => id)
    end

    def target_object(id)
      target_model.find_by(target_sort_key => id)
    end
    # rubocop: enable CodeReuse/ActiveRecord

    def create_sync_event_for(id)
      if source_model == Namespace
        sync_event_class.create!(namespace_id: id)
      elsif source_model == Project
        sync_event_class.create!(project_id: id)
      else
        raise("Unknown Source Model #{source_model.name}")
      end
    end
  end
end

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Database::ConsistencyFixService, feature_category: :cell do
  describe '#execute' do
    context 'fixing namespaces inconsistencies' do
      subject(:consistency_fix_service) do
        described_class.new(
          source_model: Namespace,
          target_model: Ci::NamespaceMirror,
          sync_event_class: Namespaces::SyncEvent,
          source_sort_key: :id,
          target_sort_key: :namespace_id
        )
      end

      let(:table) { 'public.namespaces' }
      let!(:namespace) { create(:namespace) }
      let!(:namespace_mirror) { Ci::NamespaceMirror.find_by(namespace_id: namespace.id) }

      context 'when both objects exist' do
        it 'creates a Namespaces::SyncEvent to modify the target object' do
          expect do
            consistency_fix_service.execute(ids: [namespace.id])
          end.to change {
            Namespaces::SyncEvent.where(namespace_id: namespace.id).count
          }.by(1)
        end

        it 'enqueues the worker to process the Namespaces::SyncEvents' do
          expect(::Namespaces::ProcessSyncEventsWorker).to receive(:perform_async)
          consistency_fix_service.execute(ids: [namespace.id])
        end
      end

      context 'when the source object has been deleted, but not the target' do
        before do
          namespace.delete
        end

        it 'deletes the target object' do
          expect do
            consistency_fix_service.execute(ids: [namespace.id])
          end.to change { Ci::NamespaceMirror.where(namespace_id: namespace.id).count }.by(-1)
        end
      end
    end

    context 'fixing projects inconsistencies' do
      subject(:consistency_fix_service) do
        described_class.new(
          source_model: Project,
          target_model: Ci::ProjectMirror,
          sync_event_class: Projects::SyncEvent,
          source_sort_key: :id,
          target_sort_key: :project_id
        )
      end

      let(:table) { 'public.projects' }
      let!(:project) { create(:project) }
      let!(:project_mirror) { Ci::ProjectMirror.find_by(project_id: project.id) }

      context 'when both objects exist' do
        it 'creates a Projects::SyncEvent to modify the target object' do
          expect do
            consistency_fix_service.execute(ids: [project.id])
          end.to change {
            Projects::SyncEvent.where(project_id: project.id).count
          }.by(1)
        end

        it 'enqueues the worker to process the Projects::SyncEvents' do
          expect(::Projects::ProcessSyncEventsWorker).to receive(:perform_async)
          consistency_fix_service.execute(ids: [project.id])
        end
      end

      context 'when the source object has been deleted, but not the target' do
        before do
          project.delete
        end

        it 'deletes the target object' do
          expect do
            consistency_fix_service.execute(ids: [project.id])
          end.to change { Ci::ProjectMirror.where(project_id: project.id).count }.by(-1)
        end
      end
    end
  end

  describe '#create_sync_event_for' do
    context 'when the source model is Namespace' do
      let(:namespace) { create(:namespace) }

      let(:service) do
        described_class.new(
          source_model: Namespace,
          target_model: Ci::NamespaceMirror,
          sync_event_class: Namespaces::SyncEvent,
          source_sort_key: :id,
          target_sort_key: :namespace_id
        )
      end

      it 'creates a Namespaces::SyncEvent object' do
        expect do
          service.send(:create_sync_event_for, namespace.id)
        end.to change { Namespaces::SyncEvent.where(namespace_id: namespace.id).count }.by(1)
      end
    end

    context 'when the source model is Project' do
      let(:project) { create(:project) }

      let(:service) do
        described_class.new(
          source_model: Project,
          target_model: Ci::ProjectMirror,
          sync_event_class: Projects::SyncEvent,
          source_sort_key: :id,
          target_sort_key: :project_id
        )
      end

      it 'creates a Projects::SyncEvent object' do
        expect do
          service.send(:create_sync_event_for, project.id)
        end.to change { Projects::SyncEvent.where(project_id: project.id).count }.by(1)
      end
    end
  end

  context 'when the source model is User' do
    let(:service) do
      described_class.new(
        source_model: User,
        target_model: Ci::ProjectMirror,
        sync_event_class: Projects::SyncEvent,
        source_sort_key: :id,
        target_sort_key: :project_id
      )
    end

    it 'raises an error' do
      expect do
        service.send(:create_sync_event_for, 1)
      end.to raise_error("Unknown Source Model User")
    end
  end
end


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

module Database
  class ConsistencyCheckService
    CURSOR_REDIS_KEY_TTL = 7.days
    EMPTY_RESULT = { matches: 0, mismatches: 0, batches: 0, mismatches_details: [] }.freeze

    def initialize(source_model:, target_model:, source_columns:, target_columns:)
      @source_model = source_model
      @target_model = target_model
      @source_columns = source_columns
      @target_columns = target_columns
      @source_sort_column = source_columns.first
      @target_sort_column = target_columns.first
    end

    # This class takes two ActiveRecord models, and compares the selected columns
    # of the two models tables, for the purposes of checking the consistency of
    # mirroring of tables. For example Namespace and Ci::NamepaceMirror
    #
    # It compares up to 25 batches (1000 records / batch), or up to 30 seconds
    # for all the batches in total.
    #
    # It saves the cursor of the next start_id (cusror) in Redis. If the start_id
    # wasn't saved in Redis, for example, in the first run, it will choose some random start_id
    #
    # Example:
    #    service = Database::ConsistencyCheckService.new(
    #      source_model: Namespace,
    #      target_model: Ci::NamespaceMirror,
    #      source_columns: %w[id traversal_ids],
    #      target_columns: %w[namespace_id traversal_ids],
    #    )
    #    result = service.execute
    #
    # result is a hash that has the following fields:
    # - batches: Number of batches checked
    # - matches: The number of matched records
    # - mismatches: The number of mismatched records
    # - mismatches_details: It's an array that contains details about the mismatched records.
    #     each record in this array is a hash of format {id: ID, source_table: [...], target_table: [...]}
    #     Each record represents the attributes of the records in the two tables.
    # - start_id: The start id cursor of the current batch. <nil> means no records.
    # - next_start_id: The ID that can be used for the next batch iteration check. <nil> means no records
    def execute
      start_id = next_start_id

      return EMPTY_RESULT if start_id.nil?

      result = consistency_checker.execute(start_id: start_id)
      result[:start_id] = start_id

      save_next_start_id(result[:next_start_id])

      result
    end

    private

    attr_reader :source_model, :target_model, :source_columns, :target_columns, :source_sort_column, :target_sort_column

    def consistency_checker
      @consistency_checker ||= Gitlab::Database::ConsistencyChecker.new(
        source_model: source_model,
        target_model: target_model,
        source_columns: source_columns,
        target_columns: target_columns
      )
    end

    def next_start_id
      return if min_id.nil?

      fetch_next_start_id || random_start_id
    end

    # rubocop: disable CodeReuse/ActiveRecord
    def min_id
      @min_id ||= source_model.minimum(source_sort_column)
    end

    def max_id
      @max_id ||= source_model.maximum(source_sort_column)
    end
    # rubocop: enable CodeReuse/ActiveRecord

    def fetch_next_start_id
      Gitlab::Redis::SharedState.with { |redis| redis.get(cursor_redis_shared_state_key)&.to_i }
    end

    # This returns some random start_id, so that we don't always start checking
    # from the start of the table, in case we lose the cursor in Redis.
    def random_start_id
      range_start = min_id
      range_end = [min_id, max_id - Gitlab::Database::ConsistencyChecker::BATCH_SIZE].max
      rand(range_start..range_end)
    end

    def save_next_start_id(start_id)
      Gitlab::Redis::SharedState.with do |redis|
        redis.set(cursor_redis_shared_state_key, start_id, ex: CURSOR_REDIS_KEY_TTL)
      end
    end

    def cursor_redis_shared_state_key
      "consistency_check_cursor:#{source_model.table_name}:#{target_model.table_name}"
    end
  end
end

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Database::ConsistencyCheckService, feature_category: :cell do
  let(:batch_size) { 5 }
  let(:max_batches) { 2 }

  before do
    stub_const("Gitlab::Database::ConsistencyChecker::BATCH_SIZE", batch_size)
    stub_const("Gitlab::Database::ConsistencyChecker::MAX_BATCHES", max_batches)
  end

  after do
    redis_shared_state_cleanup!
  end

  subject(:consistency_check_service) do
    described_class.new(
      source_model: Namespace,
      target_model: Ci::NamespaceMirror,
      source_columns: %w[id traversal_ids],
      target_columns: %w[namespace_id traversal_ids]
    )
  end

  describe '#min_id' do
    before do
      create_list(:namespace, 3)
    end

    it 'returns the id of the first record in the database' do
      expect(subject.send(:min_id)).to eq(Namespace.first.id)
    end
  end

  describe '#max_id' do
    before do
      create_list(:namespace, 3)
    end

    it 'returns the id of the first record in the database' do
      expect(subject.send(:max_id)).to eq(Namespace.last.id)
    end
  end

  describe '#random_start_id' do
    before do
      create_list(:namespace, 50) # This will also create Ci::NameSpaceMirror objects
    end

    it 'generates a random start_id within the records ids' do
      10.times do
        start_id = subject.send(:random_start_id)
        expect(start_id).to be_between(Namespace.first.id, Namespace.last.id).inclusive
      end
    end
  end

  describe '#execute' do
    let(:empty_results) do
      { batches: 0, matches: 0, mismatches: 0, mismatches_details: [] }
    end

    context 'when empty tables' do
      it 'returns results with zero counters' do
        result = consistency_check_service.execute

        expect(result).to eq(empty_results)
      end

      it 'does not call the ConsistencyCheckService' do
        expect(Gitlab::Database::ConsistencyChecker).not_to receive(:new)
        consistency_check_service.execute
      end
    end

    context 'no cursor has been saved before' do
      let(:min_id) { Namespace.first.id }
      let(:max_id) { Namespace.last.id }

      before do
        create_list(:namespace, 50) # This will also create Ci::NameSpaceMirror objects
      end

      it 'picks a random start_id' do
        expected_result = {
          batches: 2,
          matches: 10,
          mismatches: 0,
          mismatches_details: [],
          start_id: be_between(min_id, max_id),
          next_start_id: be_between(min_id, max_id)
        }
        expect(consistency_check_service).to receive(:rand).with(min_id..max_id).and_call_original
        result = consistency_check_service.execute
        expect(result).to match(expected_result)
      end

      it 'calls the ConsistencyCheckService with the expected parameters' do
        expect(consistency_check_service).to receive(:random_start_id).and_return(min_id)

        allow_next_instance_of(Gitlab::Database::ConsistencyChecker) do |instance|
          expect(instance).to receive(:execute).with(start_id: min_id).and_return({
            batches: 2,
            next_start_id: min_id + batch_size,
            matches: 10,
            mismatches: 0,
            mismatches_details: []
          })
        end

        expect(Gitlab::Database::ConsistencyChecker).to receive(:new).with(
          source_model: Namespace,
          target_model: Ci::NamespaceMirror,
          source_columns: %w[id traversal_ids],
          target_columns: %w[namespace_id traversal_ids]
        ).and_call_original

        expected_result = {
          batches: 2,
          matches: 10,
          mismatches: 0,
          mismatches_details: [],
          start_id: be_between(min_id, max_id),
          next_start_id: be_between(min_id, max_id)
        }
        result = consistency_check_service.execute
        expect(result).to match(expected_result)
      end

      it 'saves the next_start_id in Redis for he next iteration' do
        expect(consistency_check_service).to receive(:save_next_start_id)
          .with(be_between(min_id, max_id)).and_call_original
        consistency_check_service.execute
      end
    end

    context 'cursor saved in Redis and moving' do
      let(:first_namespace_id) { Namespace.order(:id).first.id }
      let(:second_namespace_id) { Namespace.order(:id).second.id }

      before do
        create_list(:namespace, 30) # This will also create Ci::NameSpaceMirror objects
      end

      it "keeps moving the cursor with each call to the service" do
        expect(consistency_check_service).to receive(:random_start_id).at_most(:once).and_return(first_namespace_id)

        allow_next_instance_of(Gitlab::Database::ConsistencyChecker) do |instance|
          expect(instance).to receive(:execute).ordered.with(start_id: first_namespace_id).and_call_original
          expect(instance).to receive(:execute).ordered.with(start_id: first_namespace_id + 10).and_call_original
          expect(instance).to receive(:execute).ordered.with(start_id: first_namespace_id + 20).and_call_original
          # Gets back to the start of the table
          expect(instance).to receive(:execute).ordered.with(start_id: first_namespace_id).and_call_original
        end

        4.times do
          consistency_check_service.execute
        end
      end

      it "keeps moving the cursor from any start point" do
        expect(consistency_check_service).to receive(:random_start_id).at_most(:once).and_return(second_namespace_id)

        allow_next_instance_of(Gitlab::Database::ConsistencyChecker) do |instance|
          expect(instance).to receive(:execute).ordered.with(start_id: second_namespace_id).and_call_original
          expect(instance).to receive(:execute).ordered.with(start_id: second_namespace_id + 10).and_call_original
        end

        2.times do
          consistency_check_service.execute
        end
      end
    end
  end
end


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

module Database
  class MarkMigrationService
    def initialize(connection:, version:)
      @connection = connection
      @version = version
    end

    def execute
      return error(reason: :not_found) unless migration.present?
      return error(reason: :invalid) if all_versions.include?(migration.version)

      if create_version(version)
        ServiceResponse.success
      else
        error(reason: :invalid)
      end
    end

    private

    attr_reader :connection, :version

    def migration
      @migration ||= connection
        .migration_context
        .migrations
        .find { |migration| migration.version == version }
    end

    def all_versions
      all_executed_migrations.map(&:to_i)
    end

    def all_executed_migrations
      sm = Arel::SelectManager.new(arel_table)
      sm.project(arel_table[:version])
      sm.order(arel_table[:version].asc) # rubocop: disable CodeReuse/ActiveRecord
      connection.select_values(sm, "#{self.class} Load")
    end

    def create_version(version)
      im = Arel::InsertManager.new
      im.into(arel_table)
      im.insert(arel_table[:version] => version)
      connection.insert(im, "#{self.class} Create", :version, version)
    end

    def arel_table
      @arel_table ||= Arel::Table.new(:schema_migrations)
    end

    def error(reason:)
      ServiceResponse.error(message: 'error', reason: reason)
    end
  end
end

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Database::MarkMigrationService, feature_category: :database do
  let(:service) { described_class.new(connection: connection, version: version) }
  let(:version) { 1 }
  let(:connection) { ApplicationRecord.connection }

  let(:migrations) do
    [
      instance_double(
        ActiveRecord::MigrationProxy,
        version: 1,
        name: 'migration_pending',
        filename: 'db/migrate/1_migration_pending.rb'
      )
    ]
  end

  before do
    ctx = instance_double(ActiveRecord::MigrationContext, migrations: migrations)
    allow(connection).to receive(:migration_context).and_return(ctx)
  end

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

    it 'marks the migration as successful' do
      expect { execute }
        .to change { ActiveRecord::SchemaMigration.where(version: version).count }
        .by(1)

      is_expected.to be_success
    end

    context 'when the migration does not exist' do
      let(:version) { 123 }

      it { is_expected.to be_error }
      it { expect(execute.reason).to eq(:not_found) }

      it 'does not insert records' do
        expect { execute }
          .not_to change { ActiveRecord::SchemaMigration.where(version: version).count }
      end
    end

    context 'when the migration was already executed' do
      before do
        allow(service).to receive(:all_versions).and_return([version])
      end

      it { is_expected.to be_error }
      it { expect(execute.reason).to eq(:invalid) }

      it 'does not insert records' do
        expect { execute }
          .not_to change { ActiveRecord::SchemaMigration.where(version: version).count }
      end
    end

    context 'when the insert fails' do
      it 'returns an error response' do
        expect(service).to receive(:create_version).with(version).and_return(false)

        is_expected.to be_error
      end
    end
  end
end


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

module FeatureFlags
  class HookService
    HOOK_NAME = :feature_flag_hooks

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

    def execute
      project.execute_hooks(hook_data, HOOK_NAME)
    end

    private

    attr_reader :feature_flag, :current_user

    def project
      @project ||= feature_flag.project
    end

    def hook_data
      Gitlab::DataBuilder::FeatureFlag.build(feature_flag, current_user)
    end
  end
end

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe FeatureFlags::HookService, feature_category: :feature_flags do
  describe '#execute_hooks' do
    let_it_be(:namespace) { create(:namespace) }
    let_it_be(:project) { create(:project, :repository, namespace: namespace) }
    let_it_be(:feature_flag) { create(:operations_feature_flag, project: project) }
    let_it_be(:user) { namespace.first_owner }

    let!(:hook) { create(:project_hook, project: project) }
    let(:hook_data) { double }

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

    before do
      allow(Gitlab::DataBuilder::FeatureFlag).to receive(:build).with(feature_flag, user).once.and_return(hook_data)
    end

    describe 'HOOK_NAME' do
      specify { expect(described_class::HOOK_NAME).to eq(:feature_flag_hooks) }
    end

    it 'calls feature_flag.project.execute_hooks' do
      expect(feature_flag.project).to receive(:execute_hooks).with(hook_data, described_class::HOOK_NAME)

      service.execute
    end
  end
end


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

module FeatureFlags
  class DestroyService < FeatureFlags::BaseService
    def execute(feature_flag)
      destroy_feature_flag(feature_flag)
    end

    private

    def destroy_feature_flag(feature_flag)
      return error('Access Denied', 403) unless can_destroy?(feature_flag)

      ApplicationRecord.transaction do
        if feature_flag.destroy
          update_last_feature_flag_updated_at!

          success(feature_flag: feature_flag)
        else
          error(feature_flag.errors.full_messages)
        end
      end
    end

    def audit_context(feature_flag)
      {
        name: 'feature_flag_deleted',
        message: audit_message(feature_flag),
        author: current_user,
        scope: feature_flag.project,
        target: feature_flag
      }
    end

    def audit_message(feature_flag)
      "Deleted feature flag #{feature_flag.name}."
    end

    def can_destroy?(feature_flag)
      Ability.allowed?(current_user, :destroy_feature_flag, feature_flag)
    end
  end
end

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe FeatureFlags::DestroyService, feature_category: :feature_flags do
  include FeatureFlagHelpers

  let_it_be(:project) { create(:project) }
  let_it_be(:developer) { create(:user) }
  let_it_be(:reporter) { create(:user) }

  let(:user) { developer }
  let!(:feature_flag) { create(:operations_feature_flag, project: project) }

  before_all do
    project.add_developer(developer)
    project.add_reporter(reporter)
  end

  describe '#execute' do
    subject { described_class.new(project, user, params).execute(feature_flag) }

    let(:audit_event_details) { AuditEvent.last.details }
    let(:audit_event_message) { audit_event_details[:custom_message] }
    let(:params) { {} }

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

    it 'destroys feature flag' do
      expect { subject }.to change { Operations::FeatureFlag.count }.by(-1)
    end

    it 'creates audit log', :with_license do
      expect { subject }.to change { AuditEvent.count }.by(1)
      expect(audit_event_message).to eq("Deleted feature flag #{feature_flag.name}.")
    end

    it_behaves_like 'update feature flag client'

    context 'when user is reporter' do
      let(:user) { reporter }

      it 'returns error status' do
        expect(subject[:status]).to eq(:error)
        expect(subject[:message]).to eq('Access Denied')
      end
    end

    context 'when feature flag can not be destroyed' do
      before do
        allow(feature_flag).to receive(:destroy).and_return(false)
      end

      it 'returns status error' do
        expect(subject[:status]).to eq(:error)
      end

      it 'does not create audit log' do
        expect { subject }.not_to change { AuditEvent.count }
      end

      it_behaves_like 'does not update feature flag client'
    end
  end
end