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

module Git
  class BranchPushService < ::BaseService
    include Gitlab::Access
    include Gitlab::Utils::StrongMemoize
    include ChangeParams

    # This method will be called after each git update
    # and only if the provided user and project are present in GitLab.
    #
    # All callbacks for post receive action should be placed here.
    #
    # Next, this method:
    #  1. Creates the push event
    #  2. Updates merge requests
    #  3. Recognizes cross-references from commit messages
    #  4. Executes the project's webhooks
    #  5. Executes the project's services
    #  6. Checks if the project's main language has changed
    #
    def execute
      return unless Gitlab::Git.branch_ref?(ref)

      enqueue_update_mrs
      enqueue_detect_repository_languages
      enqueue_record_project_target_platforms

      execute_related_hooks

      stop_environments
      unlock_artifacts

      true
    end

    # Update merge requests that may be affected by this push. A new branch
    # could cause the last commit of a merge request to change.
    def enqueue_update_mrs
      return if params[:merge_request_branches]&.exclude?(branch_name)

      UpdateMergeRequestsWorker.perform_async(
        project.id,
        current_user.id,
        oldrev,
        newrev,
        ref,
        params.slice(:push_options).deep_stringify_keys
      )
    end

    def enqueue_detect_repository_languages
      return unless default_branch?

      DetectRepositoryLanguagesWorker.perform_async(project.id)
    end

    def enqueue_record_project_target_platforms
      return unless default_branch?

      project.enqueue_record_project_target_platforms
    end

    # Only stop environments if the ref is a branch that is being deleted
    def stop_environments
      return unless removing_branch?

      Environments::StopService.new(project, current_user).execute_for_branch(branch_name)
    end

    def unlock_artifacts
      return unless removing_branch?

      Ci::RefDeleteUnlockArtifactsWorker.perform_async(project.id, current_user.id, ref)
    end

    def execute_related_hooks
      BranchHooksService.new(project, current_user, params).execute
    end

    def removing_branch?
      Gitlab::Git.blank_ref?(newrev)
    end

    def branch_name
      strong_memoize(:branch_name) { Gitlab::Git.ref_name(ref) }
    end

    def default_branch?
      strong_memoize(:default_branch) do
        [nil, branch_name].include?(project.default_branch)
      end
    end
  end
end

