Instruction:
Write RSpec test file for following ruby class
```ruby
module Authorization
  class UserPermissibleService
    attr_accessor :user

    def initialize(user)
      @user = user
    end

    def allowed_globally?(permission)
      perms = contextual_permissions(permission, :global)
      return false unless authorizable_user?
      return true if admin_and_all_granted_to_admin?(perms)

      cached_permissions(nil).intersect?(perms.map(&:name))
    end

    def allowed_in_project?(permission, projects_to_check)
      perms = contextual_permissions(permission, :project)
      return false if projects_to_check.blank?
      return false unless authorizable_user?

      projects = Array(projects_to_check)

      projects.all? do |project|
        allowed_in_single_project?(perms, project)
      end
    end

    def allowed_in_any_project?(permission)
      perms = contextual_permissions(permission, :project)
      return false unless authorizable_user?
      return true if admin_and_all_granted_to_admin?(perms)

      Project.allowed_to(user, perms).exists?
    end

    def allowed_in_entity?(permission, entities_to_check, entity_class)
      return false if entities_to_check.blank?
      return false unless authorizable_user?

      perms = contextual_permissions(permission, context_name(entity_class))

      entities = Array(entities_to_check)

      entities.all? do |entity|
        allowed_in_single_entity?(perms, entity)
      end
    end

    def allowed_in_any_entity?(permission, entity_class, in_project: nil)
      perms = contextual_permissions(permission, context_name(entity_class))
      return false unless authorizable_user?
      return true if admin_and_all_granted_to_admin?(perms)

      # entity_class.allowed_to will also check whether the user has the permission via a membership in the project.
      # ^-- still a problem in some cases
      allowed_scope = entity_class.allowed_to(user, perms)

      if in_project
        allowed_in_single_project?(perms, in_project) || allowed_scope.exists?(project: in_project)
      else
        allowed_in_any_project?(perms) || allowed_scope.exists?
      end
    end

    private

    def cached_permissions(context)
      @cached_permissions ||= Hash.new do |hash, context_key|
        hash[context_key] = user.all_permissions_for(context_key)
      end

      @cached_permissions[context]
    end

    def allowed_in_single_project?(permissions, project)
      return false if project.nil?
      return false unless project.active? || project.being_archived?

      permissions_filtered_for_project = permissions_by_enabled_project_modules(project, permissions)

      return false if permissions_filtered_for_project.empty?
      return true if admin_and_all_granted_to_admin?(permissions)

      cached_permissions(project).intersect?(permissions_filtered_for_project)
    end

    def allowed_in_single_entity?(permissions, entity)
      return false if entity.nil?
      return false if entity.project.nil?
      return false unless entity.project.active? || entity.project.being_archived?

      permissions_filtered_for_project = permissions_by_enabled_project_modules(entity.project, permissions)

      return false if permissions_filtered_for_project.empty?
      return true if admin_and_all_granted_to_admin?(permissions)

      # The combination of this is better then doing
      # EntityClass.allowed_to(user, permission).exists?.
      # Because this way, all permissions for that context are fetched and cached.
      allowed_in_single_project?(permissions, entity.project) ||
        cached_permissions(entity).intersect?(permissions_filtered_for_project)
    end

    def admin_and_all_granted_to_admin?(permissions)
      user.admin? && permissions.all?(&:grant_to_admin?)
    end

    def authorizable_user?
      !user.locked? || user.is_a?(SystemUser)
    end

    def permissions_by_enabled_project_modules(project, permissions)
      project
        .allowed_permissions
        .intersection(permissions.map(&:name))
        .map { |perm| perm.name.to_sym }
    end

    def contextual_permissions(permission, context)
      Authorization.contextual_permissions(permission, context, raise_on_unknown: true)
    end

    def context_name(entity_class)
      entity_class.model_name.element.to_sym
    end
  end
end

```

Response:
require 'rails_helper'

