Instruction:
Write RSpec test file for following ruby class
```ruby
# -- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2010-2023 the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
# ++

class WorkPackageMembers::CreateService < BaseServices::Create
  private

  def instance_class
    Member
  end

  def after_perform(service_call)
    return service_call unless service_call.success?

    work_package_member = service_call.result

    add_group_memberships(work_package_member)
    send_notification(work_package_member)

    service_call
  end

  def add_group_memberships(work_package_member)
    return unless work_package_member.principal.is_a?(Group)

    Groups::CreateInheritedRolesService
      .new(work_package_member.principal,
           current_user: user,
           contract_class: EmptyContract)
      .call(user_ids: work_package_member.principal.user_ids,
            send_notifications: false,
            project_ids: [work_package_member.project_id])
  end

  def send_notification(work_package_member)
    OpenProject::Notifications.send(OpenProject::Events::WORK_PACKAGE_SHARED,
                                    work_package_member:,
                                    send_notifications: true)
  end
end

```

Response:
# frozen_string_literal: true

# -- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2023 the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
# ++

require 'spec_helper'
require 'services/base_services/behaves_like_create_service'

RSpec.describe WorkPackageMembers::CreateService, type: :model do
  subject(:service_call) { instance.call(call_attributes) }

  let(:instance) { described_class.new(user:) }
  let(:user) { build_stubbed(:user) }

  let(:richard) { build_stubbed(:user) }
  let(:dinesh) { build_stubbed(:user) }
  let(:gilfoyle) { build_stubbed(:user) }
  let(:cool_group) do
    build_stubbed(:group).tap do |group|
      allow(group)
        .to receive(:user_ids)
              .and_return([dinesh.id, gilfoyle.id])
    end
  end

  let(:role) { build_stubbed(:view_work_package_role) }
  let(:work_package) { build_stubbed(:work_package) }

  let!(:inherited_roles_service) do
    instance_double(Groups::CreateInheritedRolesService).tap do |inherited_roles_service|
      allow(Groups::CreateInheritedRolesService)
        .to receive(:new)
              .and_return(inherited_roles_service)

      allow(inherited_roles_service)
        .to receive(:call)
    end
  end

  def stub_notifications
    allow(OpenProject::Notifications)
      .to receive(:send)
  end

  before { stub_notifications }

  it_behaves_like 'BaseServices create service' do
    let(:model_class) { Member }
    let(:principal) { richard }
    let(:call_attributes) { { principal:, roles: [role], entity: work_package, project: work_package.project } }

    context 'when successful' do
      it 'sends a notification' do
        service_call

        expect(OpenProject::Notifications)
          .to have_received(:send)
                .with(OpenProject::Events::WORK_PACKAGE_SHARED,
                      work_package_member: model_instance,
                      send_notifications: true)
      end

      context 'for a User' do
        it 'does not create any inherited roles' do
          service_call

          expect(Groups::CreateInheritedRolesService)
            .not_to have_received(:new)
        end
      end

      context 'for a Group' do
        let(:principal) { cool_group }
        let(:model_instance) { build_stubbed(:member, principal:) }

        it "generates the work package memberships and roles for the group's users" do
          service_call

          expect(Groups::CreateInheritedRolesService)
            .to have_received(:new)
                  .with(principal,
                        current_user: user,
                        contract_class: EmptyContract)

          expect(inherited_roles_service)
            .to have_received(:call)
                  .with(user_ids: principal.user_ids,
                        project_ids: [model_instance.project_id],
                        send_notifications: false)
        end
      end
    end
  end
end


Instruction:
Write RSpec test file for following ruby class
```ruby
# -- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2010-2023 the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
# ++

class WorkPackageMembers::DeleteService < BaseServices::Delete
  include Members::Concerns::CleanedUp

  protected

  def after_perform(service_call)
    super(service_call).tap do |call|
      work_package_member = call.result

      cleanup_for_group(work_package_member)
    end
  end

  def cleanup_for_group(work_package_member)
    return unless work_package_member.principal.is_a?(Group)

    Groups::CleanupInheritedRolesService
      .new(work_package_member.principal, current_user: user, contract_class: EmptyContract)
      .call
  end
end

```

Response:
# frozen_string_literal: true

# -- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2010-2023 the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
# ++

require 'spec_helper'
require 'services/base_services/behaves_like_delete_service'

RSpec.describe WorkPackageMembers::DeleteService, type: :model do
  it_behaves_like 'BaseServices delete service' do
    let(:model_class) { Member }
    let(:model_instance) { build_stubbed(:work_package_member, principal:) }
    let(:principal) { user }

    let!(:cleanup_service_instance) do
      instance_double(Members::CleanupService, call: nil).tap do |service_double|
        allow(Members::CleanupService)
          .to receive(:new)
                .with(principal, model_instance.project_id)
                .and_return(service_double)
      end
    end

    describe '#call' do
      context 'when contract validates and the model is destroyed successfully' do
        it 'calls the cleanup service' do
          service_call

          expect(cleanup_service_instance)
            .to have_received(:call)
        end

        context "when the model's principal is a group" do
          let(:principal) { build_stubbed(:group) }
          let!(:cleanup_inherited_roles_service_instance) do
            instance_double(Groups::CleanupInheritedRolesService, call: nil).tap do |service_double|
              allow(Groups::CleanupInheritedRolesService)
                .to receive(:new)
                      .with(principal,
                            current_user: user,
                            contract_class: EmptyContract)
                      .and_return(service_double)
            end
          end

          it "calls the cleanup inherited roles service" do
            service_call

            expect(cleanup_inherited_roles_service_instance)
              .to have_received(:call)
          end
        end
      end
    end
  end
end


Instruction:
Write RSpec test file for following ruby class
```ruby
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2023 the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++

##
# Implements the creation of a local repository.
class SCM::CreateManagedRepositoryService < SCM::BaseRepositoryService
  ##
  # Checks if a given repository may be created and managed locally.
  # Registers an job to create the repository on disk.
  #
  # @return True if the repository creation request has been initiated, false otherwise.
  def call
    if repository.managed? && repository.manageable?

      ##
      # We want to move this functionality in a Delayed Job,
      # but this heavily interferes with the error handling of the whole
      # repository management process.
      # Instead, this will be refactored into a single service wrapper for
      # creating and deleting repositories, which provides transactional DB access
      # as well as filesystem access.
      if repository.class.manages_remote?
        SCM::CreateRemoteRepositoryJob.perform_now(repository)
      else
        SCM::CreateLocalRepositoryJob.ensure_not_existing!(repository)
        SCM::CreateLocalRepositoryJob.perform_later(repository)
      end
      return true
    end

    false
  rescue Errno::EACCES
    @rejected = I18n.t('repositories.errors.path_permission_failed',
                       path: repository.root_url)
    false
  rescue SystemCallError => e
    @rejected = I18n.t('repositories.errors.filesystem_access_failed',
                       message: e.message)
    false
  rescue OpenProject::SCM::Exceptions::SCMError => e
    @rejected = e.message
    false
  end

  ##
  # Returns the error symbol
  def localized_rejected_reason
    @rejected ||= I18n.t('repositories.errors.not_manageable')
  end
end

```