Git::BranchPushService.prepend_mod_with('Git::BranchPushService')

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Git::BranchPushService, :use_clean_rails_redis_caching, services: true, feature_category: :source_code_management do
  include RepoHelpers

  let_it_be(:user) { create(:user) }
  let_it_be_with_refind(:project) { create(:project, :repository) }

  let(:blankrev) { Gitlab::Git::BLANK_SHA }
  let(:oldrev)   { sample_commit.parent_id }
  let(:newrev)   { sample_commit.id }
  let(:branch)   { 'master' }
  let(:ref)      { "refs/heads/#{branch}" }
  let(:push_options) { nil }
  let(:service) do
    described_class
      .new(project, user, change: { oldrev: oldrev, newrev: newrev, ref: ref }, push_options: push_options)
  end

  before do
    project.add_maintainer(user)
  end

  subject(:execute_service) do
    service.execute
  end

  describe 'Push branches' do
    context 'new branch' do
      let(:oldrev) { blankrev }

      it { is_expected.to be_truthy }

      it 'calls the after_push_commit hook' do
        expect(project.repository).to receive(:after_push_commit).with('master')

        subject
      end

      it 'calls the after_create_branch hook' do
        expect(project.repository).to receive(:after_create_branch)

        subject
      end
    end

    context 'existing branch' do
      it { is_expected.to be_truthy }

      it 'calls the after_push_commit hook' do
        expect(project.repository).to receive(:after_push_commit).with('master')

        subject
      end
    end

    context 'rm branch' do
      let(:newrev) { blankrev }

      it { is_expected.to be_truthy }

      it 'calls the after_push_commit hook' do
        expect(project.repository).to receive(:after_push_commit).with('master')

        subject
      end

      it 'calls the after_remove_branch hook' do
        expect(project.repository).to receive(:after_remove_branch)

        subject
      end
    end
  end

  describe "Pipelines" do
    before do
      stub_ci_pipeline_to_return_yaml_file
    end

    it 'creates a pipeline with the right parameters' do
      expect(Ci::CreatePipelineService).to receive(:new).with(
        project,
        user,
        {
          before: oldrev,
          after: newrev,
          ref: ref,
          checkout_sha: SeedRepo::Commit::ID,
          variables_attributes: [],
          push_options: {}
        }
      ).and_call_original

      subject
    end

    it "creates a new pipeline" do
      expect { subject }.to change { Ci::Pipeline.count }

      pipeline = Ci::Pipeline.last
      expect(pipeline).to be_push
      expect(Gitlab::Git::BRANCH_REF_PREFIX + pipeline.ref).to eq(ref)
    end

    context 'when pipeline has errors' do
      before do
        config = YAML.dump({ test: { script: 'ls', only: ['feature'] } })
        stub_ci_pipeline_yaml_file(config)
      end

      it 'reports an error' do
        allow(Gitlab::Runtime).to receive(:sidekiq?).and_return(true)
        expect(Sidekiq.logger).to receive(:warn)

        expect { subject }.not_to change { Ci::Pipeline.count }
      end

      context 'with push options' do
        let(:push_options) { { 'mr' => { 'create' => true } } }

        it 'sanitizes push options' do
          allow(Gitlab::Runtime).to receive(:sidekiq?).and_return(true)
          expect(Sidekiq.logger).to receive(:warn) do |args|
            pipeline_params = args[:pipeline_params]
            expect(pipeline_params.keys).to match_array(%i[before after ref variables_attributes checkout_sha])
          end

          expect { subject }.not_to change { Ci::Pipeline.count }
        end
      end
    end

    context 'when .gitlab-ci.yml file is invalid' do
      before do
        stub_ci_pipeline_yaml_file('invalid yaml file')
      end

      it 'persists an error pipeline' do
        expect { subject }.to change { Ci::Pipeline.count }

        pipeline = Ci::Pipeline.last
        expect(pipeline).to be_push
        expect(pipeline).to be_failed
        expect(pipeline).to be_config_error
      end
    end
  end

  describe "Updates merge requests" do
    let(:oldrev) { blankrev }

    it "when pushing a new branch for the first time" do
      expect(UpdateMergeRequestsWorker)
        .to receive(:perform_async)
        .with(project.id, user.id, blankrev, newrev, ref, { 'push_options' => nil })
        .ordered

      subject
    end
  end

  describe "Updates git attributes" do
    context "for default branch" do
      context "when first push" do
        let(:oldrev) { blankrev }

        it "calls the copy attributes method for the first push to the default branch" do
          expect(project.repository).to receive(:copy_gitattributes).with('master')

          subject
        end
      end

      it "calls the copy attributes method for changes to the default branch" do
        expect(project.repository).to receive(:copy_gitattributes).with(ref)

        subject
      end
    end

    context "for non-default branch" do
      before do
        # Make sure the "default" branch is different
        allow(project).to receive(:default_branch).and_return('not-master')
      end

      it "does not call copy attributes method" do
        expect(project.repository).not_to receive(:copy_gitattributes)

        subject
      end
    end
  end

  describe "Webhooks" do
    before do
      create(:project_hook, push_events: true, project: project)
    end

    context "when pushing a branch for the first time" do
      let(:oldrev) { blankrev }

      it "executes webhooks" do
        expect(project).to receive(:execute_hooks)
        expect(project.default_branch).to eq("master")

        subject

        expect(project.protected_branches).not_to be_empty
        expect(project.protected_branches.first.push_access_levels.map(&:access_level)).to eq([Gitlab::Access::MAINTAINER])
        expect(project.protected_branches.first.merge_access_levels.map(&:access_level)).to eq([Gitlab::Access::MAINTAINER])
      end

      it "with default branch protection disabled" do
        expect(project.namespace).to receive(:default_branch_protection).and_return(Gitlab::Access::PROTECTION_NONE)

        expect(project).to receive(:execute_hooks)
        expect(project.default_branch).to eq("master")
        subject
        expect(project.protected_branches).to be_empty
      end

      it "with default branch protection set to 'developers can push'" do
        expect(project.namespace).to receive(:default_branch_protection).and_return(Gitlab::Access::PROTECTION_DEV_CAN_PUSH)

        expect(project).to receive(:execute_hooks)
        expect(project.default_branch).to eq("master")

        subject

        expect(project.protected_branches).not_to be_empty
        expect(project.protected_branches.last.push_access_levels.map(&:access_level)).to eq([Gitlab::Access::DEVELOPER])
        expect(project.protected_branches.last.merge_access_levels.map(&:access_level)).to eq([Gitlab::Access::MAINTAINER])
      end

      it "with an existing branch permission configured" do
        expect(project.namespace).to receive(:default_branch_protection).and_return(Gitlab::Access::PROTECTION_DEV_CAN_PUSH)

        create(:protected_branch, :no_one_can_push, :developers_can_merge, project: project, name: 'master')
        expect(project).to receive(:execute_hooks)
        expect(project.default_branch).to eq("master")
        expect(ProtectedBranches::CreateService).not_to receive(:new)

        subject

        expect(project.protected_branches).not_to be_empty
        expect(project.protected_branches.last.push_access_levels.map(&:access_level)).to eq([Gitlab::Access::NO_ACCESS])
        expect(project.protected_branches.last.merge_access_levels.map(&:access_level)).to eq([Gitlab::Access::DEVELOPER])
      end

      it "with default branch protection set to 'developers can merge'" do
        expect(project.namespace).to receive(:default_branch_protection).and_return(Gitlab::Access::PROTECTION_DEV_CAN_MERGE)

        expect(project).to receive(:execute_hooks)
        expect(project.default_branch).to eq("master")
        subject
        expect(project.protected_branches).not_to be_empty
        expect(project.protected_branches.first.push_access_levels.map(&:access_level)).to eq([Gitlab::Access::MAINTAINER])
        expect(project.protected_branches.first.merge_access_levels.map(&:access_level)).to eq([Gitlab::Access::DEVELOPER])
      end
    end

    context "when pushing new commits to existing branch" do
      it "executes webhooks" do
        expect(project).to receive(:execute_hooks)
        subject
      end
    end
  end

  describe "cross-reference notes" do
    let(:issue) { create :issue, project: project }
    let(:commit_author) { create :user }
    let(:commit) { project.commit }

    before do
      project.add_developer(commit_author)
      project.add_developer(user)

      allow(commit).to receive_messages(
        safe_message: "this commit \n mentions #{issue.to_reference}",
        references: [issue],
        author_name: commit_author.name,
        author_email: commit_author.email
      )

      allow(Commit).to receive(:build_from_sidekiq_hash)
        .and_return(commit)

      allow(project.repository).to receive(:commits_between).and_return([commit])
    end

    it "creates a note if a pushed commit mentions an issue", :sidekiq_might_not_need_inline do
      expect(SystemNoteService).to receive(:cross_reference).with(issue, commit, commit_author)

      subject
    end

    it "only creates a cross-reference note if one doesn't already exist" do
      SystemNoteService.cross_reference(issue, commit, user)

      expect(SystemNoteService).not_to receive(:cross_reference).with(issue, commit, commit_author)

      subject
    end

    it "defaults to the pushing user if the commit's author is not known", :sidekiq_inline, :use_clean_rails_redis_caching do
      allow(commit).to receive_messages(
        author_name: 'unknown name',
        author_email: 'unknown@email.com'
      )
      expect(SystemNoteService).to receive(:cross_reference).with(issue, commit, user)

      subject
    end

    context "when first push on a non-default branch" do
      let(:oldrev) { blankrev }
      let(:ref) { 'refs/heads/other' }

      it "finds references", :sidekiq_might_not_need_inline do
        allow(project.repository).to receive(:commits_between).with(blankrev, newrev).and_return([])
        allow(project.repository).to receive(:commits_between).with("master", newrev).and_return([commit])

        expect(SystemNoteService).to receive(:cross_reference).with(issue, commit, commit_author)

        subject
      end
    end
  end

  describe "issue metrics" do
    let(:issue) { create :issue, project: project }
    let(:commit_author) { create :user }
    let(:commit) { project.commit }
    let(:commit_time) { Time.current }

    before do
      project.add_developer(commit_author)
      project.add_developer(user)

      allow(commit).to receive_messages(
        safe_message: "this commit \n mentions #{issue.to_reference}",
        references: [issue],
        author_name: commit_author.name,
        author_email: commit_author.email,
        committed_date: commit_time
      )

      allow(Commit).to receive(:build_from_sidekiq_hash)
        .and_return(commit)

      allow(project.repository).to receive(:commits_between).and_return([commit])
    end

    context "while saving the 'first_mentioned_in_commit_at' metric for an issue" do
      it 'sets the metric for referenced issues', :sidekiq_inline, :use_clean_rails_redis_caching do
        subject

        expect(issue.reload.metrics.first_mentioned_in_commit_at).to be_like_time(commit_time)
      end

      it 'does not set the metric for non-referenced issues' do
        non_referenced_issue = create(:issue, project: project)
        subject

        expect(non_referenced_issue.reload.metrics.first_mentioned_in_commit_at).to be_nil
      end
    end
  end

  describe "closing issues from pushed commits containing a closing reference" do
    let(:issue) { create :issue, project: project }
    let(:other_issue) { create :issue, project: project }
    let(:commit_author) { create :user }
    let(:closing_commit) { project.commit }

    before do
      allow(closing_commit).to receive_messages(
        issue_closing_regex: /^([Cc]loses|[Ff]ixes) #\d+/,
        safe_message: "this is some work.\n\ncloses ##{issue.iid}",
        author_name: commit_author.name,
        author_email: commit_author.email
      )

      allow(project.repository).to receive(:commits_between)
        .and_return([closing_commit])

      allow(Commit).to receive(:build_from_sidekiq_hash)
        .and_return(closing_commit)

      project.add_maintainer(commit_author)
    end

    context "to default branches" do
      let(:user) { commit_author }

      it "closes issues", :sidekiq_might_not_need_inline do
        subject
        expect(Issue.find(issue.id)).to be_closed
      end

      it "adds a note indicating that the issue is now closed", :sidekiq_might_not_need_inline do
        expect(SystemNoteService).to receive(:change_status).with(issue, project, commit_author, "closed", closing_commit)
        subject
      end

      it "doesn't create additional cross-reference notes" do
        expect(SystemNoteService).not_to receive(:cross_reference)
        subject
      end
    end

    context "to non-default branches" do
      before do
        # Make sure the "default" branch is different
        allow(project).to receive(:default_branch).and_return('not-master')
      end

      it "creates cross-reference notes", :sidekiq_inline, :use_clean_rails_redis_caching do
        expect(SystemNoteService).to receive(:cross_reference).with(issue, closing_commit, commit_author)
        subject
      end

      it "doesn't close issues" do
        subject
        expect(Issue.find(issue.id)).to be_opened
      end
    end

    context "for jira issue tracker" do
      include JiraIntegrationHelpers

      let(:jira_tracker) { project.create_jira_integration if project.jira_integration.nil? }

      before do
        # project.create_jira_integration doesn't seem to invalidate the cache here
        project.has_external_issue_tracker = true
        stub_jira_integration_test
        jira_integration_settings
        stub_jira_urls("JIRA-1")

        allow(closing_commit).to receive_messages({
          issue_closing_regex: Regexp.new(Gitlab.config.gitlab.issue_closing_pattern),
          safe_message: message,
          author_name: commit_author.name,
          author_email: commit_author.email
        })

        allow(JIRA::Resource::Remotelink).to receive(:all).and_return([])

        allow(project.repository).to receive_messages(commits_between: [closing_commit])
      end

      after do
        jira_tracker.destroy!
      end

      context "mentioning an issue" do
        let(:message) { "this is some work.\n\nrelated to JIRA-1" }

        it "initiates one api call to jira server to mention the issue", :sidekiq_inline, :use_clean_rails_redis_caching do
          subject

          expect(WebMock).to have_requested(:post, jira_api_comment_url('JIRA-1')).with(
            body: /mentioned this issue in/
          ).once
        end
      end

      context "closing an issue" do
        let(:message) { "this is some work.\n\ncloses JIRA-1" }
        let(:comment_body) do
          {
            body: "Issue solved with [#{closing_commit.id}|http://#{Gitlab.config.gitlab.host}/#{project.full_path}/-/commit/#{closing_commit.id}]."
          }.to_json
        end

        before do
          open_issue   = JIRA::Resource::Issue.new(jira_tracker.client, attrs: { "id" => "JIRA-1" })
          closed_issue = open_issue.dup
          allow(open_issue).to receive(:resolution).and_return(false)
          allow(closed_issue).to receive(:resolution).and_return(true)
          allow(JIRA::Resource::Issue).to receive(:find).and_return(open_issue, closed_issue)

          allow_any_instance_of(JIRA::Resource::Issue).to receive(:key).and_return("JIRA-1")
        end

        context "using right markdown", :sidekiq_might_not_need_inline do
          let(:user) { commit_author }

          it "initiates one api call to jira server to close the issue" do
            subject

            expect(WebMock).to have_requested(:post, jira_api_transition_url('JIRA-1')).once
          end

          it "initiates one api call to jira server to comment on the issue" do
            subject

            expect(WebMock)
              .to have_requested(:post, jira_api_comment_url('JIRA-1'))
              .with(body: comment_body)
              .once
          end
        end

        context "using internal issue reference" do
          let(:user) { commit_author }

          context 'when internal issues are disabled' do
            before do
              project.issues_enabled = false
              project.save!
            end

            let(:message) { "this is some work.\n\ncloses #1" }

            it "does not initiates one api call to jira server to close the issue" do
              subject

              expect(WebMock).not_to have_requested(:post, jira_api_transition_url('JIRA-1'))
            end

            it "does not initiates one api call to jira server to comment on the issue" do
              subject

              expect(WebMock).not_to have_requested(:post, jira_api_comment_url('JIRA-1')).with(
                body: comment_body
              ).once
            end
          end

          context 'when internal issues are enabled', :sidekiq_might_not_need_inline do
            let(:issue) { create(:issue, project: project) }
            let(:message) { "this is some work.\n\ncloses JIRA-1 \n\n closes #{issue.to_reference}" }

            it "initiates one api call to jira server to close the jira issue" do
              subject

              expect(WebMock).to have_requested(:post, jira_api_transition_url('JIRA-1')).once
            end

            it "initiates one api call to jira server to comment on the jira issue" do
              subject

              expect(WebMock).to have_requested(:post, jira_api_comment_url('JIRA-1')).with(
                body: comment_body
              ).once
            end

            it "closes the internal issue" do
              subject
              expect(issue.reload).to be_closed
            end

            it "adds a note indicating that the issue is now closed" do
              expect(SystemNoteService).to receive(:change_status)
                .with(issue, project, commit_author, "closed", closing_commit)
              subject
            end
          end
        end
      end
    end
  end

  describe "empty project" do
    let(:project) { create(:project_empty_repo) }
    let(:ref) { 'refs/heads/feature' }
    let(:oldrev) { blankrev }

    before do
      allow(project).to receive(:default_branch).and_return('feature')
      expect(project).to receive(:change_head) { 'feature' }
    end

    it 'push to first branch updates HEAD' do
      subject
    end
  end

  describe "CI environments" do
    context 'create branch' do
      let(:oldrev) { blankrev }

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

        subject
      end
    end

    context 'update branch' do
      it 'does nothing' do
        expect(::Environments::StopService).not_to receive(:new)

        subject
      end
    end

    context 'delete branch' do
      let(:newrev) { blankrev }

      it 'stops environments' do
        expect_next_instance_of(::Environments::StopService) do |stop_service|
          expect(stop_service.project).to eq(project)
          expect(stop_service.current_user).to eq(user)
          expect(stop_service).to receive(:execute_for_branch).with(branch)
        end

        subject
      end
    end
  end

  describe 'artifacts' do
    context 'create branch' do
      let(:oldrev) { blankrev }

      it 'does nothing' do
        expect(::Ci::RefDeleteUnlockArtifactsWorker).not_to receive(:perform_async)

        subject
      end
    end

    context 'update branch' do
      it 'does nothing' do
        expect(::Ci::RefDeleteUnlockArtifactsWorker).not_to receive(:perform_async)

        subject
      end
    end

    context 'delete branch' do
      let(:newrev) { blankrev }

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

        subject
      end
    end
  end

  describe 'Hooks' do
    context 'run on a branch' do
      it 'delegates to Git::BranchHooksService' do
        expect_next_instance_of(::Git::BranchHooksService) do |hooks_service|
          expect(hooks_service.project).to eq(project)
          expect(hooks_service.current_user).to eq(user)
          expect(hooks_service.params).to include(
            change: {
              oldrev: oldrev,
              newrev: newrev,
              ref: ref
            }
          )

          expect(hooks_service).to receive(:execute)
        end

        subject
      end
    end

    context 'run on a tag' do
      let(:ref) { 'refs/tags/v1.1.0' }

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

        subject
      end
    end
  end

  context 'Jira Connect hooks' do
    let(:branch_to_sync) { nil }
    let(:commits_to_sync) { [] }

    shared_examples 'enqueues Jira sync worker' do
      specify :aggregate_failures do
        Sidekiq::Testing.fake! do
          if commits_to_sync.any?
            expect(JiraConnect::SyncBranchWorker)
              .to receive(:perform_in)
              .with(kind_of(Numeric), project.id, branch_to_sync, commits_to_sync, kind_of(Numeric))
              .and_call_original
          else
            expect(JiraConnect::SyncBranchWorker)
              .to receive(:perform_async)
              .with(project.id, branch_to_sync, commits_to_sync, kind_of(Numeric))
              .and_call_original
          end

          expect { subject }.to change(JiraConnect::SyncBranchWorker.jobs, :size).by(1)
        end
      end
    end

    shared_examples 'does not enqueue Jira sync worker' do
      specify do
        Sidekiq::Testing.fake! do
          expect { subject }.not_to change(JiraConnect::SyncBranchWorker.jobs, :size)
        end
      end
    end

    context 'with a Jira subscription' do
      before do
        create(:jira_connect_subscription, namespace: project.namespace)
      end

      context 'branch name contains Jira issue key' do
        let(:branch_to_sync) { 'branch-JIRA-123' }
        let(:ref) { "refs/heads/#{branch_to_sync}" }

        it_behaves_like 'enqueues Jira sync worker'
      end

      context 'commit message contains Jira issue key' do
        let(:commits_to_sync) { [newrev] }

        before do
          allow_any_instance_of(Commit).to receive(:safe_message).and_return('Commit with key JIRA-123')
        end

        it_behaves_like 'enqueues Jira sync worker'

        describe 'batch requests' do
          let(:commits_to_sync) { [sample_commit.id, another_sample_commit.id] }

          it 'enqueues multiple jobs' do
            # We have to stub this as we only have two valid commits to use
            stub_const('Git::BranchHooksService::JIRA_SYNC_BATCH_SIZE', 1)

            expect_any_instance_of(Git::BranchHooksService).to receive(:filtered_commit_shas).and_return(commits_to_sync)

            expect(JiraConnect::SyncBranchWorker)
              .to receive(:perform_in)
              .with(0.seconds, project.id, branch_to_sync, [commits_to_sync.first], kind_of(Numeric))
              .and_call_original

            expect(JiraConnect::SyncBranchWorker)
              .to receive(:perform_in)
              .with(10.seconds, project.id, branch_to_sync, [commits_to_sync.last], kind_of(Numeric))
              .and_call_original

            subject
          end
        end
      end

      context 'branch name and commit message does not contain Jira issue key' do
        it_behaves_like 'does not enqueue Jira sync worker'
      end
    end

    context 'without a Jira subscription' do
      it_behaves_like 'does not enqueue Jira sync worker'
    end
  end

  describe 'project target platforms detection' do
    let(:oldrev) { blankrev }

    it 'calls enqueue_record_project_target_platforms on the project' do
      expect(project).to receive(:enqueue_record_project_target_platforms)

      subject
    end
  end