RSpec.describe Authorization::UserPermissibleService do
  shared_let(:user) { create(:user) }
  shared_let(:anonymous_user) { create(:anonymous) }
  shared_let(:project) { create(:project) }
  shared_let(:work_package) { create(:work_package, project:) }
  shared_let(:non_member_role) { create(:non_member, permissions: [:view_news]) }
  shared_let(:anonymous_role) { create(:anonymous_role, permissions: [:view_meetings]) }

  let(:queried_user) { user }

  subject(:service) { described_class.new(queried_user) }

  # The specs in this file do not cover all the various cases yet. Thus,
  # we rely on the Authorization.roles scope and the Project/WorkPackage.allowed_to scopes
  # to be called which is speced precisely.
  shared_examples_for 'the Authorization.roles scope used' do
    before do
      allow(Authorization)
        .to receive(:roles)
              .and_call_original
    end

    it 'calls the Authorization.roles scope once (cached for the second request)' do
      subject
      subject

      expect(Authorization)
        .to have_received(:roles)
              .once
              .with(queried_user, context)
    end
  end

  shared_examples_for 'the Project.allowed_to scope used' do
    before do
      allow(Project)
        .to receive(:allowed_to)
              .and_call_original
    end

    it 'calls the Project.allowed_to scope' do
      subject

      expect(Project)
        .to have_received(:allowed_to) do |user, perm|
        expect(user).to eq(queried_user)
        expect(perm[0]).to be_a(OpenProject::AccessControl::Permission)
        expect(perm[0].name).to eq permission
      end
    end
  end

  shared_examples_for 'the WorkPackage.allowed_to scope used' do
    before do
      allow(WorkPackage)
        .to receive(:allowed_to)
              .and_call_original
    end

    it 'calls the WorkPackage.allowed_to scope' do
      subject

      expect(WorkPackage)
        .to have_received(:allowed_to) do |user, perm|
        expect(user).to eq(queried_user)
        expect(perm[0]).to be_a(OpenProject::AccessControl::Permission)
        expect(perm[0].name).to eq permission
      end
    end
  end

  describe '#allowed_globally?' do
    context 'when asking for a permission that is not defined' do
      let(:permission) { :not_defined }

      it 'raises an error' do
        expect { subject.allowed_globally?(permission) }.to raise_error(Authorization::UnknownPermissionError)
      end
    end

    context 'when asking for a permission that is defined' do
      let(:permission) { :create_user }

      context 'and the user is a regular user' do
        context 'without a role granting the permission' do
          it { is_expected.not_to be_allowed_globally(permission) }
        end

        context 'with a role granting the permission' do
          let(:global_role) { create(:global_role, permissions: [permission]) }
          let!(:member) { create(:global_member, user:, roles: [global_role]) }

          it { is_expected.to be_allowed_globally(permission) }

          context 'and the account is locked' do
            before { user.locked! }

            it { is_expected.not_to be_allowed_globally(permission) }
          end
        end
      end

      context 'and the user is an admin' do
        let(:user) { create(:admin) }

        it { is_expected.to be_allowed_globally(permission) }

        context 'and the account is locked' do
          before { user.locked! }

          it { is_expected.not_to be_allowed_globally(permission) }
        end
      end

      it_behaves_like 'the Authorization.roles scope used' do
        let(:context) { nil }
        subject { service.allowed_globally?(permission) }
      end
    end
  end

  describe '#allowed_in_project?' do
    context 'when asking for a permission that is not defined' do
      let(:permission) { :not_defined }

      it 'raises an error' do
        expect { subject.allowed_in_project?(permission, project) }.to raise_error(Authorization::UnknownPermissionError)
      end
    end

    context 'when asking for a permission that is defined' do
      let(:permission) { :view_work_packages }

      it_behaves_like 'the Authorization.roles scope used' do
        let(:context) { project }
        subject { service.allowed_in_project?(permission, project) }
      end

      context 'and the user is not a member of any work package or project' do
        it { is_expected.not_to be_allowed_in_project(permission, project) }

        context 'and the project is public' do
          before { project.update(public: true) }

          context 'when requesting a permission that is not granted to the non-member role' do
            it { is_expected.not_to be_allowed_in_project(permission, project) }
          end

          context 'when requesting a permission that is granted to the non-member role' do
            let(:permission) { :view_news }

            it { is_expected.to be_allowed_in_project(permission, project) }
          end

          context 'when an anonymous user is requesting a permission that is granted to the anonymous role' do
            let(:queried_user) { anonymous_user }
            let(:permission) { :view_meetings }

            it { is_expected.to be_allowed_in_project(permission, project) }
          end
        end
      end

      context 'and the user is a member of a project' do
        let(:role) { create(:project_role, permissions: [permission]) }
        let!(:project_member) { create(:member, user:, project:, roles: [role]) }

        it { is_expected.to be_allowed_in_project(permission, project) }

        context 'with the project being archived' do
          before { project.update(active: false) }

          it { is_expected.not_to be_allowed_in_project(permission, project) }
        end
      end

      context 'and the user is a member of a work package' do
        let(:role) { create(:work_package_role, permissions: [permission]) }
        let!(:wp_member) { create(:work_package_member, user:, project:, entity: work_package, roles: [role]) }

        it { is_expected.not_to be_allowed_in_project(permission, project) }
      end
    end
  end

  describe '#allowed_in_any_project?' do
    context 'when asking for a permission that is not defined' do
      let(:permission) { :not_defined }

      it 'raises an error' do
        expect { subject.allowed_in_any_project?(permission) }.to raise_error(Authorization::UnknownPermissionError)
      end
    end

    context 'when asking for a permission that is defined' do
      let(:permission) { :view_work_packages }

      context 'and the user is not a member of any work package or project' do
        it { is_expected.not_to be_allowed_in_any_project(permission) }

        context 'and the project is public' do
          before { project.update_column(:public, true) }

          context 'and a permission is requested that is not granted to the non-member role' do
            it { is_expected.not_to be_allowed_in_any_project(permission) }
          end

          context 'and a permission is requested that is granted to the non-member role' do
            let(:permission) { :view_news }

            it { is_expected.to be_allowed_in_any_project(permission) }
          end

          context 'and the user is the anonymous user' do
            let(:queried_user) { anonymous_user }
            let(:permission) { :view_meetings }

            it { is_expected.to be_allowed_in_any_project(permission) }
          end

          context 'and the project is archived' do
            before { project.update_column(:active, false) }

            it { is_expected.not_to be_allowed_in_any_project(permission) }
          end
        end
      end

      context 'and the user is a member of a project' do
        let(:role) { create(:project_role, permissions: [permission]) }
        let!(:project_member) { create(:member, user:, project:, roles: [role]) }

        it { is_expected.to be_allowed_in_any_project(permission) }

        context 'and the project is archived' do
          before { project.update_column(:active, false) }

          it { is_expected.not_to be_allowed_in_any_project(permission) }
        end
      end

      context 'and the user is a member of a work package' do
        let(:role) { create(:work_package_role, permissions: [permission]) }
        let!(:wp_member) { create(:work_package_member, user:, project:, entity: work_package, roles: [role]) }

        it { is_expected.not_to be_allowed_in_any_project(permission) }

        context 'and the project is public' do
          before { project.update_column(:public, true) }

          context 'and a permission is requested that is not granted to the non-member role' do
            it { is_expected.not_to be_allowed_in_any_project(permission) }
          end

          context 'and a permission is requested that is granted to the non-member role' do
            let(:permission) { :view_news }

            it { is_expected.to be_allowed_in_any_project(permission) }
          end

          context 'and the project is archived' do
            before { project.update_column(:active, false) }

            it { is_expected.not_to be_allowed_in_any_project(permission) }
          end
        end
      end

      it_behaves_like 'the Project.allowed_to scope used' do
        subject { service.allowed_in_any_project?(permission) }
      end
    end
  end

  describe '#allowed_in_entity?' do
    context 'when asking for a permission that is not defined' do
      let(:permission) { :not_defined }

      it 'raises an error' do
        expect do
          subject.allowed_in_entity?(permission, work_package, WorkPackage)
        end.to raise_error(Authorization::UnknownPermissionError)
      end
    end

    context 'when asking for a permission that is defined' do
      let(:permission) { :view_work_packages }

      context 'and the user is not a member of the project or the work package' do
        it { is_expected.not_to be_allowed_in_entity(permission, work_package, WorkPackage) }
      end

      context 'and the user is a member of the project' do
        let(:role) { create(:project_role, permissions: [permission]) }
        let!(:project_member) { create(:member, user:, project:, roles: [role]) }

        it { is_expected.to be_allowed_in_entity(permission, work_package, WorkPackage) }

        context 'with the project being archived' do
          before { project.update(active: false) }

          it { is_expected.not_to be_allowed_in_entity(permission, work_package, WorkPackage) }
        end

        context 'without the module enabled in the project' do
          before { project.enabled_module_names = project.enabled_modules - [:work_package_tracking] }

          it { is_expected.not_to be_allowed_in_entity(permission, work_package, WorkPackage) }
        end

        it_behaves_like 'the Authorization.roles scope used' do
          let(:context) { project }
          subject { service.allowed_in_entity?(permission, work_package, WorkPackage) }
        end
      end

      context 'and the user is a member of the work package' do
        let(:role) { create(:work_package_role, permissions: [permission]) }
        let!(:wp_member) { create(:work_package_member, user:, project:, entity: work_package, roles: [role]) }

        it { is_expected.to be_allowed_in_entity(permission, work_package, WorkPackage) }

        context 'with the project being archived' do
          before { project.update(active: false) }

          it { is_expected.not_to be_allowed_in_entity(permission, work_package, WorkPackage) }
        end

        it_behaves_like 'the Authorization.roles scope used' do
          let(:context) { work_package }
          subject { service.allowed_in_entity?(permission, work_package, WorkPackage) }
        end
      end

      context 'and user is member in the project (not granting the permission) and the work package (granting the permission)' do
        let(:permission) { :edit_work_packages }

        let(:role) { create(:project_role, permissions: [:view_work_packages]) }
        let!(:project_member) { create(:member, user:, project:, roles: [role]) }

        let(:wp_role) { create(:work_package_role, permissions: [permission]) }
        let!(:wp_member) { create(:work_package_member, user:, project:, entity: work_package, roles: [wp_role]) }

        it { is_expected.to be_allowed_in_entity(permission, work_package, WorkPackage) }

        it_behaves_like 'the Authorization.roles scope used' do
          let(:context) { work_package }
          subject { service.allowed_in_entity?(permission, work_package, WorkPackage) }
        end
      end
    end
  end

  describe '#allowed_in_any_entity?' do
    context 'when asking for a permission that is not defined' do
      let(:permission) { :not_defined }

      it 'raises an error' do
        expect { subject.allowed_in_any_entity?(permission, WorkPackage) }.to raise_error(Authorization::UnknownPermissionError)
      end
    end

    context 'when asking for a permission that is defined' do
      let(:permission) { :view_work_packages }

      context 'and the user is not a member of any work package or project' do
        it { is_expected.not_to be_allowed_in_any_entity(permission, WorkPackage) }
      end

      context 'and the user is a member of a project' do
        let(:role) { create(:project_role, permissions: [permission]) }
        let!(:project_member) { create(:member, user:, project:, roles: [role]) }

        it { is_expected.to be_allowed_in_any_entity(permission, WorkPackage) }

        context 'when specifying the same project' do
          it { is_expected.to be_allowed_in_any_entity(permission, WorkPackage, in_project: project) }
        end

        context 'when specifying a different project' do
          let(:other_project) { create(:project) }

          it { is_expected.not_to be_allowed_in_any_entity(permission, WorkPackage, in_project: other_project) }
        end
      end

      context 'and the user is a member of a work package' do
        let(:role) { create(:work_package_role, permissions: [permission]) }
        let!(:wp_member) { create(:work_package_member, user:, project:, entity: work_package, roles: [role]) }

        it { is_expected.to be_allowed_in_any_entity(permission, WorkPackage) }
      end

      it_behaves_like 'the WorkPackage.allowed_to scope used' do
        subject { service.allowed_in_any_entity?(permission, WorkPackage) }
      end

      it_behaves_like 'the WorkPackage.allowed_to scope used' do
        subject { service.allowed_in_any_entity?(permission, WorkPackage, in_project: project) }
      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::UserEntityRolesQuery < Authorization::UserRolesQuery
  transformations.register :all, :user_restriction do |statement, user, _|
    statement.where(users_table[:id].eq(user.id))
  end

  transformations.register users_members_join, :entity_restriction do |statement, _, entity|
    statement = statement
      .and(members_table[:entity_type].eq(entity.class.to_s))
      .and(members_table[:entity_id].eq(entity.id))

    statement = statement.and(members_table[:project_id].eq(entity.project_id)) if entity.respond_to?(:project_id)

    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::UserEntityRolesQuery do
  let(:user) { build(:user) }
  let(:project) { build(:project, public: false) }
  let(:work_package) { build(:work_package, project:) }
  let(:work_package2) { build(:work_package, project:) }
  let(:role) { build(:project_role) }
  let(:wp_role) { build(:work_package_role) }
  let(:other_wp_role) { build(:work_package_role) }
  let(:non_member) { build(:non_member) }
  let(:member) { build(:member, project:, roles: [wp_role], principal: user, entity: work_package) }
  let(:project_member) { build(:member, project:, roles: [role], principal: user) }
  let(:other_member) { build(:member, project:, roles: [other_wp_role], principal: user, entity: work_package2) }

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

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

    context 'with the user being a member of the work package' do
      before do
        member.save!
      end

      it 'returns the work package role' do
        expect(described_class.query(user, work_package)).to contain_exactly(wp_role)
      end

      context 'when the user also has a membership with a different role on another work package' do
        before do
          other_member.save!
        end

        it 'does not include the second role' do
          expect(described_class.query(user, work_package)).not_to include(other_wp_role)
        end
      end

      context 'when the user also has a membership with a different role on the project' do
        before do
          project_member.save!
        end

        it 'does not include the second role' do
          expect(described_class.query(user, work_package)).not_to include(role)
        end
      end
    end

    context 'without the user being member in the work package' do
      it 'is empty' do
        expect(described_class.query(user, work_package)).to be_empty
      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::UserAllowedService
  attr_accessor :user

  def initialize(user, role_cache: Users::ProjectRoleCache.new(user))
    self.user = user
    self.project_role_cache = role_cache
  end

  # Return true if the user is allowed to do the specified action on a specific context
  # Action can be:
  # * a parameter-like Hash (eg. { controller: '/projects', action: 'edit' })
  # * a permission Symbol (eg. :edit_project)
  # Context can be:
  # * a project : returns true if user is allowed to do the specified action on this project
  # * a group of projects : returns true if user is allowed on every project
  # * an entity that a user can become a member of specifically (listed in Member::ALLOWED_ENTITIES) :
  #   * returns true if user is allowed to do the specified action on the given item or
  #   * returns ture if user is allowed to do the specified action on the project the entity belongs to
  # * nil with +global+ set to +true+ : check if user has at least one role allowed for this action,
  #   or falls back to Non Member / Anonymous permissions depending if the user is logged
  def call(action, context, global: false)
    if supported_context?(context, global:)
      allowed_to?(action, context, global:)
    else
      false
    end
  end

  def preload_projects_allowed_to(action)
    project_authorization_cache.cache(action)
  end

  private

  attr_accessor :project_role_cache

  def allowed_to?(action, context, global: false)
    action = normalize_action(action)

    if context.nil? && global
      allowed_to_globally?(action)
    elsif context.is_a? Project
      allowed_to_in_project?(action, context)
    elsif supported_entity?(context)
      allowed_to_in_entity?(action, context)
    elsif context.respond_to?(:to_a)
      allowed_to_in_all_projects?(action, context)
    else
      false
    end
  end

  def allowed_to_in_entity?(action, entity)
    # Inactive users are never authorized
    return false unless authorizable_user?

    # Short circuit: When the user is already allowed to execute the action baed
    # on the project, there's no need to do a check on the entity
    return true if entity.respond_to?(:project) && allowed_to_in_project?(action, entity.project)

    # Admin users are authorized for anything else
    # unless the permission is explicitly flagged not to be granted to admins.
    return true if granted_to_admin?(action)

    has_authorized_role?(action, entity)
  end

  def allowed_to_in_project?(action, project)
    return false if project.nil?

    if project_authorization_cache.cached?(action)
      return project_authorization_cache.allowed?(action, project)
    end

    # No action allowed on archived projects
    return false unless project.active? || project.being_archived?
    # No action allowed on disabled modules
    return false unless project.allows_to?(action)
    # Inactive users are never authorized
    return false unless authorizable_user?
    # Admin users are authorized for anything else
    # unless the permission is explicitly flagged not to be granted to admins.
    return true if granted_to_admin?(action)

    has_authorized_role?(action, project)
  end

  # Authorize if user is authorized on every element of the array
  def allowed_to_in_all_projects?(action, projects)
    projects.present? && Array(projects).all? do |project|
      allowed_to?(action, project)
    end
  end

  # Is the user allowed to do the specified action on any project?
  # See allowed_to? for the action parameter description.
  def allowed_to_globally?(action)
    # Inactive users are never authorized
    return false unless authorizable_user?
    # Admin users are always authorized
    return true if granted_to_admin?(action)

    has_authorized_role?(action)
  end

  ##
  # Only users that are not locked may be granted actions
  # with the exception of a temporary-granted system user
  def authorizable_user?
    !user.locked? || user.is_a?(SystemUser)
  end

  # Admin users are granted every permission unless the
  # permission explicitly disables it.
  def granted_to_admin?(action)
    user.admin? && OpenProject::AccessControl.grant_to_admin?(action)
  end

  def has_authorized_role?(action, context = nil)
    project_role_cache
      .fetch(context)
      .any? do |role|
      role.allowed_to?(action)
    end
  end

  def project_authorization_cache
    @project_authorization_cache ||= Users::ProjectAuthorizationCache.new(user)
  end

  def normalize_action(action)
    if action.is_a?(Hash) && action[:controller] && action[:controller].to_s.starts_with?('/')
      action = action.dup
      action[:controller] = action[:controller][1..]
    end

    action
  end

  def supported_context?(context, global:)
    (context.nil? && global) ||
      context.is_a?(Project) ||
      supported_entity?(context) ||
      (!context.nil? && context.respond_to?(:to_a))
  end

  def supported_entity?(entity)
    Member.can_be_member_of?(entity)
  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'

