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

module Discussions
  class CaptureDiffNotePositionService
    def initialize(merge_request, paths)
      @project = merge_request.project
      @tracer = build_tracer(merge_request, paths)
    end

    def execute(discussion)
      # The service has been implemented for text only
      # We don't need to capture positions for images
      return unless discussion.on_text?

      result = tracer&.trace(discussion.position)
      return unless result

      position = result[:position]
      return unless position

      line_code = position.line_code(project.repository)
      return unless line_code

      # Currently position data is copied across all notes of a discussion
      # It makes sense to store a position only for the first note instead
      # Within the newly introduced table we can start doing just that
      DiffNotePosition.create_or_update_for(discussion.notes.first,
        diff_type: :head,
        position: position,
        line_code: line_code)
    end

    private

    attr_reader :tracer, :project

    def build_tracer(merge_request, paths)
      return if paths.blank?

      old_diff_refs, new_diff_refs = build_diff_refs(merge_request)

      return unless old_diff_refs && new_diff_refs

      Gitlab::Diff::PositionTracer.new(
        project: project,
        old_diff_refs: old_diff_refs,
        new_diff_refs: new_diff_refs,
        paths: paths.uniq)
    end

    def build_diff_refs(merge_request)
      merge_ref_head = merge_request.merge_ref_head
      return unless merge_ref_head

      start_sha, _ = merge_ref_head.parent_ids
      new_diff_refs = Gitlab::Diff::DiffRefs.new(
        base_sha: start_sha,
        start_sha: start_sha,
        head_sha: merge_ref_head.id)

      old_diff_refs = merge_request.diff_refs

      return if new_diff_refs == old_diff_refs

      [old_diff_refs, new_diff_refs]
    end
  end
end

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Discussions::CaptureDiffNotePositionService, feature_category: :code_review_workflow do
  subject { described_class.new(note.noteable, paths) }

  context 'image note on diff' do
    let!(:note) { create(:image_diff_note_on_merge_request) }
    let(:paths) { ['files/images/any_image.png'] }

    it 'is note affected by the service' do
      expect(Gitlab::Diff::PositionTracer).not_to receive(:new)

      expect(subject.execute(note.discussion)).to eq(nil)
      expect(note.diff_note_positions).to be_empty
    end
  end

  context 'when empty paths are passed as a param' do
    let!(:note) { create(:diff_note_on_merge_request) }
    let(:paths) { [] }

    it 'does not calculate positons' do
      expect(Gitlab::Diff::PositionTracer).not_to receive(:new)

      expect(subject.execute(note.discussion)).to eq(nil)
      expect(note.diff_note_positions).to be_empty
    end
  end

  context 'when position tracer returned position' do
    let!(:note) { create(:diff_note_on_merge_request) }
    let(:paths) { ['files/any_file.txt'] }

    before do
      expect(note.noteable).to receive(:merge_ref_head).and_return(double.as_null_object)
      expect_next_instance_of(Gitlab::Diff::PositionTracer) do |tracer|
        expect(tracer).to receive(:trace).and_return({ position: position })
      end
    end

    context 'which is nil' do
      let(:position) { nil }

      it 'does not create diff note position' do
        expect(subject.execute(note.discussion)).to eq(nil)
        expect(note.diff_note_positions).to be_empty
      end
    end

    context 'which does not have a corresponding line' do
      let(:position) { double(line_code: nil) }

      it 'does not create diff note position' do
        expect(subject.execute(note.discussion)).to eq(nil)
        expect(note.diff_note_positions).to be_empty
      end
    end
  end
end


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

module Achievements
  class AwardService
    attr_reader :current_user, :achievement_id, :recipient_id

    def initialize(current_user, achievement_id, recipient_id)
      @current_user = current_user
      @achievement_id = achievement_id
      @recipient_id = recipient_id
    end

    def execute
      achievement = Achievements::Achievement.find(achievement_id)
      return error_no_permissions unless allowed?(achievement)

      recipient = User.find(recipient_id)

      user_achievement = Achievements::UserAchievement.create(
        achievement: achievement,
        user: recipient,
        awarded_by_user: current_user)
      return error_awarding(user_achievement) unless user_achievement.persisted?

      NotificationService.new.new_achievement_email(recipient, achievement).deliver_later
      ServiceResponse.success(payload: user_achievement)
    rescue ActiveRecord::RecordNotFound => e
      error(e.message)
    end

    private

    def allowed?(achievement)
      current_user&.can?(:award_achievement, achievement)
    end

    def error_no_permissions
      error('You have insufficient permissions to award this achievement')
    end

    def error_awarding(user_achievement)
      error(user_achievement&.errors&.full_messages || 'Failed to award achievement')
    end

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

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Achievements::AwardService, feature_category: :user_profile do
  describe '#execute' do
    let_it_be(:developer) { create(:user) }
    let_it_be(:maintainer) { create(:user) }
    let_it_be(:group) { create(:group) }
    let_it_be(:achievement) { create(:achievement, namespace: group) }
    let_it_be(:recipient) { create(:user) }

    let(:achievement_id) { achievement.id }
    let(:recipient_id) { recipient.id }

    subject(:response) { described_class.new(current_user, achievement_id, recipient_id).execute }

    before_all do
      group.add_developer(developer)
      group.add_maintainer(maintainer)
    end

    context 'when user does not have permission' do
      let(:current_user) { developer }

      it 'returns an error' do
        expect(response).to be_error
        expect(response.message).to match_array(
          ['You have insufficient permissions to award this achievement'])
      end
    end

    context 'when user has permission' do
      let(:current_user) { maintainer }
      let(:notification_service) { instance_double(NotificationService) }
      let(:mail_message) { instance_double(ActionMailer::MessageDelivery) }

      it 'creates an achievement and sends an e-mail' do
        allow(NotificationService).to receive(:new).and_return(notification_service)
        expect(notification_service).to receive(:new_achievement_email).with(recipient, achievement)
          .and_return(mail_message)
        expect(mail_message).to receive(:deliver_later)

        expect(response).to be_success
      end

      context 'when the achievement is not persisted' do
        let(:user_achievement) { instance_double('Achievements::UserAchievement') }

        it 'returns the correct error' do
          allow(user_achievement).to receive(:persisted?).and_return(false)
          allow(user_achievement).to receive(:errors).and_return(nil)
          allow(Achievements::UserAchievement).to receive(:create).and_return(user_achievement)

          expect(response).to be_error
          expect(response.message).to match_array(["Failed to award achievement"])
        end
      end

      context 'when the achievement does not exist' do
        let(:achievement_id) { non_existing_record_id }

        it 'returns the correct error' do
          expect(response).to be_error
          expect(response.message)
            .to contain_exactly("Couldn't find Achievements::Achievement with 'id'=#{non_existing_record_id}")
        end
      end

      context 'when the recipient does not exist' do
        let(:recipient_id) { non_existing_record_id }

        it 'returns the correct error' do
          expect(response).to be_error
          expect(response.message).to contain_exactly("Couldn't find User with 'id'=#{non_existing_record_id}")
        end
      end
    end
  end
end


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

module Achievements
  class UpdateUserAchievementPrioritiesService
    attr_reader :current_user, :user_achievements

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

    def execute
      return error_no_permissions unless allowed?

      prioritized_user_achievements_map = Hash[user_achievements.map.with_index { |ua, idx| [ua.id, idx] }]

      user_achievements_priorities_mapping = current_user.user_achievements.each_with_object({}) do |ua, result|
        next if ua.priority.nil? && !prioritized_user_achievements_map.key?(ua.id)

        result[ua] = { priority: prioritized_user_achievements_map.fetch(ua.id, nil) }
      end

      return ServiceResponse.success(payload: []) if user_achievements_priorities_mapping.empty?

      ::Gitlab::Database::BulkUpdate.execute(%i[priority], user_achievements_priorities_mapping)

      ServiceResponse.success(payload: user_achievements_priorities_mapping.keys.map(&:reload))
    end

    private

    def allowed?
      user_achievements.all? { |user_achievement| current_user&.can?(:update_owned_user_achievement, user_achievement) }
    end

    def error(message)
      ServiceResponse.error(payload: user_achievements, message: Array(message))
    end

    def error_no_permissions
      error("You can't update at least one of the given user achievements.")
    end
  end
end

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Achievements::UpdateUserAchievementPrioritiesService, feature_category: :user_profile do
  describe '#execute' do
    let_it_be(:achievement_owner) { create(:user) }
    let_it_be(:group) { create(:group) }

    let_it_be(:achievement) { create(:achievement, namespace: group) }

    let!(:user_achievement1) do
      create(:user_achievement, achievement: achievement, user: achievement_owner, priority: 0)
    end

    let_it_be(:user_achievement2) { create(:user_achievement, achievement: achievement, user: achievement_owner) }
    let_it_be(:user_achievement3) { create(:user_achievement, achievement: achievement, user: achievement_owner) }

    subject(:response) { described_class.new(current_user, user_achievements).execute }

    context 'when user does not have permission' do
      let(:current_user) { create(:user) }
      let(:user_achievements) { [user_achievement1] }

      it 'returns an error', :aggregate_failures do
        expect(response).to be_error
        expect(response.message).to match_array(["You can't update at least one of the given user achievements."])
      end
    end

    context 'when user has permission' do
      let_it_be_with_reload(:current_user) { achievement_owner }

      context 'with empty input' do
        let(:user_achievements) { [] }

        it 'removes all priorities', :aggregate_failures do
          expect(response).to be_success

          [user_achievement1, user_achievement2, user_achievement3].each do |ua|
            expect(ua.reload.priority).to be_nil
          end
        end
      end

      context 'with prioritised achievements' do
        let(:user_achievements) { [user_achievement3, user_achievement1] }

        it 're-orders the achievements correctly', :aggregate_failures do
          expect(response).to be_success

          expect(user_achievement1.reload.priority).to eq(1)
          expect(user_achievement2.reload.priority).to be_nil
          expect(user_achievement3.reload.priority).to be_zero
        end
      end

      context 'when no achievement is prioritized and no prioritizations are made' do
        let!(:user_achievement1) { create(:user_achievement, achievement: achievement, user: achievement_owner) }

        let(:user_achievements) { [] }

        it 'works without errors', :aggregate_failures do
          expect(response).to be_success

          [user_achievement1, user_achievement2, user_achievement3].each do |ua|
            expect(ua.reload.priority).to be_nil
          end
        end
      end
    end
  end
end


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

