Skip to content

Commit

Permalink
Add factory boundaries cop
Browse files Browse the repository at this point in the history
  • Loading branch information
Drowze committed Mar 16, 2024
1 parent dfe7423 commit 2183afe
Show file tree
Hide file tree
Showing 4 changed files with 143 additions and 0 deletions.
12 changes: 12 additions & 0 deletions config/default.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,15 @@ Overhaul/AssignmentInsteadOfComparison:
Description: Detect enumerable comparison blocks returning an assignment.
Enabled: true
VersionAdded: '0.0.1'
Overhaul/FactoryBoundaries:
Description: Detect usage of factories outside where they are supposed to be
Enabled: false
VersionAdded: '0.0.1'
Include:
- spec/**/*_spec.rb
Factories: {}
# e.g.:
# apple:
# - '**/food_service/**'
# car:
# - '**/cars_service/**'
80 changes: 80 additions & 0 deletions lib/rubocop/cop/overhaul/factory_boundaries.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# frozen_string_literal: true

module RuboCop
module Cop
module Overhaul
# Checks whether the used factory is allowed in the current path.
# Requires configuration.
#
# @example
# # with:
# # Factories: {}
# # apple:
# # - '**/food_service/**'
#
# # bad:
# spec/car_service/apple_spec.rb
# create(:apple)
#
# # good:
# spec/food_service/apple_spec.rb
# create(:apple)
class FactoryBoundaries < RuboCop::Cop::Cop
MSG = "Do not use this cop here"

RESTRICT_ON_SEND = %i[
attributes_for
attributes_for_list
build
build_list
build_stubbed
build_stubbed_list
create
create_list
generate
generate_list
].freeze

# @!method factory_bot_factory(node)
def_node_matcher :factory_bot_factory, <<~PATTERN
(send #factory_call? _
$(sym _)
...
)
PATTERN

# @!method factory_bot?(node)
def_node_matcher :factory_bot?, <<~PATTERN
(const nil? :FactoryBot)
PATTERN

def on_send(node)
factory_bot_factory(node) do |value|
allowed_paths = allowed_paths_for(value.value)
file_path = expanded_file_path

return if allowed_paths.nil?
return if allowed_paths.any? do |path|
file_path.end_with?(path) || # if it's a relative path
File.fnmatch(path, expanded_file_path, File::FNM_PATHNAME) # if it's a glob
end

add_offense(value)
end
end

def factory_call?(node)
factory_bot?(node) || node.nil?
end

def allowed_paths_for(factory)
cop_config["Factories"][factory.to_s]
end

def expanded_file_path
File.expand_path(processed_source.file_path)
end
end
end
end
end
1 change: 1 addition & 0 deletions lib/rubocop/cop/overhaul_cops.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@

require_relative "overhaul/mutable_reform_defaults"
require_relative "overhaul/assignment_instead_of_comparison"
require_relative "overhaul/factory_boundaries"
50 changes: 50 additions & 0 deletions spec/rubocop/cop/overhaul/factory_boundaries_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# frozen_string_literal: true

RSpec.describe RuboCop::Cop::Overhaul::FactoryBoundaries, :config do
let(:cop_config) do
{ "Factories" => { "apple" => ["**/food_service/**"] } }
end

shared_examples "usage of factory outside permitted paths" do
it "flags use of factory outside permitted paths" do
expect_offense(<<~RUBY, "spec/other_service/fac_spec.rb", method: method)
%{method}(:apple)
_{method} ^^^^^^ Do not use this cop here
RUBY
end
end

shared_examples "usage of factory inside permitted paths" do
it "does not flag usage of factory in permitted paths" do
expect_no_offenses(<<~RUBY, "spec/food_service/fac_spec.rb")
#{method}(:apple)
RUBY
end
end

described_class::RESTRICT_ON_SEND.flat_map { |method| [method, "FactoryBot.#{method}"] }.each do |method|
context "when method is #{method}" do
let(:method) { method.to_s }

context "when a factory is configured with globs" do
include_examples "usage of factory outside permitted paths"
include_examples "usage of factory inside permitted paths"
end

context "when a factory is configured with absolute path" do
let(:cop_config) do
{ "Factories" => { "apple" => ["spec/food_service/fac_spec.rb"] } }
end

include_examples "usage of factory outside permitted paths"
include_examples "usage of factory inside permitted paths"
end
end
end

it "ignores non-related method calls" do
expect_no_offenses(<<~RUBY, "spec/other_service/fac_spec.rb")
custom_create(:apple)
RUBY
end
end

0 comments on commit 2183afe

Please sign in to comment.