Response:
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2023 the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.

require 'spec_helper'

RSpec.describe SCM::CreateManagedRepositoryService, skip_if_command_unavailable: 'svnadmin' do
  let(:user) { build(:user) }
  let(:config) { {} }
  let(:project) { build(:project) }

  let(:repository) { build(:repository_subversion) }

  subject(:service) { SCM::CreateManagedRepositoryService.new(repository) }

  before do
    allow(OpenProject::Configuration).to receive(:[]).and_call_original
    allow(OpenProject::Configuration).to receive(:[]).with('scm').and_return(config)
  end

  shared_examples 'does not create a filesystem repository' do
    it 'does not create a filesystem repository' do
      expect(repository.managed?).to be false
      expect(service.call).to be false
    end
  end

  context 'with no managed configuration' do
    it_behaves_like 'does not create a filesystem repository'
  end

  context 'with managed repository' do
    # Must not .create a managed repository, or it will call this service itself!
    let(:repository) do
      repo = Repository::Subversion.new(scm_type: :managed)
      repo.project = project
      repo
    end

    context 'but no managed config' do
      it 'does not create a filesystem repository' do
        expect(repository.managed?).to be true
        expect(service.call).to be false
      end
    end
  end

  context 'with managed local config' do
    include_context 'with tmpdir'
    let(:config) do
      {
        subversion: { manages: File.join(tmpdir, 'svn') },
        git: { manages: File.join(tmpdir, 'git') }
      }
    end

    let(:repository) do
      repo = Repository::Subversion.new(scm_type: :managed)
      repo.project = project
      repo.configure(:managed, nil)

      # Ignore default creation
      allow(repo).to receive(:create_managed_repository)
      repo.save!
      repo
    end

    before do
      allow_any_instance_of(SCM::CreateLocalRepositoryJob)
        .to receive(:repository).and_return(repository)
      allow_any_instance_of(SCM::CreateRemoteRepositoryJob)
        .to receive(:repository).and_return(repository)
    end

    it 'creates the repository' do
      expect(service.call).to be true
      perform_enqueued_jobs
      expect(File.directory?(repository.root_url)).to be true
    end

    context 'with pre-existing path on filesystem' do
      before do
        allow(File).to receive(:directory?).and_return(true)
      end

      it 'does not create the repository' do
        expect(service.call).to be false
        expect(service.localized_rejected_reason)
          .to eq(I18n.t('repositories.errors.exists_on_filesystem'))
      end
    end

    context 'with a permission error occurring in the Job' do
      before do
        allow(SCM::CreateLocalRepositoryJob)
          .to receive(:new).and_raise(Errno::EACCES)
      end

      it 'returns the correct error' do
        expect(service.call).to be false
        expect(service.localized_rejected_reason)
          .to eq(I18n.t('repositories.errors.path_permission_failed',
                        path: repository.root_url))
      end
    end

    context 'with an OS error occurring in the Job' do
      before do
        allow(SCM::CreateLocalRepositoryJob)
          .to receive(:new).and_raise(Errno::ENOENT)
      end

      it 'returns the correct error' do
        expect(service.call).to be false
        expect(service.localized_rejected_reason)
          .to include('An error occurred while accessing the repository in the filesystem')
      end
    end
  end

  context 'with managed remote config', webmock: true do
    let(:url) { 'http://myreposerver.example.com/api/' }
    let(:config) do
      {
        subversion: { manages: url }
      }
    end

    let(:repository) do
      repo = build(:repository_subversion, scm_type: :managed)
      repo.project = project
      repo.configure(:managed, nil)
      repo
    end

    it 'detects the remote config' do
      expect(repository.class.managed_remote.to_s).to eq(url)
      expect(repository.class).to be_manages_remote
    end

    context 'with a remote callback' do
      let(:returned_url) { 'file:///tmp/some/url/to/repo' }
      let(:root_url) { '/tmp/some/url/to/repo' }

      before do
        stub_request(:post, url)
          .to_return(
            status: 200,
            body: { url: returned_url, path: root_url }.to_json
          )
      end

      shared_examples 'calls the callback' do
        before do
          # Avoid setting up a second call to the remote during save
          # since we only templated the repository, not created one!
          expect(repository).to receive(:save).and_return(true)
        end

        it do
          expect(SCM::CreateRemoteRepositoryJob)
            .to receive(:new).and_call_original

          expect(service.call).to be true
          expect(repository.root_url).to eq(root_url)
          expect(repository.url).to eq(returned_url)

          expect(WebMock)
            .to have_requested(:post, url)
            .with(body: hash_including(action: 'create'))
        end
      end

      context 'with http' do
        it_behaves_like 'calls the callback'
      end

      context 'with https' do
        let(:url) { 'https://myreposerver.example.com/api/' }
        let(:config) do
          {
            subversion: { manages: url, insecure: }
          }
        end

        let(:instance) { SCM::CreateRemoteRepositoryJob.new }
        let(:job_call) { instance.perform(repository) }

        context 'with insecure option' do
          let(:insecure) { true }

          it_behaves_like 'calls the callback'
          it 'uses the insecure option' do
            job_call
            expect(instance.send(:configured_verification)).to eq(OpenSSL::SSL::VERIFY_NONE)
          end
        end

        context 'without insecure option' do
          let(:insecure) { false }

          it 'uses the insecure option' do
            job_call
            expect(instance.send(:configured_verification)).to eq(OpenSSL::SSL::VERIFY_PEER)
          end
        end
      end
    end

    context 'with a faulty remote callback' do
      before do
        stub_request(:post, url)
          .to_return(status: 400, body: { success: false, message: 'An error occurred' }.to_json)
      end

      it 'calls the callback' do
        expect(SCM::CreateRemoteRepositoryJob)
          .to receive(:new).and_call_original

        expect(service.call).to be false
        expect(service.localized_rejected_reason)
          .to eq("Calling the managed remote failed with message 'An error occurred' (Code: 400)")
        expect(WebMock)
          .to have_requested(:post, url)
                .with(body: hash_including(action: 'create'))
      end
    end
  end
