From 6820c4ed0509dae4acbbe87f4e49c0127262d6bf Mon Sep 17 00:00:00 2001 From: Robert Blakey Date: Fri, 7 Jun 2024 19:07:16 -0400 Subject: [PATCH] feat: add rswag cop --- config/default.yml | 7 ++ lib/rubocop/cop/betterment.rb | 1 + lib/rubocop/cop/betterment/not_using_rswag.rb | 34 +++++++++ .../cop/betterment/not_using_rswag_spec.rb | 74 +++++++++++++++++++ 4 files changed, 116 insertions(+) create mode 100644 lib/rubocop/cop/betterment/not_using_rswag.rb create mode 100644 spec/rubocop/cop/betterment/not_using_rswag_spec.rb diff --git a/config/default.yml b/config/default.yml index de92e2c..9bd2ced 100644 --- a/config/default.yml +++ b/config/default.yml @@ -95,6 +95,13 @@ Betterment/UnscopedFind: FactoryBot/AssociationStyle: Enabled: false +Betterment/NotUsingRswag: + Enabled: false + SafeAutoCorrect: false + Description: Detect API specs missing OpenAPI documentation using rswag + Include: + - spec/requests/**/*_spec.rb + FactoryBot/ConsistentParenthesesStyle: Enabled: false diff --git a/lib/rubocop/cop/betterment.rb b/lib/rubocop/cop/betterment.rb index 481df59..29ffb52 100644 --- a/lib/rubocop/cop/betterment.rb +++ b/lib/rubocop/cop/betterment.rb @@ -23,3 +23,4 @@ require 'rubocop/cop/betterment/fetch_boolean' require 'rubocop/cop/betterment/render_status' require 'rubocop/cop/betterment/redirect_status' +require 'rubocop/cop/betterment/not_using_rswag' diff --git a/lib/rubocop/cop/betterment/not_using_rswag.rb b/lib/rubocop/cop/betterment/not_using_rswag.rb new file mode 100644 index 0000000..4fb930e --- /dev/null +++ b/lib/rubocop/cop/betterment/not_using_rswag.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +module RuboCop + module Cop + module Betterment + class NotUsingRswag < Base + MSG = 'API tests should use documented using rswag and not the built in `get`, `post`, `put`, `patch`, `delete` methods' + + # @!method test?(node) + def_node_matcher :test?, <<-PATTERN + (block (send nil? :it _) ...) + PATTERN + + # @!method shared_method?(node) + def_node_matcher :shared_method?, <<-PATTERN + (def ...) + PATTERN + + # @!method before_block?(node) + def_node_matcher :before_block?, <<-PATTERN + (block (send nil? :before) ...) + PATTERN + + RESTRICT_ON_SEND = %i(get put patch post delete).freeze + + def on_send(node) + return unless node.ancestors.any? { |a| test?(a) || shared_method?(a) || before_block?(a) } + + add_offense(node) + end + end + end + end +end diff --git a/spec/rubocop/cop/betterment/not_using_rswag_spec.rb b/spec/rubocop/cop/betterment/not_using_rswag_spec.rb new file mode 100644 index 0000000..55bbc04 --- /dev/null +++ b/spec/rubocop/cop/betterment/not_using_rswag_spec.rb @@ -0,0 +1,74 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe RuboCop::Cop::Betterment::NotUsingRswag, :config do + let(:error_message) do + 'API tests should use documented using rswag and not the built in `get`, `post`, `put`, `patch`, `delete` methods' + end + + %w(get put patch post delete).each do |method_name| + it "rejects using the built in #{method_name} method in a test" do + expect_offense(<<~RUBY) + RSpec.describe MyApiController do + it 'returns ok status with expected response' do + get "/api/widgets/1" + ^^^^^^^^^^^^^^^^^^^^ #{error_message} + expect(response).to have_http_status :ok + expect(response_json).to eq accepted_response.as_json + end + end + RUBY + end + + it "rejects using the built in #{method_name} method in a shared method" do + expect_offense(<<~RUBY) + RSpec.describe MyApiController do + def make_get_request + get "/api/widgets/1" + ^^^^^^^^^^^^^^^^^^^^ #{error_message} + end + + it 'returns ok status with expected response' do + make_get_request + + expect(response).to have_http_status :ok + expect(response_json).to eq accepted_response.as_json + end + end + RUBY + end + + it "rejects using the built in #{method_name} method in a before block" do + expect_offense(<<~RUBY) + RSpec.describe MyApiController do + before do + get "/api/widgets/1" + ^^^^^^^^^^^^^^^^^^^^ #{error_message} + end + + it 'returns ok status with expected response' do + expect(response).to have_http_status :ok + expect(response_json).to eq accepted_response.as_json + end + end + RUBY + end + + it "accepts using the rswag #{method_name} method in the example setup" do + expect_no_offenses(<<~RUBY) + RSpec.describe MyApiController do + path "/api/widgets/1" do + get 'shows a foo widget' do + + it 'returns ok status with expected response' do + expect(response).to have_http_status :ok + expect(response_json).to eq accepted_response.as_json + end + end + end + end + RUBY + end + end +end