module Achievements
  class DestroyUserAchievementService
    attr_reader :current_user, :user_achievement

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

    def execute
      return error_no_permissions unless allowed?

      user_achievement.delete
      ServiceResponse.success(payload: user_achievement)
    end

    private

    def allowed?
      current_user&.can?(:destroy_user_achievement, user_achievement)
    end

    def error_no_permissions
      error('You have insufficient permissions to delete this user achievement')
    end

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

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Achievements::DestroyUserAchievementService, feature_category: :user_profile do
  describe '#execute' do
    let_it_be(:maintainer) { create(:user) }
    let_it_be(:owner) { create(:user) }
    let_it_be(:group) { create(:group) }

    let_it_be(:achievement) { create(:achievement, namespace: group) }
    let_it_be(:user_achievement) { create(:user_achievement, achievement: achievement) }

    subject(:response) { described_class.new(current_user, user_achievement).execute }

    before_all do
      group.add_maintainer(maintainer)
      group.add_owner(owner)
    end

    context 'when user does not have permission' do
      let(:current_user) { maintainer }

      it 'returns an error' do
        expect(response).to be_error
        expect(response.message).to match_array(
          ['You have insufficient permissions to delete this user achievement'])
      end
    end

    context 'when user has permission' do
      let(:current_user) { owner }

      it 'deletes the achievement' do
        expect(response).to be_success
        expect(Achievements::UserAchievement.find_by(id: user_achievement.id)).to be_nil
      end
    end
  end
end


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

module Achievements
  class RevokeService
    attr_reader :current_user, :user_achievement

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

    def execute
      return error_no_permissions unless allowed?(user_achievement.achievement)
      return error_already_revoked if user_achievement.revoked?

      user_achievement.assign_attributes({
        revoked_by_user_id: current_user.id,
        revoked_at: Time.zone.now
      })
      return error_awarding unless user_achievement.save

      ServiceResponse.success(payload: user_achievement)
    end

    private

    def allowed?(achievement)
      current_user&.can?(:award_achievement, achievement)
    end

    def error_no_permissions
      error('You have insufficient permissions to revoke this achievement')
    end

    def error_already_revoked
      error('This achievement has already been revoked')
    end

    def error_awarding
      error(user_achievement&.errors&.full_messages || 'Failed to revoke achievement')
    end

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

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Achievements::RevokeService, feature_category: :user_profile do
  describe '#execute' do
    let_it_be(:developer) { create(:user) }
    let_it_be(:maintainer) { create(:user) }
    let_it_be(:group) { create(:group) }
    let_it_be(:achievement) { create(:achievement, namespace: group) }
    let_it_be(:user_achievement) { create(:user_achievement, achievement: achievement) }

    let(:user_achievement_param) { user_achievement }

    subject(:response) { described_class.new(current_user, user_achievement_param).execute }

    before_all do
      group.add_developer(developer)
      group.add_maintainer(maintainer)
    end

    context 'when user does not have permission' do
      let(:current_user) { developer }

      it 'returns an error' do
        expect(response).to be_error
        expect(response.message).to match_array(
          ['You have insufficient permissions to revoke this achievement'])
      end
    end

    context 'when user has permission' do
      let(:current_user) { maintainer }

      it 'revokes an achievement' do
        expect(response).to be_success
      end

      context 'when the achievement has already been revoked' do
        let_it_be(:revoked_achievement) { create(:user_achievement, :revoked, achievement: achievement) }
        let(:user_achievement_param) { revoked_achievement }

        it 'returns the correct error' do
          expect(response).to be_error
          expect(response.message)
            .to contain_exactly('This achievement has already been revoked')
        end
      end

      context 'when the user achievement fails to save' do
        let(:user_achievement_param) { instance_double('Achievements::UserAchievement') }

        it 'returns the correct error' do
          allow(user_achievement_param).to receive(:save).and_return(false)
          allow(user_achievement_param).to receive(:achievement).and_return(achievement)
          allow(user_achievement_param).to receive(:revoked?).and_return(false)
          allow(user_achievement_param).to receive(:errors).and_return(nil)
          expect(user_achievement_param).to receive(:assign_attributes)

          expect(response).to be_error
          expect(response.message).to match_array(["Failed to revoke achievement"])
        end
      end
    end
  end
end


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

module Achievements
  class DestroyService
    attr_reader :current_user, :achievement

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

    def execute
      return error_no_permissions unless allowed?

      achievement.delete
      ServiceResponse.success(payload: achievement)
    end

    private

    def allowed?
      current_user&.can?(:admin_achievement, achievement)
    end

    def error_no_permissions
      error('You have insufficient permissions to delete this achievement')
    end

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

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Achievements::DestroyService, feature_category: :user_profile do
  describe '#execute' do
    let_it_be(:developer) { create(:user) }
    let_it_be(:maintainer) { create(:user) }
    let_it_be(:group) { create(:group) }

    let(:achievement) { create(:achievement, namespace: group) }

    subject(:response) { described_class.new(current_user, achievement).execute }

    before_all do
      group.add_developer(developer)
      group.add_maintainer(maintainer)
    end

    context 'when user does not have permission' do
      let(:current_user) { developer }

      it 'returns an error' do
        expect(response).to be_error
        expect(response.message).to match_array(
          ['You have insufficient permissions to delete this achievement'])
      end
    end

    context 'when user has permission' do
      let(:current_user) { maintainer }

      it 'deletes the achievement' do
        expect(response).to be_success
        expect(Achievements::Achievement.find_by(id: achievement.id)).to be_nil
      end
    end
  end
end


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

module Achievements
  class UpdateService
    attr_reader :current_user, :achievement, :params

    def initialize(current_user, achievement, params)
      @current_user = current_user
      @achievement = achievement
      @params = params
    end

    def execute
      return error_no_permissions unless allowed?

      if achievement.update(params)
        ServiceResponse.success(payload: achievement)
      else
        error_updating
      end
    end

    private

    def allowed?
      current_user&.can?(:admin_achievement, achievement)
    end

    def error_no_permissions
      error('You have insufficient permission to update this achievement')
    end

    def error(message)
      ServiceResponse.error(payload: achievement, message: Array(message))
    end

    def error_updating
      error(achievement&.errors&.full_messages || 'Failed to update achievement')
    end
  end
end

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Achievements::UpdateService, feature_category: :user_profile do
  describe '#execute' do
    let_it_be(:user) { create(:user) }

    let(:params) { attributes_for(:achievement, namespace: group) }

    subject(:response) { described_class.new(user, group, params).execute }

    context 'when user does not have permission' do
      let_it_be(:group) { create(:group) }
      let_it_be(:achievement) { create(:achievement, namespace: group) }

      before_all do
        group.add_developer(user)
      end

      it 'returns an error' do
        expect(response).to be_error
        expect(response.message).to match_array(
          ['You have insufficient permission to update this achievement'])
      end
    end

    context 'when user has permission' do
      let_it_be(:group) { create(:group) }
      let_it_be(:achievement) { create(:achievement, namespace: group) }

      before_all do
        group.add_maintainer(user)
      end

      it 'updates an achievement' do
        expect(response).to be_success
      end

      it 'returns an error when the achievement cannot be updated' do
        params[:name] = nil

        expect(response).to be_error
        expect(response.message).to include("Name can't be blank")
      end
    end
  end
end


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

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

      achievement = Achievements::Achievement.create(params.merge(namespace_id: @namespace.id))

      return error_creating(achievement) unless achievement.persisted?

      ServiceResponse.success(payload: achievement)
    end

    private

    def error_no_permissions
      error('You have insufficient permissions to create achievements for this namespace')
    end

    def error_creating(achievement)
      error(achievement&.errors&.full_messages || 'Failed to create achievement')
    end
  end
end

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Achievements::CreateService, feature_category: :user_profile do
  describe '#execute' do
    let_it_be(:user) { create(:user) }

    let(:params) { attributes_for(:achievement, namespace: group) }

    subject(:response) { described_class.new(namespace: group, current_user: user, params: params).execute }

    context 'when user does not have permission' do
      let_it_be(:group) { create(:group) }

      before_all do
        group.add_developer(user)
      end

      it 'returns an error' do
        expect(response).to be_error
        expect(response.message).to match_array(
          ['You have insufficient permissions to create achievements for this namespace'])
      end
    end

    context 'when user has permission' do
      let_it_be(:group) { create(:group) }

      before_all do
        group.add_maintainer(user)
      end

      it 'creates an achievement' do
        expect(response).to be_success
      end

      it 'returns an error when the achievement is not persisted' do
        params[:name] = nil

        expect(response).to be_error
        expect(response.message).to match_array(["Name can't be blank"])
      end
    end
  end
end


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

module Security
  class MergeReportsService
    attr_reader :source_reports

    def initialize(*source_reports)
      @source_reports = source_reports
    end

    def execute
      copy_resources_to_target_report
      copy_findings_to_target
      target_report
    end

    private

    def target_report
      @target_report ||= ::Gitlab::Ci::Reports::Security::Report.new(
        source_reports.first.type,
        source_reports.first.pipeline,
        source_reports.first.created_at
      ).tap do |report|
        report.errors = source_reports.flat_map(&:errors)
        report.warnings = source_reports.flat_map(&:warnings)
      end
    end

    def copy_resources_to_target_report
      sorted_source_reports.each do |source_report|
        copy_scanners_to_target(source_report)
        copy_identifiers_to_target(source_report)
        copy_scanned_resources_to_target(source_report)
      end
    end

    def sorted_source_reports
      source_reports.sort { |a, b| a.primary_scanner_order_to(b) }
    end

    def copy_scanners_to_target(source_report)
      # no need for de-duping: it's done by Report internally
      source_report.scanners.values.each { |scanner| target_report.add_scanner(scanner) }
    end

    def copy_identifiers_to_target(source_report)
      # no need for de-duping: it's done by Report internally
      source_report.identifiers.values.each { |identifier| target_report.add_identifier(identifier) }
    end

    def copy_scanned_resources_to_target(source_report)
      target_report.scanned_resources.concat(source_report.scanned_resources).uniq!
    end

    def copy_findings_to_target
      deduplicated_findings.sort.each { |finding| target_report.add_finding(finding) }
    end

    def deduplicated_findings
      prioritized_findings.each_with_object([[], Set.new]) do |finding, (deduplicated, seen_identifiers)|
        next if seen_identifiers.intersect?(finding.keys.to_set)

        seen_identifiers.merge(finding.keys)
        deduplicated << finding
      end.first
    end

    def prioritized_findings
      source_reports.flat_map(&:findings).sort { |a, b| a.scanner_order_to(b) }
    end
  end
end