end


Instruction:
Write RSpec test file for following ruby class
```ruby
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2023 the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++

##
# Implements a repository service for building checkout instructions if supported
class SCM::CheckoutInstructionsService
  attr_reader :repository, :user, :path

  def initialize(repository, path: nil, user: User.current)
    @repository = repository
    @user = user
    @path = path
  end

  ##
  # Retrieve the checkout URL using the repository vendor information
  # It may additionally set a path parameter, if the repository supports subtree checkout
  def checkout_url(with_path = false)
    repository.scm.checkout_url(repository,
                                checkout_base_url,
                                with_path ? URI.encode_www_form_component(@path) : nil)
  end

  ##
  # Returns the checkout command from SCM adapter
  # (e.g., `git clone`)
  def checkout_command
    repository.scm.checkout_command
  end

  ##
  # Returns the checkout base URL as defined in settings.
  def checkout_base_url
    checkout_settings['base_url']
  end

  ##
  # Returns the instructions defined in the settings.
  def instructions
    checkout_settings['text'].presence ||
      I18n.t("repositories.checkout.default_instructions.#{repository.vendor}")
  end

  ##
  # Returns true when the checkout URL may target a subtree of the repository.
  def subtree_checkout?
    repository.scm.subtree_checkout?
  end

  ##
  # Determines whether the checkout URL may be built, i.e. all information is available
  # This is the case when the base_url is set or the vendor doesn't use base URLs.
  def checkout_url_buildable?
    !repository.class.requires_checkout_base_url? || checkout_base_url.present?
  end

  ##
  # Returns whether the repository supports showing checkout information
  # and has been configured for it.
  def available?
    checkout_enabled? &&
      repository.supports_checkout_info? &&
      checkout_url_buildable?
  end

  def checkout_enabled?
    checkout_settings['enabled'].to_i > 0
  end

  def supported_but_not_enabled?
    repository.supports_checkout_info? && !checkout_enabled?
  end

  ##
  # Determines whether permissions for the given repository
  # are available.
  def manages_permissions?
    repository.managed?
  end

  ##
  # Returns one of the following permission symbols for the given user
  #
  # - :readwrite: When user is allowed to read and commit (:commit_access)
  # - :read: When user is allowed to checkout the repository, but not commit (:browse_repository)
  # - :none: Otherwise
  #
  # Note that this information is only applicable when the repository is managed,
  # because otherwise OpenProject does not control the repository permissions.
  # Use +manages_permissions?+ to check whether this is the case.
  #
  def permission
    project = repository.project
    if user.allowed_in_project?(:commit_access, project)
      :readwrite
    elsif user.allowed_in_project?(:browse_repository, project)
      :read
    else
      :none
    end
  end

  ##
  # Returns whether the given user may checkout the repository
  #
  # Note that this information is only applicable when the repository is managed,
  # because otherwise OpenProject does not control the repository permissions.
  # Use +manages_permissions?+ to check whether this is the case.
  def may_checkout?
    %i[readwrite read].include?(permission)
  end

  ##
  # Returns whether the given user may commit to the repository
  #
  # Note that this information is only applicable when the repository is managed,
  # because otherwise OpenProject does not control the repository permissions.
  # Use +manages_permissions?+ to check whether this is the case.
  def may_commit?
    permission == :readwrite
  end

  private

  def checkout_settings
    @settings ||= begin
      hash = Setting.repository_checkout_data[repository.vendor.to_s]
      hash.presence || {}
    end
  end
end

```

Response:
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2023 the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.

require 'spec_helper'

RSpec.describe SCM::CheckoutInstructionsService do
  let(:user) { build(:user) }
  let(:project) { build(:project) }

  let(:url) { 'file:///tmp/some/svn/repo' }
  let(:repository) do
    build(:repository_subversion,
          url:,
          project:)
  end

  let(:base_url) { 'http://example.org/svn/' }
  let(:path) { nil }
  let(:text) { 'foo' }
  let(:checkout_hash) do
    {
      'git' => { 'enabled' => '0' },
      'subversion' => { 'enabled' => '1',
                        'text' => text,
                        'base_url' => base_url }
    }
  end

  subject(:service) { SCM::CheckoutInstructionsService.new(repository, user:, path:) }

  before do
    allow(Setting).to receive(:repository_checkout_data).and_return(checkout_hash)
  end

  describe '#checkout_url' do
    shared_examples 'builds the correct URL' do
      it 'builds the correct URL' do
        expect(service.checkout_url)
          .to eq(URI("http://example.org/svn/#{project.identifier}"))
      end

      shared_examples 'valid checkout URL' do
        it do
          expect(service.checkout_url(path))
            .to eq(URI("http://example.org/svn/#{project.identifier}/#{expected_path}"))
        end
      end

      it_behaves_like 'valid checkout URL' do
        let(:path) { 'foo/bar' }
        let(:expected_path) { path }
      end

      it_behaves_like 'valid checkout URL' do
        let(:path) { 'foo/bar with spaces' }
        let(:expected_path) { 'foo/bar%20with%20spaces' }
      end

      it_behaves_like 'valid checkout URL' do
        let(:path) { 'foo/bar with §\"!??```' }
        let(:expected_path) { 'foo/%C2%A7%22!??%60%60%60' }
      end
    end
  end

  describe '#checkout_command' do
    it 'returns the SCM vendor command' do
      expect(service.checkout_command).to eq('svn checkout')
    end
  end

  describe '#instructions' do
    it 'returns the setting when defined' do
      expect(service.instructions).to eq('foo')
    end

    context 'no setting defined' do
      let(:text) { nil }

      it 'returns the default translated instructions' do
        expect(service.instructions)
          .to eq(I18n.t('repositories.checkout.default_instructions.subversion'))
      end
    end
  end

  describe '#settings' do
    it 'svn is available for checkout' do
      expect(service.available?).to be true
      expect(service.checkout_enabled?).to be true
    end

    it 'has the correct settings' do
      expect(Setting.repository_checkout_data['subversion']['enabled']).to eq('1')
      expect(service.instructions).to eq('foo')
      expect(service.checkout_base_url).to eq(base_url)
    end

    context 'missing checkout base URL' do
      let(:base_url) { '' }

      it 'is not available for checkout even when enabled' do
        expect(service.checkout_base_url).to eq(base_url)
        expect(service.checkout_enabled?).to be true
        expect(service.available?).to be false
      end
    end

    context 'disabled repository' do
      let(:repository) { build(:repository_git) }

      it 'git is not available for checkout' do
        expect(service.available?).to be false
        expect(service.checkout_enabled?).to be false
      end
    end
  end

  describe '#permission' do
    context 'with no managed repository' do
      it 'is not applicable' do
        expect(service.manages_permissions?).to be false
      end
    end

    context 'with managed repository' do
      before do
        allow(repository).to receive(:managed?).and_return(true)
      end

      it 'is applicable' do
        expect(service.manages_permissions?).to be true
      end

      it 'returns readwrite permission when user has commit_access permission' do
        mock_permissions_for(user) do |mock|
          mock.allow_in_project :commit_access, project:
        end

        expect(service.permission).to eq(:readwrite)
      end

      it 'returns read permission when user has browse_repository permission' do
        mock_permissions_for(user) do |mock|
          mock.allow_in_project :browse_repository, project:
        end

        expect(service.permission).to eq(:read)
      end

      it 'returns none permission when user has no permission' do
        mock_permissions_for(user, &:forbid_everything)

        expect(service.permission).to eq(:none)
      end

      it 'returns the correct permissions for commit access' do
        mock_permissions_for(user) do |mock|
          mock.allow_in_project :commit_access, project:
        end

        expect(service.may_commit?).to be true
        expect(service.may_checkout?).to be true
      end

      it 'returns the correct permissions for read access' do
        mock_permissions_for(user) do |mock|
          mock.allow_in_project :browse_repository, project:
        end

        expect(service.may_commit?).to be false
        expect(service.may_checkout?).to be true
      end

      it 'returns the correct permissions for no access' do
        mock_permissions_for(user, &:forbid_everything)

        expect(service.may_commit?).to be false
        expect(service.may_checkout?).to be false
      end
    end
  end
