Skip to content

Commit

Permalink
feat: Add query_constraints modifier to AssociationMatcher (#1604)
Browse files Browse the repository at this point in the history
  • Loading branch information
matsales28 authored Jan 19, 2024
1 parent 367500d commit dde9883
Show file tree
Hide file tree
Showing 2 changed files with 88 additions and 2 deletions.
28 changes: 26 additions & 2 deletions lib/shoulda/matchers/active_record/association_matcher.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1093,6 +1093,11 @@ def with_primary_key(primary_key)
self
end

def with_query_constraints(query_constraints)
@options[:query_constraints] = query_constraints
self
end

def required(required = true)
remove_submatcher(AssociationMatchers::OptionalMatcher)
add_submatcher(
Expand Down Expand Up @@ -1157,6 +1162,7 @@ def matches?(subject)
(polymorphic? || class_exists?) &&
foreign_key_exists? &&
primary_key_exists? &&
query_constraints_exists? &&
class_name_correct? &&
join_table_correct? &&
autosave_correct? &&
Expand Down Expand Up @@ -1258,7 +1264,7 @@ def validate_inverse_of_through_association
false
end

def macro_supports_primary_key?
def macro_is_not_through?
macro == :belongs_to ||
([:has_many, :has_one].include?(macro) && !through?)
end
Expand All @@ -1268,7 +1274,25 @@ def foreign_key_exists?
end

def primary_key_exists?
!macro_supports_primary_key? || primary_key_correct?(model_class)
!macro_is_not_through? || primary_key_correct?(model_class)
end

def query_constraints_exists?
!macro_is_not_through? || query_constraints_correct?
end

def query_constraints_correct?
if options.key?(:query_constraints)
if option_verifier.correct_for_string?(:query_constraints, options[:query_constraints])
true
else
@missing = "#{model_class} should have \:query_constraints"\
" options set to #{options[:query_constraints]}"
false
end
else
true
end
end

def belongs_foreign_key_missing?
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,25 @@
expect(matcher.failure_message).to match(/Child does not have a custom_primary_key primary key/)
end

if rails_version >= 7.1
it 'accepts an association using an valid :query_constraints option' do
define_model :parent, name: :string
define_model :child, parent_id: :integer, parent_name: :string do
belongs_to :parent, query_constraints: [:parent_id, :parent_name]
end

expect(Child.new).to belong_to(:parent).with_query_constraints([:parent_id, :parent_name])
end

it 'rejects an association with a bad :query_constraints option' do
matcher = belong_to(:parent).with_query_constraints([:parent_id, :parent_name])

expect(belonging_to_parent).not_to matcher

expect(matcher.failure_message).to match(/Child should have :query_constraints options set to \[:parent_id, :parent_name\]/)
end
end

it 'accepts a polymorphic association' do
define_model :child, parent_type: :string, parent_id: :integer do
belongs_to :parent, polymorphic: true
Expand Down Expand Up @@ -821,6 +840,30 @@ def belonging_to_non_existent_class(model_name, assoc_name, options = {})
expect(matcher.failure_message).to match(/Parent does not have a custom_primary_key primary key/)
end

if rails_version >= 7.1
it 'accepts an association using an valid :query_constraints option' do
define_model :parent, first_name: :string, last_name: :string do
self.primary_key = [:first_name, :last_name]

has_many :children, query_constraints: [:parent_first_name, :parent_last_name]
end

define_model :child, parent_first_name: :integer, parent_last_name: :string do
belongs_to :parent, query_constraints: [:parent_first_name, :parent_last_name]
end

expect(Parent.new).to have_many(:children).with_query_constraints([:parent_first_name, :parent_last_name])
end

it 'rejects an association with a bad :query_constraints option' do
matcher = have_many(:children).with_query_constraints([:parent_first_name, :parent_last_name])

expect(having_many_children).not_to matcher

expect(matcher.failure_message).to match(/Parent should have :query_constraints options set to \[:parent_first_name, :parent_last_name\]/)
end
end

it 'rejects an association with a bad :as option' do
define_model(
:child,
Expand Down Expand Up @@ -1223,6 +1266,25 @@ def having_many_non_existent_class(model_name, assoc_name, options = {})
expect(matcher.failure_message).to match(/Person does not have a custom_primary_key primary key/)
end

if rails_version >= 7.1
it 'accepts an association using an valid :query_constraints option' do
define_model :detail, person_first_name: :string, person_last_name: :string
define_model :person do
has_one :detail, query_constraints: [:person_first_name, :person_last_name]
end

expect(Person.new).to have_one(:detail).with_query_constraints([:person_first_name, :person_last_name])
end

it 'rejects an association with a bad :query_constraints option' do
matcher = have_one(:detail).with_query_constraints([:person_first_name, :person_last_name])

expect(having_one_detail).not_to matcher

expect(matcher.failure_message).to match(/Person should have :query_constraints options set to \[:person_first_name, :person_last_name\]/)
end
end

it 'rejects an association with a bad :as option' do
define_model :detail, detailable_id: :integer,
detailable_type: :string
Expand Down

0 comments on commit dde9883

Please sign in to comment.