end


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

module Git
  class WikiPushService
    class Change
      include Gitlab::Utils::StrongMemoize

      # @param [Wiki] wiki
      # @param [Hash] change - must have keys `:oldrev` and `:newrev`
      # @param [Gitlab::Git::RawDiffChange] raw_change
      def initialize(wiki, change, raw_change)
        @wiki = wiki
        @raw_change = raw_change
        @change = change
      end

      def page
        strong_memoize(:page) { wiki.find_page(slug, revision) }
      end

      # See [Gitlab::Git::RawDiffChange#extract_operation] for the
      # definition of the full range of operation values.
      def event_action
        case raw_change.operation
        when :added
          :created
        when :deleted
          :destroyed
        else
          :updated
        end
      end

      def last_known_slug
        strip_extension(raw_change.old_path || raw_change.new_path)
      end

      def sha
        change[:newrev]
      end

      private

      attr_reader :raw_change, :change, :wiki

      def filename
        return raw_change.old_path if deleted?

        raw_change.new_path
      end

      def slug
        strip_extension(filename)
      end

      def revision
        return change[:oldrev] if deleted?

        change[:newrev]
      end

      def deleted?
        raw_change.operation == :deleted
      end

      def strip_extension(filename)
        return unless filename

        encoded_filename = Gitlab::EncodingHelper.encode_utf8(filename.dup)
        File.basename(encoded_filename, File.extname(encoded_filename))
      end
    end
  end
end

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Git::WikiPushService::Change, feature_category: :source_code_management do
  subject { described_class.new(project_wiki, change, raw_change) }

  let(:project_wiki) { double('ProjectWiki') }
  let(:raw_change) { double('RawChange', new_path: new_path, old_path: old_path, operation: operation) }
  let(:change) { { oldrev: generate(:sha), newrev: generate(:sha) } }

  let(:new_path) do
    case operation
    when :deleted
      nil
    else
      generate(:wiki_filename)
    end
  end

  let(:old_path) do
    case operation
    when :added
      nil
    when :deleted, :renamed
      generate(:wiki_filename)
    else
      new_path
    end
  end

  describe '#page' do
    context 'the page does not exist' do
      before do
        expect(project_wiki).to receive(:find_page).with(String, String).and_return(nil)
      end

      %i[added deleted renamed modified].each do |op|
        context "the operation is #{op}" do
          let(:operation) { op }

          it { is_expected.to have_attributes(page: be_nil) }
        end
      end
    end

    context 'the page can be found' do
      let(:wiki_page) { double('WikiPage') }

      before do
        expect(project_wiki).to receive(:find_page).with(slug, revision).and_return(wiki_page)
      end

      context 'the page has been deleted' do
        let(:operation) { :deleted }
        let(:slug) { old_path.chomp('.md') }
        let(:revision) { change[:oldrev] }

        it { is_expected.to have_attributes(page: wiki_page) }
      end

      %i[added renamed modified].each do |op|
        context "the operation is #{op}" do
          let(:operation) { op }
          let(:slug) { new_path.chomp('.md') }
          let(:revision) { change[:newrev] }

          it { is_expected.to have_attributes(page: wiki_page) }
        end
      end
    end
  end

  describe '#last_known_slug' do
    context 'the page has been created' do
      let(:operation) { :added }

      it { is_expected.to have_attributes(last_known_slug: new_path.chomp('.md')) }
    end

    %i[renamed modified deleted].each do |op|
      context "the operation is #{op}" do
        let(:operation) { op }

        it { is_expected.to have_attributes(last_known_slug: old_path.chomp('.md')) }
      end
    end
  end

  describe '#event_action' do
    context 'the page is deleted' do
      let(:operation) { :deleted }

      it { is_expected.to have_attributes(event_action: :destroyed) }
    end

    context 'the page is added' do
      let(:operation) { :added }

      it { is_expected.to have_attributes(event_action: :created) }
    end

    %i[renamed modified].each do |op|
      context "the page is #{op}" do
        let(:operation) { op }

        it { is_expected.to have_attributes(event_action: :updated) }
      end
    end
  end
end


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

module Onboarding
  class ProgressService
    class Async
      attr_reader :namespace_id

      def initialize(namespace_id)
        @namespace_id = namespace_id
      end

      def execute(action:)
        return unless Onboarding::Progress.not_completed?(namespace_id, action)

        Onboarding::ProgressWorker.perform_async(namespace_id, action)
      end
    end

    def self.async(namespace_id)
      Async.new(namespace_id)
    end

    def initialize(namespace)
      @namespace = namespace&.root_ancestor
    end

    def execute(action:)
      return unless @namespace

      Onboarding::Progress.register(@namespace, action)
    end
  end
end

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Onboarding::ProgressService, feature_category: :onboarding do
  describe '.async' do
    let_it_be(:namespace) { create(:namespace) }
    let_it_be(:action) { :git_pull }

    subject(:execute_service) { described_class.async(namespace.id).execute(action: action) }

    context 'when not onboarded' do
      it 'does not schedule a worker' do
        expect(Onboarding::ProgressWorker).not_to receive(:perform_async)

        execute_service
      end
    end

    context 'when onboarded' do
      before do
        Onboarding::Progress.onboard(namespace)
      end

      context 'when action is already completed' do
        before do
          Onboarding::Progress.register(namespace, action)
        end

        it 'does not schedule a worker' do
          expect(Onboarding::ProgressWorker).not_to receive(:perform_async)

          execute_service
        end
      end

      context 'when action is not yet completed' do
        it 'schedules a worker' do
          expect(Onboarding::ProgressWorker).to receive(:perform_async)

          execute_service
        end
      end
    end
  end

  describe '#execute' do
    let(:namespace) { create(:namespace) }
    let(:action) { :namespace_action }

    subject(:execute_service) { described_class.new(namespace).execute(action: :subscription_created) }

    context 'when the namespace is a root' do
      before do
        Onboarding::Progress.onboard(namespace)
      end

      it 'registers a namespace onboarding progress action for the given namespace' do
        execute_service

        expect(Onboarding::Progress.completed?(namespace, :subscription_created)).to eq(true)
      end
    end

    context 'when the namespace is not the root' do
      let(:group) { create(:group, :nested) }

      before do
        Onboarding::Progress.onboard(group)
      end

      it 'does not register a namespace onboarding progress action' do
        execute_service

        expect(Onboarding::Progress.completed?(group, :subscription_created)).to be(nil)
      end
    end

    context 'when no namespace is passed' do
      let(:namespace) { nil }

      it 'does not register a namespace onboarding progress action' do
        execute_service

        expect(Onboarding::Progress.completed?(namespace, :subscription_created)).to be(nil)
      end
    end
  end
end


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

module PagesDomains
  class CreateAcmeOrderService
    attr_reader :pages_domain

    def initialize(pages_domain)
      @pages_domain = pages_domain
    end

    def execute
      lets_encrypt_client = Gitlab::LetsEncrypt::Client.new
      order = lets_encrypt_client.new_order(pages_domain.domain)

      challenge = order.new_challenge

      private_key = OpenSSL::PKey::RSA.new(4096)
      saved_order = pages_domain.acme_orders.create!(
        url: order.url,
        expires_at: order.expires,
        private_key: private_key.to_pem,

        challenge_token: challenge.token,
        challenge_file_content: challenge.file_content
      )

      challenge.request_validation
      saved_order
    end
  end
end

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe PagesDomains::CreateAcmeOrderService, feature_category: :pages do
  include LetsEncryptHelpers

  let(:pages_domain) { create(:pages_domain) }

  let(:challenge) { ::Gitlab::LetsEncrypt::Challenge.new(acme_challenge_double) }

  let(:order_double) do
    Gitlab::LetsEncrypt::Order.new(acme_order_double).tap do |order|
      allow(order).to receive(:new_challenge).and_return(challenge)
    end
  end

  let(:lets_encrypt_client) do
    instance_double('Gitlab::LetsEncrypt::Client').tap do |client|
      allow(client).to receive(:new_order).with(pages_domain.domain)
        .and_return(order_double)
    end
  end

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

  before do
    allow(::Gitlab::LetsEncrypt::Client).to receive(:new).and_return(lets_encrypt_client)
  end

  it 'saves order to database before requesting validation' do
    allow(pages_domain.acme_orders).to receive(:create!).and_call_original
    allow(challenge).to receive(:request_validation).and_call_original

    service.execute

    expect(pages_domain.acme_orders).to have_received(:create!).ordered
    expect(challenge).to have_received(:request_validation).ordered
  end

  it 'generates and saves private key' do
    service.execute

    saved_order = PagesDomainAcmeOrder.last
    expect { OpenSSL::PKey::RSA.new(saved_order.private_key) }.not_to raise_error
  end

  it 'properly saves order attributes' do
    service.execute

    saved_order = PagesDomainAcmeOrder.last
    expect(saved_order.url).to eq(order_double.url)
    expect(saved_order.expires_at).to be_like_time(order_double.expires)
  end

  it 'properly saves challenge attributes' do
    service.execute

    saved_order = PagesDomainAcmeOrder.last
    expect(saved_order.challenge_token).to eq(challenge.token)
    expect(saved_order.challenge_file_content).to eq(challenge.file_content)
  end
