# frozen_string_literal: true module AccessMatchersHelpers include Gitlab::Utils::StrongMemoize USER_ACCESSOR_METHOD_NAME = 'user' def provide_user(role, membership = nil) case role when :admin create(:admin) when :auditor create(:user, :auditor) when :user create(:user) when :external create(:user, :external) when :visitor, :anonymous nil when User role when *Gitlab::Access.sym_options_with_owner.keys # owner, maintainer, developer, reporter, guest raise ArgumentError, "cannot provide #{role} when membership reference is blank" unless membership provide_user_by_membership(role, membership) else raise ArgumentError, "cannot provide user of an unknown role #{role}" end end def provide_user_by_membership(role, membership) if role == :owner && membership.owner membership.owner else create(:user).tap do |user| membership.public_send(:"add_#{role}", user) end end end def raise_if_non_block_expectation!(actual) raise ArgumentError, 'This matcher supports block expectations only.' unless actual.is_a?(Proc) end def update_owner(objects, user) return unless objects objects.each do |object| if object.respond_to?(:owner) object.update_attribute(:owner, user) elsif object.respond_to?(:user) object.update_attribute(:user, user) else raise ArgumentError, "cannot own this object #{object}" end end end def patch_example_group(user) return if user.nil? # for anonymous users # This call is evaluated in context of ExampleGroup instance in which the matcher is called. Overrides the `user` # (or defined by `method_name`) method generated by `let` definition in example group before it's used by `subject`. # This override is per concrete example only because the example group class gets re-created for each example. instance_eval(<<~CODE, __FILE__, __LINE__ + 1) def #{USER_ACCESSOR_METHOD_NAME} @#{USER_ACCESSOR_METHOD_NAME} ||= User.find(#{user.id}) end CODE end def prepare_matcher_environment(role, membership, owned_objects) user = provide_user(role, membership) if user update_owner(owned_objects, user) patch_example_group(user) end end def reset_matcher_environment instance_eval(<<~CODE, __FILE__, __LINE__ + 1) clear_memoization(:#{USER_ACCESSOR_METHOD_NAME}) undef #{USER_ACCESSOR_METHOD_NAME} if defined? user CODE end def run_matcher(action, role, membership, owned_objects) raise_if_non_block_expectation!(action) prepare_matcher_environment(role, membership, owned_objects) if block_given? yield action else action.call end reset_matcher_environment end end