```

Response:
# frozen_string_literal: true

require 'spec_helper'

# rubocop: disable RSpec/MultipleMemoizedHelpers
RSpec.describe Security::MergeReportsService, '#execute', feature_category: :code_review_workflow do
  let(:scanner_1) { build(:ci_reports_security_scanner, external_id: 'scanner-1', name: 'Scanner 1') }
  let(:scanner_2) { build(:ci_reports_security_scanner, external_id: 'scanner-2', name: 'Scanner 2') }
  let(:scanner_3) { build(:ci_reports_security_scanner, external_id: 'scanner-3', name: 'Scanner 3') }

  let(:identifier_1_primary) { build(:ci_reports_security_identifier, external_id: 'VULN-1', external_type: 'scanner-1') }
  let(:identifier_1_cve) { build(:ci_reports_security_identifier, external_id: 'CVE-2019-123', external_type: 'cve') }
  let(:identifier_2_primary) { build(:ci_reports_security_identifier, external_id: 'VULN-2', external_type: 'scanner-2') }
  let(:identifier_2_cve) { build(:ci_reports_security_identifier, external_id: 'CVE-2019-456', external_type: 'cve') }
  let(:identifier_cwe) { build(:ci_reports_security_identifier, external_id: '789', external_type: 'cwe') }
  let(:identifier_wasc) { build(:ci_reports_security_identifier, external_id: '13', external_type: 'wasc') }

  let(:finding_id_1) do
    build(
      :ci_reports_security_finding,
      identifiers: [identifier_1_primary, identifier_1_cve],
      scanner: scanner_1,
      severity: :low
    )
  end

  let(:finding_id_1_extra) do
    build(
      :ci_reports_security_finding,
      identifiers: [identifier_1_primary, identifier_1_cve],
      scanner: scanner_1,
      severity: :low
    )
  end

  let(:finding_id_2_loc_1) do
    build(
      :ci_reports_security_finding,
      identifiers: [identifier_2_primary, identifier_2_cve],
      location: build(:ci_reports_security_locations_sast, start_line: 32, end_line: 34),
      scanner: scanner_2,
      severity: :medium
    )
  end

  let(:finding_id_2_loc_1_extra) do
    build(
      :ci_reports_security_finding,
      identifiers: [identifier_2_primary, identifier_2_cve],
      location: build(:ci_reports_security_locations_sast, start_line: 32, end_line: 34),
      scanner: scanner_2,
      severity: :medium
    )
  end

  let(:finding_id_2_loc_2) do
    build(
      :ci_reports_security_finding,
      identifiers: [identifier_2_primary, identifier_2_cve],
      location: build(:ci_reports_security_locations_sast, start_line: 42, end_line: 44),
      scanner: scanner_2,
      severity: :medium
    )
  end

  let(:finding_cwe_1) do
    build(
      :ci_reports_security_finding,
      identifiers: [identifier_cwe],
      scanner: scanner_3,
      severity: :high
    )
  end

  let(:finding_cwe_2) do
    build(
      :ci_reports_security_finding,
      identifiers: [identifier_cwe],
      scanner: scanner_1,
      severity: :critical
    )
  end

  let(:finding_wasc_1) do
    build(
      :ci_reports_security_finding,
      identifiers: [identifier_wasc],
      scanner: scanner_1,
      severity: :medium
    )
  end

  let(:finding_wasc_2) do
    build(
      :ci_reports_security_finding,
      identifiers: [identifier_wasc],
      scanner: scanner_2,
      severity: :critical
    )
  end

  let(:report_1_findings) { [finding_id_1, finding_id_2_loc_1, finding_id_2_loc_1_extra, finding_cwe_2, finding_wasc_1] }

  let(:scanned_resource) do
    ::Gitlab::Ci::Reports::Security::ScannedResource.new(URI.parse('example.com'), 'GET')
  end

  let(:scanned_resource_1) do
    ::Gitlab::Ci::Reports::Security::ScannedResource.new(URI.parse('example.com'), 'POST')
  end

  let(:scanned_resource_2) do
    ::Gitlab::Ci::Reports::Security::ScannedResource.new(URI.parse('example.com/2'), 'GET')
  end

  let(:scanned_resource_3) do
    ::Gitlab::Ci::Reports::Security::ScannedResource.new(URI.parse('example.com/3'), 'GET')
  end

  let(:report_1) do
    build(
      :ci_reports_security_report,
      scanners: [scanner_1, scanner_2],
      findings: report_1_findings,
      identifiers: report_1_findings.flat_map(&:identifiers),
      scanned_resources: [scanned_resource, scanned_resource_1, scanned_resource_2]
    )
  end

  let(:report_2_findings) { [finding_id_2_loc_2, finding_wasc_2] }

  let(:report_2) do
    build(
      :ci_reports_security_report,
      scanners: [scanner_2],
      findings: report_2_findings,
      identifiers: finding_id_2_loc_2.identifiers,
      scanned_resources: [scanned_resource, scanned_resource_1, scanned_resource_3]
    )
  end

  let(:report_3_findings) { [finding_id_1_extra, finding_cwe_1] }

  let(:report_3) do
    build(
      :ci_reports_security_report,
      scanners: [scanner_1, scanner_3],
      findings: report_3_findings,
      identifiers: report_3_findings.flat_map(&:identifiers)
    )
  end

  let(:merge_service) { described_class.new(report_1, report_2, report_3) }

  subject(:merged_report) { merge_service.execute }

  describe 'errors on target report' do
    subject { merged_report.errors }

    before do
      report_1.add_error('foo', 'bar')
      report_2.add_error('zoo', 'baz')
    end

    it { is_expected.to match_array([{ type: 'foo', message: 'bar' }, { type: 'zoo', message: 'baz' }]) }
  end

  describe 'warnings on target report' do
    subject { merged_report.warnings }

    before do
      report_1.add_warning('foo', 'bar')
      report_2.add_warning('zoo', 'baz')
    end

    it { is_expected.to match_array([{ type: 'foo', message: 'bar' }, { type: 'zoo', message: 'baz' }]) }
  end

  it 'copies scanners into target report and eliminates duplicates' do
    expect(merged_report.scanners.values).to contain_exactly(scanner_1, scanner_2, scanner_3)
  end

  it 'copies identifiers into target report and eliminates duplicates' do
    expect(merged_report.identifiers.values).to(
      contain_exactly(
        identifier_1_primary,
        identifier_1_cve,
        identifier_2_primary,
        identifier_2_cve,
        identifier_cwe,
        identifier_wasc
      )
    )
  end

  it 'deduplicates (except cwe and wasc) and sorts the vulnerabilities by severity (desc) then by compare key' do
    expect(merged_report.findings).to(
      eq([
           finding_cwe_2,
           finding_wasc_2,
           finding_cwe_1,
           finding_id_2_loc_2,
           finding_id_2_loc_1,
           finding_wasc_1,
           finding_id_1
         ])
    )
  end

  it 'deduplicates scanned resources' do
    expect(merged_report.scanned_resources).to(
      eq([
           scanned_resource,
           scanned_resource_1,
           scanned_resource_2,
           scanned_resource_3
         ])
    )
  end

  context 'ordering reports for sast analyzers' do
    let(:bandit_scanner) { build(:ci_reports_security_scanner, external_id: 'bandit', name: 'Bandit') }
    let(:semgrep_scanner) { build(:ci_reports_security_scanner, external_id: 'semgrep', name: 'Semgrep') }

    let(:identifier_bandit) { build(:ci_reports_security_identifier, external_id: 'B403', external_type: 'bandit_test_id') }
    let(:identifier_cve) { build(:ci_reports_security_identifier, external_id: 'CVE-2019-123', external_type: 'cve') }
    let(:identifier_semgrep) { build(:ci_reports_security_identifier, external_id: 'rules.bandit.B105', external_type: 'semgrep_id') }

    let(:finding_id_1) { build(:ci_reports_security_finding, identifiers: [identifier_bandit, identifier_cve], scanner: bandit_scanner, report_type: :sast) }
    let(:finding_id_2) { build(:ci_reports_security_finding, identifiers: [identifier_cve], scanner: semgrep_scanner, report_type: :sast) }
    let(:finding_id_3) { build(:ci_reports_security_finding, identifiers: [identifier_semgrep], scanner: semgrep_scanner, report_type: :sast) }

    let(:bandit_report) do
      build(:ci_reports_security_report,
        type: :sast,
        scanners: [bandit_scanner],
        findings: [finding_id_1],
        identifiers: finding_id_1.identifiers
      )
    end

    let(:semgrep_report) do
      build(
        :ci_reports_security_report,
        type: :sast,
        scanners: [semgrep_scanner],
        findings: [finding_id_2, finding_id_3],
        identifiers: finding_id_2.identifiers + finding_id_3.identifiers
      )
    end

    let(:custom_analyzer_report) do
      build(
        :ci_reports_security_report,
        type: :sast,
        scanners: [scanner_2],
        findings: [finding_id_2_loc_1],
        identifiers: finding_id_2_loc_1.identifiers
      )
    end

    context 'when reports are gathered in an unprioritized order' do
      subject(:sast_merged_report) { described_class.new(semgrep_report, bandit_report).execute }

      specify { expect(sast_merged_report.scanners.values).to eql([bandit_scanner, semgrep_scanner]) }
      specify { expect(sast_merged_report.findings.count).to eq(2) }
      specify { expect(sast_merged_report.findings.first.identifiers).to eql([identifier_bandit, identifier_cve]) }
      specify { expect(sast_merged_report.findings.last.identifiers).to contain_exactly(identifier_semgrep) }
    end

    context 'when a custom analyzer is completed before the known analyzers' do
      subject(:sast_merged_report) { described_class.new(custom_analyzer_report, semgrep_report, bandit_report).execute }

      specify { expect(sast_merged_report.scanners.values).to eql([bandit_scanner, semgrep_scanner, scanner_2]) }
      specify { expect(sast_merged_report.findings.count).to eq(3) }
      specify { expect(sast_merged_report.findings.last.identifiers).to match_array(finding_id_2_loc_1.identifiers) }
    end
  end
end
# rubocop: enable RSpec/MultipleMemoizedHelpers


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

module Security
  module CiConfiguration
    class DependencyScanningCreateService < ::Security::CiConfiguration::BaseCreateService
      private

      def action
        Security::CiConfiguration::DependencyScanningBuildAction.new(project.auto_devops_enabled?, existing_gitlab_ci_content,
                                                                     project.ci_config_path).generate
      end

      def next_branch
        'set-dependency-scanning-config'
      end

      def message
        _('Configure Dependency Scanning in `.gitlab-ci.yml`, creating this file if it does not already exist')
      end

      def description
        _('Configure Dependency Scanning in `.gitlab-ci.yml` using the GitLab managed template. You can [add variable overrides](https://docs.gitlab.com/ee/user/application_security/dependency_scanning/#customizing-the-dependency-scanning-settings) to customize Dependency Scanning settings.')
      end

      def name
        'Dependency Scanning'
      end
    end
  end
end

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Security::CiConfiguration::DependencyScanningCreateService, :snowplow,
  feature_category: :software_composition_analysis do
  subject(:result) { described_class.new(project, user).execute }

  let(:branch_name) { 'set-dependency-scanning-config-1' }

  let(:snowplow_event) do
    {
      category: 'Security::CiConfiguration::DependencyScanningCreateService',
      action: 'create',
      label: ''
    }
  end

  include_examples 'services security ci configuration create service', true
end


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

module Security
  module CiConfiguration
    class SastCreateService < ::Security::CiConfiguration::BaseCreateService
      attr_reader :params

      def initialize(project, current_user, params, commit_on_default: false)
        super(project, current_user)
        @params = params

        @commit_on_default = commit_on_default
        @branch_name = project.default_branch if @commit_on_default
      end

      private

      def remove_branch_on_exception
        super unless @commit_on_default
      end

      def action
        Security::CiConfiguration::SastBuildAction.new(project.auto_devops_enabled?, params, existing_gitlab_ci_content, project.ci_config_path).generate
      end

      def next_branch
        'set-sast-config'
      end

      def message
        _('Configure SAST in `.gitlab-ci.yml`, creating this file if it does not already exist')
      end

      def description
        _('Configure SAST in `.gitlab-ci.yml` using the GitLab managed template. You can [add variable overrides](https://docs.gitlab.com/ee/user/application_security/sast/#customizing-the-sast-settings) to customize SAST settings.')
      end

      def name
        'SAST'
      end
    end
  end
end

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Security::CiConfiguration::SastCreateService, :snowplow,
  feature_category: :static_application_security_testing do
  subject(:result) { described_class.new(project, user, params).execute }

  let(:branch_name) { 'set-sast-config-1' }

  let(:non_empty_params) do
    { 'stage' => 'security',
      'SEARCH_MAX_DEPTH' => 1,
      'SECURE_ANALYZERS_PREFIX' => 'new_registry',
      'SAST_EXCLUDED_PATHS' => 'spec,docs' }
  end

  let(:snowplow_event) do
    {
      category: 'Security::CiConfiguration::SastCreateService',
      action: 'create',
      label: 'false'
    }
  end

  include_examples 'services security ci configuration create service'

  RSpec.shared_examples_for 'commits directly to the default branch' do
    it 'commits directly to the default branch' do
      expect(project).to receive(:default_branch).twice.and_return('master')

      expect(result.status).to eq(:success)
      expect(result.payload[:success_path]).to match(/#{Gitlab::Routing.url_helpers.project_new_merge_request_url(project, {})}(.*)description(.*)source_branch/)
      expect(result.payload[:branch]).to eq('master')
    end
  end

  context 'when the repository is empty' do
    let_it_be(:project) { create(:project_empty_repo) }

    context 'when initialize_with_sast is false' do
      before do
        project.add_developer(user)
      end

      let(:params) { { initialize_with_sast: false } }

      it 'returns a ServiceResponse error' do
        expect(result).to be_kind_of(ServiceResponse)
        expect(result.status).to eq(:error)
        expect(result.message).to eq('You must <a target="_blank" rel="noopener noreferrer" ' \
                                     'href="http://localhost/help/user/project/repository/index.md#' \
                                     'add-files-to-a-repository">add at least one file to the ' \
                                     'repository</a> before using Security features.')
      end
    end

    context 'when initialize_with_sast is true' do
      let(:params) { { initialize_with_sast: true } }

      subject(:result) { described_class.new(project, user, params, commit_on_default: true).execute }

      before do
        project.add_maintainer(user)
      end

      it_behaves_like 'commits directly to the default branch'
    end
  end

  context 'when committing to the default branch', :aggregate_failures do
    subject(:result) { described_class.new(project, user, params, commit_on_default: true).execute }

    let(:params) { {} }

    before do
      project.add_developer(user)
    end

    it 'does not try to remove that branch on raised exceptions' do
      expect(Files::MultiService).to receive(:new).and_raise(StandardError, '_exception_')
      expect(project.repository).not_to receive(:rm_branch)

      expect { result }.to raise_error(StandardError, '_exception_')
    end

    it_behaves_like 'commits directly to the default branch'
  end
end


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

module Security
  module CiConfiguration
    # This class parses SAST template file and .gitlab-ci.yml to populate default and current values into the JSON
    # read from app/validators/json_schemas/security_ci_configuration_schemas/sast_ui_schema.json
    class SastParserService < ::BaseService
      include Gitlab::Utils::StrongMemoize

      SAST_UI_SCHEMA_PATH = 'app/validators/json_schemas/security_ci_configuration_schemas/sast_ui_schema.json'

      def initialize(project)
        @project = project
      end

      def configuration
        result = Gitlab::Json.parse(File.read(Rails.root.join(SAST_UI_SCHEMA_PATH))).with_indifferent_access
        populate_default_value_for(result, :global)
        populate_default_value_for(result, :pipeline)
        fill_current_value_with_default_for(result, :global)
        fill_current_value_with_default_for(result, :pipeline)
        populate_current_value_for(result, :global)
        populate_current_value_for(result, :pipeline)

        fill_current_value_with_default_for_analyzers(result)
        populate_current_value_for_analyzers(result)

        result
      end

      private

      def sast_template_content
        Gitlab::Template::GitlabCiYmlTemplate.find('SAST').content
      end

      def populate_default_value_for(config, level)
        set_each(config[level], key: :default_value, with: sast_template_attributes)
      end

      def populate_current_value_for(config, level)
        set_each(config[level], key: :value, with: gitlab_ci_yml_attributes)
      end

      def fill_current_value_with_default_for(config, level)
        set_each(config[level], key: :value, with: sast_template_attributes)
      end

      def set_each(config_attributes, key:, with:)
        config_attributes.each do |entity|
          entity[key] = with[entity[:field]] if with[entity[:field]]
        end
      end

      def fill_current_value_with_default_for_analyzers(result)
        result[:analyzers].each do |analyzer|
          analyzer[:variables].each do |entity|
            entity[:value] = entity[:default_value] if entity[:default_value]
          end
        end
      end

      def populate_current_value_for_analyzers(result)
        result[:analyzers].each do |analyzer|
          analyzer[:enabled] = analyzer_enabled?(analyzer[:name])
          populate_current_value_for(analyzer, :variables)
        end
      end

      def analyzer_enabled?(analyzer_name)
        # Unless explicitly listed in the excluded analyzers, consider it enabled
        sast_excluded_analyzers.exclude?(analyzer_name)
      end

      def sast_excluded_analyzers
        strong_memoize(:sast_excluded_analyzers) do
          excluded_analyzers = gitlab_ci_yml_attributes["SAST_EXCLUDED_ANALYZERS"] || sast_template_attributes["SAST_EXCLUDED_ANALYZERS"]
          begin
            excluded_analyzers.split(',').map(&:strip)
          rescue StandardError
            []
          end
        end
      end

      def sast_template_attributes
        @sast_template_attributes ||= build_sast_attributes(sast_template_content)
      end

      def gitlab_ci_yml_attributes
        @gitlab_ci_yml_attributes ||= begin
          config_content = @project.repository.blob_data_at(
            @project.repository.root_ref_sha, @project.ci_config_path_or_default
          )
          return {} unless config_content

          build_sast_attributes(config_content)
        end
      end

      def build_sast_attributes(content)
        options = { project: @project, user: current_user, sha: @project.repository.commit.sha }
        yaml_result = Gitlab::Ci::YamlProcessor.new(content, options).execute
        return {} unless yaml_result.valid?

        extract_required_attributes(yaml_result)
      end

      def extract_required_attributes(yaml_result)
        result = {}

        yaml_result.yaml_variables_for(:sast).each do |variable|
          result[variable[:key]] = variable[:value]
        end

        result[:stage] = yaml_result.stage_for(:sast)
        result.with_indifferent_access
      end
    end
  end
end

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Security::CiConfiguration::SastParserService, feature_category: :static_application_security_testing do
  describe '#configuration' do
    include_context 'read ci configuration for sast enabled project'

    let(:configuration) { described_class.new(project).configuration }
    let(:secure_analyzers) { configuration['global'][0] }
    let(:sast_excluded_paths) { configuration['global'][1] }
    let(:sast_pipeline_stage) { configuration['pipeline'][0] }
    let(:sast_search_max_depth) { configuration['pipeline'][1] }
    let(:brakeman) { configuration['analyzers'][0] }
    let(:sast_brakeman_level) { brakeman['variables'][0] }
    let(:semgrep) { configuration['analyzers'][1] }
    let(:secure_analyzers_prefix) { '$CI_TEMPLATE_REGISTRY_HOST/security-products' }

    it 'parses the configuration for SAST' do
      expect(secure_analyzers['default_value']).to eql(secure_analyzers_prefix)
      expect(sast_excluded_paths['default_value']).to eql('spec, test, tests, tmp')
      expect(sast_pipeline_stage['default_value']).to eql('test')
      expect(sast_search_max_depth['default_value']).to eql('4')
      expect(brakeman['enabled']).to be(true)
      expect(sast_brakeman_level['default_value']).to eql('1')
    end

    context 'while populating current values of the entities' do
      context 'when .gitlab-ci.yml is present' do
        it 'populates the current values from the file' do
          allow(project.repository).to receive(:blob_data_at).and_return(gitlab_ci_yml_content)
          expect(secure_analyzers['value']).to eql("registry.gitlab.com/gitlab-org/security-products/analyzers2")
          expect(sast_excluded_paths['value']).to eql('spec, executables')
          expect(sast_pipeline_stage['value']).to eql('our_custom_security_stage')
          expect(sast_search_max_depth['value']).to eql('8')
          expect(brakeman['enabled']).to be(false)
          expect(semgrep['enabled']).to be(true)
          expect(sast_brakeman_level['value']).to eql('2')
        end

        context 'SAST_EXCLUDED_ANALYZERS is set' do
          it 'enables analyzers correctly' do
            allow(project.repository).to receive(:blob_data_at).and_return(gitlab_ci_yml_excluded_analyzers_content)

            expect(brakeman['enabled']).to be(false)
            expect(semgrep['enabled']).to be(true)
          end
        end
      end

      context 'when .gitlab-ci.yml is absent' do
        it 'populates the current values with the default values' do
          allow(project.repository).to receive(:blob_data_at).and_return(nil)
          expect(secure_analyzers['value']).to eql(secure_analyzers_prefix)
          expect(sast_excluded_paths['value']).to eql('spec, test, tests, tmp')
          expect(sast_pipeline_stage['value']).to eql('test')
          expect(sast_search_max_depth['value']).to eql('4')
          expect(brakeman['enabled']).to be(true)
          expect(sast_brakeman_level['value']).to eql('1')
        end
      end

      context 'when .gitlab-ci.yml does not include the sast job' do
        before do
          allow(project.repository).to receive(:blob_data_at).and_return(
            File.read(Rails.root.join('spec/support/gitlab_stubs/gitlab_ci.yml'))
          )
        end

        it 'populates the current values with the default values' do
          expect(secure_analyzers['value']).to eql(secure_analyzers_prefix)
          expect(sast_excluded_paths['value']).to eql('spec, test, tests, tmp')
          expect(sast_pipeline_stage['value']).to eql('test')
          expect(sast_search_max_depth['value']).to eql('4')
          expect(brakeman['enabled']).to be(true)
          expect(sast_brakeman_level['value']).to eql('1')
        end
      end
    end
  end
end


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

module Security
  module CiConfiguration
    class SecretDetectionCreateService < ::Security::CiConfiguration::BaseCreateService
      private

      def action
        Security::CiConfiguration::SecretDetectionBuildAction.new(project.auto_devops_enabled?, existing_gitlab_ci_content,
                                                                  project.ci_config_path).generate
      end

      def next_branch
        'set-secret-detection-config'
      end

      def message
        _('Configure Secret Detection in `.gitlab-ci.yml`, creating this file if it does not already exist')
      end

      def description
        _('Configure Secret Detection in `.gitlab-ci.yml` using the GitLab managed template. You can [add variable overrides](https://docs.gitlab.com/ee/user/application_security/secret_detection/#customizing-settings) to customize Secret Detection settings.')
      end

      def name
        'Secret Detection'
      end
    end
  end
end

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Security::CiConfiguration::SecretDetectionCreateService, :snowplow, feature_category: :container_scanning do
  subject(:result) { described_class.new(project, user).execute }

  let(:branch_name) { 'set-secret-detection-config-1' }

  let(:snowplow_event) do
    {
      category: 'Security::CiConfiguration::SecretDetectionCreateService',
      action: 'create',
      label: ''
    }
  end

  include_examples 'services security ci configuration create service', true
end


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

module Security
  module CiConfiguration
    class ContainerScanningCreateService < ::Security::CiConfiguration::BaseCreateService
      private

      def action
        Security::CiConfiguration::ContainerScanningBuildAction.new(project.auto_devops_enabled?, existing_gitlab_ci_content,
                                                                    project.ci_config_path).generate
      end

      def next_branch
        'set-container-scanning-config'
      end

      def message
        _('Configure Container Scanning in `.gitlab-ci.yml`, creating this file if it does not already exist')
      end

      def description
        _('Configure Container Scanning in `.gitlab-ci.yml` using the GitLab managed template. You can [add variable overrides](https://docs.gitlab.com/ee/user/application_security/container_scanning/#customizing-the-container-scanning-settings) to customize Container Scanning settings.')
      end

      def name
        'Container Scanning'
      end
    end
  end
end

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Security::CiConfiguration::ContainerScanningCreateService, :snowplow, feature_category: :container_scanning do
  subject(:result) { described_class.new(project, user).execute }

  let(:branch_name) { 'set-container-scanning-config-1' }

  let(:snowplow_event) do
    {
      category: 'Security::CiConfiguration::ContainerScanningCreateService',
      action: 'create',
      label: ''
    }
  end

  include_examples 'services security ci configuration create service', true
end


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

module Security
  module CiConfiguration
    class SastIacCreateService < ::Security::CiConfiguration::BaseCreateService
      private

      def action
        Security::CiConfiguration::SastIacBuildAction.new(project.auto_devops_enabled?, existing_gitlab_ci_content,
                                                          project.ci_config_path).generate
      end

      def next_branch
        'set-sast-iac-config'
      end

      def message
        _('Configure SAST IaC in `.gitlab-ci.yml`, creating this file if it does not already exist')
      end

      def description
        _('Configure SAST IaC in `.gitlab-ci.yml` using the GitLab managed template. You can [add variable overrides](https://docs.gitlab.com/ee/user/application_security/sast/#customizing-the-sast-settings) to customize SAST IaC settings.')
      end

      def name
        'SAST IaC'
      end
    end
  end
end

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Security::CiConfiguration::SastIacCreateService, :snowplow, feature_category: :static_application_security_testing do
  subject(:result) { described_class.new(project, user).execute }

  let(:branch_name) { 'set-sast-iac-config-1' }

  let(:snowplow_event) do
    {
      category: 'Security::CiConfiguration::SastIacCreateService',
      action: 'create',
      label: ''
    }
  end

  include_examples 'services security ci configuration create service', true
end


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

module Auth
  class DependencyProxyAuthenticationService < BaseService
    AUDIENCE = 'dependency_proxy'
    HMAC_KEY = 'gitlab-dependency-proxy'
    DEFAULT_EXPIRE_TIME = 1.minute

    def execute(authentication_abilities:)
      return error('dependency proxy not enabled', 404) unless ::Gitlab.config.dependency_proxy.enabled
      return error('access forbidden', 403) unless valid_user_actor?

      { token: authorized_token.encoded }
    end

    class << self
      include ::Gitlab::Utils::StrongMemoize

      def secret
        strong_memoize(:secret) do
          OpenSSL::HMAC.hexdigest(
            'sha256',
            ::Settings.attr_encrypted_db_key_base,
            HMAC_KEY
          )
        end
      end

      def token_expire_at
        Time.current + Gitlab::CurrentSettings.container_registry_token_expire_delay.minutes
      end
    end

    private

    def valid_user_actor?
      current_user || valid_deploy_token?
    end

    def valid_deploy_token?
      deploy_token && deploy_token.valid_for_dependency_proxy?
    end

    def authorized_token
      JSONWebToken::HMACToken.new(self.class.secret).tap do |token|
        token['user_id'] = current_user.id if current_user
        token['deploy_token'] = deploy_token.token if deploy_token
        token.expire_time = self.class.token_expire_at
      end
    end

    def deploy_token
      params[:deploy_token]
    end
  end
end

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Auth::DependencyProxyAuthenticationService, feature_category: :dependency_proxy do
  let_it_be(:user) { create(:user) }
  let_it_be(:params) { {} }

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

  before do
    stub_config(dependency_proxy: { enabled: true }, registry: { enabled: true })
  end

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

    shared_examples 'returning' do |status:, message:|
      it "returns #{message}", :aggregate_failures do
        expect(subject[:http_status]).to eq(status)
        expect(subject[:message]).to eq(message)
      end
    end

    shared_examples 'returning a token with an encoded field' do |field|
      it 'returns a token with encoded field' do
        token = subject[:token]
        expect(token).not_to be_nil

        decoded_token = decode(token)
        expect(decoded_token[field]).not_to be_nil
      end
    end

    context 'dependency proxy is not enabled' do
      before do
        stub_config(dependency_proxy: { enabled: false })
      end

      it_behaves_like 'returning', status: 404, message: 'dependency proxy not enabled'
    end

    context 'without a user' do
      let(:user) { nil }

      it_behaves_like 'returning', status: 403, message: 'access forbidden'
    end

    context 'with a deploy token' do
      let_it_be(:deploy_token) { create(:deploy_token, :group, :dependency_proxy_scopes) }
      let_it_be(:params) { { deploy_token: deploy_token } }

      it_behaves_like 'returning a token with an encoded field', 'deploy_token'
    end

    context 'with a human user' do
      it_behaves_like 'returning a token with an encoded field', 'user_id'
    end

    context 'all other user types' do
      User::USER_TYPES.except(:human, :project_bot).each_value do |user_type|
        context "with user_type #{user_type}" do
          before do
            user.update!(user_type: user_type)
          end

          it_behaves_like 'returning a token with an encoded field', 'user_id'
        end
      end
    end

    def decode(token)
      DependencyProxy::AuthTokenService.new(token).execute
    end
  end
end


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

module Auth
  class ContainerRegistryAuthenticationService < BaseService
    AUDIENCE = 'container_registry'
    REGISTRY_LOGIN_ABILITIES = [
      :read_container_image,
      :create_container_image,
      :destroy_container_image,
      :update_container_image,
      :admin_container_image,
      :build_read_container_image,
      :build_create_container_image,
      :build_destroy_container_image
    ].freeze

    FORBIDDEN_IMPORTING_SCOPES = %w[push delete *].freeze

    ActiveImportError = Class.new(StandardError)

    def execute(authentication_abilities:)
      @authentication_abilities = authentication_abilities

      return error('UNAVAILABLE', status: 404, message: 'registry not enabled') unless registry.enabled

      return error('DENIED', status: 403, message: 'access forbidden') unless has_registry_ability?

      unless scopes.any? || current_user || deploy_token || project
        return error('DENIED', status: 403, message: 'access forbidden')
      end

      { token: authorized_token(*scopes).encoded }
    rescue ActiveImportError
      error(
        'DENIED',
        status: 403,
        message: 'Your repository is currently being migrated to a new platform and writes are temporarily disabled. Go to https://gitlab.com/groups/gitlab-org/-/epics/5523 to learn more.'
      )
    end

    def self.full_access_token(*names)
      names_and_actions = names.index_with { %w[*] }
      access_token(names_and_actions)
    end

    def self.import_access_token
      access_token({ 'import' => %w[*] }, 'registry')
    end

    def self.pull_access_token(*names)
      names_and_actions = names.index_with { %w[pull] }
      access_token(names_and_actions)
    end

    def self.pull_nested_repositories_access_token(name)
      name = name.chomp('/')

      access_token({
        name => %w[pull],
        "#{name}/*" => %w[pull]
      })
    end

    def self.push_pull_nested_repositories_access_token(name)
      name = name.chomp('/')

      access_token(
        {
          name => %w[pull push],
          "#{name}/*" => %w[pull]
        },
        override_project_path: name
      )
    end

    def self.access_token(names_and_actions, type = 'repository', override_project_path: nil)
      registry = Gitlab.config.registry
      token = JSONWebToken::RSAToken.new(registry.key)
      token.issuer = registry.issuer
      token.audience = AUDIENCE
      token.expire_time = token_expire_at

      token[:access] = names_and_actions.map do |name, actions|
        {
          type: type,
          name: name,
          actions: actions,
          meta: access_metadata(path: name, override_project_path: override_project_path)
        }.compact
      end

      token.encoded
    end

    def self.token_expire_at
      Time.current + Gitlab::CurrentSettings.container_registry_token_expire_delay.minutes
    end

    def self.access_metadata(project: nil, path: nil, override_project_path: nil)
      return { project_path: override_project_path.downcase } if override_project_path

      # If the project is not given, try to infer it from the provided path
      if project.nil?
        return if path.nil? # If no path is given, return early
        return if path == 'import' # Ignore the special 'import' path

        # If the path ends with '/*', remove it so we can parse the actual repository path
        path = path.chomp('/*')

        # Parse the repository project from the path
        begin
          project = ContainerRegistry::Path.new(path).repository_project
        rescue ContainerRegistry::Path::InvalidRegistryPathError
          # If the path is invalid, gracefully handle the error
          return
        end
      end

      # Return the project path (lowercase) as metadata
      { project_path: project&.full_path&.downcase }
    end

    private

    def authorized_token(*accesses)
      JSONWebToken::RSAToken.new(registry.key).tap do |token|
        token.issuer = registry.issuer
        token.audience = params[:service]
        token.subject = current_user.try(:username)
        token.expire_time = self.class.token_expire_at
        token[:auth_type] = params[:auth_type]
        token[:access] = accesses.compact
        token[:user] = user_info_token.encoded
      end
    end

    def user_info_token
      info =
        if current_user
          {
            token_type: params[:auth_type],
            username: current_user.username,
            user_id: current_user.id
          }
        elsif deploy_token
          {
            token_type: params[:auth_type],
            username: deploy_token.username,
            deploy_token_id: deploy_token.id
          }
        end

      JSONWebToken::RSAToken.new(registry.key).tap do |token|
        token[:user_info] = info
      end
    end

    def scopes
      return [] unless params[:scopes]

      @scopes ||= params[:scopes].map do |scope|
        process_scope(scope)
      end.compact
    end

    def process_scope(scope)
      type, name, actions = scope.split(':', 3)
      actions = actions.split(',')

      case type
      when 'registry'
        process_registry_access(type, name, actions)
      when 'repository'
        path = ContainerRegistry::Path.new(name)
        process_repository_access(type, path, actions)
      end
    end

    def process_registry_access(type, name, actions)
      return unless current_user&.admin?
      return unless name == 'catalog'
      return unless actions == ['*']

      { type: type, name: name, actions: ['*'] }
    end

    def process_repository_access(type, path, actions)
      return unless path.valid?

      raise ActiveImportError if actively_importing?(actions, path)

      requested_project = path.repository_project

      return unless requested_project

      authorized_actions = actions.select do |action|
        can_access?(requested_project, action)
      end

      log_if_actions_denied(type, requested_project, actions, authorized_actions)

      return unless authorized_actions.present?

      # At this point user/build is already authenticated.
      #
      ensure_container_repository!(path, authorized_actions)

      {
        type: type,
        name: path.to_s,
        actions: authorized_actions,
        meta: self.class.access_metadata(project: requested_project)
      }
    end

    def actively_importing?(actions, path)
      return false if FORBIDDEN_IMPORTING_SCOPES.intersection(actions).empty?

      container_repository = ContainerRepository.find_by_path(path)
      return false unless container_repository

      container_repository.migration_importing?
    end

    ##
    # Because we do not have two way communication with registry yet,
    # we create a container repository image resource when push to the
    # registry is successfully authorized.
    #
    def ensure_container_repository!(path, actions)
      return if path.has_repository?
      return unless actions.include?('push')

      ContainerRepository.find_or_create_from_path(path)
    end

    # Overridden in EE
    def can_access?(requested_project, requested_action)
      return false unless requested_project.container_registry_enabled?

      case requested_action
      when 'pull'
        build_can_pull?(requested_project) || user_can_pull?(requested_project) || deploy_token_can_pull?(requested_project)
      when 'push'
        build_can_push?(requested_project) || user_can_push?(requested_project) || deploy_token_can_push?(requested_project)
      when 'delete'
        build_can_delete?(requested_project) || user_can_admin?(requested_project)
      when '*'
        user_can_admin?(requested_project)
      else
        false
      end
    end

    def build_can_delete?(requested_project)
      # Build can delete only from the project from which it originates
      has_authentication_ability?(:build_destroy_container_image) &&
        requested_project == project
    end

    def registry
      Gitlab.config.registry
    end

    def can_user?(ability, project)
      can?(current_user, ability, project)
    end

    def build_can_pull?(requested_project)
      # Build can:
      # 1. pull from its own project (for ex. a build)
      # 2. read images from dependent projects if creator of build is a team member
      has_authentication_ability?(:build_read_container_image) &&
        (requested_project == project || can_user?(:build_read_container_image, requested_project))
    end

    def user_can_admin?(requested_project)
      has_authentication_ability?(:admin_container_image) &&
        can_user?(:admin_container_image, requested_project)
    end

    def user_can_pull?(requested_project)
      has_authentication_ability?(:read_container_image) &&
        can_user?(:read_container_image, requested_project)
    end

    def deploy_token_can_pull?(requested_project)
      has_authentication_ability?(:read_container_image) &&
        deploy_token.present? &&
        can?(deploy_token, :read_container_image, requested_project)
    end

    def deploy_token_can_push?(requested_project)
      has_authentication_ability?(:create_container_image) &&
        deploy_token.present? &&
        can?(deploy_token, :create_container_image, requested_project)
    end

    ##
    # We still support legacy pipeline triggers which do not have associated
    # actor. New permissions model and new triggers are always associated with
    # an actor. So this should be improved once
    # https://gitlab.com/gitlab-org/gitlab-foss/issues/37452 is resolved.
    #
    def build_can_push?(requested_project)
      # Build can push only to the project from which it originates
      has_authentication_ability?(:build_create_container_image) &&
        requested_project == project
    end

    def user_can_push?(requested_project)
      has_authentication_ability?(:create_container_image) &&
        can_user?(:create_container_image, requested_project)
    end

    def error(code, status:, message: '')
      { errors: [{ code: code, message: message }], http_status: status }
    end

    def has_authentication_ability?(capability)
      @authentication_abilities.to_a.include?(capability)
    end

    def has_registry_ability?
      @authentication_abilities.any? do |ability|
        REGISTRY_LOGIN_ABILITIES.include?(ability)
      end
    end

    # Overridden in EE
    def extra_info
      {}
    end

    def deploy_token
      params[:deploy_token]
    end

    def log_if_actions_denied(type, requested_project, requested_actions, authorized_actions)
      return if requested_actions == authorized_actions

      log_info = {
        message: 'Denied container registry permissions',
        scope_type: type,
        requested_project_path: requested_project.full_path,
        requested_actions: requested_actions,
        authorized_actions: authorized_actions,
        username: current_user&.username,
        user_id: current_user&.id,
        project_path: project&.full_path
      }.merge!(extra_info).compact

      Gitlab::AuthLogger.warn(log_info)
    end
  end
end

Auth::ContainerRegistryAuthenticationService.prepend_mod_with('Auth::ContainerRegistryAuthenticationService')

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Auth::ContainerRegistryAuthenticationService, feature_category: :container_registry do
  include AdminModeHelper

  it_behaves_like 'a container registry auth service'
end


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

module ApplicationSettings
  class UpdateService < ApplicationSettings::BaseService
    include ValidatesClassificationLabel

    attr_reader :params, :application_setting

    MARKDOWN_CACHE_INVALIDATING_PARAMS = %w[asset_proxy_enabled asset_proxy_url asset_proxy_secret_key asset_proxy_whitelist].freeze

    def execute
      result = update_settings

      auto_approve_blocked_users if result

      result
    end

    private

    def update_settings
      validate_classification_label(application_setting, :external_authorization_service_default_label) unless bypass_external_auth?

      if application_setting.errors.any?
        return false
      end

      update_terms(@params.delete(:terms))
      update_default_branch_protection_defaults(@params[:default_branch_protection])

      add_to_outbound_local_requests_whitelist(@params.delete(:add_to_outbound_local_requests_whitelist))

      if params.key?(:performance_bar_allowed_group_path)
        group_id = process_performance_bar_allowed_group_id

        return false if application_setting.errors.any?

        params[:performance_bar_allowed_group_id] = group_id
      end

      if usage_stats_updated? && !params.delete(:skip_usage_stats_user)
        params[:usage_stats_set_by_user_id] = current_user.id
      end

      @application_setting.assign_attributes(params)

      if invalidate_markdown_cache?
        @application_setting[:local_markdown_version] = @application_setting.local_markdown_version + 1
      end

      @application_setting.save
    end

    def usage_stats_updated?
      params.key?(:usage_ping_enabled) || params.key?(:version_check_enabled)
    end

    def add_to_outbound_local_requests_whitelist(values)
      values_array = Array(values).reject(&:empty?)
      return if values_array.empty?

      @application_setting.add_to_outbound_local_requests_whitelist(values_array)
    end

    def invalidate_markdown_cache?
      !params.key?(:local_markdown_version) &&
        (@application_setting.changes.keys & MARKDOWN_CACHE_INVALIDATING_PARAMS).any?
    end

    def update_terms(terms)
      return unless terms.present?

      # Avoid creating a new terms record if the text is exactly the same.
      terms = terms.strip
      return if terms == @application_setting.terms

      ApplicationSetting::Term.create(terms: terms)
      @application_setting.reset_memoized_terms
    end

    def update_default_branch_protection_defaults(default_branch_protection)
      return unless default_branch_protection.present?

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

    def process_performance_bar_allowed_group_id
      group_full_path = params.delete(:performance_bar_allowed_group_path)
      enable_param_on = Gitlab::Utils.to_boolean(params.delete(:performance_bar_enabled))
      performance_bar_enabled = enable_param_on.nil? || enable_param_on # Default to true

      return if group_full_path.blank?
      return if enable_param_on == false # Explicitly disabling

      unless performance_bar_enabled
        application_setting.errors.add(:performance_bar_allowed_group_id, 'not allowed when performance bar is disabled')
        return
      end

      group = Group.find_by_full_path(group_full_path.chomp('/'))

      unless group
        application_setting.errors.add(:performance_bar_allowed_group_id, 'not found')
        return
      end

      group.id
    end

    def bypass_external_auth?
      params.key?(:external_authorization_service_enabled) && !Gitlab::Utils.to_boolean(params[:external_authorization_service_enabled])
    end

    def auto_approve_blocked_users
      return unless should_auto_approve_blocked_users?

      ApproveBlockedPendingApprovalUsersWorker.perform_async(current_user.id)
    end

    def should_auto_approve_blocked_users?
      return false unless application_setting.previous_changes.key?(:require_admin_approval_after_user_signup)

      enabled_previous, enabled_current = application_setting.previous_changes[:require_admin_approval_after_user_signup]

      enabled_previous && !enabled_current
    end
  end
end

ApplicationSettings::UpdateService.prepend_mod_with('ApplicationSettings::UpdateService')

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe ApplicationSettings::UpdateService, feature_category: :shared do
  include ExternalAuthorizationServiceHelpers

  let(:application_settings) { ::Gitlab::CurrentSettings.current_application_settings }
  let(:admin) { create(:user, :admin) }
  let(:params) { {} }

  subject { described_class.new(application_settings, admin, params) }

  before do
    # So the caching behaves like it would in production
    stub_env('IN_MEMORY_APPLICATION_SETTINGS', 'false')

    # Creating these settings first ensures they're used by other factories
    application_settings
  end

  describe 'updating terms' do
    context 'when the passed terms are blank' do
      let(:params) { { terms: '' } }

      it 'does not create terms' do
        expect { subject.execute }.not_to change { ApplicationSetting::Term.count }
      end
    end

    context 'when passing terms' do
      let(:params) { { terms: 'Be nice!  ' } }

      it 'creates the terms' do
        expect { subject.execute }.to change { ApplicationSetting::Term.count }.by(1)
      end

      it 'does not create terms if they are the same as the existing ones' do
        create(:term, terms: 'Be nice!')

        expect { subject.execute }.not_to change { ApplicationSetting::Term.count }
      end

      it 'updates terms if they already existed' do
        create(:term, terms: 'Other terms')

        subject.execute

        expect(application_settings.terms).to eq('Be nice!')
      end

      it 'only queries once when the terms are changed' do
        create(:term, terms: 'Other terms')
        expect(application_settings.terms).to eq('Other terms')

        subject.execute

        expect(application_settings.terms).to eq('Be nice!')
        expect { 2.times { application_settings.terms } }
          .not_to exceed_query_limit(0)
      end
    end
  end

  describe 'updating outbound_local_requests_whitelist' do
    context 'when params is blank' do
      let(:params) { {} }

      it 'does not add to allowlist' do
        expect { subject.execute }.not_to change {
          application_settings.outbound_local_requests_whitelist
        }
      end
    end

    context 'when param add_to_outbound_local_requests_whitelist contains values' do
      before do
        application_settings.outbound_local_requests_whitelist = ['127.0.0.1']
      end

      let(:params) { { add_to_outbound_local_requests_whitelist: ['example.com', ''] } }

      it 'adds to allowlist' do
        expect { subject.execute }.to change {
          application_settings.outbound_local_requests_whitelist
        }

        expect(application_settings.outbound_local_requests_whitelist).to contain_exactly(
          '127.0.0.1', 'example.com'
        )
      end
    end

    context 'when param outbound_local_requests_allowlist_raw is passed' do
      before do
        application_settings.outbound_local_requests_whitelist = ['127.0.0.1']
      end

      let(:params) { { outbound_local_requests_allowlist_raw: 'example.com;gitlab.com' } }

      it 'overwrites the existing allowlist' do
        expect { subject.execute }.to change {
          application_settings.outbound_local_requests_whitelist
        }

        expect(application_settings.outbound_local_requests_whitelist).to contain_exactly(
          'example.com', 'gitlab.com'
        )
      end
    end
  end

  describe 'markdown cache invalidators', feature_category: :team_planning do
    shared_examples 'invalidates markdown cache' do |attribute|
      let(:params) { attribute }

      it 'increments cache' do
        expect { subject.execute }.to change(application_settings, :local_markdown_version).by(1)
      end
    end

    it_behaves_like 'invalidates markdown cache', { asset_proxy_enabled: true }
    it_behaves_like 'invalidates markdown cache', { asset_proxy_url: 'http://test.com' }
    it_behaves_like 'invalidates markdown cache', { asset_proxy_secret_key: 'another secret' }
    it_behaves_like 'invalidates markdown cache', { asset_proxy_allowlist: ['domain.com'] }
    it_behaves_like 'invalidates markdown cache', { asset_proxy_whitelist: ['domain.com'] }

    context 'when also setting the local_markdown_version' do
      let(:params) { { asset_proxy_enabled: true, local_markdown_version: 12 } }

      it 'does not increment' do
        expect { subject.execute }.to change(application_settings, :local_markdown_version).to(12)
      end
    end

    context 'do not invalidate if value does not change' do
      let(:params) { { asset_proxy_enabled: true, asset_proxy_secret_key: 'secret', asset_proxy_url: 'http://test.com' } }

      it 'does not increment' do
        described_class.new(application_settings, admin, params).execute

        expect { described_class.new(application_settings, admin, params).execute }.not_to change(application_settings, :local_markdown_version)
      end
    end
  end

  describe 'performance bar settings', feature_category: :cloud_connector do
    using RSpec::Parameterized::TableSyntax

    where(
      :params_performance_bar_enabled,
      :params_performance_bar_allowed_group_path,
      :previous_performance_bar_allowed_group_id,
      :expected_performance_bar_allowed_group_id,
      :expected_valid
    ) do
      true | '' | nil | nil | true
      true | '' | 42_000_000 | nil | true
      true | nil | nil | nil | true
      true | nil | 42_000_000 | nil | true
      true | 'foo' | nil | nil | false
      true | 'foo' | 42_000_000 | 42_000_000 | false
      true | 'group_a' | nil | 42_000_000 | true
      true | 'group_b' | 42_000_000 | 43_000_000 | true
      true | 'group_b/' | 42_000_000 | 43_000_000 | true
      true | 'group_a' | 42_000_000 | 42_000_000 | true
      false | '' | nil | nil | true
      false | '' | 42_000_000 | nil | true
      false | nil | nil | nil | true
      false | nil | 42_000_000 | nil | true
      false | 'foo' | nil | nil | true
      false | 'foo' | 42_000_000 | nil | true
      false | 'group_a' | nil | nil | true
      false | 'group_b' | 42_000_000 | nil | true
      false | 'group_a' | 42_000_000 | nil | true
      nil | '' | nil | nil | true
      nil | 'foo' | nil | nil | false
      nil | 'group_a' | nil | 42_000_000 | true
    end

    with_them do
      let(:params) do
        {
          performance_bar_allowed_group_path: params_performance_bar_allowed_group_path
        }.tap do |params_hash|
          # Treat nil in the table as missing
          unless params_performance_bar_enabled.nil?
            params_hash[:performance_bar_enabled] = params_performance_bar_enabled
          end
        end
      end

      before do
        if previous_performance_bar_allowed_group_id == 42_000_000 || params_performance_bar_allowed_group_path == 'group_a'
          create(:group, id: 42_000_000, path: 'group_a')
        end

        if expected_performance_bar_allowed_group_id == 43_000_000 || params_performance_bar_allowed_group_path == 'group_b'
          create(:group, id: 43_000_000, path: 'group_b')
        end

        application_settings.update!(performance_bar_allowed_group_id: previous_performance_bar_allowed_group_id)
      end

      it 'sets performance_bar_allowed_group_id when present and performance_bar_enabled == true' do
        expect(application_settings.performance_bar_allowed_group_id).to eq(previous_performance_bar_allowed_group_id)

        if previous_performance_bar_allowed_group_id != expected_performance_bar_allowed_group_id
          expect { subject.execute }
            .to change(application_settings, :performance_bar_allowed_group_id)
            .from(previous_performance_bar_allowed_group_id).to(expected_performance_bar_allowed_group_id)
        else
          expect { subject.execute }
            .not_to change(application_settings, :performance_bar_allowed_group_id)
        end
      end

      it 'adds errors to the model for invalid params' do
        expect(subject.execute).to eq(expected_valid)

        unless expected_valid
          expect(application_settings.errors[:performance_bar_allowed_group_id]).to be_present
        end
      end
    end

    context 'when :performance_bar_allowed_group_path is not present' do
      let(:group) { create(:group) }

      before do
        application_settings.update!(performance_bar_allowed_group_id: group.id)
      end

      it 'does not change the performance bar settings' do
        expect { subject.execute }
          .not_to change(application_settings, :performance_bar_allowed_group_id)
      end
    end

    context 'when :performance_bar_enabled is not present' do
      let(:group) { create(:group) }
      let(:params) { { performance_bar_allowed_group_path: group.full_path } }

      it 'implicitly defaults to true' do
        expect { subject.execute }
          .to change(application_settings, :performance_bar_allowed_group_id)
          .from(nil).to(group.id)
      end
    end
  end

  context 'when external authorization is enabled', feature_category: :system_access do
    before do
      enable_external_authorization_service_check
    end

    it 'does not validate labels if external authorization gets disabled' do
      expect_any_instance_of(described_class).not_to receive(:validate_classification_label)

      described_class.new(application_settings, admin, { external_authorization_service_enabled: false }).execute
    end

    it 'does validate labels if external authorization gets enabled' do
      expect_any_instance_of(described_class).to receive(:validate_classification_label)

      described_class.new(application_settings, admin, { external_authorization_service_enabled: true }).execute
    end

    it 'does validate labels if external authorization is left unchanged' do
      expect_any_instance_of(described_class).to receive(:validate_classification_label)

      described_class.new(application_settings, admin, { external_authorization_service_default_label: 'new-label' }).execute
    end

    it 'does not save the settings with an error if the service denies access' do
      expect(::Gitlab::ExternalAuthorization)
        .to receive(:access_allowed?).with(admin, 'new-label') { false }

      described_class.new(application_settings, admin, { external_authorization_service_default_label: 'new-label' }).execute

      expect(application_settings.errors[:external_authorization_service_default_label]).to be_present
    end

    it 'saves the setting when the user has access to the label' do
      expect(::Gitlab::ExternalAuthorization)
        .to receive(:access_allowed?).with(admin, 'new-label') { true }

      described_class.new(application_settings, admin, { external_authorization_service_default_label: 'new-label' }).execute

      # Read the attribute directly to avoid the stub from
      # `enable_external_authorization_service_check`
      expect(application_settings[:external_authorization_service_default_label]).to eq('new-label')
    end

    it 'does not validate the label if it was not passed' do
      expect(::Gitlab::ExternalAuthorization)
        .not_to receive(:access_allowed?)

      described_class.new(application_settings, admin, { home_page_url: 'http://foo.bar' }).execute
    end
  end

  context 'when raw_blob_request_limit is passsed' do
    let(:params) do
      {
        raw_blob_request_limit: 600
      }
    end

    it 'updates raw_blob_request_limit value' do
      subject.execute

      application_settings.reload

      expect(application_settings.raw_blob_request_limit).to eq(600)
    end
  end

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

    it "updates default_branch_protection_defaults from the default_branch_protection param" do
      default_value = ::Gitlab::Access::BranchProtection.protected_fully.deep_stringify_keys

      expect { subject.execute }.to change { application_settings.default_branch_protection_defaults }.from(default_value).to(expected)
    end
  end

  context 'when protected path settings are passed' do
    let(:params) do
      {
        throttle_protected_paths_enabled: 1,
        throttle_protected_paths_period_in_seconds: 600,
        throttle_protected_paths_requests_per_period: 100,
        protected_paths_raw: "/users/password\r\n/users/sign_in\r\n",
        protected_paths_for_get_request_raw: "/users/password\r\n/users/sign_up\r\n"
      }
    end

    it 'updates protected path settings' do
      subject.execute

      application_settings.reload

      expect(application_settings.throttle_protected_paths_enabled).to be_truthy
      expect(application_settings.throttle_protected_paths_period_in_seconds).to eq(600)
      expect(application_settings.throttle_protected_paths_requests_per_period).to eq(100)
      expect(application_settings.protected_paths).to eq(['/users/password', '/users/sign_in'])
      expect(application_settings.protected_paths_for_get_request).to match_array(['/users/password', '/users/sign_up'])
    end
  end

  context 'when general rate limits are passed' do
    let(:params) do
      {
        throttle_authenticated_api_enabled: true,
        throttle_authenticated_api_period_in_seconds: 10,
        throttle_authenticated_api_requests_per_period: 20,
        throttle_authenticated_web_enabled: true,
        throttle_authenticated_web_period_in_seconds: 30,
        throttle_authenticated_web_requests_per_period: 40,
        throttle_unauthenticated_api_enabled: true,
        throttle_unauthenticated_api_period_in_seconds: 50,
        throttle_unauthenticated_api_requests_per_period: 60,
        throttle_unauthenticated_enabled: true,
        throttle_unauthenticated_period_in_seconds: 50,
        throttle_unauthenticated_requests_per_period: 60
      }
    end

    it 'updates general throttle settings' do
      subject.execute

      expect(application_settings.reload).to have_attributes(params)
    end
  end

  context 'when package registry rate limits are passed' do
    let(:params) do
      {
        throttle_unauthenticated_packages_api_enabled: 1,
        throttle_unauthenticated_packages_api_period_in_seconds: 500,
        throttle_unauthenticated_packages_api_requests_per_period: 20,
        throttle_authenticated_packages_api_enabled: 1,
        throttle_authenticated_packages_api_period_in_seconds: 600,
        throttle_authenticated_packages_api_requests_per_period: 10
      }
    end

    it 'updates package registry throttle settings' do
      subject.execute

      application_settings.reload

      expect(application_settings.throttle_unauthenticated_packages_api_enabled).to be_truthy
      expect(application_settings.throttle_unauthenticated_packages_api_period_in_seconds).to eq(500)
      expect(application_settings.throttle_unauthenticated_packages_api_requests_per_period).to eq(20)
      expect(application_settings.throttle_authenticated_packages_api_enabled).to be_truthy
      expect(application_settings.throttle_authenticated_packages_api_period_in_seconds).to eq(600)
      expect(application_settings.throttle_authenticated_packages_api_requests_per_period).to eq(10)
    end
  end

  context 'when files API rate limits are passed' do
    let(:params) do
      {
        throttle_unauthenticated_files_api_enabled: 1,
        throttle_unauthenticated_files_api_period_in_seconds: 500,
        throttle_unauthenticated_files_api_requests_per_period: 20,
        throttle_authenticated_files_api_enabled: 1,
        throttle_authenticated_files_api_period_in_seconds: 600,
        throttle_authenticated_files_api_requests_per_period: 10
      }
    end

    it 'updates files API throttle settings' do
      subject.execute

      application_settings.reload

      expect(application_settings.throttle_unauthenticated_files_api_enabled).to be_truthy
      expect(application_settings.throttle_unauthenticated_files_api_period_in_seconds).to eq(500)
      expect(application_settings.throttle_unauthenticated_files_api_requests_per_period).to eq(20)
      expect(application_settings.throttle_authenticated_files_api_enabled).to be_truthy
      expect(application_settings.throttle_authenticated_files_api_period_in_seconds).to eq(600)
      expect(application_settings.throttle_authenticated_files_api_requests_per_period).to eq(10)
    end
  end

  context 'when deprecated API rate limits are passed' do
    let(:params) do
      {
        throttle_unauthenticated_deprecated_api_enabled: 1,
        throttle_unauthenticated_deprecated_api_period_in_seconds: 500,
        throttle_unauthenticated_deprecated_api_requests_per_period: 20,
        throttle_authenticated_deprecated_api_enabled: 1,
        throttle_authenticated_deprecated_api_period_in_seconds: 600,
        throttle_authenticated_deprecated_api_requests_per_period: 10
      }
    end

    it 'updates deprecated API throttle settings' do
      subject.execute

      application_settings.reload

      expect(application_settings.throttle_unauthenticated_deprecated_api_enabled).to be_truthy
      expect(application_settings.throttle_unauthenticated_deprecated_api_period_in_seconds).to eq(500)
      expect(application_settings.throttle_unauthenticated_deprecated_api_requests_per_period).to eq(20)
      expect(application_settings.throttle_authenticated_deprecated_api_enabled).to be_truthy
      expect(application_settings.throttle_authenticated_deprecated_api_period_in_seconds).to eq(600)
      expect(application_settings.throttle_authenticated_deprecated_api_requests_per_period).to eq(10)
    end
  end

  context 'when git lfs rate limits are passed' do
    let(:params) do
      {
        throttle_authenticated_git_lfs_enabled: 1,
        throttle_authenticated_git_lfs_period_in_seconds: 600,
        throttle_authenticated_git_lfs_requests_per_period: 10
      }
    end

    it 'updates git lfs throttle settings' do
      subject.execute

      application_settings.reload

      expect(application_settings.throttle_authenticated_git_lfs_enabled).to be_truthy
      expect(application_settings.throttle_authenticated_git_lfs_period_in_seconds).to eq(600)
      expect(application_settings.throttle_authenticated_git_lfs_requests_per_period).to eq(10)
    end
  end

  context 'when issues_create_limit is passed' do
    let(:params) do
      {
        issues_create_limit: 600
      }
    end

    it 'updates issues_create_limit value' do
      subject.execute

      application_settings.reload

      expect(application_settings.issues_create_limit).to eq(600)
    end
  end

  context 'when users_get_by_id_limit and users_get_by_id_limit_allowlist_raw are passed' do
    let(:params) do
      {
        users_get_by_id_limit: 456,
        users_get_by_id_limit_allowlist_raw: 'someone, someone_else'
      }
    end

    it 'updates users_get_by_id_limit and users_get_by_id_limit_allowlist value' do
      subject.execute

      application_settings.reload

      expect(application_settings.users_get_by_id_limit).to eq(456)
      expect(application_settings.users_get_by_id_limit_allowlist).to eq(%w[someone someone_else])
    end
  end

  context 'when require_admin_approval_after_user_signup changes' do
    context 'when it goes from enabled to disabled' do
      let(:params) { { require_admin_approval_after_user_signup: false } }

      it 'calls ApproveBlockedPendingApprovalUsersWorker' do
        expect(ApproveBlockedPendingApprovalUsersWorker).to receive(:perform_async)

        subject.execute
      end
    end

    context 'when it goes from disabled to enabled' do
      let(:params) { { require_admin_approval_after_user_signup: true } }

      it 'does not call ApproveBlockedPendingApprovalUsersWorker' do
        application_settings.update!(require_admin_approval_after_user_signup: false)

        expect(ApproveBlockedPendingApprovalUsersWorker).not_to receive(:perform_async)

        subject.execute
      end
    end
  end
end


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

module BatchedGitRefUpdates
  class CleanupSchedulerService
    include Gitlab::ExclusiveLeaseHelpers
    MAX_PROJECTS = 10_000
    BATCH_SIZE = 100
    LOCK_TIMEOUT = 10.minutes

    def execute
      total_projects = 0

      in_lock(self.class.name, retries: 0, ttl: LOCK_TIMEOUT) do
        Deletion.status_pending.distinct_each_batch(column: :project_id, of: BATCH_SIZE) do |deletions|
          ProjectCleanupWorker.bulk_perform_async_with_contexts(
            deletions,
            arguments_proc: ->(deletion) { deletion.project_id },
            context_proc: ->(_) { {} } # No project context because loading the project is wasteful
          )

          total_projects += deletions.count
          break if total_projects >= MAX_PROJECTS
        end
      end

      { total_projects: total_projects }
    end
  end
end

```