end


Instruction:
Write RSpec test file for following ruby class
```ruby
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2023 the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++

##
# Implements a repository factory for building temporary and permanent repositories.
class SCM::RepositoryFactoryService
  attr_reader :project, :params, :repository

  def initialize(project, params)
    @project = project
    @params = params
  end

  ##
  # Build a full repository from a given scm_type
  # and persists it.
  #
  # @return [Boolean] true iff the repository was built
  def build_and_save
    build_guarded do
      repository = build_with_type(params.fetch(:scm_type).to_sym)
      if repository.save
        repository
      else
        raise OpenProject::SCM::Exceptions::RepositoryBuildError.new(
          repository.errors.full_messages.join("\n")
        )
      end
    end
  end

  ##
  # Build a temporary repository used only for determining available settings and types
  # of that particular vendor.
  #
  # @return [Boolean] true iff the repository was built
  def build_temporary
    build_guarded do
      build_with_type(nil)
    end
  end

  def build_error
    I18n.t('repositories.errors.build_failed', reason: @build_failed_msg)
  end

  private

  ##
  # Helper to actually build the repository and return it.
  # May raise +OpenProject::SCM::Exceptions::RepositoryBuildError+ internally.
  #
  # @param [Symbol] scm_type Type to build the repository with. May be nil
  #                          during temporary build
  def build_with_type(scm_type)
    Repository.build(
      project,
      params.fetch(:scm_vendor).to_sym,
      params.fetch(:repository, {}),
      scm_type
    )
  end

  def build_guarded
    @repository = yield
    @repository.present?
  rescue OpenProject::SCM::Exceptions::RepositoryBuildError => e
    @build_failed_msg = e.message
    nil
  end
end

```

Response:
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2023 the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.

require 'spec_helper'

RSpec.describe SCM::RepositoryFactoryService do
  let(:user) { build(:user) }
  let(:project) { build(:project) }

  let(:enabled_scms) { ['subversion', 'git'] }

  let(:params_hash) { {} }
  let(:params) { ActionController::Parameters.new params_hash }

  subject(:service) { SCM::RepositoryFactoryService.new(project, params) }

  before do
    allow(Setting).to receive(:enabled_scm).and_return(enabled_scms)
  end

  context 'with empty hash' do
    it 'does not build a repository' do
      expect { service.build_temporary }
        .to raise_error KeyError
      expect(service.repository).to be_nil
    end
  end

  context 'with valid vendor' do
    let(:params_hash) do
      { scm_vendor: 'subversion' }
    end

    it 'allows temporary build repository' do
      expect(service.build_temporary).to be true
      expect(service.repository).not_to be_nil
    end

    it 'does not allow to persist a repository' do
      expect { service.build_and_save }
        .to raise_error(ActionController::ParameterMissing)

      expect(service.repository).to be_nil
    end
  end

  context 'with invalid vendor' do
    let(:params_hash) do
      { scm_vendor: 'not_subversion', scm_type: 'foo' }
    end

    it 'does not allow to temporary build repository' do
      expect { service.build_temporary }.not_to raise_error

      expect(service.repository).to be_nil
      expect(service.build_error).to include('The SCM vendor not_subversion is disabled')
    end

    it 'does not allow to persist a repository' do
      expect { service.build_temporary }.not_to raise_error

      expect(service.repository).to be_nil
      expect(service.build_error).to include('The SCM vendor not_subversion is disabled')
    end
  end

  context 'with vendor and type' do
    let(:params_hash) do
      { scm_vendor: 'subversion', scm_type: 'existing' }
    end

    it 'does not allow to persist a repository without URL' do
      expect(service.build_and_save).not_to be true

      expect(service.repository).to be_nil
      expect(service.build_error).to include("URL can't be blank")
    end
  end

  context 'with invalid hash' do
    let(:params_hash) do
      {
        scm_vendor: 'subversion', scm_type: 'existing',
        repository: { url: '/tmp/foo.svn' }
      }
    end

    it 'does not allow to persist a repository URL' do
      expect(service.build_and_save).not_to be true

      expect(service.repository).to be_nil
      expect(service.build_error).to include('URL is invalid')
    end
  end

  context 'with valid hash' do
    let(:params_hash) do
      {
        scm_vendor: 'subversion', scm_type: 'existing',
        repository: { url: 'file:///tmp/foo.svn' }
      }
    end

    it 'allows to persist a repository without URL' do
      expect(service.build_and_save).to be true
      expect(service.repository).to be_a(Repository::Subversion)
    end
  end
end