end


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

module PagesDomains
  class RetryAcmeOrderService
    attr_reader :pages_domain

    def initialize(pages_domain)
      @pages_domain = pages_domain
    end

    def execute
      updated = pages_domain.with_lock do
        next unless pages_domain.auto_ssl_enabled && pages_domain.auto_ssl_failed

        pages_domain.update!(auto_ssl_failed: false)
      end

      return unless updated

      PagesDomainSslRenewalWorker.perform_async(pages_domain.id)

      publish_event(pages_domain)
    end

    private

    def publish_event(domain)
      event = PagesDomainUpdatedEvent.new(
        data: {
          project_id: domain.project.id,
          namespace_id: domain.project.namespace_id,
          root_namespace_id: domain.project.root_namespace.id,
          domain_id: domain.id,
          domain: domain.domain
        }
      )

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

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe PagesDomains::RetryAcmeOrderService, feature_category: :pages do
  let_it_be(:project) { create(:project) }

  let(:domain) { create(:pages_domain, project: project, auto_ssl_enabled: true, auto_ssl_failed: true) }

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

  it 'clears auto_ssl_failed' do
    expect { service.execute }
      .to change { domain.auto_ssl_failed }
      .from(true).to(false)
      .and publish_event(PagesDomains::PagesDomainUpdatedEvent)
      .with(
        project_id: project.id,
        namespace_id: project.namespace.id,
        root_namespace_id: project.root_namespace.id,
        domain_id: domain.id,
        domain: domain.domain
      )
  end

  it 'schedules renewal worker and publish PagesDomainUpdatedEvent event' do
    expect(PagesDomainSslRenewalWorker).to receive(:perform_async).with(domain.id).and_return(nil).once

    expect { service.execute }
      .to publish_event(PagesDomains::PagesDomainUpdatedEvent)
      .with(
        project_id: project.id,
        namespace_id: project.namespace.id,
        root_namespace_id: project.root_namespace.id,
        domain_id: domain.id,
        domain: domain.domain
      )
  end

  it "doesn't schedule renewal worker if Let's Encrypt integration is not enabled" do
    domain.update!(auto_ssl_enabled: false)

    expect(PagesDomainSslRenewalWorker).not_to receive(:new)

    expect { service.execute }
      .to not_publish_event(PagesDomains::PagesDomainUpdatedEvent)
  end

  it "doesn't schedule renewal worker if auto ssl has not failed yet" do
    domain.update!(auto_ssl_failed: false)

    expect(PagesDomainSslRenewalWorker).not_to receive(:new)

    expect { service.execute }
      .to not_publish_event(PagesDomains::PagesDomainUpdatedEvent)
  end
end


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

module PagesDomains
  class UpdateService < BaseService
    def execute(domain)
      return unless authorized?

      return false unless domain.update(params)

      publish_event(domain)

      true
    end

    private

    def authorized?
      current_user.can?(:update_pages, project)
    end

    def publish_event(domain)
      event = PagesDomainUpdatedEvent.new(
        data: {
          project_id: project.id,
          namespace_id: project.namespace_id,
          root_namespace_id: project.root_namespace.id,
          domain_id: domain.id,
          domain: domain.domain
        }
      )

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

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe PagesDomains::UpdateService, feature_category: :pages do
  let_it_be(:user) { create(:user) }
  let_it_be(:pages_domain) { create(:pages_domain, :with_project) }

  let(:params) do
    attributes_for(:pages_domain, :with_trusted_chain).slice(:key, :certificate).tap do |params|
      params[:user_provided_key] = params.delete(:key)
      params[:user_provided_certificate] = params.delete(:certificate)
    end
  end

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

  context 'when the user does not have the required permissions' do
    it 'does not update the pages domain and does not publish a PagesDomainUpdatedEvent' do
      expect do
        expect(service.execute(pages_domain)).to be_nil
      end.to not_publish_event(PagesDomains::PagesDomainUpdatedEvent)
    end
  end

  context 'when the user has the required permissions' do
    before do
      pages_domain.project.add_maintainer(user)
    end

    context 'when it updates the domain successfully' do
      it 'updates the domain' do
        expect(service.execute(pages_domain)).to eq(true)
      end

      it 'publishes a PagesDomainUpdatedEvent' do
        expect { service.execute(pages_domain) }
          .to publish_event(PagesDomains::PagesDomainUpdatedEvent)
          .with(
            project_id: pages_domain.project.id,
            namespace_id: pages_domain.project.namespace.id,
            root_namespace_id: pages_domain.project.root_namespace.id,
            domain_id: pages_domain.id,
            domain: pages_domain.domain
          )
      end
    end

    context 'when it fails to update the domain' do
      let(:params) { { user_provided_certificate: 'blabla' } }

      it 'does not update a pages domain' do
        expect(service.execute(pages_domain)).to be(false)
      end

      it 'does not publish a PagesDomainUpdatedEvent' do
        expect { service.execute(pages_domain) }
          .not_to publish_event(PagesDomains::PagesDomainUpdatedEvent)
      end
    end
  end
end


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

module PagesDomains
  class ObtainLetsEncryptCertificateService
    # time for processing validation requests for acme challenges
    # 5-15 seconds is usually enough
    CHALLENGE_PROCESSING_DELAY = 1.minute.freeze

    # time LetsEncrypt ACME server needs to generate the certificate
    # no particular SLA, usually takes 10-15 seconds
    CERTIFICATE_PROCESSING_DELAY = 1.minute.freeze

    attr_reader :pages_domain

    def initialize(pages_domain)
      @pages_domain = pages_domain
    end

    def execute
      pages_domain.acme_orders.expired.delete_all
      acme_order = pages_domain.acme_orders.first

      unless acme_order
        ::PagesDomains::CreateAcmeOrderService.new(pages_domain).execute
        PagesDomainSslRenewalWorker.perform_in(CHALLENGE_PROCESSING_DELAY, pages_domain.id)
        return
      end

      api_order = ::Gitlab::LetsEncrypt::Client.new.load_order(acme_order.url)

      # https://www.rfc-editor.org/rfc/rfc8555#section-7.1.6 - statuses diagram
      case api_order.status
      when 'ready'
        api_order.request_certificate(private_key: acme_order.private_key, domain: pages_domain.domain)
        PagesDomainSslRenewalWorker.perform_in(CERTIFICATE_PROCESSING_DELAY, pages_domain.id)
      when 'valid'
        save_certificate(acme_order.private_key, api_order)
        acme_order.destroy!
      when 'invalid'
        save_order_error(acme_order, api_order)
      end
    end

    private

    def save_certificate(private_key, api_order)
      certificate = api_order.certificate
      pages_domain.update!(gitlab_provided_key: private_key, gitlab_provided_certificate: certificate)
    end

    def save_order_error(acme_order, api_order)
      log_error(api_order)

      pages_domain.assign_attributes(auto_ssl_failed: true)
      pages_domain.save!(validate: false)

      acme_order.destroy!

      NotificationService.new.pages_domain_auto_ssl_failed(pages_domain)
    end

    def log_error(api_order)
      Gitlab::AppLogger.error(
        message: "Failed to obtain Let's Encrypt certificate",
        acme_error: api_order.challenge_error,
        project_id: pages_domain.project_id,
        pages_domain: pages_domain.domain
      )
    rescue StandardError => e
      # getting authorizations is an additional network request which can raise errors
      Gitlab::ErrorTracking.track_exception(e)
    end
  end
end

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe PagesDomains::ObtainLetsEncryptCertificateService, feature_category: :pages do
  include LetsEncryptHelpers

  let(:pages_domain) { create(:pages_domain, :without_certificate, :without_key) }
  let(:service) { described_class.new(pages_domain) }

  before do
    stub_lets_encrypt_settings
  end

  around do |example|
    Sidekiq::Testing.fake! do
      example.run
    end
  end

  def expect_to_create_acme_challenge
    expect(::PagesDomains::CreateAcmeOrderService).to receive(:new).with(pages_domain)
      .and_wrap_original do |m, *args|
      create_service = m.call(*args)

      expect(create_service).to receive(:execute)

      create_service
    end
  end

  def stub_lets_encrypt_order(url, status)
    order = ::Gitlab::LetsEncrypt::Order.new(acme_order_double(status: status))

    allow_next_instance_of(::Gitlab::LetsEncrypt::Client) do |instance|
      allow(instance).to receive(:load_order).with(url).and_return(order)
    end

    order
  end

  context 'when there is no acme order' do
    it 'creates acme order and schedules next step' do
      expect_to_create_acme_challenge
      expect(PagesDomainSslRenewalWorker).to(
        receive(:perform_in).with(described_class::CHALLENGE_PROCESSING_DELAY, pages_domain.id)
          .and_return(nil).once
      )

      service.execute
    end
  end

  context 'when there is expired acme order' do
    let!(:existing_order) do
      create(:pages_domain_acme_order, :expired, pages_domain: pages_domain)
    end

    it 'removes acme order and creates new one' do
      expect_to_create_acme_challenge

      service.execute

      expect(PagesDomainAcmeOrder.find_by_id(existing_order.id)).to be_nil
    end
  end

  %w[pending processing].each do |status|
    context "there is an order in '#{status}' status" do
      let(:existing_order) do
        create(:pages_domain_acme_order, pages_domain: pages_domain)
      end

      before do
        stub_lets_encrypt_order(existing_order.url, status)
      end

      it 'does not raise errors' do
        expect do
          service.execute
        end.not_to raise_error
      end
    end
  end

  context 'when order is ready' do
    let(:existing_order) do
      create(:pages_domain_acme_order, pages_domain: pages_domain)
    end

    let!(:api_order) do
      stub_lets_encrypt_order(existing_order.url, 'ready')
    end

    it 'request certificate and schedules next step' do
      expect(api_order).to receive(:request_certificate).and_call_original
      expect(PagesDomainSslRenewalWorker).to(
        receive(:perform_in).with(described_class::CERTIFICATE_PROCESSING_DELAY, pages_domain.id)
          .and_return(nil).once
      )

      service.execute
    end
  end

  context 'when order is valid' do
    let(:existing_order) do
      create(:pages_domain_acme_order, pages_domain: pages_domain)
    end

    let!(:api_order) do
      stub_lets_encrypt_order(existing_order.url, 'valid')
    end

    let(:certificate) do
      key = OpenSSL::PKey.read(existing_order.private_key)

      subject = "/C=BE/O=Test/OU=Test/CN=#{pages_domain.domain}"

      cert = OpenSSL::X509::Certificate.new
      cert.subject = cert.issuer = OpenSSL::X509::Name.parse(subject)
      cert.not_before = Time.current
      cert.not_after = 1.year.from_now
      cert.public_key = key.public_key
      cert.serial = 0x0
      cert.version = 2

      ef = OpenSSL::X509::ExtensionFactory.new
      ef.subject_certificate = cert
      ef.issuer_certificate = cert
      cert.extensions = [
        ef.create_extension("basicConstraints", "CA:TRUE", true),
        ef.create_extension("subjectKeyIdentifier", "hash")
      ]
      cert.add_extension ef.create_extension("authorityKeyIdentifier", "keyid:always,issuer:always")

      cert.sign key, OpenSSL::Digest.new('SHA256')

      cert.to_pem
    end

    before do
      expect(api_order).to receive(:certificate) { certificate }
    end

    it 'saves private_key and certificate for domain' do
      service.execute

      expect(pages_domain.key).to be_present
      expect(pages_domain.certificate).to eq(certificate)
    end

    it 'marks certificate as gitlab_provided' do
      service.execute

      expect(pages_domain.certificate_source).to eq("gitlab_provided")
    end

    it 'removes order from database' do
      service.execute

      expect(PagesDomainAcmeOrder.find_by_id(existing_order.id)).to be_nil
    end
  end

  context 'when order is invalid' do
    let(:existing_order) do
      create(:pages_domain_acme_order, pages_domain: pages_domain)
    end

    let!(:api_order) do
      stub_lets_encrypt_order(existing_order.url, 'invalid')
    end

    it 'saves error to domain and deletes acme order' do
      expect do
        service.execute
      end.to change { pages_domain.reload.auto_ssl_failed }.from(false).to(true)

      expect(PagesDomainAcmeOrder.find_by_id(existing_order.id)).to be_nil
    end

    it 'sends notification' do
      expect_next_instance_of(NotificationService) do |notification_service|
        expect(notification_service).to receive(:pages_domain_auto_ssl_failed).with(pages_domain)
      end

      service.execute
    end
  end
