pundit 2.1.0 → 2.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/ISSUE_TEMPLATE/bug_report.md +20 -0
- data/.github/ISSUE_TEMPLATE/feature_request.md +26 -0
- data/.github/PULL_REQUEST_TEMPLATE/gem_release_template.md +8 -0
- data/.github/pull_request_template.md +9 -0
- data/.github/workflows/main.yml +147 -0
- data/.github/workflows/push_gem.yml +33 -0
- data/.gitignore +1 -0
- data/.rubocop.yml +26 -29
- data/.rubocop_ignore_git.yml +7 -0
- data/.yardopts +1 -1
- data/CHANGELOG.md +120 -21
- data/CODE_OF_CONDUCT.md +1 -1
- data/CONTRIBUTING.md +3 -5
- data/Gemfile +23 -2
- data/README.md +175 -78
- data/Rakefile +1 -0
- data/SECURITY.md +19 -0
- data/config/rubocop-rspec.yml +5 -0
- data/lib/generators/pundit/install/install_generator.rb +6 -2
- data/lib/generators/pundit/install/templates/{application_policy.rb → application_policy.rb.tt} +7 -3
- data/lib/generators/pundit/policy/policy_generator.rb +6 -2
- data/lib/generators/pundit/policy/templates/policy.rb.tt +16 -0
- data/lib/generators/rspec/policy_generator.rb +7 -2
- data/lib/generators/rspec/templates/{policy_spec.rb → policy_spec.rb.tt} +1 -1
- data/lib/generators/test_unit/policy_generator.rb +7 -2
- data/lib/pundit/authorization.rb +251 -0
- data/lib/pundit/cache_store/legacy_store.rb +24 -0
- data/lib/pundit/cache_store/null_store.rb +27 -0
- data/lib/pundit/cache_store.rb +22 -0
- data/lib/pundit/context.rb +177 -0
- data/lib/pundit/policy_finder.rb +24 -3
- data/lib/pundit/railtie.rb +19 -0
- data/lib/pundit/rspec.rb +93 -20
- data/lib/pundit/version.rb +2 -1
- data/lib/pundit.rb +68 -257
- data/pundit.gemspec +10 -10
- data/spec/authorization_spec.rb +331 -0
- data/spec/generators_spec.rb +43 -0
- data/spec/policies/post_policy_spec.rb +28 -1
- data/spec/policy_finder_spec.rb +84 -17
- data/spec/pundit/helper_spec.rb +18 -0
- data/spec/pundit_spec.rb +110 -233
- data/spec/rspec_dsl_spec.rb +81 -0
- data/spec/simple_cov_check_action_formatter.rb +79 -0
- data/spec/spec_helper.rb +29 -265
- data/spec/support/lib/controller.rb +38 -0
- data/spec/support/lib/custom_cache.rb +19 -0
- data/spec/support/lib/instance_tracking.rb +20 -0
- data/spec/support/models/article.rb +4 -0
- data/spec/support/models/article_tag.rb +7 -0
- data/spec/support/models/artificial_blog.rb +7 -0
- data/spec/support/models/blog.rb +4 -0
- data/spec/support/models/comment.rb +5 -0
- data/spec/support/models/comment_four_five_six.rb +5 -0
- data/spec/support/models/comment_scope.rb +13 -0
- data/spec/support/models/comments_relation.rb +15 -0
- data/spec/support/models/customer/post.rb +11 -0
- data/spec/support/models/default_scope_contains_error.rb +5 -0
- data/spec/support/models/dummy_current_user.rb +7 -0
- data/spec/support/models/foo.rb +4 -0
- data/spec/support/models/post.rb +25 -0
- data/spec/support/models/post_four_five_six.rb +9 -0
- data/spec/support/models/project_one_two_three/avatar_four_five_six.rb +7 -0
- data/spec/support/models/project_one_two_three/tag_four_five_six.rb +11 -0
- data/spec/support/models/wiki.rb +4 -0
- data/spec/support/policies/article_tag_other_name_policy.rb +13 -0
- data/spec/support/policies/base_policy.rb +23 -0
- data/spec/support/policies/blog_policy.rb +5 -0
- data/spec/support/policies/comment_policy.rb +11 -0
- data/spec/support/policies/criteria_policy.rb +5 -0
- data/spec/support/policies/default_scope_contains_error_policy.rb +10 -0
- data/spec/support/policies/denier_policy.rb +7 -0
- data/spec/support/policies/dummy_current_user_policy.rb +9 -0
- data/spec/support/policies/nil_class_policy.rb +17 -0
- data/spec/support/policies/post_policy.rb +36 -0
- data/spec/support/policies/project/admin/comment_policy.rb +15 -0
- data/spec/support/policies/project/comment_policy.rb +17 -0
- data/spec/support/policies/project/criteria_policy.rb +7 -0
- data/spec/support/policies/project/post_policy.rb +13 -0
- data/spec/support/policies/project_one_two_three/avatar_four_five_six_policy.rb +6 -0
- data/spec/support/policies/project_one_two_three/comment_four_five_six_policy.rb +6 -0
- data/spec/support/policies/project_one_two_three/criteria_four_five_six_policy.rb +6 -0
- data/spec/support/policies/project_one_two_three/post_four_five_six_policy.rb +6 -0
- data/spec/support/policies/project_one_two_three/tag_four_five_six_policy.rb +6 -0
- data/spec/support/policies/publication_policy.rb +13 -0
- data/spec/support/policies/wiki_policy.rb +8 -0
- metadata +80 -130
- data/.travis.yml +0 -21
- data/lib/generators/pundit/policy/templates/policy.rb +0 -9
- /data/lib/generators/test_unit/templates/{policy_test.rb → policy_test.rb.tt} +0 -0
data/spec/pundit_spec.rb
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
require "spec_helper"
|
4
4
|
|
5
|
-
describe Pundit do
|
5
|
+
RSpec.describe Pundit do
|
6
6
|
let(:user) { double }
|
7
7
|
let(:post) { Post.new(user) }
|
8
8
|
let(:customer_post) { Customer::Post.new(user) }
|
@@ -10,23 +10,37 @@ describe Pundit do
|
|
10
10
|
let(:comment) { Comment.new }
|
11
11
|
let(:comment_four_five_six) { CommentFourFiveSix.new }
|
12
12
|
let(:article) { Article.new }
|
13
|
-
let(:controller) { Controller.new(user, "update", {}) }
|
14
13
|
let(:artificial_blog) { ArtificialBlog.new }
|
15
14
|
let(:article_tag) { ArticleTag.new }
|
16
|
-
let(:comments_relation) { CommentsRelation.new }
|
17
|
-
let(:empty_comments_relation) { CommentsRelation.new(true) }
|
15
|
+
let(:comments_relation) { CommentsRelation.new(empty: false) }
|
16
|
+
let(:empty_comments_relation) { CommentsRelation.new(empty: true) }
|
18
17
|
let(:tag_four_five_six) { ProjectOneTwoThree::TagFourFiveSix.new(user) }
|
19
18
|
let(:avatar_four_five_six) { ProjectOneTwoThree::AvatarFourFiveSix.new }
|
20
19
|
let(:wiki) { Wiki.new }
|
21
|
-
let(:thread) { Thread.new }
|
22
20
|
|
23
21
|
describe ".authorize" do
|
24
22
|
it "infers the policy and authorizes based on it" do
|
25
23
|
expect(Pundit.authorize(user, post, :update?)).to be_truthy
|
26
24
|
end
|
27
25
|
|
28
|
-
it "
|
29
|
-
expect(Pundit.authorize(user, post, :
|
26
|
+
it "returns the record on successful authorization" do
|
27
|
+
expect(Pundit.authorize(user, post, :update?)).to eq(post)
|
28
|
+
end
|
29
|
+
|
30
|
+
it "returns the record when passed record with namespace " do
|
31
|
+
expect(Pundit.authorize(user, [:project, comment], :update?)).to eq(comment)
|
32
|
+
end
|
33
|
+
|
34
|
+
it "returns the record when passed record with nested namespace " do
|
35
|
+
expect(Pundit.authorize(user, [:project, :admin, comment], :update?)).to eq(comment)
|
36
|
+
end
|
37
|
+
|
38
|
+
it "returns the policy name symbol when passed record with headless policy" do
|
39
|
+
expect(Pundit.authorize(user, :publication, :create?)).to eq(:publication)
|
40
|
+
end
|
41
|
+
|
42
|
+
it "returns the class when passed record not a particular instance" do
|
43
|
+
expect(Pundit.authorize(user, Post, :show?)).to eq(Post)
|
30
44
|
end
|
31
45
|
|
32
46
|
it "works with anonymous class policies" do
|
@@ -34,14 +48,51 @@ describe Pundit do
|
|
34
48
|
expect { Pundit.authorize(user, article_tag, :destroy?) }.to raise_error(Pundit::NotAuthorizedError)
|
35
49
|
end
|
36
50
|
|
37
|
-
it "raises an error with
|
51
|
+
it "raises an error with the policy, query and record" do
|
38
52
|
# rubocop:disable Style/MultilineBlockChain
|
39
53
|
expect do
|
40
54
|
Pundit.authorize(user, post, :destroy?)
|
41
|
-
end.to raise_error(Pundit::NotAuthorizedError, "not allowed to destroy? this Post") do |error|
|
55
|
+
end.to raise_error(Pundit::NotAuthorizedError, "not allowed to PostPolicy#destroy? this Post") do |error|
|
42
56
|
expect(error.query).to eq :destroy?
|
43
57
|
expect(error.record).to eq post
|
44
|
-
expect(error.policy).to
|
58
|
+
expect(error.policy).to have_attributes(
|
59
|
+
user: user,
|
60
|
+
record: post
|
61
|
+
)
|
62
|
+
expect(error.policy).to be_a(PostPolicy)
|
63
|
+
end
|
64
|
+
# rubocop:enable Style/MultilineBlockChain
|
65
|
+
end
|
66
|
+
|
67
|
+
it "raises an error with the policy, query and record when the record is namespaced" do
|
68
|
+
# rubocop:disable Style/MultilineBlockChain
|
69
|
+
expect do
|
70
|
+
Pundit.authorize(user, [:project, :admin, comment], :destroy?)
|
71
|
+
end.to raise_error(Pundit::NotAuthorizedError,
|
72
|
+
"not allowed to Project::Admin::CommentPolicy#destroy? this Comment") do |error|
|
73
|
+
expect(error.query).to eq :destroy?
|
74
|
+
expect(error.record).to eq comment
|
75
|
+
expect(error.policy).to have_attributes(
|
76
|
+
user: user,
|
77
|
+
record: comment
|
78
|
+
)
|
79
|
+
expect(error.policy).to be_a(Project::Admin::CommentPolicy)
|
80
|
+
end
|
81
|
+
# rubocop:enable Style/MultilineBlockChain
|
82
|
+
end
|
83
|
+
|
84
|
+
it "raises an error with the policy, query and the class name when a Class is given" do
|
85
|
+
# rubocop:disable Style/MultilineBlockChain
|
86
|
+
expect do
|
87
|
+
Pundit.authorize(user, Post, :destroy?)
|
88
|
+
end.to raise_error(Pundit::NotAuthorizedError, "not allowed to PostPolicy#destroy? Post") do |error|
|
89
|
+
expect(error.query).to eq :destroy?
|
90
|
+
expect(error.record).to eq Post
|
91
|
+
expect(error.policy).to have_attributes(
|
92
|
+
user: user,
|
93
|
+
record: Post
|
94
|
+
)
|
95
|
+
expect(error.policy).to be_a(PostPolicy)
|
45
96
|
end
|
46
97
|
# rubocop:enable Style/MultilineBlockChain
|
47
98
|
end
|
@@ -51,6 +102,41 @@ describe Pundit do
|
|
51
102
|
Pundit.authorize(user, wiki, :update?)
|
52
103
|
end.to raise_error(Pundit::InvalidConstructorError, "Invalid #<WikiPolicy> constructor is called")
|
53
104
|
end
|
105
|
+
|
106
|
+
context "when passed a policy class" do
|
107
|
+
it "uses the passed policy class" do
|
108
|
+
expect(Pundit.authorize(user, post, :create?, policy_class: PublicationPolicy)).to be_truthy
|
109
|
+
end
|
110
|
+
|
111
|
+
# This is documenting past behaviour.
|
112
|
+
it "doesn't cache the policy class" do
|
113
|
+
cache = {}
|
114
|
+
|
115
|
+
expect do
|
116
|
+
Pundit.authorize(user, post, :create?, policy_class: PublicationPolicy, cache: cache)
|
117
|
+
Pundit.authorize(user, post, :create?, policy_class: PublicationPolicy, cache: cache)
|
118
|
+
end.to change { PublicationPolicy.instances }.by(2)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
context "when passed a policy class while simultaenously passing a namespace" do
|
123
|
+
it "uses the passed policy class" do
|
124
|
+
expect(PublicationPolicy).to receive(:new).with(user, comment).and_call_original
|
125
|
+
expect(Pundit.authorize(user, [:project, comment], :create?, policy_class: PublicationPolicy)).to be_truthy
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
context "when passed an explicit cache" do
|
130
|
+
it "uses the hash assignment interface on the cache" do
|
131
|
+
custom_cache = CustomCache.new
|
132
|
+
|
133
|
+
Pundit.authorize(user, post, :update?, cache: custom_cache)
|
134
|
+
|
135
|
+
expect(custom_cache.to_h).to match({
|
136
|
+
post => kind_of(PostPolicy)
|
137
|
+
})
|
138
|
+
end
|
139
|
+
end
|
54
140
|
end
|
55
141
|
|
56
142
|
describe ".policy_scope" do
|
@@ -94,8 +180,8 @@ describe Pundit do
|
|
94
180
|
|
95
181
|
it "raises an original error with a policy scope that contains error" do
|
96
182
|
expect do
|
97
|
-
Pundit.policy_scope(user,
|
98
|
-
end.to raise_error(
|
183
|
+
Pundit.policy_scope(user, DefaultScopeContainsError)
|
184
|
+
end.to raise_error(RuntimeError, "This is an arbitrary error that should bubble up")
|
99
185
|
end
|
100
186
|
end
|
101
187
|
|
@@ -360,231 +446,22 @@ describe Pundit do
|
|
360
446
|
end
|
361
447
|
end
|
362
448
|
|
363
|
-
describe "
|
364
|
-
it "
|
365
|
-
|
366
|
-
controller.verify_authorized
|
367
|
-
end
|
368
|
-
|
369
|
-
it "raises an exception when not authorized" do
|
370
|
-
expect { controller.verify_authorized }.to raise_error(Pundit::AuthorizationNotPerformedError)
|
371
|
-
end
|
372
|
-
end
|
373
|
-
|
374
|
-
describe "#verify_policy_scoped" do
|
375
|
-
it "does nothing when policy_scope is used" do
|
376
|
-
controller.policy_scope(Post)
|
377
|
-
controller.verify_policy_scoped
|
378
|
-
end
|
379
|
-
|
380
|
-
it "raises an exception when policy_scope is not used" do
|
381
|
-
expect { controller.verify_policy_scoped }.to raise_error(Pundit::PolicyScopingNotPerformedError)
|
382
|
-
end
|
383
|
-
end
|
384
|
-
|
385
|
-
describe "#pundit_policy_authorized?" do
|
386
|
-
it "is true when authorized" do
|
387
|
-
controller.authorize(post)
|
388
|
-
expect(controller.pundit_policy_authorized?).to be true
|
389
|
-
end
|
390
|
-
|
391
|
-
it "is false when not authorized" do
|
392
|
-
expect(controller.pundit_policy_authorized?).to be false
|
393
|
-
end
|
394
|
-
end
|
395
|
-
|
396
|
-
describe "#pundit_policy_scoped?" do
|
397
|
-
it "is true when policy_scope is used" do
|
398
|
-
controller.policy_scope(Post)
|
399
|
-
expect(controller.pundit_policy_scoped?).to be true
|
400
|
-
end
|
449
|
+
describe ".included" do
|
450
|
+
it "includes Authorization module" do
|
451
|
+
klass = Class.new
|
401
452
|
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
end
|
406
|
-
|
407
|
-
describe "#authorize" do
|
408
|
-
it "infers the policy name and authorizes based on it" do
|
409
|
-
expect(controller.authorize(post)).to be_truthy
|
410
|
-
end
|
411
|
-
|
412
|
-
it "returns the record on successful authorization" do
|
413
|
-
expect(controller.authorize(post)).to be(post)
|
414
|
-
end
|
415
|
-
|
416
|
-
it "can be given a different permission to check" do
|
417
|
-
expect(controller.authorize(post, :show?)).to be_truthy
|
418
|
-
expect { controller.authorize(post, :destroy?) }.to raise_error(Pundit::NotAuthorizedError)
|
419
|
-
end
|
420
|
-
|
421
|
-
it "can be given a different policy class" do
|
422
|
-
expect(controller.authorize(post, :create?, policy_class: PublicationPolicy)).to be_truthy
|
423
|
-
end
|
424
|
-
|
425
|
-
it "works with anonymous class policies" do
|
426
|
-
expect(controller.authorize(article_tag, :show?)).to be_truthy
|
427
|
-
expect { controller.authorize(article_tag, :destroy?) }.to raise_error(Pundit::NotAuthorizedError)
|
428
|
-
end
|
429
|
-
|
430
|
-
it "throws an exception when the permission check fails" do
|
431
|
-
expect { controller.authorize(Post.new) }.to raise_error(Pundit::NotAuthorizedError)
|
432
|
-
end
|
433
|
-
|
434
|
-
it "throws an exception when a policy cannot be found" do
|
435
|
-
expect { controller.authorize(Article) }.to raise_error(Pundit::NotDefinedError)
|
436
|
-
end
|
437
|
-
|
438
|
-
it "caches the policy" do
|
439
|
-
expect(controller.policies[post]).to be_nil
|
440
|
-
controller.authorize(post)
|
441
|
-
expect(controller.policies[post]).not_to be_nil
|
442
|
-
end
|
443
|
-
|
444
|
-
it "raises an error when the given record is nil" do
|
445
|
-
expect { controller.authorize(nil, :destroy?) }.to raise_error(Pundit::NotAuthorizedError)
|
446
|
-
end
|
447
|
-
|
448
|
-
it "raises an error with a invalid policy constructor" do
|
449
|
-
expect { controller.authorize(wiki, :destroy?) }.to raise_error(Pundit::InvalidConstructorError)
|
450
|
-
end
|
451
|
-
end
|
452
|
-
|
453
|
-
describe "#skip_authorization" do
|
454
|
-
it "disables authorization verification" do
|
455
|
-
controller.skip_authorization
|
456
|
-
expect { controller.verify_authorized }.not_to raise_error
|
457
|
-
end
|
458
|
-
end
|
459
|
-
|
460
|
-
describe "#skip_policy_scope" do
|
461
|
-
it "disables policy scope verification" do
|
462
|
-
controller.skip_policy_scope
|
463
|
-
expect { controller.verify_policy_scoped }.not_to raise_error
|
464
|
-
end
|
465
|
-
end
|
466
|
-
|
467
|
-
describe "#pundit_user" do
|
468
|
-
it "returns the same thing as current_user" do
|
469
|
-
expect(controller.pundit_user).to eq controller.current_user
|
470
|
-
end
|
471
|
-
end
|
472
|
-
|
473
|
-
describe "#policy" do
|
474
|
-
it "returns an instantiated policy" do
|
475
|
-
policy = controller.policy(post)
|
476
|
-
expect(policy.user).to eq user
|
477
|
-
expect(policy.post).to eq post
|
478
|
-
end
|
479
|
-
|
480
|
-
it "throws an exception if the given policy can't be found" do
|
481
|
-
expect { controller.policy(article) }.to raise_error(Pundit::NotDefinedError)
|
482
|
-
end
|
483
|
-
|
484
|
-
it "raises an error with a invalid policy constructor" do
|
485
|
-
expect { controller.policy(wiki) }.to raise_error(Pundit::InvalidConstructorError)
|
486
|
-
end
|
487
|
-
|
488
|
-
it "allows policy to be injected" do
|
489
|
-
new_policy = OpenStruct.new
|
490
|
-
controller.policies[post] = new_policy
|
491
|
-
|
492
|
-
expect(controller.policy(post)).to eq new_policy
|
493
|
-
end
|
494
|
-
end
|
495
|
-
|
496
|
-
describe "#policy_scope" do
|
497
|
-
it "returns an instantiated policy scope" do
|
498
|
-
expect(controller.policy_scope(Post)).to eq :published
|
499
|
-
end
|
500
|
-
|
501
|
-
it "allows policy scope class to be overriden" do
|
502
|
-
expect(controller.policy_scope(Post, policy_scope_class: PublicationPolicy::Scope)).to eq :published
|
503
|
-
end
|
504
|
-
|
505
|
-
it "throws an exception if the given policy can't be found" do
|
506
|
-
expect { controller.policy_scope(Article) }.to raise_error(Pundit::NotDefinedError)
|
507
|
-
end
|
508
|
-
|
509
|
-
it "raises an error with a invalid policy scope constructor" do
|
510
|
-
expect { controller.policy_scope(Wiki) }.to raise_error(Pundit::InvalidConstructorError)
|
511
|
-
end
|
512
|
-
|
513
|
-
it "allows policy_scope to be injected" do
|
514
|
-
new_scope = OpenStruct.new
|
515
|
-
controller.policy_scopes[Post] = new_scope
|
516
|
-
|
517
|
-
expect(controller.policy_scope(Post)).to eq new_scope
|
518
|
-
end
|
519
|
-
end
|
453
|
+
expect do
|
454
|
+
klass.include Pundit
|
455
|
+
end.to output.to_stderr
|
520
456
|
|
521
|
-
|
522
|
-
it "checks policy for permitted attributes" do
|
523
|
-
params = ActionController::Parameters.new(
|
524
|
-
post: {
|
525
|
-
title: "Hello",
|
526
|
-
votes: 5,
|
527
|
-
admin: true
|
528
|
-
}
|
529
|
-
)
|
530
|
-
|
531
|
-
action = "update"
|
532
|
-
|
533
|
-
expect(Controller.new(user, action, params).permitted_attributes(post).to_h).to eq(
|
534
|
-
"title" => "Hello",
|
535
|
-
"votes" => 5
|
536
|
-
)
|
537
|
-
expect(Controller.new(double, action, params).permitted_attributes(post).to_h).to eq("votes" => 5)
|
538
|
-
end
|
539
|
-
|
540
|
-
it "checks policy for permitted attributes for record of a ActiveModel type" do
|
541
|
-
params = ActionController::Parameters.new(
|
542
|
-
customer_post: {
|
543
|
-
title: "Hello",
|
544
|
-
votes: 5,
|
545
|
-
admin: true
|
546
|
-
}
|
547
|
-
)
|
548
|
-
|
549
|
-
action = "update"
|
550
|
-
|
551
|
-
expect(Controller.new(user, action, params).permitted_attributes(customer_post).to_h).to eq(
|
552
|
-
"title" => "Hello",
|
553
|
-
"votes" => 5
|
554
|
-
)
|
555
|
-
expect(Controller.new(double, action, params).permitted_attributes(customer_post).to_h).to eq(
|
556
|
-
"votes" => 5
|
557
|
-
)
|
457
|
+
expect(klass).to include Pundit::Authorization
|
558
458
|
end
|
559
|
-
end
|
560
459
|
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
body: "blah",
|
567
|
-
votes: 5,
|
568
|
-
admin: true
|
569
|
-
}
|
570
|
-
)
|
571
|
-
|
572
|
-
action = "revise"
|
573
|
-
expect(Controller.new(user, action, params).permitted_attributes(post).to_h).to eq("body" => "blah")
|
574
|
-
end
|
575
|
-
|
576
|
-
it "can be explicitly set" do
|
577
|
-
params = ActionController::Parameters.new(
|
578
|
-
post: {
|
579
|
-
title: "Hello",
|
580
|
-
body: "blah",
|
581
|
-
votes: 5,
|
582
|
-
admin: true
|
583
|
-
}
|
584
|
-
)
|
585
|
-
|
586
|
-
action = "update"
|
587
|
-
expect(Controller.new(user, action, params).permitted_attributes(post, :revise).to_h).to eq("body" => "blah")
|
460
|
+
it "warns about deprecation" do
|
461
|
+
klass = Class.new
|
462
|
+
expect do
|
463
|
+
klass.include Pundit
|
464
|
+
end.to output(a_string_starting_with("'include Pundit' is deprecated")).to_stderr
|
588
465
|
end
|
589
466
|
end
|
590
467
|
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "spec_helper"
|
4
|
+
|
5
|
+
RSpec.describe "Pundit RSpec DSL" do
|
6
|
+
include Pundit::RSpec::PolicyExampleGroup
|
7
|
+
|
8
|
+
let(:fake_rspec) do
|
9
|
+
double = class_double(RSpec::ExampleGroups)
|
10
|
+
double.extend(::Pundit::RSpec::DSL)
|
11
|
+
double
|
12
|
+
end
|
13
|
+
let(:block) { proc { "block content" } }
|
14
|
+
|
15
|
+
let(:user) { double }
|
16
|
+
let(:other_user) { double }
|
17
|
+
let(:post) { Post.new(user) }
|
18
|
+
let(:policy) { PostPolicy }
|
19
|
+
|
20
|
+
it "calls describe with the correct metadata and without :focus" do
|
21
|
+
expected_metadata = { permissions: %i[item1 item2], caller: instance_of(Array) }
|
22
|
+
expect(fake_rspec).to receive(:describe).with("item1 and item2", match(expected_metadata)) do |&block|
|
23
|
+
expect(block.call).to eq("block content")
|
24
|
+
end
|
25
|
+
|
26
|
+
fake_rspec.permissions(:item1, :item2, &block)
|
27
|
+
end
|
28
|
+
|
29
|
+
it "calls describe with the correct metadata and with :focus" do
|
30
|
+
expected_metadata = { permissions: %i[item1 item2], caller: instance_of(Array), focus: true }
|
31
|
+
expect(fake_rspec).to receive(:describe).with("item1 and item2", match(expected_metadata)) do |&block|
|
32
|
+
expect(block.call).to eq("block content")
|
33
|
+
end
|
34
|
+
|
35
|
+
fake_rspec.permissions(:item1, :item2, :focus, &block)
|
36
|
+
end
|
37
|
+
|
38
|
+
describe "#permit" do
|
39
|
+
context "when not appropriately wrapped in permissions" do
|
40
|
+
it "raises a descriptive error" do
|
41
|
+
expect do
|
42
|
+
expect(policy).to permit(user, post)
|
43
|
+
end.to raise_error(KeyError, <<~MSG.strip)
|
44
|
+
No permissions in example metadata, did you forget to wrap with `permissions :show?, ...`?
|
45
|
+
MSG
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
permissions :edit?, :update? do
|
50
|
+
it "succeeds when action is permitted" do
|
51
|
+
expect(policy).to permit(user, post)
|
52
|
+
end
|
53
|
+
|
54
|
+
context "when it fails" do
|
55
|
+
it "fails with a descriptive error message" do
|
56
|
+
expect do
|
57
|
+
expect(policy).to permit(other_user, post)
|
58
|
+
end.to raise_error(RSpec::Expectations::ExpectationNotMetError, <<~MSG.strip)
|
59
|
+
Expected PostPolicy to grant edit? and update? on Post but edit? and update? were not granted
|
60
|
+
MSG
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
context "when negated" do
|
65
|
+
it "succeeds when action is not permitted" do
|
66
|
+
expect(policy).not_to permit(other_user, post)
|
67
|
+
end
|
68
|
+
|
69
|
+
context "when it fails" do
|
70
|
+
it "fails with a descriptive error message" do
|
71
|
+
expect do
|
72
|
+
expect(policy).not_to permit(user, post)
|
73
|
+
end.to raise_error(RSpec::Expectations::ExpectationNotMetError, <<~MSG.strip)
|
74
|
+
Expected PostPolicy not to grant edit? and update? on Post but edit? and update? were granted
|
75
|
+
MSG
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "simplecov"
|
4
|
+
require "json"
|
5
|
+
|
6
|
+
class SimpleCovCheckActionFormatter
|
7
|
+
SourceFile = Data.define(:source_file) do
|
8
|
+
def covered_strength = source_file.covered_strength
|
9
|
+
def covered_percent = source_file.covered_percent
|
10
|
+
|
11
|
+
def to_json(*args)
|
12
|
+
{
|
13
|
+
filename: source_file.filename,
|
14
|
+
covered_percent: covered_percent.nan? ? 0.0 : covered_percent,
|
15
|
+
coverage: source_file.coverage_data,
|
16
|
+
covered_strength: covered_strength.nan? ? 0.0 : covered_strength,
|
17
|
+
covered_lines: source_file.covered_lines.count,
|
18
|
+
lines_of_code: source_file.lines_of_code
|
19
|
+
}.to_json(*args)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
Result = Data.define(:result) do
|
24
|
+
def included?(source_file) = result.filenames.include?(source_file.filename)
|
25
|
+
|
26
|
+
def files
|
27
|
+
result.files.filter_map do |source_file|
|
28
|
+
next unless result.filenames.include? source_file.filename
|
29
|
+
|
30
|
+
SourceFile.new(source_file)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def to_json(*args) # rubocop:disable Metrics/AbcSize
|
35
|
+
{
|
36
|
+
timestamp: result.created_at.to_i,
|
37
|
+
command_name: result.command_name,
|
38
|
+
files: files,
|
39
|
+
metrics: {
|
40
|
+
covered_percent: result.covered_percent,
|
41
|
+
covered_strength: result.covered_strength.nan? ? 0.0 : result.covered_strength,
|
42
|
+
covered_lines: result.covered_lines,
|
43
|
+
total_lines: result.total_lines
|
44
|
+
}
|
45
|
+
}.to_json(*args)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
FormatterWithOptions = Data.define(:formatter) do
|
50
|
+
def new = formatter
|
51
|
+
end
|
52
|
+
|
53
|
+
class << self
|
54
|
+
def with_options(...)
|
55
|
+
FormatterWithOptions.new(new(...))
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def initialize(output_filename: "coverage.json", output_directory: SimpleCov.coverage_path)
|
60
|
+
@output_filename = output_filename
|
61
|
+
@output_directory = output_directory
|
62
|
+
end
|
63
|
+
|
64
|
+
attr_reader :output_filename, :output_directory
|
65
|
+
|
66
|
+
def output_filepath = File.join(output_directory, output_filename)
|
67
|
+
|
68
|
+
def format(result_data)
|
69
|
+
result = Result.new(result_data)
|
70
|
+
json = JSON.generate(result)
|
71
|
+
File.write(output_filepath, json)
|
72
|
+
puts output_message(result_data)
|
73
|
+
json
|
74
|
+
end
|
75
|
+
|
76
|
+
def output_message(result)
|
77
|
+
"Coverage report generated for #{result.command_name} to #{output_filepath}. #{result.covered_lines} / #{result.total_lines} LOC (#{result.covered_percent.round(2)}%) covered." # rubocop:disable Layout/LineLength
|
78
|
+
end
|
79
|
+
end
|