Instruction:
Write RSpec test file for following ruby class
```ruby
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2023 the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++

##
# Implements the asynchronous deletion of a local repository.
class SCM::DeleteManagedRepositoryService < SCM::BaseRepositoryService
  ##
  # Checks if a given repository may be deleted
  # Registers an asynchronous job to delete the repository on disk.
  #
  def call
    return false unless repository.managed?

    if repository.class.manages_remote?
      SCM::DeleteRemoteRepositoryJob.perform_now(repository)
      true
    else
      delete_local_repository
    end
  rescue OpenProject::SCM::Exceptions::SCMError => e
    @rejected = e.message
    false
  end

  def delete_local_repository
    # Create necessary changes to repository to mark
    # it as managed by OP, but delete asynchronously.
    managed_path = repository.root_url

    if File.directory?(managed_path)
      ##
      # We want to move this functionality in a Delayed Job,
      # but this heavily interferes with the error handling of the whole
      # repository management process.
      # Instead, this will be refactored into a single service wrapper for
      # creating and deleting repositories, which provides transactional DB access
      # as well as filesystem access.
      SCM::DeleteLocalRepositoryJob.perform_now(managed_path)
    end

    true
  rescue SystemCallError => e
    @rejected = I18n.t('repositories.errors.managed_delete_local',
                       path: repository.root_url,
                       error_message: e.message)
    false
  end

  ##
  # Returns the error symbol
  def localized_rejected_reason
    @rejected ||= I18n.t('repositories.errors.managed_delete')
  end
end

```

Response:
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2023 the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.

require 'spec_helper'

RSpec.describe SCM::DeleteManagedRepositoryService, skip_if_command_unavailable: 'svnadmin' do
  let(:user) { build(:user) }
  let(:config) { {} }
  let(:project) { build(:project) }

  let(:repository) { build(:repository_subversion) }

  subject(:service) { SCM::DeleteManagedRepositoryService.new(repository) }

  before do
    allow(OpenProject::Configuration).to receive(:[]).and_call_original
    allow(OpenProject::Configuration).to receive(:[]).with('scm').and_return(config)
    allow(Setting).to receive(:enabled_scm).and_return(['subversion', 'git'])
  end

  shared_examples 'does not delete the repository' do
    it 'does not delete the repository' do
      expect(repository.managed?).to be false
      expect(service.call).to be false
    end
  end

  context 'with no managed configuration' do
    it_behaves_like 'does not delete the repository'
  end

  context 'with managed repository, but no config' do
    let(:repository) { build(:repository_subversion, scm_type: :managed) }

    it 'does allow to delete the repository' do
      expect(repository.managed?).to be true
      expect(service.call).to be true
    end
  end

  context 'with managed repository and managed config' do
    include_context 'with tmpdir'
    let(:config) do
      {
        subversion: { manages: File.join(tmpdir, 'svn') },
        git: { manages: File.join(tmpdir, 'git') }
      }
    end

    let(:repository) do
      repo = Repository::Subversion.new(scm_type: :managed)
      repo.project = project
      repo.configure(:managed, nil)

      repo.save!
      perform_enqueued_jobs
      repo
    end

    it 'deletes the repository' do
      expect(File.directory?(repository.root_url)).to be true
      expect(service.call).to be true
      expect(File.directory?(repository.root_url)).to be false
    end

    it 'does not raise an exception upon permission errors' do
      expect(File.directory?(repository.root_url)).to be true
      expect(SCM::DeleteLocalRepositoryJob)
        .to receive(:new).and_raise(Errno::EACCES)

      expect(service.call).to be false
    end

    context 'and parent project' do
      let(:parent) { create(:project) }
      let(:project) { create(:project, parent:) }
      let(:repo_path) do
        Pathname.new(File.join(tmpdir, 'svn', project.identifier))
      end

      it 'does not delete anything but the repository itself' do
        expect(service.call).to be true

        path = Pathname.new(repository.root_url)
        expect(path).to eq(repo_path)

        expect(path.exist?).to be false
        expect(path.parent.exist?).to be true
        expect(path.parent.to_s).to eq(repository.class.managed_root)
      end
    end
  end

  context 'with managed remote config', webmock: true do
    let(:url) { 'http://myreposerver.example.com/api/' }
    let(:config) do
      {
        subversion: { manages: url }
      }
    end

    let(:repository) do
      repo = Repository::Subversion.new(scm_type: :managed)
      repo.project = project
      repo.configure(:managed, nil)

      repo
    end

    context 'with a valid remote' do
      before do
        stub_request(:post, url).to_return(status: 200, body: {}.to_json)
      end

      it 'calls the callback' do
        expect(SCM::DeleteRemoteRepositoryJob)
          .to receive(:perform_now)
          .and_call_original

        expect(service.call).to be true
        expect(WebMock)
          .to have_requested(:post, url)
          .with(body: hash_including(identifier: repository.repository_identifier,
                                     action: 'delete'))
      end
    end

    context 'with a remote callback returning an error' do
      before do
        stub_request(:post, url)
          .to_return(status: 400, body: { success: false, message: 'An error occurred' }.to_json)
      end

      it 'calls the callback' do
        expect(SCM::DeleteRemoteRepositoryJob)
          .to receive(:perform_now)
          .and_call_original

        expect(service.call).to be false

        expect(service.localized_rejected_reason)
          .to eq("Calling the managed remote failed with message 'An error occurred' (Code: 400)")
        expect(WebMock)
          .to have_requested(:post, url)
                .with(body: hash_including(action: 'delete'))
      end
    end
  end
end


Instruction:
Write RSpec test file for following ruby class
```ruby
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2023 the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++

class Authorization::EnterpriseService
  attr_accessor :token

  GUARDED_ACTIONS = %i(
    baseline_comparison
    board_view
    conditional_highlighting
    custom_actions
    custom_fields_in_projects_list
    date_alerts
    define_custom_style
    edit_attribute_groups
    grid_widget_wp_graph
    ldap_groups
    openid_providers
    placeholder_users
    readonly_work_packages
    team_planner_view
    two_factor_authentication
    work_package_query_relation_columns
    work_package_sharing
  ).freeze

  def initialize(token)
    self.token = token
  end

  # Return a true ServiceResult if the token contains this particular action.
  def call(action)
    allowed =
      if token.nil? || token.token_object.nil? || token.expired?
        false
      else
        process(action)
      end

    result(allowed)
  end

  private

  def process(action)
    # Every non-expired token
    GUARDED_ACTIONS.include?(action.to_sym)
  end

  def result(bool)
    ServiceResult.new(success: bool, result: bool)
  end
end

```

Response:
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2023 the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++

require 'spec_helper'