end


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

module PagesDomains
  class CreateService < BaseService
    def execute
      return unless authorized?

      domain = project.pages_domains.create(params)

      publish_event(domain) if domain.persisted?

      domain
    end

    private

    def authorized?
      current_user.can?(:update_pages, project)
    end

    def publish_event(domain)
      event = PagesDomainCreatedEvent.new(
        data: {
          project_id: project.id,
          namespace_id: project.namespace_id,
          root_namespace_id: project.root_namespace.id,
          domain_id: domain.id,
          domain: domain.domain
        }
      )

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

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe ::PagesDomains::CreateService, feature_category: :pages do
  let_it_be(:user) { create(:user) }
  let_it_be(:project) { create(:project, :in_subgroup) }

  let(:domain) { 'new.domain.com' }
  let(:attributes) { { domain: domain } }

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

  context 'when the user does not have the required permissions' do
    it 'does not create a pages domain and does not publish a PagesDomainCreatedEvent' do
      expect(service.execute).to be_nil

      expect { service.execute }
        .to not_publish_event(PagesDomains::PagesDomainCreatedEvent)
        .and not_change(project.pages_domains, :count)
    end
  end

  context 'when the user has the required permissions' do
    before do
      project.add_maintainer(user)
    end

    context 'when it saves the domain successfully' do
      it 'creates the domain and publishes a PagesDomainCreatedEvent' do
        pages_domain = nil

        expect { pages_domain = service.execute }
          .to change(project.pages_domains, :count)
          .and publish_event(PagesDomains::PagesDomainCreatedEvent)
          .with(
            project_id: project.id,
            namespace_id: project.namespace.id,
            root_namespace_id: project.root_namespace.id,
            domain_id: kind_of(Numeric),
            domain: domain
          )

        expect(pages_domain).to be_persisted
      end
    end

    context 'when it fails to save the domain' do
      let(:domain) { nil }

      it 'does not create a pages domain and does not publish a PagesDomainCreatedEvent' do
        pages_domain = nil

        expect { pages_domain = service.execute }
          .to not_publish_event(PagesDomains::PagesDomainCreatedEvent)
          .and not_change(project.pages_domains, :count)

        expect(pages_domain).not_to be_persisted
      end
    end
  end
end


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

module PagesDomains
  class DeleteService < BaseService
    def execute(domain)
      return unless authorized?

      domain.destroy

      publish_event(domain)
    end

    private

    def authorized?
      current_user.can?(:update_pages, project)
    end

    def publish_event(domain)
      event = PagesDomainDeletedEvent.new(
        data: {
          project_id: project.id,
          namespace_id: project.namespace_id,
          root_namespace_id: project.root_namespace.id,
          domain_id: domain.id,
          domain: domain.domain
        }
      )

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

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe ::PagesDomains::DeleteService, feature_category: :pages do
  let_it_be(:user) { create(:user) }
  let_it_be(:pages_domain) { create(:pages_domain, :with_project) }

  let(:params) do
    attributes_for(:pages_domain, :with_trusted_chain).slice(:key, :certificate).tap do |params|
      params[:user_provided_key] = params.delete(:key)
      params[:user_provided_certificate] = params.delete(:certificate)
    end
  end

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

  context 'when the user does not have the required permissions' do
    it 'does not delete the pages domain and does not publish a PagesDomainDeletedEvent' do
      result_match = -> { expect(service.execute(pages_domain)).to be_nil }

      expect(&result_match)
        .to not_publish_event(PagesDomains::PagesDomainDeletedEvent)
    end
  end

  context 'when the user has the required permissions' do
    before do
      pages_domain.project.add_maintainer(user)
    end

    context 'when it updates the domain successfully' do
      it 'deletes the domain and publishes a PagesDomainDeletedEvent' do
        result_match = -> { expect(service.execute(pages_domain)).not_to be_nil }

        expect(&result_match)
          .to publish_event(PagesDomains::PagesDomainDeletedEvent)
          .with(
            project_id: pages_domain.project.id,
            namespace_id: pages_domain.project.namespace.id,
            root_namespace_id: pages_domain.project.root_namespace.id,
            domain_id: pages_domain.id,
            domain: pages_domain.domain
          )
      end
    end
  end
end


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

#
# Concern that helps with getting an exclusive lease for running a block
# of code.
#
# `#try_obtain_lease` takes a block which will be run if it was able to
# obtain  the lease. Implement `#lease_timeout` to configure the timeout
# for the exclusive lease.
#
# Optionally override `#lease_key` to set the
# lease key, it defaults to the class name with underscores.
#
# Optionally override `#lease_release?` to prevent the job to
# be re-executed more often than LEASE_TIMEOUT.
#
module ExclusiveLeaseGuard
  extend ActiveSupport::Concern

  def try_obtain_lease
    lease = exclusive_lease.try_obtain

    unless lease
      log_lease_taken
      return
    end

    begin
      yield lease
    ensure
      release_lease(lease) if lease_release?
    end
  end

  def exclusive_lease
    @lease ||= Gitlab::ExclusiveLease.new(lease_key, timeout: lease_timeout)
  end

  def lease_key
    @lease_key ||= self.class.name.underscore
  end

  def lease_timeout
    raise NotImplementedError,
      "#{self.class.name} does not implement #{__method__}"
  end

  def lease_release?
    true
  end

  def release_lease(uuid)
    Gitlab::ExclusiveLease.cancel(lease_key, uuid)
  end

  def renew_lease!
    exclusive_lease.renew
  end

  def log_lease_taken
    logger = Gitlab::AppJsonLogger
    args = { message: lease_taken_message, lease_key: lease_key, class_name: self.class.name, lease_timeout: lease_timeout }

    case lease_taken_log_level
    when :debug then logger.debug(args)
    when :info  then logger.info(args)
    when :warn  then logger.warn(args)
    else             logger.error(args)
    end
  end

  def lease_taken_message
    "Cannot obtain an exclusive lease. There must be another instance already in execution."
  end

  def lease_taken_log_level
    :error
  end
end

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe ExclusiveLeaseGuard, :clean_gitlab_redis_shared_state, feature_category: :shared do
  subject :subject_class do
    Class.new do
      include ExclusiveLeaseGuard

      def self.name
        'ExclusiveLeaseGuardTestClass'
      end

      def call(&block)
        try_obtain_lease do
          internal_method(&block)
        end
      end

      def internal_method
        yield
      end

      def lease_timeout
        1.second
      end
    end
  end

  describe '#try_obtain_lease' do
    let(:subject) { subject_class.new }

    it 'obtains the lease, calls internal_method and releases the lease', :aggregate_failures do
      expect(subject).to receive(:internal_method).and_call_original

      subject.call do
        expect(subject.exclusive_lease.exists?).to be_truthy
      end

      expect(subject.exclusive_lease.exists?).to be_falsey
    end

    context 'when the lease is already obtained' do
      before do
        subject.exclusive_lease.try_obtain
      end

      after do
        subject.exclusive_lease.cancel
      end

      context 'when the class does not override lease_taken_log_level' do
        it 'does not call internal_method but logs error', :aggregate_failures do
          expect(subject).not_to receive(:internal_method)
          expect(Gitlab::AppJsonLogger).to receive(:error).with({ message: "Cannot obtain an exclusive lease. There must be another instance already in execution.", lease_key: 'exclusive_lease_guard_test_class', class_name: 'ExclusiveLeaseGuardTestClass', lease_timeout: 1.second })

          subject.call
        end
      end

      context 'when the class overrides lease_taken_log_level to return :info' do
        subject :overwritten_subject_class do
          Class.new(subject_class) do
            def lease_taken_log_level
              :info
            end
          end
        end

        let(:subject) { overwritten_subject_class.new }

        it 'logs info', :aggregate_failures do
          expect(Gitlab::AppJsonLogger).to receive(:info).with({ message: "Cannot obtain an exclusive lease. There must be another instance already in execution.", lease_key: 'exclusive_lease_guard_test_class', class_name: 'ExclusiveLeaseGuardTestClass', lease_timeout: 1.second })

          subject.call
        end
      end

      context 'when the class overrides lease_taken_log_level to return :debug' do
        subject :overwritten_subject_class do
          Class.new(subject_class) do
            def lease_taken_log_level
              :debug
            end
          end
        end

        let(:subject) { overwritten_subject_class.new }

        it 'logs debug', :aggregate_failures do
          expect(Gitlab::AppJsonLogger).to receive(:debug).with({ message: "Cannot obtain an exclusive lease. There must be another instance already in execution.", lease_key: 'exclusive_lease_guard_test_class', class_name: 'ExclusiveLeaseGuardTestClass', lease_timeout: 1.second })

          subject.call
        end
      end
    end

    context 'with overwritten lease_release?' do
      subject :overwritten_subject_class do
        Class.new(subject_class) do
          def lease_release?
            false
          end
        end
      end

      let(:subject) { overwritten_subject_class.new }

      it 'does not release the lease after execution', :aggregate_failures do
        subject.call do
          expect(subject.exclusive_lease.exists?).to be_truthy
        end

        expect(subject.exclusive_lease.exists?).to be_truthy
      end
    end
  end

  describe '#exclusive_lease' do
    it 'uses the class name as lease key' do
      expect(Gitlab::ExclusiveLease).to receive(:new).with('exclusive_lease_guard_test_class', timeout: 1.second)

      subject_class.new.exclusive_lease
    end

    context 'with overwritten lease_key' do
      subject :overwritten_class do
        Class.new(subject_class) do
          def lease_key
            'other_lease_key'
          end
        end
      end

      it 'uses the custom lease key' do
        expect(Gitlab::ExclusiveLease).to receive(:new).with('other_lease_key', timeout: 1.second)

        overwritten_class.new.exclusive_lease
      end
    end
  end

  describe '#release_lease' do
    it 'sends a cancel message to ExclusiveLease' do
      expect(Gitlab::ExclusiveLease).to receive(:cancel).with('exclusive_lease_guard_test_class', 'some_uuid')

      subject_class.new.release_lease('some_uuid')
    end
  end

  describe '#renew_lease!' do
    let(:subject) { subject_class.new }

    it 'sends a renew message to the exclusive_lease instance' do
      expect(subject.exclusive_lease).to receive(:renew)
      subject.renew_lease!
    end
  end
