diff --git a/CHANGELOG.md b/CHANGELOG.md index cad75145..4bdfe4cc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## Master (Unreleased) +- Handle unknown HTTP status codes for `RSpecRails/HttpStatus` cop. ([@viralpraxis]) + ## 2.30.0 (2024-06-12) - Fix an runtime error for rubocop-rspec +3.0. ([@bquorning]) @@ -81,4 +83,5 @@ [@r7kamura]: https://github.com/r7kamura [@splattael]: https://github.com/splattael [@tmaier]: https://github.com/tmaier +[@viralpraxis]: https://github.com/viralpraxis [@ydah]: https://github.com/ydah diff --git a/docs/modules/ROOT/pages/cops_rspecrails.adoc b/docs/modules/ROOT/pages/cops_rspecrails.adoc index 685e6b6c..2a248089 100644 --- a/docs/modules/ROOT/pages/cops_rspecrails.adoc +++ b/docs/modules/ROOT/pages/cops_rspecrails.adoc @@ -115,6 +115,7 @@ This cop inspects only `have_http_status` calls. So, this cop does not check if a method starting with `be_*` is used when setting for `EnforcedStyle: symbolic` or `EnforcedStyle: numeric`. +This cop is also capable of detecting unknown HTTP status codes. === Examples @@ -171,6 +172,15 @@ it { is_expected.to have_http_status :success } it { is_expected.to have_http_status :error } ---- +[source,ruby] +---- +# bad +it { is_expected.to have_http_status :oki_doki } + +# good +it { is_expected.to have_http_status :ok } +---- + === Configurable attributes |=== diff --git a/lib/rubocop/cop/rspec_rails/http_status.rb b/lib/rubocop/cop/rspec_rails/http_status.rb index 3d200b22..4a7766a4 100644 --- a/lib/rubocop/cop/rspec_rails/http_status.rb +++ b/lib/rubocop/cop/rspec_rails/http_status.rb @@ -15,6 +15,7 @@ module RSpecRails # So, this cop does not check if a method starting with `be_*` is used # when setting for `EnforcedStyle: symbolic` or # `EnforcedStyle: numeric`. + # This cop is also capable of detecting unknown HTTP status codes. # # @example `EnforcedStyle: symbolic` (default) # # bad @@ -57,6 +58,12 @@ module RSpecRails # it { is_expected.to have_http_status :success } # it { is_expected.to have_http_status :error } # + # @example + # # bad + # it { is_expected.to have_http_status :oki_doki } + # + # # good + # it { is_expected.to have_http_status :ok } class HttpStatus < ::RuboCop::Cop::Base extend AutoCorrector include ConfigurableEnforcedStyle @@ -67,7 +74,7 @@ class HttpStatus < ::RuboCop::Cop::Base (send nil? :have_http_status ${int sym str}) PATTERN - def on_send(node) + def on_send(node) # rubocop:disable Metrics/MethodLength return unless defined?(::Rack::Utils::SYMBOL_TO_STATUS_CODE) http_status(node) do |arg| @@ -78,6 +85,8 @@ def on_send(node) add_offense(checker.offense_range, message: checker.message) do |corrector| + next unless checker.autocorrectable? + corrector.replace(checker.offense_range, checker.prefer) end end @@ -100,6 +109,7 @@ def checker_class class StyleCheckerBase MSG = 'Prefer `%s` over `%s` ' \ 'to describe HTTP status code.' + MSG_UNKNOWN_STATUS_CODE = 'Unknown status code.' ALLOWED_STATUSES = %i[error success missing redirect].freeze attr_reader :node @@ -109,7 +119,15 @@ def initialize(node) end def message - format(MSG, prefer: prefer, current: current) + if autocorrectable? + format(MSG, prefer: prefer, current: current) + else + MSG_UNKNOWN_STATUS_CODE + end + end + + def autocorrectable? + true end def current @@ -136,6 +154,10 @@ def offensive? !node.sym_type? && !custom_http_status_code? end + def autocorrectable? + !!symbol + end + def prefer symbol.inspect end @@ -157,6 +179,10 @@ def offensive? !node.int_type? && !allowed_symbol? end + def autocorrectable? + !!number + end + def prefer number.to_s end @@ -179,22 +205,30 @@ def offensive? (!node.int_type? && !allowed_symbol?) end + def autocorrectable? + !!status_code + end + def offense_range node.parent end def prefer + "be_#{status_code}" + end + + private + + def status_code if node.sym_type? - "be_#{node.value}" + node.value elsif node.int_type? - "be_#{symbol}" - elsif node.str_type? - "be_#{normalize_str}" + symbol + else + normalize_str end end - private - def symbol ::Rack::Utils::SYMBOL_TO_STATUS_CODE.key(number) end @@ -207,7 +241,7 @@ def normalize_str str = node.value.to_s if str.match?(/\A\d+\z/) ::Rack::Utils::SYMBOL_TO_STATUS_CODE.key(str.to_i) - else + elsif ::Rack::Utils::SYMBOL_TO_STATUS_CODE.key?(str.to_sym) str end end diff --git a/spec/rubocop/cop/rspec_rails/http_status_spec.rb b/spec/rubocop/cop/rspec_rails/http_status_spec.rb index a61abb74..2b8b9884 100644 --- a/spec/rubocop/cop/rspec_rails/http_status_spec.rb +++ b/spec/rubocop/cop/rspec_rails/http_status_spec.rb @@ -80,6 +80,15 @@ RUBY end end + + it 'registers an offense for unknown status code' do + expect_offense(<<~RUBY) + it { is_expected.to have_http_status("some-custom-string") } + ^^^^^^^^^^^^^^^^^^^^ Unknown status code. + RUBY + + expect_no_corrections + end end context 'when EnforcedStyle is `numeric`' do @@ -133,6 +142,15 @@ RUBY end + it 'registers an offense for unknown status code' do + expect_offense(<<~RUBY) + it { is_expected.to have_http_status("some-custom-string") } + ^^^^^^^^^^^^^^^^^^^^ Unknown status code. + RUBY + + expect_no_corrections + end + context 'with parenthesis' do it 'registers an offense when using symbolic value' do expect_offense(<<~RUBY) @@ -237,6 +255,15 @@ RUBY end + it 'registers an offense for unknown status code' do + expect_offense(<<~RUBY) + it { is_expected.to have_http_status("some-custom-string") } + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Unknown status code. + RUBY + + expect_no_corrections + end + context 'with parenthesis' do it 'registers an offense when using numeric value' do expect_offense(<<~RUBY)