RSpec.describe Authorization::EnterpriseService do
  let(:token_object) do
    token = OpenProject::Token.new
    token.subscriber = 'Foobar'
    token.mail = 'foo@example.org'
    token.starts_at = Date.today
    token.expires_at = nil

    token
  end
  let(:token) { double('EnterpriseToken', token_object:) }
  let(:instance) { described_class.new(token) }
  let(:result) { instance.call(action) }
  let(:action) { :an_action }

  describe '#initialize' do
    it 'has the token' do
      expect(instance.token).to eql token
    end
  end

  describe 'expiry' do
    before do
      allow(token).to receive(:expired?).and_return(expired)
    end

    context 'when expired' do
      let(:expired) { true }

      it 'returns a false result' do
        expect(result).to be_a ServiceResult
        expect(result.result).to be_falsey
        expect(result.success?).to be_falsey
      end
    end

    context 'when active' do
      let(:expired) { false }

      context 'invalid action' do
        it 'returns false' do
          expect(result.result).to be_falsey
        end
      end

      %i(baseline_comparison
         board_view
         conditional_highlighting
         custom_actions
         custom_fields_in_projects_list
         date_alerts
         define_custom_style
         edit_attribute_groups
         grid_widget_wp_graph
         ldap_groups
         openid_providers
         placeholder_users
         readonly_work_packages
         team_planner_view
         two_factor_authentication
         work_package_query_relation_columns
         work_package_sharing).each do |guarded_action|
        context "guarded action #{guarded_action}" do
          let(:action) { guarded_action }

          it 'returns a true result' do
            expect(result).to be_a ServiceResult
            expect(result.result).to be_truthy
            expect(result.success?).to be_truthy
          end
        end
      end
    end
  end
end


Instruction:
Write RSpec test file for following ruby class
```ruby
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2023 the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++

class Authorization::QueryTransformation
  attr_accessor :on, :name, :after, :before, :block

  def initialize(on,
                 name,
                 after,
                 before,
                 block)

    self.on = on
    self.name = name
    self.after = after
    self.before = before
    self.block = block
  end

  def apply(*args)
    block.call(*args)
  end
end

```

Response:
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2023 the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++

require 'spec_helper'

RSpec.describe Authorization::QueryTransformation do
  let(:on) { 'on' }
  let(:name) { 'name' }
  let(:after) { 'after' }
  let(:before) { 'before' }
  let(:block) { ->(*args) { args } }

  let(:instance) do
    described_class.new on,
                        name,
                        after,
                        before,
                        block
  end

  context 'initialSetup' do
    it 'sets on' do
      expect(instance.on).to eql on
    end

    it 'sets name' do
      expect(instance.name).to eql name
    end

    it 'sets after' do
      expect(instance.after).to eql after
    end

    it 'sets before' do
      expect(instance.before).to eql before
    end

    it 'sets block' do
      expect(instance.block).to eql block
    end
  end

  context 'apply' do
    it 'calls the block' do
      expect(instance.apply(1, 2, 3)).to match_array [1, 2, 3]
    end
  end
end


Instruction:
Write RSpec test file for following ruby class
```ruby
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2023 the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++

class Authorization::UserProjectRolesQuery < Authorization::UserRolesQuery
  transformations.register :all, :project_where_projection do |statement, user, _|
    statement.where(users_table[:id].eq(user.id))
  end

  transformations.register users_members_join, :project_id_limit do |statement, _, project|
    statement
      .and(members_table[:project_id].eq(project.id))
      .and(members_table[:entity_type].eq(nil))
      .and(members_table[:entity_id].eq(nil))
  end

  transformations.register roles_member_roles_join, :builtin_role do |statement, user, project|
    if project.public?
      builtin_role = if user.logged?
                       Role::BUILTIN_NON_MEMBER
                     else
                       Role::BUILTIN_ANONYMOUS
                     end

      builtin_role_condition = members_table[:id]
                               .eq(nil)
                               .and(roles_table[:builtin]
                                    .eq(builtin_role))

      statement = statement.or(builtin_role_condition)
    end

    statement
  end
end

```

Response:
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2023 the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++

require 'spec_helper'

RSpec.describe Authorization::UserProjectRolesQuery do
  let(:user) { build(:user) }
  let(:anonymous) { build(:anonymous) }
  let(:project) { build(:project, public: false) }
  let(:project2) { build(:project, public: false) }
  let(:public_project) { build(:project, public: true) }
  let(:work_package) { build(:work_package, project:) }
  let(:role) { build(:project_role) }
  let(:role2) { build(:project_role) }
  let(:wp_role) { build(:work_package_role) }
  let(:anonymous_role) { build(:anonymous_role) }
  let(:non_member) { build(:non_member) }
  let(:member) { build(:member, project:, roles: [role], principal: user) }
  let(:member2) { build(:member, project: project2, roles: [role2], principal: user) }
  let(:wp_member) { build(:member, project:, roles: [wp_role], principal: user, entity: work_package) }

  describe '.query' do
    before do
      non_member.save!
      anonymous_role.save!
      user.save!
    end

    it 'is a user relation' do
      expect(described_class.query(user, project)).to be_a ActiveRecord::Relation
    end

    context 'with the user being a member in the project' do
      before do
        member.save!
      end

      it 'is the project roles' do
        expect(described_class.query(user, project)).to match [role]
      end
    end

    context 'without the user being member in the project' do
      context 'with the project being private' do
        it 'is empty' do
          expect(described_class.query(user, project)).to be_empty
        end
      end

      context 'with the project being public' do
        it 'is the non member role' do
          expect(described_class.query(user, public_project)).to contain_exactly(non_member)
        end
      end
    end

    context 'with the user being anonymous' do
      context 'with the project being public' do
        it 'is empty' do
          expect(described_class.query(anonymous, public_project)).to contain_exactly(anonymous_role)
        end
      end

      context 'without the project being public' do
        it 'is empty' do
          expect(described_class.query(anonymous, project)).to be_empty
        end
      end
    end

    context 'with the user being a member in two projects' do
      before do
        member.save!
        member2.save!
      end

      it 'returns only the roles from the requested project' do
        expect(described_class.query(user, project)).to contain_exactly(role)
      end
    end

    context 'with the user being a member of a work package of the project' do
      before { wp_member.save! }

      context 'and not being a member of the project itself' do
        it { expect(described_class.query(user, project)).to be_empty }
      end

      context 'and being a member of the project as well' do
        before { member.save! }

        it { expect(described_class.query(user, project)).to contain_exactly(role) }
      end
    end
  end
end