Response:
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe BatchedGitRefUpdates::CleanupSchedulerService, feature_category: :gitaly do
  let(:service) { described_class.new }

  describe '#execute' do
    before do
      BatchedGitRefUpdates::Deletion.create!(project_id: 123, ref: 'ref1')
      BatchedGitRefUpdates::Deletion.create!(project_id: 123, ref: 'ref2')
      BatchedGitRefUpdates::Deletion.create!(project_id: 456, ref: 'ref3')
      BatchedGitRefUpdates::Deletion.create!(project_id: 789, ref: 'ref4', status: :processed)
    end

    it 'schedules ProjectCleanupWorker for each project in pending BatchedGitRefUpdates::Deletion' do
      project_ids = []
      expect(BatchedGitRefUpdates::ProjectCleanupWorker)
        .to receive(:bulk_perform_async_with_contexts) do |deletions, arguments_proc:, context_proc:| # rubocop:disable Lint/UnusedBlockArgument
        project_ids += deletions.map(&arguments_proc)
      end

      service.execute

      expect(project_ids).to contain_exactly(123, 456)
    end

    it 'returns stats' do
      stats = service.execute

      expect(stats).to eq({
        total_projects: 2
      })
    end

    it 'acquires a lock to avoid running duplicate instances' do
      expect(service).to receive(:in_lock) # Mock and don't yield
        .with(described_class.name, retries: 0, ttl: described_class::LOCK_TIMEOUT)
      expect(BatchedGitRefUpdates::ProjectCleanupWorker).not_to receive(:bulk_perform_async_with_contexts)

      service.execute
    end

    it 'limits to MAX_PROJECTS before it stops' do
      stub_const("#{described_class}::BATCH_SIZE", 1)
      stub_const("#{described_class}::MAX_PROJECTS", 1)

      stats = service.execute

      expect(stats).to eq({
        total_projects: 1
      })
    end
  end
end