end


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

module RateLimitedService
  extend ActiveSupport::Concern

  RateLimitedNotSetupError = Class.new(StandardError)

  class RateLimitedError < StandardError
    def initialize(key:, rate_limiter:)
      @key = key
      @rate_limiter = rate_limiter
    end

    def headers
      # TODO: This will be fleshed out in https://gitlab.com/gitlab-org/gitlab/-/issues/342370
      {}
    end

    def log_request(request, current_user)
      rate_limiter.log_request(request, "#{key}_request_limit".to_sym, current_user)
    end

    private

    attr_reader :key, :rate_limiter
  end

  class RateLimiterScopedAndKeyed
    attr_reader :key, :opts, :rate_limiter

    def initialize(key:, opts:, rate_limiter:)
      @key = key
      @opts = opts
      @rate_limiter = rate_limiter
    end

    def rate_limit!(service)
      evaluated_scope = evaluated_scope_for(service)

      if rate_limiter.throttled?(key, **opts.merge(scope: evaluated_scope.values, users_allowlist: users_allowlist))
        raise RateLimitedError.new(key: key, rate_limiter: rate_limiter), _('This endpoint has been requested too many times. Try again later.')
      end
    end

    private

    def users_allowlist
      @users_allowlist ||= opts[:users_allowlist] ? opts[:users_allowlist].call : []
    end

    def evaluated_scope_for(service)
      opts[:scope].index_with do |var|
        service.public_send(var) # rubocop: disable GitlabSecurity/PublicSend
      end
    end
  end

  prepended do
    attr_accessor :rate_limiter_bypassed

    cattr_accessor :rate_limiter_scoped_and_keyed

    def self.rate_limit(key:, opts:, rate_limiter: ::Gitlab::ApplicationRateLimiter)
      self.rate_limiter_scoped_and_keyed = RateLimiterScopedAndKeyed.new(
        key: key,
        opts: opts,
        rate_limiter: rate_limiter
      )
    end
  end

  def execute_without_rate_limiting(*args, **kwargs)
    self.rate_limiter_bypassed = true
    execute(*args, **kwargs)
  ensure
    self.rate_limiter_bypassed = false
  end

  def execute(*args, **kwargs)
    raise RateLimitedNotSetupError if rate_limiter_scoped_and_keyed.nil?

    rate_limiter_scoped_and_keyed.rate_limit!(self) unless rate_limiter_bypassed

    super
  end
end

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe RateLimitedService, feature_category: :rate_limiting do
  let(:key) { :issues_create }
  let(:scope) { [:container, :current_user] }
  let(:opts) { { scope: scope, users_allowlist: -> { [Users::Internal.support_bot.username] } } }
  let(:rate_limiter) { ::Gitlab::ApplicationRateLimiter }

  describe 'RateLimitedError' do
    subject { described_class::RateLimitedError.new(key: key, rate_limiter: rate_limiter) }

    describe '#headers' do
      it 'returns a Hash of HTTP headers' do
        # TODO: This will be fleshed out in https://gitlab.com/gitlab-org/gitlab/-/issues/342370
        expected_headers = {}

        expect(subject.headers).to eq(expected_headers)
      end
    end

    describe '#log_request' do
      it 'logs the request' do
        request = instance_double(Grape::Request)
        user = instance_double(User)

        expect(rate_limiter).to receive(:log_request).with(request, "#{key}_request_limit".to_sym, user)

        subject.log_request(request, user)
      end
    end
  end

  describe 'RateLimiterScopedAndKeyed' do
    subject { described_class::RateLimiterScopedAndKeyed.new(key: key, opts: opts, rate_limiter: rate_limiter) }

    describe '#rate_limit!' do
      let_it_be(:project) { create(:project) }
      let_it_be(:current_user) { create(:user) }

      let(:service) { instance_double(Issues::CreateService, container: project, current_user: current_user) }
      let(:evaluated_scope) { [project, current_user] }
      let(:evaluated_opts) { { scope: evaluated_scope, users_allowlist: %w[support-bot] } }

      context 'when rate limiting is not in effect' do
        let(:throttled) { false }

        it 'does not raise an exception' do
          expect(subject.rate_limit!(service)).to be_nil
        end
      end

      context 'when rate limiting is in effect' do
        before do
          allow(rate_limiter).to receive(:throttled?).and_return(true)
        end

        it 'raises a RateLimitedError exception' do
          expect { subject.rate_limit!(service) }.to raise_error(described_class::RateLimitedError, 'This endpoint has been requested too many times. Try again later.')
        end
      end
    end
  end

  describe '#execute_without_rate_limiting' do
    let(:rate_limiter_scoped_and_keyed) { instance_double(RateLimitedService::RateLimiterScopedAndKeyed) }
    let(:subject) do
      local_key = key
      local_opts = opts

      Class.new do
        prepend RateLimitedService

        rate_limit key: local_key, opts: local_opts

        def execute(*args, **kwargs)
          'main logic here'
        end
      end.new
    end

    before do
      allow(RateLimitedService::RateLimiterScopedAndKeyed).to receive(:new).with(key: key, opts: opts, rate_limiter: rate_limiter).and_return(rate_limiter_scoped_and_keyed)
    end

    context 'bypasses rate limiting' do
      it 'calls super' do
        expect(rate_limiter_scoped_and_keyed).not_to receive(:rate_limit!).with(subject)

        expect(subject.execute_without_rate_limiting).to eq('main logic here')
      end
    end
  end

  describe '#execute' do
    context 'when rate_limit has not been called' do
      let(:subject) { Class.new { prepend RateLimitedService }.new }

      it 'raises an RateLimitedNotSetupError exception' do
        expect { subject.execute }.to raise_error(described_class::RateLimitedNotSetupError)
      end
    end

    context 'when rate_limit has been called' do
      let(:rate_limiter_scoped_and_keyed) { instance_double(RateLimitedService::RateLimiterScopedAndKeyed) }
      let(:subject) do
        local_key = key
        local_opts = opts

        Class.new do
          prepend RateLimitedService

          rate_limit key: local_key, opts: local_opts

          def execute(*args, **kwargs)
            'main logic here'
          end
        end.new
      end

      before do
        allow(RateLimitedService::RateLimiterScopedAndKeyed).to receive(:new).with(key: key, opts: opts, rate_limiter: rate_limiter).and_return(rate_limiter_scoped_and_keyed)
      end

      context 'and applies rate limiting' do
        it 'raises an RateLimitedService::RateLimitedError exception' do
          expect(rate_limiter_scoped_and_keyed).to receive(:rate_limit!).with(subject).and_raise(RateLimitedService::RateLimitedError.new(key: key, rate_limiter: rate_limiter))

          expect { subject.execute }.to raise_error(RateLimitedService::RateLimitedError)
        end
      end

      context 'but does not apply rate limiting' do
        it 'calls super' do
          expect(rate_limiter_scoped_and_keyed).to receive(:rate_limit!).with(subject).and_return(nil)

          expect(subject.execute).to eq('main logic here')
        end
      end
    end
  end
end


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

module AuditEventSaveType
  SAVE_TYPES = {
    database: 0b01,
    stream: 0b10,
    database_and_stream: 0b11
  }.freeze

  # def should_save_stream?(type)
  # def should_save_database?(type)
  [:database, :stream].each do |type|
    define_method("should_save_#{type}?") do |param_type|
      return false unless save_type_valid?(param_type)

      # If the current type does not support query, the result of the `&` operation is 0 .
      SAVE_TYPES[param_type] & SAVE_TYPES[type] != 0
    end
  end

  private

  def save_type_valid?(type)
    SAVE_TYPES.key?(type)
  end
end

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe AuditEventSaveType, feature_category: :audit_events do
  subject(:target) { Object.new.extend(described_class) }

  describe '#should_save_database? and #should_save_stream?' do
    using RSpec::Parameterized::TableSyntax

    where(:query_method, :query_param, :result) do
      :should_save_stream?    | :stream               | true
      :should_save_stream?    | :database_and_stream  | true
      :should_save_database?  | :database             | true
      :should_save_database?  | :database_and_stream  | true
      :should_save_stream?    | :database             | false
      :should_save_stream?    | nil                   | false
      :should_save_database?  | :stream               | false
      :should_save_database?  | nil                   | false
    end

    with_them do
      it 'returns corresponding results according to the query_method and query_param' do
        expect(target.send(query_method, query_param)).to eq result
      end
    end
  end
end


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

module MergeRequests
  module AssignsMergeParams
    def self.included(klass)
      raise "#{self} can not be included in #{klass} without implementing #current_user" unless klass.method_defined?(:current_user)
    end

    def assign_allowed_merge_params(merge_request, merge_params)
      known_merge_params = merge_params.to_h.with_indifferent_access.slice(*MergeRequest::KNOWN_MERGE_PARAMS)

      # Not checking `MergeRequest#can_remove_source_branch` as that includes
      # other checks that aren't needed here.
      known_merge_params.delete(:force_remove_source_branch) unless current_user.can?(:push_code, merge_request.source_project)

      merge_request.merge_params.merge!(known_merge_params)

      # Delete the known params now that they're assigned, so we don't try to
      # assign them through an `#assign_attributes` later.
      # They could be coming in as strings or symbols
      merge_params.to_h.with_indifferent_access.except!(*MergeRequest::KNOWN_MERGE_PARAMS)
    end
  end
