From 9aa3c43f2d0481a94458b1fc5fe68be3832a1da3 Mon Sep 17 00:00:00 2001 From: Sebastien Savater Date: Wed, 11 Dec 2024 11:17:37 +0100 Subject: [PATCH] Add tests, documentation & changelog entry --- CHANGELOG.md | 2 ++ docs/testing.md | 6 ++++ lib/action_policy/rspec/dsl.rb | 14 +++++--- spec/action_policy/dsl_spec.rb | 61 ++++++++++++++++++++++++++++++++++ spec/spec_helper.rb | 2 ++ 5 files changed, 80 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 61a32f1d..de3f20a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## master +- Allow to match failure reasons with RSpec DSL. ([@inkstak]) + ## 0.7.2 (2024-11-21) - Fix missing details in deny! message interpolation. ([@palkan][]) diff --git a/docs/testing.md b/docs/testing.md index 413a8244..f7e1610f 100644 --- a/docs/testing.md +++ b/docs/testing.md @@ -104,6 +104,12 @@ describe PostPolicy do before { user.role = "manager" } end end + + # `failed` allowed to validates feailures reasons when they are provided + # (see Failure Reasons) + failed "when post is archived", reason: {post: [{archived?: {since: "2 days"}}]} do + before { post.archived_at = 2.days.ago } + end end end ``` diff --git a/lib/action_policy/rspec/dsl.rb b/lib/action_policy/rspec/dsl.rb index 9d34cdbd..fa5a8502 100644 --- a/lib/action_policy/rspec/dsl.rb +++ b/lib/action_policy/rspec/dsl.rb @@ -87,17 +87,21 @@ def formatted_policy(policy) end case reason - when nil then next - when Hash then next if subject.reasons.details == reason - when Symbol then next if subject.reasons.details.values.flatten.include?(reason) - when String then next if subject.reasons.full_messages.flatten.include?(reason) + when nil + next + when Hash + next if subject.reasons.details == reason + when Symbol + next if subject.reasons.details.values.flatten.any? { |h| h.include?(reason) } + when String + next if subject.reasons.respond_to?(:full_messages) && subject.reasons.full_messages.flatten.include?(reason) else raise TypeError, "unexpected reason: #{reason.inspect}" end raise( RSpec::Expectations::ExpectationNotMetError, - "Expected to fail with #{reason.inspect} but but actually failed for another reason:\n#{subject.inspect}", + "Expected to fail with #{reason.inspect} but but actually failed for another reason:\n#{formatted_policy(policy)}}", the_caller ) end diff --git a/spec/action_policy/dsl_spec.rb b/spec/action_policy/dsl_spec.rb index 43787189..3efb7843 100644 --- a/spec/action_policy/dsl_spec.rb +++ b/spec/action_policy/dsl_spec.rb @@ -28,6 +28,15 @@ def show? def manage? user.admin? && !record.admin? end + + def delete? + user.admin? && check?(:not_admin?) + end + + def not_admin? + details[:username] = record.name + !record.admin? + end end describe UserPolicy, type: :policy do @@ -74,4 +83,56 @@ def manage? end end end + + describe_rule :delete? do + let(:user) { admin } + let(:record) { User.new("admin") } + + around do |ex| + I18n.backend.store_translations( + :en, + action_policy: { + policy: { + user: { + not_admin?: "Only admins are authorized to perform this action" + } + } + } + ) + + ex.run + I18n.backend.reload! + end + + failed "and matches reasons", reason: {user: [{not_admin?: {username: "admin"}}]} + + failed "and partially matches reasons", reason: :not_admin? + + failed "and match i18n reasons", reason: "Only admins are authorized to perform this action" + + context "test errors with reasons" do + after do |ex| + msg = ex.exception.message + # mark as not failed + ex.remove_instance_variable(:@exception) + + expect(msg).to include(<<~MESSAGE.strip) + Expected to fail with :unexpected but but actually failed for another reason: + [{:not_admin?=>{:username=>"admin"}}]}) + MESSAGE + + if ActionPolicy::PrettyPrint.available? + expect(msg).to include(<<~MESSAGE.strip) + ↳ user.admin? #=> #{ActionPolicy::PrettyPrint.colorize(true)} + AND + check?(:not_admin?) #=> #{ActionPolicy::PrettyPrint.colorize(false)} + MESSAGE + end + end + + failed reason: :unexpected do + let(:record) { User.new("admin") } + end + end + end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 413e533d..bcf2a3aa 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -23,6 +23,8 @@ ]) end +require "i18n" + $LOAD_PATH.unshift File.expand_path("../lib", __dir__) ENV["RUBY_NEXT_TRANSPILE_MODE"] = "rewrite"