Skip to content

Commit

Permalink
validate_presence_of(:active_storage_field) throws exception (#1224)
Browse files Browse the repository at this point in the history
When trying to test validation of presence against a active storage relation, shoulda tries to set the attribute as empty string. This makes the association fail to be set and breaks the test.

I've introduced a block that checks if the field is an active storage relation and only allows `nil` to be set to the object.

Fixes #1223
  • Loading branch information
gmmcal authored and mcmire committed Jul 9, 2019
1 parent e8ba529 commit a2231f8
Show file tree
Hide file tree
Showing 3 changed files with 117 additions and 0 deletions.
11 changes: 11 additions & 0 deletions lib/shoulda/matchers/active_model/validate_presence_of_matcher.rb
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,8 @@ def disallows_original_or_typecast_value?(value)
def disallowed_values
if collection?
[Array.new]
elsif attachment?
[nil]
else
values = []

Expand Down Expand Up @@ -341,6 +343,11 @@ def association_reflection
model.reflect_on_association(@attribute)
end

def attachment?
model.respond_to?(:reflect_on_association) &&
model_has_associations?(["#{@attribute}_attachment", "#{@attribute}_attachments"])
end

def attribute_type
if model.respond_to?(:attribute_types)
model.attribute_types[@attribute.to_s]
Expand All @@ -349,6 +356,10 @@ def attribute_type
end
end

def model_has_associations?(associations)
associations.any? { |association| !!model.reflect_on_association(association) }
end

def presence_validation_exists_on_attribute?
model._validators.include?(@attribute)
end
Expand Down
4 changes: 4 additions & 0 deletions spec/support/unit/helpers/active_record_versions.rb
Original file line number Diff line number Diff line change
Expand Up @@ -44,5 +44,9 @@ def active_record_supports_optional_for_associations?
def active_record_supports_expression_indexes?
active_record_version >= 5
end

def active_record_supports_validate_presence_on_active_storage?
active_record_version >= '6.0.0.beta1'
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,88 @@ def model_creator
end
end

if active_record_supports_validate_presence_on_active_storage?
context 'a has_one_attached association with a presence validation' do
it 'requires the attribute to be set' do
expect(has_one_attached_child(presence: true)).to validate_presence_of(:child)
end

it_supports(
'ignoring_interference_by_writer',
tests: {
accept_if_qualified_but_changing_value_does_not_interfere: {
changing_values_with: :nil_to_blank
},
reject_if_qualified_but_changing_value_interferes: {
model_name: 'Example',
attribute_name: :attr,
changing_values_with: :never_falsy,
expected_message: <<-MESSAGE
Expected Example to validate that :attr cannot be empty/falsy, but this
could not be proved.
After setting :attr to ‹nil› -- which was read back as ‹"dummy value"›
-- the matcher expected the Example to be invalid, but it was valid
instead.
As indicated in the message above, :attr seems to be changing certain
values as they are set, and this could have something to do with why
this test is failing. If you've overridden the writer method for this
attribute, then you may need to change it to make this test pass, or
do something else entirely.
MESSAGE
}
}
)
end

context 'a has_one_attached association without a presence validation' do
it 'requires the attribute to be set' do
expect(has_one_attached_child(presence: false)).
not_to validate_presence_of(:child)
end
end

context 'a has_many_attached association with a presence validation' do
it 'requires the attribute to be set' do
expect(has_many_attached_children(presence: true)).to validate_presence_of(:children)
end

it_supports(
'ignoring_interference_by_writer',
tests: {
accept_if_qualified_but_changing_value_does_not_interfere: {
changing_values_with: :nil_to_blank
},
reject_if_qualified_but_changing_value_interferes: {
model_name: 'Example',
attribute_name: :attr,
changing_values_with: :never_falsy,
expected_message: <<-MESSAGE
Expected Example to validate that :attr cannot be empty/falsy, but this
could not be proved.
After setting :attr to ‹nil› -- which was read back as ‹"dummy value"›
-- the matcher expected the Example to be invalid, but it was valid
instead.
As indicated in the message above, :attr seems to be changing certain
values as they are set, and this could have something to do with why
this test is failing. If you've overridden the writer method for this
attribute, then you may need to change it to make this test pass, or
do something else entirely.
MESSAGE
}
}
)
end

context 'a has_many_attached association without a presence validation' do
it 'does not require the attribute to be set' do
expect(has_many_attached_children(presence: false)).
not_to validate_presence_of(:children)
end
end
end

context 'a has_many association with a presence validation' do
it 'requires the attribute to be set' do
expect(has_many_children(presence: true)).to validate_presence_of(:children)
Expand Down Expand Up @@ -960,6 +1042,26 @@ def has_many_children(options = {})
end.new
end

def has_one_attached_child(options = {})
define_model :child
define_model :parent do
has_one_attached :child
if options[:presence]
validates_presence_of :child
end
end.new
end

def has_many_attached_children(options = {})
define_model :child
define_model :parent do
has_many_attached :children
if options[:presence]
validates_presence_of :children
end
end.new
end

def active_resource_model
define_active_resource_class :foo, attr: :string do
validates_presence_of :attr
Expand Down

0 comments on commit a2231f8

Please sign in to comment.