end

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe MergeRequests::AssignsMergeParams, feature_category: :code_review_workflow do
  it 'raises an error when used from an instance that does not respond to #current_user' do
    define_class = -> { Class.new { include MergeRequests::AssignsMergeParams }.new }

    expect { define_class.call }.to raise_error %r{can not be included in (.*) without implementing #current_user}
  end

  describe '#assign_allowed_merge_params' do
    let(:merge_request) { build(:merge_request) }

    let(:params) do
      { commit_message: 'Commit Message',
        'should_remove_source_branch' => true,
        unknown_symbol: 'Unknown symbol',
        'unknown_string' => 'Unknown String' }
    end

    subject(:merge_request_service) do
      Class.new do
        attr_accessor :current_user

        include MergeRequests::AssignsMergeParams

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

    it 'only assigns known parameters to the merge request' do
      service = merge_request_service.new(merge_request.author)

      service.assign_allowed_merge_params(merge_request, params)

      expect(merge_request.merge_params).to eq('commit_message' => 'Commit Message', 'should_remove_source_branch' => true)
    end

    it 'returns a hash without the known merge params' do
      service = merge_request_service.new(merge_request.author)

      result = service.assign_allowed_merge_params(merge_request, params)

      expect(result).to eq({ 'unknown_symbol' => 'Unknown symbol', 'unknown_string' => 'Unknown String' })
    end

    context 'the force_remove_source_branch param' do
      let(:params) { { force_remove_source_branch: true } }

      it 'assigns the param if the user is allowed to do that' do
        service = merge_request_service.new(merge_request.author)

        result = service.assign_allowed_merge_params(merge_request, params)

        expect(merge_request.force_remove_source_branch?).to be true
        expect(result).to be_empty
      end

      it 'only removes the param if the user is not allowed to do that' do
        service = merge_request_service.new(build(:user))

        result = service.assign_allowed_merge_params(merge_request, params)

        expect(merge_request.force_remove_source_branch?).to be_falsy
        expect(result).to be_empty
      end
    end
  end
end


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

module Services
  # adapter for existing services over BaseServiceUtility - add error and
  # success methods returning ServiceResponse objects
  module ReturnServiceResponses
    def error(message, http_status, pass_back: {})
      ServiceResponse.error(message: message, http_status: http_status, payload: pass_back)
    end

    def success(payload)
      ServiceResponse.success(payload: payload)
    end
  end
end

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Services::ReturnServiceResponses, feature_category: :rate_limiting do
  subject(:object) { Class.new { include Services::ReturnServiceResponses }.new }

  let(:message) { 'a delivering message' }
  let(:payload) { 'string payload' }

  describe '#success' do
    it 'returns a ServiceResponse instance' do
      response = object.success(payload)
      expect(response).to be_an(ServiceResponse)
      expect(response).to be_success
      expect(response.message).to be_nil
      expect(response.payload).to eq(payload)
      expect(response.http_status).to eq(:ok)
    end
  end

  describe '#error' do
    it 'returns a ServiceResponse instance' do
      response = object.error(message, :not_found, pass_back: payload)
      expect(response).to be_an(ServiceResponse)
      expect(response).to be_error
      expect(response.message).to eq(message)
      expect(response.payload).to eq(payload)
      expect(response.http_status).to eq(:not_found)
    end
  end
end


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

module Topics
  class MergeService
    attr_accessor :source_topic, :target_topic

    def initialize(source_topic, target_topic)
      @source_topic = source_topic
      @target_topic = target_topic
    end

    def execute
      validate_parameters!

      ::Projects::ProjectTopic.transaction do
        move_project_topics
        refresh_target_topic_counters
        delete_source_topic
      end

      ServiceResponse.success
    rescue ArgumentError => e
      ServiceResponse.error(message: e.message)
    rescue StandardError => e
      Gitlab::ErrorTracking.track_exception(e, source_topic_id: source_topic.id, target_topic_id: target_topic.id)
      ServiceResponse.error(message: _('Topics could not be merged!'))
    end

    private

    def validate_parameters!
      raise ArgumentError, _('The source topic is not a topic.') unless source_topic.is_a?(Projects::Topic)
      raise ArgumentError, _('The target topic is not a topic.') unless target_topic.is_a?(Projects::Topic)
      raise ArgumentError, _('The source topic and the target topic are identical.') if source_topic == target_topic
    end

    # rubocop: disable CodeReuse/ActiveRecord
    def move_project_topics
      project_ids_for_projects_currently_using_source_and_target = ::Projects::ProjectTopic
        .where(topic_id: target_topic).select(:project_id)
      # Only update for projects that exclusively use the source topic
      ::Projects::ProjectTopic.where(topic_id: source_topic.id)
        .where.not(project_id: project_ids_for_projects_currently_using_source_and_target)
        .update_all(topic_id: target_topic.id)

      # Delete source topic for projects that were using source and target
      ::Projects::ProjectTopic.where(topic_id: source_topic.id).delete_all
    end

    def refresh_target_topic_counters
      target_topic.update!(
        total_projects_count: total_projects_count(target_topic.id),
        non_private_projects_count: non_private_projects_count(target_topic.id)
      )
    end

    def delete_source_topic
      source_topic.destroy!
    end

    def total_projects_count(topic_id)
      ::Projects::ProjectTopic.where(topic_id: topic_id).count
    end

    def non_private_projects_count(topic_id)
      ::Projects::ProjectTopic.joins(:project).where(topic_id: topic_id).where('projects.visibility_level in (10, 20)')
        .count
    end
    # rubocop: enable CodeReuse/ActiveRecord
  end
end

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Topics::MergeService, feature_category: :shared do
  let_it_be(:source_topic) { create(:topic, name: 'source_topic') }
  let_it_be(:target_topic) { create(:topic, name: 'target_topic') }
  let_it_be(:project_1) { create(:project, :public, topic_list: source_topic.name) }
  let_it_be(:project_2) { create(:project, :private, topic_list: source_topic.name) }
  let_it_be(:project_3) { create(:project, :public, topic_list: target_topic.name) }
  let_it_be(:project_4) { create(:project, :public, topic_list: [source_topic.name, target_topic.name]) }

  subject { described_class.new(source_topic, target_topic).execute }

  describe '#execute' do
    it 'merges source topic into target topic' do
      subject

      expect(target_topic.projects).to contain_exactly(project_1, project_2, project_3, project_4)
      expect { source_topic.reload }.to raise_error(ActiveRecord::RecordNotFound)
    end

    it 'refreshes counters of target topic' do
      expect { subject }
        .to change { target_topic.reload.total_projects_count }.by(2)
        .and change { target_topic.reload.non_private_projects_count }.by(1)
    end

    context 'when source topic fails to delete' do
      it 'reverts previous changes' do
        allow(source_topic.reload).to receive(:destroy!).and_raise(ActiveRecord::RecordNotDestroyed)

        response = subject
        expect(response).to be_error
        expect(response.message).to eq('Topics could not be merged!')

        expect(source_topic.projects).to contain_exactly(project_1, project_2, project_4)
        expect(target_topic.projects).to contain_exactly(project_3, project_4)
      end
    end

    context 'for parameter validation' do
      using RSpec::Parameterized::TableSyntax

      subject { described_class.new(source_topic_parameter, target_topic_parameter).execute }

      where(:source_topic_parameter, :target_topic_parameter, :expected_message) do
        nil                | ref(:target_topic) | 'The source topic is not a topic.'
        ref(:source_topic) | nil                | 'The target topic is not a topic.'
        ref(:target_topic) | ref(:target_topic) | 'The source topic and the target topic are identical.'
      end

      with_them do
        it 'raises correct error' do
          response = subject
          expect(response).to be_error
          expect(response.message).to eq(expected_message)
        end
      end
    end
  end
end


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

module Analytics
  module CycleAnalytics
    module Stages
      class ListService < Analytics::CycleAnalytics::Stages::BaseService
        def execute
          return forbidden unless allowed?

          success(build_default_stages)
        end

        private

        def allowed?
          can?(current_user, :read_cycle_analytics, parent.project)
        end

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

Analytics::CycleAnalytics::Stages::ListService.prepend_mod_with('Analytics::CycleAnalytics::Stages::ListService')

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Analytics::CycleAnalytics::Stages::ListService, feature_category: :value_stream_management do
  let_it_be(:project) { create(:project) }
  let_it_be(:user) { create(:user) }
  let_it_be(:project_namespace) { project.project_namespace.reload }

  let(:value_stream) { Analytics::CycleAnalytics::ValueStream.build_default_value_stream(project_namespace) }
  let(:stages) { subject.payload[:stages] }

  subject do
    described_class.new(parent: project_namespace, current_user: user, params: { value_stream: value_stream }).execute
  end

  before_all do
    project.add_reporter(user)
  end

  it 'returns only the default stages' do
    expect(stages.size).to eq(Gitlab::Analytics::CycleAnalytics::DefaultStages.all.size)
  end

  it 'provides the default stages as non-persisted objects' do
    expect(stages.map(&:id)).to all(be_nil)
  end
end


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

module Deployments
  # Service class for linking merge requests to deployments.
  class LinkMergeRequestsService
    attr_reader :deployment

    # The number of commits per query for which to find merge requests.
    COMMITS_PER_QUERY = 5_000

    def initialize(deployment)
      @deployment = deployment
    end

    def execute
      # Review apps have the environment type set (e.g. to `review`, though the
      # exact value may differ). We don't want to link merge requests to review
      # app deployments, as this is not useful.
      return unless deployment.environment.should_link_to_merge_requests?

      # This service is triggered by a Sidekiq worker, which only runs when a
      # deployment is successful. We add an extra check here in case we ever
      # call this service elsewhere and forget to check the status there.
      #
      # The reason we only want to link successful deployments is as follows:
      # when we link a merge request, we don't link it to future deployments for
      # the same environment. If we were to link an MR to a failed deploy, we
      # wouldn't be able to later on link it to a successful deploy (e.g. after
      # the deploy is retried).
      #
      # In addition, showing failed deploys in the UI of a merge request isn't
      # useful to users, as they can't act upon the information in any
      # meaningful way (i.e. they can't just retry the deploy themselves).
      return unless deployment.success?

      if (prev = deployment.previous_deployment)
        link_merge_requests_for_range(prev.sha, deployment.sha)
      else
        # When no previous deployment is found we fall back to linking all merge
        # requests merged into the deployed branch. This will not always be
        # accurate, but it's better than having no data.
        #
        # We can't use the first commit in the repository as a base to compare
        # to, as this will not scale to large repositories. For example, GitLab
        # itself has over 150 000 commits.
        link_all_merged_merge_requests
      end
    end

    def link_merge_requests_for_range(from, to)
      commits = project
        .repository
        .commits_between(from, to)
        .map(&:id)

      # For some projects the list of commits to deploy may be very large. To
      # ensure we do not end up running SQL queries with thousands of WHERE IN
      # values, we run one query per a certain number of commits.
      #
      # In most cases this translates to only a single query. For very large
      # deployment we may end up running a handful of queries to get and insert
      # the data.
      commits.each_slice(COMMITS_PER_QUERY) do |slice|
        merge_requests =
          project.merge_requests.merged.by_merge_commit_sha(slice)

        deployment.link_merge_requests(merge_requests)

        # The cherry picked commits are tracked via `notes.commit_id`
        # See https://gitlab.com/gitlab-org/gitlab/-/merge_requests/22209
        #
        # NOTE: cross-joining `merge_requests` table and `notes` table could
        # result in very poor performance because PG planner often uses an
        # inappropriate index.
        # See https://gitlab.com/gitlab-org/gitlab/-/issues/321032.
        mr_ids = project.notes.cherry_picked_merge_requests(slice)
        picked_merge_requests = project.merge_requests.id_in(mr_ids)

        deployment.link_merge_requests(picked_merge_requests)
      end
    end

    def link_all_merged_merge_requests
      merge_requests =
        project.merge_requests.merged.by_target_branch(deployment.ref)

      deployment.link_merge_requests(merge_requests)
    end

    private

    def project
      deployment.project
    end
  end
end

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Deployments::LinkMergeRequestsService, feature_category: :continuous_delivery do
  let(:project) { create(:project, :repository) }

  # *   ddd0f15 Merge branch 'po-fix-test-env-path' into 'master'
  # |\
  # | * 2d1db52 Correct test_env.rb path for adding branch
  # |/
  # *   1e292f8 Merge branch 'cherry-pick-ce369011' into 'master'
  # |\
  # | * c1c67ab Add file with a _flattable_ path
  # |/
  # *   7975be0 Merge branch 'rd-add-file-larger-than-1-mb' into 'master'
  let_it_be(:first_deployment_sha) { '7975be0116940bf2ad4321f79d02a55c5f7779aa' }
  let_it_be(:mr1_merge_commit_sha) { '1e292f8fedd741b75372e19097c76d327140c312' }
  let_it_be(:mr2_merge_commit_sha) { 'ddd0f15ae83993f5cb66a927a28673882e99100b' }

  describe '#execute' do
    context 'when the deployment is for a review environment' do
      it 'does nothing' do
        environment =
          create(:environment, environment_type: 'review', name: 'review/foo')

        deploy = create(:deployment, :success, environment: environment)

        expect(deploy).not_to receive(:link_merge_requests)

        described_class.new(deploy).execute
      end
    end

    context 'when the deployment is for one of the production environments' do
      it 'links merge requests' do
        environment =
          create(:environment, environment_type: 'production', name: 'production/gcp')

        deploy = create(:deployment, :success, environment: environment)

        expect(deploy).to receive(:link_merge_requests).once

        described_class.new(deploy).execute
      end
    end

    context 'when the deployment failed' do
      it 'does nothing' do
        environment = create(:environment, name: 'foo')
        deploy = create(:deployment, :failed, environment: environment)

        expect(deploy).not_to receive(:link_merge_requests)

        described_class.new(deploy).execute
      end
    end

    context 'when there is a previous deployment' do
      it 'links all merge requests merged since the previous deployment' do
        deploy1 = create(
          :deployment,
          :success,
          project: project,
          sha: first_deployment_sha
        )

        deploy2 = create(
          :deployment,
          :success,
          project: deploy1.project,
          environment: deploy1.environment,
          sha: mr2_merge_commit_sha
        )

        service = described_class.new(deploy2)

        expect(service)
          .to receive(:link_merge_requests_for_range)
          .with(first_deployment_sha, mr2_merge_commit_sha)

        service.execute
      end
    end

    context 'when there are no previous deployments' do
      it 'links all merged merge requests' do
        deploy = create(:deployment, :success, project: project)
        service = described_class.new(deploy)

        expect(service).to receive(:link_all_merged_merge_requests)

        service.execute
      end
    end
  end

  describe '#link_merge_requests_for_range' do
    it 'links merge requests' do
      environment = create(:environment, project: project)
      deploy =
        create(:deployment, :success, project: project, environment: environment)

      mr1 = create(
        :merge_request,
        :merged,
        merge_commit_sha: mr1_merge_commit_sha,
        source_project: project,
        target_project: project
      )

      mr2 = create(
        :merge_request,
        :merged,
        merge_commit_sha: mr2_merge_commit_sha,
        source_project: project,
        target_project: project
      )

      described_class.new(deploy).link_merge_requests_for_range(
        first_deployment_sha,
        mr2_merge_commit_sha
      )

      expect(deploy.merge_requests).to include(mr1, mr2)
    end

    it 'links picked merge requests' do
      environment = create(:environment, project: project)
      deploy =
        create(:deployment, :success, project: project, environment: environment)

      picked_mr = create(
        :merge_request,
        :merged,
        merge_commit_sha: '123abc',
        source_project: project,
        target_project: project
      )

      mr1 = create(
        :merge_request,
        :merged,
        merge_commit_sha: mr1_merge_commit_sha,
        source_project: project,
        target_project: project
      )

      # mr1 includes c1c67abba which is a cherry-pick of the fake picked_mr merge request
      create(:track_mr_picking_note, noteable: picked_mr, project: project, commit_id: 'c1c67abbaf91f624347bb3ae96eabe3a1b742478')

      described_class.new(deploy).link_merge_requests_for_range(
        first_deployment_sha,
        mr1_merge_commit_sha
      )

      expect(deploy.merge_requests).to include(mr1, picked_mr)
    end

    it "doesn't link the same merge_request twice" do
      create(:merge_request, :merged, merge_commit_sha: mr1_merge_commit_sha, source_project: project)

      picked_mr = create(:merge_request, :merged, merge_commit_sha: '123abc', source_project: project)

      # the first MR includes c1c67abba which is a cherry-pick of the fake picked_mr merge request
      create(:track_mr_picking_note, noteable: picked_mr, project: project, commit_id: 'c1c67abbaf91f624347bb3ae96eabe3a1b742478')

      environment = create(:environment, project: project)
      old_deploy =
        create(:deployment, :success, project: project, environment: environment)

      # manually linking all the MRs to the old_deploy
      old_deploy.link_merge_requests(project.merge_requests)

      deploy =
        create(:deployment, :success, project: project, environment: environment)

      described_class.new(deploy).link_merge_requests_for_range(
        first_deployment_sha,
        mr1_merge_commit_sha
      )

      expect(deploy.merge_requests).to be_empty
    end
  end

  describe '#link_all_merged_merge_requests' do
    it 'links all merged merge requests targeting the deployed branch' do
      environment = create(:environment, project: project)
      deploy =
        create(:deployment, :success, project: project, environment: environment)

      mr1 = create(
        :merge_request,
        :merged,
        source_project: project,
        target_project: project,
        source_branch: 'source1',
        target_branch: deploy.ref
      )

      mr2 = create(
        :merge_request,
        :merged,
        source_project: project,
        target_project: project,
        source_branch: 'source2',
        target_branch: deploy.ref
      )

      mr3 = create(
        :merge_request,
        :merged,
        source_project: project,
        target_project: project,
        target_branch: 'foo'
      )

      described_class.new(deploy).link_all_merged_merge_requests

      expect(deploy.merge_requests).to include(mr1, mr2)
      expect(deploy.merge_requests).not_to include(mr3)
    end
  end
end


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

module Deployments
  # This service archives old deploymets and deletes deployment refs for
  # keeping the project repository performant.
  class ArchiveInProjectService < ::BaseService
    BATCH_SIZE = 100

    def execute
      deployments = Deployment.archivables_in(project, limit: BATCH_SIZE)

      return success(result: :empty) if deployments.empty?

      ids = deployments.map(&:id)
      ref_paths = deployments.map(&:ref_path)

      project.repository.delete_refs(*ref_paths)
      project.deployments.id_in(ids).update_all(archived: true)

      success(result: :archived, count: ids.count)
    end
  end
end

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Deployments::ArchiveInProjectService, feature_category: :continuous_delivery do
  let_it_be(:project) { create(:project, :repository) }

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

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

    context 'when there are archivable deployments' do
      let!(:deployments) { create_list(:deployment, 3, project: project) }
      let!(:deployment_refs) { deployments.map(&:ref_path) }

      before do
        deployments.each(&:create_ref)
        allow(Deployment).to receive(:archivables_in) { deployments }
      end

      it 'returns result code' do
        expect(subject[:result]).to eq(:archived)
        expect(subject[:status]).to eq(:success)
        expect(subject[:count]).to eq(3)
      end

      it 'archives the deployment' do
        expect(deployments.map(&:archived?)).to be_all(false)
        expect(deployment_refs_exist?).to be_all(true)

        subject

        deployments.each(&:reload)
        expect(deployments.map(&:archived?)).to be_all(true)
        expect(deployment_refs_exist?).to be_all(false)
      end

      context 'when ref does not exist by some reason' do
        before do
          project.repository.delete_refs(*deployment_refs)
        end

        it 'does not raise an error' do
          expect(deployment_refs_exist?).to be_all(false)

          expect { subject }.not_to raise_error

          expect(deployment_refs_exist?).to be_all(false)
        end
      end

      def deployment_refs_exist?
        deployment_refs.map { |path| project.repository.ref_exists?(path) }
      end
    end

    context 'when there are no archivable deployments' do
      before do
        allow(Deployment).to receive(:archivables_in) { Deployment.none }
      end

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


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

module Deployments
  # This class creates a deployment record for a pipeline job.
  class CreateForJobService
    DeploymentCreationError = Class.new(StandardError)

    def execute(job)
      return unless job.is_a?(::Ci::Processable) && job.persisted_environment.present?

      environment = job.actual_persisted_environment

      deployment = to_resource(job, environment)

      return unless deployment

      deployment.save!
      job.association(:deployment).target = deployment
      job.association(:deployment).loaded!

      deployment
    rescue ActiveRecord::RecordInvalid => e
      Gitlab::ErrorTracking.track_and_raise_for_dev_exception(
        DeploymentCreationError.new(e.message), job_id: job.id)
    end

    private

    def to_resource(job, environment)
      return job.deployment if job.deployment
      return unless job.deployment_job?

      deployment = ::Deployment.new(attributes(job, environment))

      # If there is a validation error on environment creation, such as
      # the name contains invalid character, the job will fall back to a
      # non-environment job.
      return unless deployment.valid? && deployment.environment.persisted?

      if cluster = deployment.environment.deployment_platform&.cluster # rubocop: disable Lint/AssignmentInCondition
        deployment.deployment_cluster = ::DeploymentCluster.new(
          cluster_id: cluster.id,
          kubernetes_namespace: cluster.kubernetes_namespace_for(deployment.environment, deployable: job)
        )
      end

      # Allocate IID for deployments.
      # This operation must be outside of transactions of pipeline creations.
      deployment.ensure_project_iid!

      deployment
    end

    def attributes(job, environment)
      {
        project: job.project,
        environment: environment,
        deployable: job,
        user: job.user,
        ref: job.ref,
        tag: job.tag,
        sha: job.sha,
        on_stop: job.on_stop
      }
    end
  end
end

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Deployments::CreateForJobService, feature_category: :continuous_delivery do
  let_it_be(:project) { create(:project, :repository) }
  let_it_be(:user) { create(:user) }

  let(:service) { described_class.new }

  it_behaves_like 'create deployment for job' do
    let(:factory_type) { :ci_build }
  end

  it_behaves_like 'create deployment for job' do
    let(:factory_type) { :ci_bridge }
  end
end