Instruction:
Write RSpec test file for following ruby class
```ruby
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2023 the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++

class Authorization::UserAllowedQuery < Authorization::AbstractUserQuery
  self.model = User

  transformations.register :all,
                           :member_roles_join do |statement|
    statement.outer_join(member_roles_table)
             .on(members_member_roles_join)
  end

  transformations.register :all,
                           :where_projection do |statement, action, project|
    statement = statement.group(users_table[:id])

    # No action allowed on archived projects
    # No action allowed on disabled modules
    if project.active? && project.allows_to?(action)
      has_role = roles_table[:id].not_eq(nil)
      has_permission = role_permissions_table[:id].not_eq(nil)

      has_role_and_permission = if OpenProject::AccessControl.permission(action).public?
                                  has_role
                                else
                                  has_role.and(has_permission)
                                end

      is_admin = if OpenProject::AccessControl.grant_to_admin?(action)
                   users_table[:admin].eq(true)
                 else
                   Arel::Nodes::Equality.new(1, 0)
                 end

      statement.where(has_role_and_permission.or(is_admin))
    else
      statement.where(Arel::Nodes::Equality.new(1, 0))
    end
  end

  transformations.register users_members_join,
                           :project_id_limit do |statement, _, project|
    statement.and(members_table[:project_id].eq(project.id))
  end

  transformations.register :all,
                           :roles_join,
                           after: [:member_roles_join] do |statement, _, project|
    statement.outer_join(roles_table)
             .on(roles_member_roles_join(project))
  end

  transformations.register :all,
                           :role_permissions_join,
                           after: [:roles_join] do |statement, action|
    if OpenProject::AccessControl.permission(action).public?
      statement
    else
      statement.outer_join(role_permissions_table)
               .on(roles_table[:id]
                   .eq(role_permissions_table[:role_id])
                   .and(role_permissions_table[:permission].eq(action.to_s)))
    end
  end

  def self.roles_member_roles_join(project)
    id_equal = roles_table[:id].eq(member_roles_table[:role_id])

    if project.public?
      member_or_public_project_condition(id_equal)
    else
      id_equal
    end
  end

  def self.no_membership_and_non_member_role_condition
    roles_table
      .grouping(member_roles_table[:role_id]
                .eq(nil)
                .and(roles_table[:builtin].eq(Role::BUILTIN_NON_MEMBER)))
  end

  def self.anonymous_user_condition
    users_table[:type]
      .eq('AnonymousUser')
      .and(roles_table[:builtin].eq(Role::BUILTIN_ANONYMOUS))
  end

  def self.member_or_public_project_condition(id_equal)
    roles_table
      .grouping(users_table[:type]
                .eq('User')
                .and(id_equal.or(no_membership_and_non_member_role_condition)))
      .or(anonymous_user_condition)
  end
end

```

Response:
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2023 the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++

require 'spec_helper'

RSpec.describe Authorization::UserAllowedQuery do
  describe '.query' do
    let(:user) { member.principal }
    let(:anonymous) { build(:anonymous) }
    let(:project) { build(:project, public: false) }
    let(:project2) { build(:project, public: false) }
    let(:role) { build(:project_role) }
    let(:role2) { build(:project_role) }
    let(:anonymous_role) { build(:anonymous_role) }
    let(:non_member_role) { build(:non_member) }
    let(:member) do
      build(:member, project:,
                     roles: [role])
    end

    let(:action) { :view_work_packages }
    let(:other_action) { :another }
    let(:public_action) { :view_project }

    before do
      user.save!
      anonymous.save!
      anonymous_role.save!
      non_member_role.save!
    end

    it 'is an AR relation' do
      expect(described_class.query(action, project)).to be_a ActiveRecord::Relation
    end

    context 'w/o the project being public
             w/ the user being member in the project
             w/ the role having the necessary permission' do
      before do
        role.add_permission! action
        role.save!

        member.save!
      end

      it 'returns the user' do
        expect(described_class.query(action, project)).to match_array [user]
      end
    end

    context 'w/o the project being public
             w/o the user being member in the project
             w/ the user being admin' do
      before do
        user.update_attribute(:admin, true)
      end

      it 'returns the user' do
        expect(described_class.query(action, project)).to match_array [user]
      end
    end

    context 'w/o the project being public
             w/ the user being member in the project
             w/o the role having the necessary permission' do
      before do
        role.save!
        member.save!
      end

      it 'is empty' do
        expect(described_class.query(action, project)).to be_empty
      end
    end

    context 'w/o the project being public
             w/o the user being member in the project
             w/ the role having the necessary permission' do
      before do
        role.add_permission! action
        role.save!
      end

      it 'returns the user' do
        expect(described_class.query(action, project)).to be_empty
      end
    end

    context 'w/o the project being public
             w/o the user being member in the project
             w/ the user being member in a different project
             w/ the role having the permission' do
      before do
        role.add_permission! action
        role.save!

        member.project = project2
        member.save!
      end

      it 'is empty' do
        expect(described_class.query(action, project)).to be_empty
      end
    end

    context 'w/ the project being public
             w/o the user being member in the project
             w/ the user being member in a different project
             w/ the role having the permission' do
      before do
        role.add_permission! action
        role.save!

        project.public = true
        project.save!

        member.project = project2
        member.save!
      end

      it 'is empty' do
        expect(described_class.query(action, project)).to be_empty
      end
    end

    context 'w/ the project being public
             w/o the user being member in the project
             w/ the non member role having the necessary permission' do
      before do
        project.public = true

        non_member = ProjectRole.non_member
        non_member.add_permission! action
        non_member.save

        project.save!
      end

      it 'returns the user' do
        expect(described_class.query(action, project)).to match_array [user]
      end
    end

    context 'w/ the project being public
             w/o the user being member in the project
             w/ the anonymous role having the necessary permission' do
      before do
        project.public = true

        anonymous_role = ProjectRole.anonymous
        anonymous_role.add_permission! action
        anonymous_role.save

        project.save!
      end

      it 'returns the anonymous user' do
        expect(described_class.query(action, project)).to match_array([anonymous])
      end
    end

    context 'w/ the project being public
             w/o the user being member in the project
             w/ the non member role having another permission' do
      before do
        project.public = true

        non_member = ProjectRole.non_member
        non_member.add_permission! other_action
        non_member.save

        project.save!
      end

      it 'is empty' do
        expect(described_class.query(action, project)).to be_empty
      end
    end

    context 'w/ the project being private
             w/o the user being member in the project
             w/ the non member role having the permission' do
      before do
        non_member = ProjectRole.non_member
        non_member.add_permission! action
        non_member.save

        project.save!
      end

      it 'is empty' do
        expect(described_class.query(action, project)).to be_empty
      end
    end

    context 'w/ the project being public
             w/ the user being member in the project
             w/o the role having the necessary permission
             w/ the non member role having the permission' do
      before do
        project.public = true
        project.save

        role.add_permission! other_action
        member.save!

        non_member = ProjectRole.non_member
        non_member.add_permission! action
        non_member.save
      end

      it 'is empty' do
        expect(described_class.query(action, project)).to be_empty
      end
    end

    context 'w/o the project being public
             w/ the user being member in the project
             w/o the role having the permission
             w/ the permission being public' do
      before do
        member.save!
      end

      it 'returns the user' do
        expect(described_class.query(public_action, project)).to match_array [user]
      end
    end

    context 'w/ the project being public
             w/o the user being member in the project
             w/o the role having the permission
             w/ the permission being public' do
      before do
        project.public = true
        project.save
      end

      it 'returns the user and anonymous' do
        expect(described_class.query(public_action, project)).to match_array [user, anonymous]
      end
    end

    context 'w/ the user being member in the project
             w/ asking for a certain permission
             w/ the permission belonging to a module
             w/o the module being active' do
      let(:permission) do
        OpenProject::AccessControl.permissions.find { |p| p.project_module.present? }
      end

      before do
        project.enabled_module_names = []

        role.add_permission! permission.name
        member.save!
      end

      it 'is empty' do
        expect(described_class.query(permission.name, project)).to eq []
      end
    end

    context 'w/ the user being member in the project
             w/ asking for a certain permission
             w/ the permission belonging to a module
             w/ the module being active' do
      let(:permission) do
        OpenProject::AccessControl.permissions.find { |p| p.project_module.present? }
      end

      before do
        project.enabled_module_names = [permission.project_module]

        role.add_permission! permission.name
        member.save!
      end

      it 'returns the user' do
        expect(described_class.query(permission.name, project)).to eq [user]
      end
    end

    context 'w/ the user being member in the project
             w/ asking for a certain permission
             w/ the user having the permission in the project
             w/o the project being active' do
      before do
        role.add_permission! action
        member.save!

        project.update(active: false)
      end

      it 'is empty' do
        expect(described_class.query(action, project)).to eq []
      end
    end
  end