# TODO: Fix tests here

RSpec.describe Authorization::UserAllowedService do
  let(:user) { build_stubbed(:user) }
  let(:instance) { described_class.new(user) }
  let(:action) { :an_action }
  let(:action_hash) { { controller: '/controller', action: 'action' } }
  let(:project) { build_stubbed(:project) }
  let(:other_project) { build_stubbed(:project) }
  let(:role) { build_stubbed(:project_role) }
  let(:user_roles_in_project) do
    array = [role]
    allow(array)
      .to receive(:eager_load)
      .and_return(array)

    array
  end
  let(:role_grants_action) { true }
  let(:project_allows_to) { true }

  subject { instance.call(action, context) }

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

  shared_examples_for 'allowed to checked' do
    before do
      Array(context).each do |project|
        project.active = true

        allow(project).to receive(:allows_to?).with(action).and_return(project_allows_to)
        allow(Authorization).to receive(:roles).with(user, project).and_return(user_roles_in_project)
      end

      allow(role).to receive(:allowed_to?).with(action).and_return(role_grants_action)
    end

    context 'with the user having a granting role' do
      it 'is true' do
        expect(subject).to be_truthy
      end

      it 'does not call the db twice for a project' do
        Array(context).each do |project|
          allow(Authorization).to receive(:roles).with(user, project).and_return(user_roles_in_project)
        end

        subject
        subject

        Array(context).each do |project|
          expect(Authorization)
            .to have_received(:roles)
            .once
            .with(user, project)
        end
      end

      context 'but the user not being active' do
        before do
          user.lock
        end

        it 'returns false', :aggregate_failures do
          expect(instance.call(action, nil, global: true)).not_to be_truthy
        end
      end
    end

    context 'with the user having a nongranting role' do
      let(:role_grants_action) { false }

      it 'is false' do
        expect(subject).to be_falsey
      end
    end

    context 'with the user being admin
             with the user not having a granting role' do
      let(:user_roles_in_project) { [] }

      before do
        user.admin = true
      end

      it 'is true' do
        expect(subject).to be_truthy
      end
    end

    context 'with the project not being active' do
      before do
        Array(context).each do |project|
          project.active = false
          project.clear_changes_information
        end
      end

      it 'is false' do
        expect(subject).to be_falsey
      end

      it 'is false even if the user is admin' do
        user.admin = true

        expect(subject).to be_falsey
      end
    end

    context 'with the project being archived' do
      before do
        Array(context).each do |project|
          project.active = false
        end
      end

      it 'is true' do
        expect(subject).to be_truthy
      end
    end

    context 'with the project not having the action enabled' do
      let(:project_allows_to) { false }

      it 'is false' do
        expect(subject).to be_falsey
      end

      it 'is false even if the user is admin' do
        user.admin = true

        expect(subject).to be_falsey
      end
    end

    context 'with having precached the results' do
      before do
        auth_cache = double('auth_cache')

        allow(Users::ProjectAuthorizationCache)
          .to receive(:new)
          .and_return(auth_cache)

        allow(auth_cache)
          .to receive(:cache)
          .with(action)

        allow(auth_cache)
          .to receive(:cached?)
          .with(action)
          .and_return(true)

        Array(context).each do |project|
          allow(auth_cache)
            .to receive(:allowed?)
            .with(action, project)
            .and_return(true)
        end

        instance.preload_projects_allowed_to(action)
      end

      it 'is true' do
        expect(subject).to be_truthy
      end

      it 'does not call the db' do
        subject

        expect(Authorization)
          .not_to have_received(:roles)
      end
    end
  end

  describe '#call' do
    context 'for a project' do
      let(:context) { project }

      it_behaves_like 'allowed to checked'
    end

    context 'for an array of projects' do
      let(:context) { [project, other_project] }

      it_behaves_like 'allowed to checked'

      it 'is false' do
        expect(instance.call(action, [])).to be_falsey
      end

      context 'with one project not allowing an action' do
        before do
          allow(project)
            .to receive(:allows_to?)
            .with(action)
            .and_return(false)
        end

        it 'is false' do
          expect(instance.call(action, [project, other_project])).to be_falsey
        end
      end
    end

    context 'for a relation of projects' do
      let(:context) { double('relation', class: ActiveRecord::Relation, to_a: [project]) }

      it_behaves_like 'allowed to checked'
    end

    context 'for anything else' do
      let(:context) { nil }

      it 'is false' do
        expect(subject).to be_falsey
      end
    end

    context 'for a global check' do
      context 'with the user being admin' do
        before do
          user.admin = true
        end

        it 'is true' do
          expect(instance.call(action, nil, global: true)).to be_truthy
        end
      end

      context 'with the user having a granting role' do
        before do
          allow(Authorization).to receive(:roles).with(user, nil).and_return(user_roles_in_project)
          allow(role).to receive(:allowed_to?).with(action).and_return(true)
        end

        context 'but the user not being active' do
          before do
            user.lock
          end

          it 'is unsuccessful', :aggregate_failures do
            expect(instance.call(action, nil, global: true)).not_to be_truthy
          end
        end

        it 'is successful', :aggregate_failures do
          expect(instance.call(action, nil, global: true)).to be_truthy
        end
      end

      context 'with the user not having a granting role' do
        before do
          allow(Authorization).to receive(:roles).with(user, nil).and_return(user_roles_in_project)
          allow(role).to receive(:allowed_to?).with(action).and_return(false)
        end

        it 'is false' do
          expect(instance.call(action, nil, global: true)).to be_falsey
        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::UserGlobalRolesQuery < Authorization::UserRolesQuery
  transformations.register roles_member_roles_join,
                           :builtin_role do |statement, user|
    builtin_role = if user.logged?
                     Role::BUILTIN_NON_MEMBER
                   else
                     Role::BUILTIN_ANONYMOUS
                   end

    builtin_role_condition = roles_table[:builtin].eq(builtin_role)

    statement.or(builtin_role_condition)
  end

  transformations.register :all, :global_group_where_projection do |statement, user|
    statement.group(roles_table[:id])
             .where(users_table[:id].eq(user.id))
  end

  transformations.register users_members_join, :entity_restriction do |statement, _|
    statement
      .and(members_table[:entity_type].eq(nil))
      .and(members_table[:entity_id].eq(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 Authorization::UserGlobalRolesQuery 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(:role) { build(:project_role) }
  let(:role2) { build(:project_role) }
  let(:anonymous_role) { build(:anonymous_role) }
  let(:non_member) { build(:non_member) }
  let(:member) do
    build(:member, project:,
                   roles: [role],
                   principal: user)
  end
  let(:member2) do
    build(:member, project: project2,
                   roles: [role2],
                   principal: user)
  end
  let(:global_permission) { OpenProject::AccessControl.permissions.find(&:global?) }
  let(:global_role) do
    build(:global_role,
          permissions: [global_permission.name])
  end
  let(:global_member) do
    build(:global_member,
          principal: user,
          roles: [global_role])
  end
  let(:work_package_permission) { [:edit_work_packages] }
  let(:work_package_editor_role) { create(:work_package_role, permissions: work_package_permission) }
  let(:work_package) { create(:work_package, project:) }
  let(:work_package_member) do
    build(:member, entity: work_package, project: work_package.project, roles: [work_package_editor_role], principal: user)
  end

  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 a project' do
      before do
        member.save!
      end

      it 'is the member and non member role' do
        expect(described_class.query(user)).to contain_exactly(role, non_member)
      end
    end

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

      it 'is both member and the non member role' do
        expect(described_class.query(user)).to contain_exactly(role, role2, non_member)
      end
    end

    context 'without the user being a member in a project' do
      it 'is the non member role' do
        expect(described_class.query(user)).to contain_exactly(non_member)
      end
    end

    context 'with the user being anonymous' do
      it 'is the anonymous role' do
        expect(described_class.query(anonymous)).to contain_exactly(anonymous_role)
      end
    end

    context 'with the user having a global role' do
      before do
        global_member.save!
      end

      it 'is the global role and non member role' do
        expect(described_class.query(user)).to contain_exactly(global_role, non_member)
      end
    end

    context 'with the user having a global role and a member role' do
      before do
        member.save!
        global_member.save!
      end

      it 'is the global role, member role and non member role' do
        expect(described_class.query(user)).to contain_exactly(global_role, role, non_member)
      end
    end

    context 'with the user having a global role, a member role and a work package role' do
      before do
        member.save!
        global_member.save!
        work_package_member.save!
      end

      it 'is the global role, member role and non member role' do
        expect(described_class.query(user)).to contain_exactly(global_role, role, non_member)
      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.
#++

module Authentication
  class OmniauthService
    include Contracted

    attr_accessor :auth_hash,
                  :strategy,
                  :controller,
                  :contract,
                  :user_attributes,
                  :identity_url,
                  :user

    delegate :session, to: :controller

    def initialize(strategy:, auth_hash:, controller:)
      self.strategy = strategy
      self.auth_hash = auth_hash
      self.controller = controller
      self.contract = ::Authentication::OmniauthAuthHashContract.new(auth_hash)
    end

    def call(additional_user_params = nil)
      inspect_response(Logger::DEBUG)

      unless contract.validate
        result = ServiceResult.failure(errors: contract.errors)
        Rails.logger.error do
          "[OmniAuth strategy #{strategy.name}] Failed to process omniauth response for #{auth_uid}: #{result.message}"
        end
        inspect_response(Logger::ERROR)

        return result
      end

      # Create or update the user from omniauth
      # and assign non-nil parameters from the registration form - if any
      assignable_params = (additional_user_params || {}).reject { |_, v| v.nil? }
      update_user_from_omniauth!(assignable_params)

      # If we have a new or invited user, we still need to register them
      call = activate_user!

      # Update the admin flag when present successful
      call = update_admin_flag(call) if call.success?

      # The user should be logged in now
      tap_service_result call
    end

    private

    ##
    # Inspect the response object, trying to find out what got returned
    def inspect_response(log_level)
      case strategy
      when ::OmniAuth::Strategies::SAML
        ::OpenProject::AuthSaml::Inspector.inspect_response(auth_hash) do |message|
          Rails.logger.add log_level, message
        end
      else
        Rails.logger.add(log_level) do
          "[OmniAuth strategy #{strategy.name}] Returning from omniauth with hash " \
            "#{auth_hash&.to_hash.inspect} Valid? #{auth_hash&.valid?}"
        end
      end
    rescue StandardError => e
      OpenProject.logger.error "[OmniAuth strategy #{strategy&.name}] Failed to inspect OmniAuth response: #{e.message}"
    end

    ##
    # After login flow
    def tap_service_result(call)
      if call.success? && user.active?
        OpenProject::Hook.call_hook :omniauth_user_authorized, { auth_hash:, controller: }
        # Call deprecated login hook
        OpenProject::OmniAuth::Authorization.after_login! user, auth_hash, self
      end

      call
    end

    ##
    # After validating the omniauth hash
    # and the authorization is successful,
    #
    # login the user by locating or creating it
    def update_user_from_omniauth!(additional_user_params)
      # Find or create the user from the auth hash
      self.user_attributes = build_omniauth_hash_to_user_attributes.merge(additional_user_params)
      self.identity_url = user_attributes[:identity_url]
      self.user = lookup_or_initialize_user

      # Assign or update the user with the omniauth attributes
      update_attributes
    end

    ##
    # Try to find or create the user
    # in the following order:
    #
    # 1. Look for an active invitation token
    # 2. Look for an existing user for the current identity_url
    # 3. Look for an existing user that we can remap (IF remapping is allowed)
    # 4. Try to register a new user and activate according to settings
    def lookup_or_initialize_user
      find_invited_user ||
        find_existing_user ||
        remap_existing_user ||
        initialize_new_user
    end

    ##
    # Return an invited user, if there is a token
    def find_invited_user
      return unless session.include?(:invitation_token)

      tok = Token::Invitation.find_by value: session[:invitation_token]
      return unless tok

      tok.user.tap do |user|
        user.identity_url = user_attributes[:identity_url]
        tok.destroy
        session.delete :invitation_token
      end
    end

    ##
    # Find an existing user by the identity url
    def find_existing_user
      User.find_by(identity_url:)
    end

    ##
    # Allow to map existing users with an Omniauth source if the login
    # already exists, and no existing auth source or omniauth provider is
    # linked
    def remap_existing_user
      return unless Setting.oauth_allow_remapping_of_existing_users?

      User.not_builtin.find_by_login(user_attributes[:login]) # rubocop:disable Rails/DynamicFindBy
    end

    ##
    # Create the new user and try to activate it
    # according to settings and system limits
    def initialize_new_user
      User.new(identity_url: user_attributes[:identity_url])
    end

    ##
    # Update or assign the user attributes
    def update_attributes
      if user.new_record? || user.invited?
        user.register unless user.invited?

        ::Users::SetAttributesService
          .new(user: User.system, model: user, contract_class: ::Users::UpdateContract)
          .call(user_attributes)
      else
        # Update the user, but do not change the admin flag
        # as this call is not validated.
        # we do this separately in +update_admin_flag+
        ::Users::UpdateService
          .new(user: User.system, model: user)
          .call(user_attributes.except(:admin))
      end
    end

    def update_admin_flag(call)
      return call unless user_attributes.key?(:admin)

      new_admin = ActiveRecord::Type::Boolean.new.cast(user_attributes[:admin])
      return call if user.admin == new_admin

      ::Users::UpdateService
        .new(user: User.system, model: user)
        .call(admin: new_admin)
        .on_failure { |res| update_admin_flag_failure(res) }
        .on_success { update_admin_flag_success(new_admin) }
    end

    def update_admin_flag_success(new_admin)
      if new_admin
        OpenProject.logger.info { "[OmniAuth strategy #{strategy.name}] Granted user##{update.result.id} admin permissions" }
      else
        OpenProject.logger.info { "[OmniAuth strategy #{strategy.name}] Revoked user##{update.result.id} admin permissions" }
      end
    end

    def update_admin_flag_failure(call)
      OpenProject.logger.error do
        "[OmniAuth strategy #{strategy.name}] Failed to update admin user permissions: #{call.message}"
      end
    end

    def activate_user!
      if activatable?
        ::Users::RegisterUserService
          .new(user)
          .call
      else
        ServiceResult.success(result: user)
      end
    end

    ##
    # Determines if the given user is activatable on the fly, that is:
    #
    # 1. The user has just been initialized by us
    # 2. The user has been invited
    # 3. The user had been registered manually (e.g., through a previous self-registration setting)
    def activatable?
      user.new_record? || user.invited? || user.registered?
    end

    ##
    # Maps the omniauth attribute hash
    # to our internal user attributes
    def build_omniauth_hash_to_user_attributes
      info = auth_hash[:info]

      attribute_map = {
        login: info[:login] || info[:email],
        mail: info[:email],
        firstname: info[:first_name] || info[:name],
        lastname: info[:last_name],
        identity_url: identity_url_from_omniauth,
      }

      # Map the admin attribute if provided in an attribute mapping
      attribute_map[:admin] = ActiveRecord::Type::Boolean.new.cast(info[:admin]) if info.key?(:admin)

      # Allow strategies to override mapping
      if strategy.respond_to?(:omniauth_hash_to_user_attributes)
        attribute_map.merge!(strategy.omniauth_hash_to_user_attributes(auth_hash))
      end

      # Remove any nil values to avoid
      # overriding existing attributes
      attribute_map.reject! { |_, value| value.nil? || value == '' }

      Rails.logger.debug { "Mapped auth_hash user attributes #{attribute_map.inspect}" }
      attribute_map
    end

    ##
    # Allow strategies to map a value for uid instead
    # of always taking the global UID.
    # For SAML, the global UID may change with every session
    # (in case of transient nameIds)
    def identity_url_from_omniauth
      identifier = auth_hash[:info][:uid] || auth_hash[:uid]
      "#{auth_hash[:provider]}:#{identifier}"
    end

    ##
    # Try to provide some context of the auth_hash in case of errors
    def auth_uid
      hash = (auth_hash || {})
      hash.dig(:info, :uid) || hash.dig(:uid) || 'unknown'
    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 Authentication::OmniauthService do
  let(:strategy) { double('Omniauth Strategy', name: 'saml') }
  let(:additional_info) do
    {}
  end
  let(:auth_hash) do
    OmniAuth::AuthHash.new(
      provider: 'google',
      uid: '123545',
      info: {
        name: 'foo',
        email: auth_email,
        first_name: 'foo',
        last_name: 'bar'
      }.merge(additional_info)
    )
  end
  let(:auth_email) { 'foo@bar.com' }
  let(:controller) { double('Controller', session: session_stub) }
  let(:session_stub) { [] }

  let(:instance) { described_class.new(strategy:, auth_hash:, controller:) }
  let(:user_attributes) { instance.send :build_omniauth_hash_to_user_attributes }

  describe '#contract' do
    before do
      allow(instance.contract)
        .to(receive(:validate))
        .and_return(valid)
    end

    context 'if valid' do
      let(:valid) { true }

      it 'calls the registration service' do
        expect(Users::RegisterUserService)
          .to(receive(:new))
          .with(kind_of(User))
          .and_call_original

        instance.call
      end
    end

    context 'if invalid' do
      let(:valid) { false }

      it 'does not call the registration service' do
        expect(Users::RegisterUserService)
          .not_to(receive(:new))

        expect(instance.contract)
          .to(receive(:errors))
          .and_return(['foo'])

        call = instance.call
        expect(call).to be_failure
        expect(call.errors).to eq ['foo']
      end
    end
  end

  describe 'activation of users' do
    let(:call) { instance.call }

    context 'with an active found user' do
      shared_let(:user) { create(:user, login: 'foo@bar.com', identity_url: 'google:123545') }

      it 'does not call register user service and logs in the user' do
        allow(Users::RegisterUserService).to receive(:new)

        expect(OpenProject::OmniAuth::Authorization)
          .to(receive(:after_login!))
          .with(user, auth_hash, instance)

        expect(call).to be_success
        expect(call.result).to eq user
        expect(call.result.firstname).to eq 'foo'
        expect(call.result.lastname).to eq 'bar'
        expect(call.result.mail).to eq 'foo@bar.com'

        expect(Users::RegisterUserService).not_to have_received(:new)
      end

      context 'if the user is to become admin' do
        let(:additional_info) { { admin: true } }

        it 'updates the user' do
          expect(user).not_to be_admin
          expect(call).to be_success
          expect(call.result).to eq user
          expect(call.result).to be_admin
        end

        context 'if the user cannot be updated for some reason' do
          let(:stub) { instance_double(Users::UpdateService) }

          it 'fails the request' do
            allow(Users::UpdateService).to receive(:new).and_return(stub)
            allow(stub).to receive(:call).and_return(ServiceResult.failure(message: 'Oh noes!'))

            expect(call).not_to be_success
            expect(call.result).to be_nil
            expect(user.reload).not_to be_admin
          end
        end
      end

      context 'if the user is the last admin and is about to be revoked' do
        let(:additional_info) { { admin: false } }

        it 'does not revoke admin status' do
          user.update!(admin: true)
          expect(user).to be_admin

          expect(call).not_to be_success
          expect(call.result).to eq user
          expect(user.reload).to be_admin
          expect(call.errors.details[:base]).to contain_exactly({ error: :one_must_be_active })
        end
      end

      context 'if the user is not the last admin and is about to be revoked' do
        let!(:other_admin) { create(:admin) }
        let(:additional_info) { { admin: false } }

        it 'revokes admin status' do
          user.update!(admin: true)
          expect(user).to be_admin

          expect(call).to be_success
          expect(call.result).to eq user
          expect(user.reload).not_to be_admin
        end
      end
    end

    context 'without remapping allowed',
            with_settings: { oauth_allow_remapping_of_existing_users?: false } do
      let!(:user) { create(:user, login: 'foo@bar.com') }

      it 'does not look for the user by login' do
        allow(Users::RegisterUserService).to receive(:new).and_call_original

        expect(call).not_to be_success
        expect(call.result.firstname).to eq 'foo'
        expect(call.result.lastname).to eq 'bar'
        expect(call.result.mail).to eq 'foo@bar.com'
        expect(call.result).not_to eq user
        expect(call.result).to be_new_record
        expect(call.result.errors[:login]).to eq ['has already been taken.']

        expect(Users::RegisterUserService).to have_received(:new)
      end
    end

    context 'with an active user remapped',
            with_settings: { oauth_allow_remapping_of_existing_users?: true } do
      let!(:user) { create(:user, identity_url: 'foo', login: auth_email.downcase) }

      shared_examples_for 'a successful remapping of foo' do
        before do
          allow(Users::RegisterUserService).to receive(:new)
          allow(OpenProject::OmniAuth::Authorization)
            .to(receive(:after_login!))
            .with(user, auth_hash, instance)
        end

        it 'does not call register user service and logs in the user' do
          aggregate_failures 'Service call' do
            expect(call).to be_success
            expect(call.result).to eq user
            expect(call.result.firstname).to eq 'foo'
            expect(call.result.lastname).to eq 'bar'
            expect(call.result.login).to eq auth_email
            expect(call.result.mail).to eq auth_email
          end

          user.reload

          aggregate_failures 'User attributes' do
            expect(user.firstname).to eq 'foo'
            expect(user.lastname).to eq 'bar'
            expect(user.identity_url).to eq 'google:123545'
          end

          aggregate_failures 'Message expectations' do
            expect(OpenProject::OmniAuth::Authorization)
              .to(have_received(:after_login!))

            expect(Users::RegisterUserService).not_to have_received(:new)
          end
        end
      end

      context 'with an all lower case login on the IdP side' do
        it_behaves_like 'a successful remapping of foo'
      end

      context 'with a partially upper case login on the IdP side' do
        let(:auth_email) { 'FoO@Bar.COM' }

        it_behaves_like 'a successful remapping of foo'
      end
    end

    describe 'assuming registration/activation worked' do
      let(:register_call) { ServiceResult.new(success: register_success, message: register_message) }
      let(:register_success) { true }
      let(:register_message) { 'It worked!' }

      before do
        expect(Users::RegisterUserService).to receive_message_chain(:new, :call).and_return(register_call)
      end

      describe 'with a new user' do
        it 'calls the register service' do
          expect(call).to be_success

          # The user might get activated in the register service
          expect(instance.user).not_to be_active
          expect(instance.user).to be_new_record

          # Expect notifications to be present (Regression #38066)
          expect(instance.user.notification_settings).not_to be_empty
        end
      end
    end

    describe 'assuming registration/activation failed' do
      let(:register_success) { false }
      let(:register_message) { 'Oh noes :(' }
    end
  end

  describe '#identity_url_from_omniauth' do
    let(:auth_hash) { { provider: 'developer', uid: 'veryuniqueid', info: {} } }

    subject { instance.send(:identity_url_from_omniauth) }

    it 'returns the correct identity_url' do
      expect(subject).to eql('developer:veryuniqueid')
    end

    context 'with uid mapped from info' do
      let(:auth_hash) { { provider: 'developer', uid: 'veryuniqueid', info: { uid: 'internal' } } }

      it 'returns the correct identity_url' do
        expect(subject).to eql('developer:internal')
      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.
#++

module Backups
  class CreateService < ::BaseServices::Create
    def initialize(user:, backup_token:, include_attachments: true, contract_class: ::Backups::CreateContract)
      super user:, contract_class:, contract_options: { backup_token: }

      @include_attachments = include_attachments
    end

    def include_attachments?
      @include_attachments
    end

    def after_perform(call)
      if call.success?
        BackupJob.perform_later(
          backup: call.result,
          user:,
          include_attachments: include_attachments?
        )
      end

      call
    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'
require 'services/base_services/behaves_like_create_service'

RSpec.describe Backups::CreateService, type: :model do
  let(:user) { create(:admin) }
  let(:service) { described_class.new user:, backup_token: backup_token.plain_value }
  let(:backup_token) { create(:backup_token, user:) }

  it_behaves_like 'BaseServices create service' do
    let(:instance) { service }
    let(:backup_token) { build_stubbed(:backup_token, user:) }
    let(:contract_options) { { backup_token: backup_token.plain_value } }
  end

  context "with right permissions" do
    context "with no further options" do
      it "enqueues a BackupJob which includes attachments" do
        expect { service.call }.to have_enqueued_job(BackupJob).with do |args|
          expect(args["include_attachments"]).to be true
        end
      end
    end

    context "with include_attachments: false" do
      let(:service) do
        described_class.new user:, backup_token: backup_token.plain_value, include_attachments: false
      end

      it "enqueues a BackupJob which does not include attachments" do
        expect(BackupJob)
          .to receive(:perform_later)
          .with(hash_including(include_attachments: false, user:))

        expect(service.call).to be_success
      end
    end
  end

  context "with missing permission" do
    let(:user) { create(:user) }

    it "does not enqueue a BackupJob" do
      expect { expect(service.call).to be_failure }.not_to have_enqueued_job(BackupJob)
    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.
#++

module Notifications
  class SetAttributesService < ::BaseServices::SetAttributes
    private

    def set_default_attributes(params)
      super

      set_default_project unless model.project
    end

    ##
    # Try to determine the project context from the journal (if any)
    # or the resource if it has a project set
    def set_default_project
      model.project = model.journal&.project || model.resource.try(:project)
    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 Notifications::SetAttributesService, type: :model do
  let(:user) { build_stubbed(:user) }
  let(:contract_class) do
    contract = double('contract_class')

    allow(contract)
      .to receive(:new)
            .with(event, user, options: {})
            .and_return(contract_instance)

    contract
  end
  let(:contract_instance) do
    double('contract_instance', validate: contract_valid, errors: contract_errors)
  end
  let(:contract_valid) { true }
  let(:contract_errors) do
    double('contract_errors')
  end
  let(:member_valid) { true }
  let(:instance) do
    described_class.new(user:,
                        model: event,
                        contract_class:)
  end
  let(:call_attributes) { {} }
  let(:project) { build_stubbed(:project) }
  let(:reason) { :mentioned }
  let(:journal) { build_stubbed(:journal, journable:, data: journal_data) }
  let(:journable) { nil }
  let(:journal_data) { nil }
  let(:event_subject) { 'I find it important' }
  let(:recipient_id) { 1 }

  describe 'call' do
    let(:call_attributes) do
      {
        recipient_id:,
        reason:,
        resource: journable,
        journal:,
        subject: event_subject,
        project:
      }
    end

    subject { instance.call(call_attributes) }

    context 'for a new record' do
      let(:event) do
        Notification.new
      end

      it 'is successful' do
        expect(subject)
        .to be_success
      end

      it 'sets the attributes add adds default values' do
        subject

        expect(event.attributes.compact.symbolize_keys)
          .to eql({
                    project_id: project.id,
                    reason: 'mentioned',
                    journal_id: journal.id,
                    recipient_id: 1,
                    subject: event_subject,
                    read_ian: false,
                    mail_reminder_sent: false
                  })
      end

      context 'with only the minimal set of attributes for a notification' do
        let(:journable) do
          build_stubbed(:work_package, project:).tap do |wp|
            allow(wp)
              .to receive(:to_s)
              .and_return("wp to s")
          end
        end
        let(:journal_data) do
          build_stubbed(:journal_work_package_journal, project:)
        end
        let(:call_attributes) do
          {
            recipient_id:,
            reason:,
            journal:,
            resource: journable
          }
        end

        it 'sets the attributes and adds default values that are deduced' do
          subject

          expect(event.attributes.compact.symbolize_keys)
            .to eql({
                      project_id: project.id,
                      reason: 'mentioned',
                      resource_id: journable.id,
                      resource_type: 'WorkPackage',
                      journal_id: journal.id,
                      recipient_id: 1,
                      read_ian: false,
                      mail_reminder_sent: false
                    })
        end
      end

      it 'does not persist the notification' do
        expect(event)
          .not_to receive(:save)

        subject
      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.
#++

module Notifications
  class MailService
    include WithMarkedNotifications

    def initialize(notification)
      self.notification = notification
    end

    def call
      return unless supported?
      return if ian_read?

      with_marked_notifications(notification.id) do
        strategy.send_mail(notification)
      end
    end

    private

    attr_accessor :notification

    def ian_read?
      notification.read_ian
    end

    def strategy
      @strategy ||= if self.class.const_defined?("#{strategy_model}Strategy")
                      "#{self.class}::#{strategy_model}Strategy".constantize
                    end
    end

    def strategy_model
      journal&.journable_type || resource&.class
    end

    def journal
      notification.journal
    end

    def resource
      notification.resource
    end

    def supported?
      strategy.present?
    end

    def notification_marked_attribute
      :mail_alert_sent
    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 Notifications::MailService, type: :model do
  require_relative './mentioned_journals_shared'

  subject(:call) { instance.call }

  let(:recipient) do
    build_stubbed(:user,
                  preference: build_stubbed(:user_preference,
                                            settings: {
                                              immediate_reminders: {
                                                mentioned: immediate_reminders_mentioned
                                              }
                                            }))
  end
  let(:actor) do
    build_stubbed(:user)
  end
  let(:instance) { described_class.new(notification) }
  let(:immediate_reminders_mentioned) { true }

  context 'with a work package journal notification' do
    let(:journal) do
      build_stubbed(:work_package_journal).tap do |j|
        allow(j)
          .to receive(:initial?)
                .and_return(journal_initial)
      end
    end
    let(:read_ian) { false }
    let(:reason) { :mentioned }
    let(:notification) do
      build_stubbed(:notification,
                    journal:,
                    recipient:,
                    actor:,
                    reason:,
                    read_ian:)
    end
    let(:journal_initial) { false }

    let(:mail) do
      mail = instance_double(ActionMailer::MessageDelivery)

      allow(WorkPackageMailer)
        .to receive(:mentioned)
              .and_return(mail)

      allow(mail)
        .to receive(:deliver_later)

      mail
    end

    before do
      mail
    end

    shared_examples_for 'sends a mentioned mail' do
      it 'sends a mail' do
        call

        expect(WorkPackageMailer)
          .to have_received(:mentioned)
                .with(recipient,
                      journal)

        expect(mail)
          .to have_received(:deliver_later)
      end
    end

    shared_examples_for 'sends no mentioned mail' do
      it 'sends no mail' do
        call

        expect(WorkPackageMailer)
          .not_to have_received(:mentioned)
      end
    end

    context 'with the notification mentioning the user' do
      it_behaves_like 'sends a mentioned mail'
    end

    context 'with the notification not mentioning the user' do
      let(:reason) { false }

      it_behaves_like 'sends no mentioned mail'
    end

    context 'with the notification mentioning the user but with the recipient having deactivated the mail' do
      let(:immediate_reminders_mentioned) { false }

      it_behaves_like 'sends no mentioned mail'
    end
  end

  context 'with a wiki_content journal notification' do
    let(:journal) do
      build_stubbed(:wiki_page_journal,
                    journable: build_stubbed(:wiki_page)).tap do |j|
        allow(j)
          .to receive(:initial?)
                .and_return(journal_initial)
      end
    end
    let(:read_ian) { false }
    let(:notification) do
      build_stubbed(:notification,
                    journal:,
                    recipient:,
                    actor:,
                    read_ian:)
    end
    let(:mail) do
      mail = instance_double(ActionMailer::MessageDelivery)

      allow(UserMailer)
        .to receive(:wiki_page_added)
              .and_return(mail)

      allow(UserMailer)
        .to receive(:wiki_page_updated)
              .and_return(mail)

      allow(mail)
        .to receive(:deliver_now)

      mail
    end
    let(:journal_initial) { false }

    before do
      mail
    end

    context 'with the notification being for an initial journal' do
      let(:journal_initial) { true }

      it 'sends a mail' do
        call

        expect(UserMailer)
          .to have_received(:wiki_page_added)
                .with(recipient,
                      journal.journable)

        expect(mail)
          .to have_received(:deliver_now)
      end
    end

    context 'with the notification being for an update journal' do
      let(:journal_initial) { false }

      it 'sends a mail' do
        call

        expect(UserMailer)
          .to have_received(:wiki_page_updated)
                .with(recipient,
                      journal.journable)

        expect(mail)
          .to have_received(:deliver_now)
      end
    end

    context 'with the notification read in app already' do
      let(:read_ian) { true }

      it 'sends no mail' do
        call

        expect(UserMailer)
          .not_to have_received(:wiki_page_added)
        expect(UserMailer)
          .not_to have_received(:wiki_page_updated)
      end
    end
  end

  context 'with a news journal notification' do
    let(:journal) do
      build_stubbed(:news_journal,
                    journable: build_stubbed(:news)).tap do |j|
        allow(j)
          .to receive(:initial?)
                .and_return(journal_initial)
      end
    end
    let(:notification) do
      build_stubbed(:notification,
                    journal:,
                    recipient:,
                    actor:)
    end
    let(:mail) do
      mail = instance_double(ActionMailer::MessageDelivery)

      allow(UserMailer)
        .to receive(:news_added)
              .and_return(mail)

      allow(mail)
        .to receive(:deliver_now)

      mail
    end
    let(:journal_initial) { false }

    before do
      mail
    end

    context 'with the notification being for an initial journal' do
      let(:journal_initial) { true }

      it 'sends a mail' do
        call

        expect(UserMailer)
          .to have_received(:news_added)
                .with(recipient,
                      journal.journable)

        expect(mail)
          .to have_received(:deliver_now)
      end
    end

    # This case should not happen as no notification is created in this case that would
    # trigger the NotificationJob. But as this might change, this test case is in place.
    context 'with the notification being for an update journal' do
      let(:journal_initial) { false }

      it 'sends no mail' do
        call

        expect(UserMailer)
          .not_to have_received(:news_added)
      end
    end
  end

  context 'with a message journal notification' do
    let(:journal) do
      build_stubbed(:message_journal,
                    journable: build_stubbed(:message))
    end
    let(:read_ian) { false }
    let(:notification) do
      build_stubbed(:notification,
                    journal:,
                    resource: journal.journable,
                    recipient:,
                    actor:,
                    read_ian:)
    end
    let(:mail) do
      mail = instance_double(ActionMailer::MessageDelivery)

      allow(UserMailer)
        .to receive(:message_posted)
              .and_return(mail)

      allow(mail)
        .to receive(:deliver_now)

      mail
    end

    before do
      mail
    end

    it 'sends a mail' do
      call

      expect(UserMailer)
        .to have_received(:message_posted)
              .with(recipient,
                    journal.journable)

      expect(mail)
        .to have_received(:deliver_now)
    end

    context 'with the notification read in app already' do
      let(:read_ian) { true }

      it 'sends no mail' do
        call

        expect(UserMailer)
          .not_to have_received(:message_posted)
      end
    end
  end

  context 'with a different journal notification' do
    let(:journal) do
      build_stubbed(:journal,
                    journable: build_stubbed(:work_package))
    end
    let(:notification) do
      build_stubbed(:notification,
                    journal:,
                    recipient:,
                    actor:)
    end

    # did that before
    it 'does nothing' do
      expect { call }
        .not_to raise_error(ArgumentError)
    end

    it 'does not send a mail' do
      expect { call }
        .not_to change(ActionMailer::Base.deliveries, :count)
    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 Notifications::CreateService < BaseServices::Create
  protected

  def persist(service_result)
    super
  rescue ActiveRecord::InvalidForeignKey
    service_result.success = false
    service_result.errors.add(:journal_id, :does_not_exist)
    service_result
  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'
require 'services/base_services/behaves_like_create_service'

RSpec.describe Notifications::CreateService, type: :model do
  it_behaves_like 'BaseServices create service'
end


Instruction: In the context of a Ruby programming project, there is a module named 'Dictionary'. This module is defined to have a class method called `table_name_prefix`. The purpose of this module seems to be to provide a prefix for database table names related to the Dictionary. In simpler terms, it's used in the Rails environment where Active Record automatically appends the name of the model to tables (i.e., "dictionary_users" rather than just "users") if the class method `table_name_prefix` is defined in a module named 'Dictionary'.

Response:
module Dictionary
  def self.table_name_prefix
    'dictionary_'
  end
end


Instruction:
In the context of this Ruby code, a standalone instruction would be:

"The Lock class represents a lock object for {CloudUser} and {Application}s that it owns. It has three read-only attributes `apps`, `user`, and `locked` representing the list of applications locked, the owner user, and whether the user is locked respectively. The class also includes Mongoid::Document which allows interaction with a MongoDB database.

The Lock class has two methods for creating or deleting locks on users: `create_lock(user)` and `delete_lock(user)`.

There are two other methods, `lock_user(user, app, timeout=1800)` and `unlock_user(user, app)` that attempt to lock or unlock a user respectively. These methods also check for existing locks on the same application owned by the same user before proceeding with the operation.

There are two additional methods, `lock_application(application, timeout=1800)` and `unlock_application(application)`, that attempt to lock or unlock an application respectively. These methods also retrieve the owner of the application from its domain before attempting to lock or unlock it."

Please note: The actual implementation details might be too complex for a standalone instruction as they involve Mongoid::Document interaction with a MongoDB database and object-oriented concepts in Ruby. However, this brief explanation should provide an understanding of what the code does.

Response:
# Represents a lock object for a {CloudUser} and {Application}s that it owns
# @!attribute [r] apps
#   @return List of {Application}s that are locked
# @!attribute [r] user
#   @return {CloudUser} owner of the lock
# @!attribute [r] locked
#   @return [Boolean] representing if the user is locked
class Lock
  include Mongoid::Document

  belongs_to :user, class_name: CloudUser.name
  field :locked, type: Boolean, default: false
  field :timeout, type: Integer, default: 0
  field :app_ids, type: Hash, default: {}

  index({:user_id => 1})
  create_indexes

  # Attempts to lock the {CloudUser}. Once locked, no other threads can obtain a lock on the {CloudUser} or any owned {Application}s.
  # This lock is denied if any of the {Application}s owned by the {CloudUser} are currently locked.
  #
  # == Parameters:
  # user::
  #   The {CloudUser} to attempt to lock
  #
  # == Returns:
  # True if the lock was successful.

  def self.create_lock(user)
    lock = Lock.find_or_create_by( :user_id => user._id )
  end

  def self.delete_lock(user)
    lock = Lock.delete( :user_id => user._id )
  end

  # Attempts to lock the {CloudUser}.
  # NOTE: User lock is available only for user apps with application lock.
  def self.lock_user(user, app, timeout=1800)
    begin
      now = Time.now.to_i
      lock = Lock.find_or_create_by( :user_id => user._id )
      query = {:user_id => user._id, "$or" => [{:locked => false}, {:timeout.lt => now}], "app_ids.#{app._id}" => { "$exists" => true }}
      updates = {"$set" => { locked: true, timeout: (now + timeout) }}
      lock = Lock.where(query).find_and_modify(updates, new: true)
      return (not lock.nil?)
    rescue Moped::Errors::OperationFailure
      return false
    end
  end

  # Attempts to unlock the {CloudUser}.
  #
  # == Parameters:
  # user::
  #   The {CloudUser} to attempt to unlock
  #
  # == Returns:
  # True if the unlock was successful.
  def self.unlock_user(user, app)
    begin
      query = {:user_id => user._id, :locked => true, "app_ids.#{app._id}" => { "$exists" => true }}
      updates = {"$set" => { "locked" => false }}
      lock = Lock.where(query).find_and_modify(updates, new: true)
      return (not lock.nil?)
    rescue Moped::Errors::OperationFailure
      return false
    end
  end

  # Attempts to lock an {Application}. Once locked, no other threads can obtain a lock on the {Application} or the {CloudUser} that owns it.
  # This lock is denied if the owning {CloudUser} is locked or the {Application} has been locked by another thread.
  #
  # == Parameters:
  # application::
  #   The {Application} to attempt to lock
  #
  # == Returns:
  # True if the lock was successful.
  def self.lock_application(application, timeout=1800)
    begin
      # application.domain can be nil if the application is being created immediately after domain creation
      # If the domain is being read from the secondary, it may not be present
      # If domain is nil, try to load the domain from the primary
      # Note: If there is a way to load the domain relationship from the primary, we should do that
      if application.domain.nil?
        user_id = Domain.find_by(_id: application.domain_id).owner_id
      else
        user_id = application.domain.owner_id
      end

      app_id = application._id.to_s
      now = Time.now.to_i
      query = { :user_id => user_id, "$or" => [{"app_ids.#{app_id}" => {"$exists" => false}}, {"app_ids.#{app_id}" => {"$lt" => now}}] }
      updates = {"$set"=> { "app_ids.#{app_id}" => (now + timeout) }}
      lock = Lock.where(query).find_and_modify(updates, new: true)
      return (not lock.nil?)
    rescue Moped::Errors::OperationFailure => ex
      Rails.logger.error "Failed to obtain lock for application #{application.name}: #{ex.message}"
      return false
    end
  end

  # Attempts to unlock an {Application}.
  #
  # == Parameters:
  # application::
  #   The {Application} to attempt to unlock
  #
  # == Returns:
  # True if the unlock was successful.
  def self.unlock_application(application)
    begin
      # application.domain can be nil if the application is being created immediately after domain creation
      # If the domain is being read from the secondary, it may not be present
      # If domain is nil, try to load the domain from the primary
      # Note: If there is a way to load the domain relationship from the primary, we should do that
      if application.domain.nil?
        user_id = Domain.find_by(_id: application.domain_id).owner_id
      else
        user_id = application.domain.owner_id
      end

      app_id = application._id.to_s
      query = {:user_id => user_id, "app_ids.#{app_id}" => { "$exists" => true }}
      updates = {"$unset"=> {"app_ids.#{app_id}" => ""}}
      lock = Lock.where(query).find_and_modify(updates, new: true)
      return (not lock.nil?)
    rescue Moped::Errors::OperationFailure => ex
      Rails.logger.error "Failed to unlock application #{application.name}: #{ex.message}"
      return false
    end
  end
end