Class: ActionPolicy::RSpec::HaveAuthorizedScope

Inherits:
RSpec::Matchers::BuiltIn::BaseMatcher
  • Object
show all
Defined in:
lib/action_policy/rspec/have_authorized_scope.rb

Overview

Implements ‘have_authorized_scope` matcher.

Verifies that a block of code applies authorization scoping using specific policy.

Example:

# in controller/request specs
subject { get :index }

it "has authorized scope" do
  expect { subject }
    .to have_authorized_scope(:active_record_relation)
    .with(ProductPolicy)
end

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(type) ⇒ HaveAuthorizedScope

Returns a new instance of HaveAuthorizedScope.



26
27
28
29
30
# File 'lib/action_policy/rspec/have_authorized_scope.rb', line 26

def initialize(type)
  @type = type
  @name = :default
  @scope_options = nil
end

Instance Attribute Details

#actual_scopesObject (readonly)

Returns the value of attribute actual_scopes.



23
24
25
# File 'lib/action_policy/rspec/have_authorized_scope.rb', line 23

def actual_scopes
  @actual_scopes
end

#nameObject (readonly)

Returns the value of attribute name.



23
24
25
# File 'lib/action_policy/rspec/have_authorized_scope.rb', line 23

def name
  @name
end

#policyObject (readonly)

Returns the value of attribute policy.



23
24
25
# File 'lib/action_policy/rspec/have_authorized_scope.rb', line 23

def policy
  @policy
end

#scope_optionsObject (readonly)

Returns the value of attribute scope_options.



23
24
25
# File 'lib/action_policy/rspec/have_authorized_scope.rb', line 23

def scope_options
  @scope_options
end

#target_expectationsObject (readonly)

Returns the value of attribute target_expectations.



23
24
25
# File 'lib/action_policy/rspec/have_authorized_scope.rb', line 23

def target_expectations
  @target_expectations
end

#typeObject (readonly)

Returns the value of attribute type.



23
24
25
# File 'lib/action_policy/rspec/have_authorized_scope.rb', line 23

def type
  @type
end

Instance Method Details

#actual_scopes_messageObject



100
101
102
103
104
105
106
107
# File 'lib/action_policy/rspec/have_authorized_scope.rb', line 100

def actual_scopes_message
  if actual_scopes.empty?
    "no scopings have been made"
  else
    "the following scopings were encountered:\n" \
    "#{formatted_scopings}"
  end
end

#as(name) ⇒ Object



37
38
39
40
# File 'lib/action_policy/rspec/have_authorized_scope.rb', line 37

def as(name)
  @name = name
  self
end

#does_not_match?Boolean

Returns:

  • (Boolean)


74
75
76
# File 'lib/action_policy/rspec/have_authorized_scope.rb', line 74

def does_not_match?(*)
  raise "This matcher doesn't support negation"
end

#failure_messageObject



80
81
82
83
84
85
# File 'lib/action_policy/rspec/have_authorized_scope.rb', line 80

def failure_message
  "expected a scoping named :#{name} for type :#{type} " \
  "#{scope_options_message} " \
  "from #{policy} to have been applied, " \
  "but #{actual_scopes_message}"
end

#formatted_scopingsObject



109
110
111
112
113
# File 'lib/action_policy/rspec/have_authorized_scope.rb', line 109

def formatted_scopings
  actual_scopes.map do
    " - #{_1.inspect}"
  end.join("\n")
end

#match(_expected, actual) ⇒ Object



52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/action_policy/rspec/have_authorized_scope.rb', line 52

def match(_expected, actual)
  raise "This matcher only supports block expectations" unless actual.is_a?(Proc)

  ActionPolicy::Testing::AuthorizeTracker.tracking { actual.call }

  @actual_scopes = ActionPolicy::Testing::AuthorizeTracker.scopings

  matching_scopes = actual_scopes.select { _1.matches?(policy, type, name, scope_options) }

  return false if matching_scopes.empty?

  return true unless target_expectations

  if matching_scopes.size > 1
    raise "Too many matching scopings (#{matching_scopes.size}), " \
          "you can run `.with_target` only when there is the only one match"
  end

  target_expectations.call(matching_scopes.first.target)
  true
end

#scope_options_messageObject



87
88
89
90
91
92
93
94
95
96
97
98
# File 'lib/action_policy/rspec/have_authorized_scope.rb', line 87

def scope_options_message
  if scope_options
    if defined?(::RSpec::Matchers::Composable) &&
        scope_options.is_a?(::RSpec::Matchers::Composable)
      "with scope options #{scope_options.description}"
    else
      "with scope options #{scope_options}"
    end
  else
    "without scope options"
  end
end

#supports_block_expectations?Boolean

Returns:

  • (Boolean)


78
# File 'lib/action_policy/rspec/have_authorized_scope.rb', line 78

def supports_block_expectations?() = true

#with(policy) ⇒ Object



32
33
34
35
# File 'lib/action_policy/rspec/have_authorized_scope.rb', line 32

def with(policy)
  @policy = policy
  self
end

#with_scope_options(scope_options) ⇒ Object



42
43
44
45
# File 'lib/action_policy/rspec/have_authorized_scope.rb', line 42

def with_scope_options(scope_options)
  @scope_options = scope_options
  self
end

#with_target(&block) ⇒ Object



47
48
49
50
# File 'lib/action_policy/rspec/have_authorized_scope.rb', line 47

def with_target(&block)
  @target_expectations = block
  self
end