end


Instruction:
Write RSpec test file for following ruby class
```ruby
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2023 the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++

class Authorization::QueryTransformations
  def for?(on)
    !!transformations[transformation_key(on)]
  end

  def for(on)
    transformations[transformation_key(on)]
  end

  def register(on,
               name,
               after: [],
               before: [],
               &block)

    transformation = ::Authorization::QueryTransformation.new(on, name, after, before, block)

    add_transformation(transformation)
    sort_transformations(on)
  end

  def copy
    the_new = self.class.new

    the_new.transformations = transformations.deep_dup
    the_new.transformation_order = transformation_order.deep_dup

    the_new
  end

  protected

  attr_accessor :transformations,
                :transformation_order

  private

  def transformation_key(on)
    if on.respond_to?(:to_sql)
      on.to_sql
    else
      on
    end
  end

  def transformations
    @transformations ||= {}
  end

  def transformation_order
    @transformation_order ||= ::Authorization::QueryTransformationsOrder.new
  end

  def add_transformation(transformation)
    transformations[transformation_key(transformation.on)] ||= []

    transformations[transformation_key(transformation.on)] << transformation

    transformation_order << transformation
  end

  def sort_transformations(on)
    desired_order = transformation_order.full_order

    transformations[transformation_key(on)].sort_by! { |x| desired_order.index x.name }
  end
end

```

Response:
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2023 the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
#
# See COPYRIGHT and LICENSE files for more details.
#++

require 'spec_helper'

RSpec.describe Authorization::QueryTransformations do
  let(:instance) { described_class.new }

  context 'registering a transformation' do
    before do
      instance.register(:on, :name) do |*args|
        args
      end
    end

    describe '#for?' do
      it 'is true for the registered name' do
        expect(instance.for?(:on)).to be_truthy
      end

      it 'is false for another name' do
        expect(instance.for?(:other_name)).to be_falsey
      end
    end

    describe '#for' do
      it 'returns an array of transformations for the registered name' do
        expect(instance.for(:on).length).to be 1

        expect(instance.for(:on)[0].on).to be :on
        expect(instance.for(:on)[0].name).to be :name
        expect(instance.for(:on)[0].block.call(1, 2, 3)).to match_array [1, 2, 3]
      end

      it 'is nil for another name' do
        expect(instance.for(:other_name)).to be_nil
      end
    end
  end

  context 'registering two transformations depending via after' do
    before do
      instance.register(:on, :transformation1, after: [:transformation2]) do |*args|
        args
      end

      instance.register(:on, :transformation2) do |*args|
        args.join(', ')
      end
    end

    describe '#for?' do
      it 'is true for the registered name' do
        expect(instance.for?(:on)).to be_truthy
      end

      it 'is false for another name' do
        expect(instance.for?(:other_name)).to be_falsey
      end
    end

    describe '#for' do
      it 'returns an array of transformations for the registered name' do
        expect(instance.for(:on).length).to be 2

        expect(instance.for(:on)[0].on).to be :on
        expect(instance.for(:on)[0].name).to be :transformation2
        expect(instance.for(:on)[0].block.call(1, 2, 3)).to eql '1, 2, 3'

        expect(instance.for(:on)[1].on).to be :on
        expect(instance.for(:on)[1].name).to be :transformation1
        expect(instance.for(:on)[1].block.call(1, 2, 3)).to match_array [1, 2, 3]
      end
    end
  end

  context 'registering two transformations depending via before' do
    before do
      instance.register(:on, :transformation1) do |*args|
        args
      end

      instance.register(:on, :transformation2, before: [:transformation1]) do |*args|
        args.join(', ')
      end
    end

    describe '#for?' do
      it 'is true for the registered name' do
        expect(instance.for?(:on)).to be_truthy
      end

      it 'is false for another name' do
        expect(instance.for?(:other_name)).to be_falsey
      end
    end

    describe '#for' do
      it 'returns an array of transformations for the registered name' do
        expect(instance.for(:on).length).to be 2

        expect(instance.for(:on)[0].on).to be :on
        expect(instance.for(:on)[0].name).to be :transformation2
        expect(instance.for(:on)[0].block.call(1, 2, 3)).to eql '1, 2, 3'

        expect(instance.for(:on)[1].on).to be :on
        expect(instance.for(:on)[1].name).to be :transformation1
        expect(instance.for(:on)[1].block.call(1, 2, 3)).to match_array [1, 2, 3]
      end
    end
  end

  context 'registering two mutually dependent transformations' do
    it 'fails' do
      instance.register(:on, :transformation1, before: [:transformation2]) do |*args|
        args
      end

      expected_order = %i[transformation1 transformation2]

      expect do
        instance.register(:on, :transformation2, before: [:transformation1]) do |*args|
          args.join(', ')
        end
      end.to raise_error "Cannot sort #{expected_order} into the list of transformations"
    end
  end
end