Skip to content

Commit b166593

Browse files
authored
Merge pull request #15 from ydah/add-new5
feat: add Committee/SchemaConformConsistency cop to enforce preferred schema conformance assertion style
2 parents 33a7e7c + da93e3a commit b166593

File tree

7 files changed

+248
-0
lines changed

7 files changed

+248
-0
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
- [#15](https://github.com/ydah/rubocop-committee/pull/15) Add `Committee/SchemaConformConsistency` cop to enforce preferred schema conformance assertion style. ([@ydah])

config/default.yml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,15 @@ Committee/RedundantResponseStatusAssertions:
2626
Enabled: pending
2727
VersionAdded: "0.1"
2828

29+
Committee/SchemaConformConsistency:
30+
Description: Prefer `assert_schema_conform` or separate request/response confirmations based on EnforcedStyle.
31+
Enabled: pending
32+
SupportedStyles:
33+
- combined
34+
- separate
35+
EnforcedStyle: combined
36+
VersionAdded: "<<next>>"
37+
2938
Committee/UnspecifiedExpectedStatus:
3039
Description: |
3140
Check if the status code is specified as an argument to the method of the Committee

docs/modules/ROOT/pages/cops.adoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
* xref:cops_committee.adoc#committeedeprecatedoldassertbehavior[Committee/DeprecatedOldAssertBehavior]
77
* xref:cops_committee.adoc#committeemultipleschemaconform[Committee/MultipleSchemaConform]
88
* xref:cops_committee.adoc#committeeredundantresponsestatusassertions[Committee/RedundantResponseStatusAssertions]
9+
* xref:cops_committee.adoc#committeeschemaconformconsistency[Committee/SchemaConformConsistency]
910
* xref:cops_committee.adoc#committeeunspecifiedexpectedstatus[Committee/UnspecifiedExpectedStatus]
1011
* xref:cops_committee.adoc#committeeunusedschemacoverage[Committee/UnusedSchemaCoverage]
1112

docs/modules/ROOT/pages/cops_committee.adoc

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,63 @@ it 'does something' do
137137
end
138138
----
139139
140+
[#committeeschemaconformconsistency]
141+
== Committee/SchemaConformConsistency
142+
143+
|===
144+
| Enabled by default | Safe | Supports autocorrection | Version Added | Version Changed
145+
146+
| Enabled
147+
| Yes
148+
| No
149+
| -
150+
| -
151+
|===
152+
153+
Prefer `assert_schema_conform` when request and response schema confirmations
154+
are both asserted in the same example.
155+
156+
[#examples-committeeschemaconformconsistency]
157+
=== Examples
158+
159+
[#enforcedstyle_-combined-_default_-committeeschemaconformconsistency]
160+
==== EnforcedStyle: combined (default)
161+
162+
[source,ruby]
163+
----
164+
# bad
165+
it 'returns users' do
166+
get '/users'
167+
assert_request_schema_confirm
168+
assert_response_schema_confirm(200)
169+
end
170+
171+
# good
172+
it 'returns users' do
173+
get '/users'
174+
assert_schema_conform(200)
175+
end
176+
----
177+
178+
[#enforcedstyle_-separate-committeeschemaconformconsistency]
179+
==== EnforcedStyle: separate
180+
181+
[source,ruby]
182+
----
183+
# bad
184+
it 'returns users' do
185+
get '/users'
186+
assert_schema_conform(200)
187+
end
188+
189+
# good
190+
it 'returns users' do
191+
get '/users'
192+
assert_request_schema_confirm
193+
assert_response_schema_confirm(200)
194+
end
195+
----
196+
140197
[#committeeunspecifiedexpectedstatus]
141198
== Committee/UnspecifiedExpectedStatus
142199

lib/rubocop-committee.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
require_relative "rubocop/cop/committee/assert_schema_conform_without_request"
1010
require_relative "rubocop/cop/committee/deprecated_old_assert_behavior"
1111
require_relative "rubocop/cop/committee/multiple_schema_conform"
12+
require_relative "rubocop/cop/committee/schema_conform_consistency"
1213
require_relative "rubocop/cop/committee/redundant_response_status_assertions"
1314
require_relative "rubocop/cop/committee/unused_schema_coverage"
1415
require_relative "rubocop/cop/committee/unspecified_expected_status"
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
# frozen_string_literal: true
2+
3+
module RuboCop
4+
module Cop
5+
module Committee
6+
# Prefer `assert_schema_conform` when request and response schema confirmations
7+
# are both asserted in the same example.
8+
#
9+
# @example EnforcedStyle: combined (default)
10+
# # bad
11+
# it 'returns users' do
12+
# get '/users'
13+
# assert_request_schema_confirm
14+
# assert_response_schema_confirm(200)
15+
# end
16+
#
17+
# # good
18+
# it 'returns users' do
19+
# get '/users'
20+
# assert_schema_conform(200)
21+
# end
22+
#
23+
# @example EnforcedStyle: separate
24+
# # bad
25+
# it 'returns users' do
26+
# get '/users'
27+
# assert_schema_conform(200)
28+
# end
29+
#
30+
# # good
31+
# it 'returns users' do
32+
# get '/users'
33+
# assert_request_schema_confirm
34+
# assert_response_schema_confirm(200)
35+
# end
36+
#
37+
class SchemaConformConsistency < Base
38+
include ConfigurableEnforcedStyle
39+
40+
MSG_COMBINED = "Use `assert_schema_conform` instead of separate request/response confirmations."
41+
MSG_SEPARATE = "Use separate `assert_*_schema_confirm` calls instead of `assert_schema_conform`."
42+
43+
REQUEST_METHOD = :assert_request_schema_confirm
44+
RESPONSE_METHOD = :assert_response_schema_confirm
45+
SCHEMA_METHOD = :assert_schema_conform
46+
47+
EXAMPLE_METHODS = %i[it specify example scenario].freeze
48+
RESTRICT_ON_SEND = [REQUEST_METHOD, RESPONSE_METHOD, SCHEMA_METHOD].freeze
49+
50+
def on_send(node) # rubocop:disable InternalAffairs/OnSendWithoutOnCSend
51+
example_block = example_block_for(node)
52+
return unless example_block
53+
54+
return unless offense?(node, example_block)
55+
56+
add_offense(node, message: offense_message(node))
57+
end
58+
59+
private
60+
61+
def offense?(node, example_block)
62+
return request_or_response?(node) && request_and_response_in?(example_block) if style == :combined
63+
return schema_conform?(node) if style == :separate
64+
65+
false
66+
end
67+
68+
def offense_message(_node)
69+
if style == :combined
70+
MSG_COMBINED
71+
elsif style == :separate
72+
MSG_SEPARATE
73+
else
74+
# :nocov:
75+
:noop
76+
# :nocov:
77+
end
78+
end
79+
80+
def example_block_for(node)
81+
node.each_ancestor(:block).find { |ancestor| example_block?(ancestor) }
82+
end
83+
84+
def example_block?(node)
85+
return false unless node.block_type?
86+
87+
send_node = node.send_node
88+
return false unless send_node&.send_type?
89+
90+
EXAMPLE_METHODS.include?(send_node.method_name)
91+
end
92+
93+
def request_and_response_in?(example_block)
94+
has_request = false
95+
has_response = false
96+
97+
example_block.each_descendant(:send).each do |send_node|
98+
next unless send_node.receiver.nil?
99+
100+
has_request ||= send_node.method?(REQUEST_METHOD)
101+
has_response ||= send_node.method?(RESPONSE_METHOD)
102+
return true if has_request && has_response
103+
end
104+
105+
false
106+
end
107+
108+
def request_or_response?(node)
109+
node.receiver.nil? && [REQUEST_METHOD, RESPONSE_METHOD].include?(node.method_name)
110+
end
111+
112+
def schema_conform?(node)
113+
node.receiver.nil? && node.method?(SCHEMA_METHOD)
114+
end
115+
end
116+
end
117+
end
118+
end
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# frozen_string_literal: true
2+
3+
RSpec.describe RuboCop::Cop::Committee::SchemaConformConsistency, :config do
4+
context "when EnforcedStyle is combined" do
5+
let(:cop_config) { { "EnforcedStyle" => "combined" } }
6+
7+
it "registers offenses when request and response confirmations are both present" do
8+
expect_offense(<<~RUBY)
9+
it 'returns users' do
10+
get '/users'
11+
assert_request_schema_confirm
12+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use `assert_schema_conform` instead of separate request/response confirmations.
13+
assert_response_schema_confirm(200)
14+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Use `assert_schema_conform` instead of separate request/response confirmations.
15+
end
16+
RUBY
17+
end
18+
19+
it "does not register an offense when using assert_schema_conform" do
20+
expect_no_offenses(<<~RUBY)
21+
it 'returns users' do
22+
get '/users'
23+
assert_schema_conform(200)
24+
end
25+
RUBY
26+
end
27+
28+
it "does not register an offense when only one confirmation is present" do
29+
expect_no_offenses(<<~RUBY)
30+
it 'returns users' do
31+
get '/users'
32+
assert_request_schema_confirm
33+
end
34+
RUBY
35+
end
36+
end
37+
38+
context "when EnforcedStyle is separate" do
39+
let(:cop_config) { { "EnforcedStyle" => "separate" } }
40+
41+
it "registers an offense for assert_schema_conform" do
42+
expect_offense(<<~RUBY)
43+
it 'returns users' do
44+
get '/users'
45+
assert_schema_conform(200)
46+
^^^^^^^^^^^^^^^^^^^^^^^^^^ Use separate `assert_*_schema_confirm` calls instead of `assert_schema_conform`.
47+
end
48+
RUBY
49+
end
50+
51+
it "does not register an offense when using request and response confirmations" do
52+
expect_no_offenses(<<~RUBY)
53+
it 'returns users' do
54+
get '/users'
55+
assert_request_schema_confirm
56+
assert_response_schema_confirm(200)
57+
end
58+
RUBY
59+
end
60+
end
61+
end

0 commit comments

Comments
 (0)