2016-08-06 12:38:23 -04:00
require " cases/helper "
2009-03-20 11:07:49 -04:00
2016-08-06 12:38:23 -04:00
require " models/topic "
require " models/reply "
require " models/custom_reader "
2009-03-20 11:07:49 -04:00
2016-08-06 12:38:23 -04:00
require " active_support/json "
require " active_support/xml_mini "
2010-06-26 05:57:43 -04:00
2009-03-20 11:07:49 -04:00
class ValidationsTest < ActiveModel :: TestCase
2012-08-06 06:45:27 -04:00
class CustomStrictValidationException < StandardError ; end
2009-12-23 07:30:58 -05:00
def teardown
2014-01-27 05:22:26 -05:00
Topic . clear_validators!
2009-12-23 07:30:58 -05:00
end
2009-03-20 11:07:49 -04:00
def test_single_field_validation
r = Reply . new
r . title = " There's no content! "
2014-08-25 12:37:27 -04:00
assert r . invalid? , " A reply without content should be invalid "
2010-08-20 10:17:29 -04:00
assert r . after_validation_performed , " after_validation callback should be called "
2009-03-20 11:07:49 -04:00
r . content = " Messa content! "
2014-08-25 12:37:27 -04:00
assert r . valid? , " A reply with content should be valid "
2010-08-20 10:17:29 -04:00
assert r . after_validation_performed , " after_validation callback should be called "
2009-03-20 11:07:49 -04:00
end
def test_single_attr_validation_and_error_msg
r = Reply . new
r . title = " There's no content! "
2010-05-08 16:27:49 -04:00
assert r . invalid?
2009-03-21 14:29:15 -04:00
assert r . errors [ :content ] . any? , " A reply without content should mark that attribute as invalid "
2010-05-08 16:27:49 -04:00
assert_equal [ " is Empty " ] , r . errors [ " content " ] , " A reply without content should contain an error "
2009-03-20 11:07:49 -04:00
assert_equal 1 , r . errors . count
end
def test_double_attr_validation_and_error_msg
r = Reply . new
2010-05-08 16:27:49 -04:00
assert r . invalid?
2009-03-20 11:07:49 -04:00
2009-03-21 14:29:15 -04:00
assert r . errors [ :title ] . any? , " A reply without title should mark that attribute as invalid "
2010-05-08 16:27:49 -04:00
assert_equal [ " is Empty " ] , r . errors [ " title " ] , " A reply without title should contain an error "
2009-03-20 11:07:49 -04:00
2009-03-21 14:29:15 -04:00
assert r . errors [ :content ] . any? , " A reply without content should mark that attribute as invalid "
2010-05-08 16:27:49 -04:00
assert_equal [ " is Empty " ] , r . errors [ " content " ] , " A reply without content should contain an error "
2009-03-20 11:07:49 -04:00
assert_equal 2 , r . errors . count
end
def test_single_error_per_attr_iteration
r = Reply . new
2010-05-08 16:27:49 -04:00
r . valid?
2009-03-20 11:07:49 -04:00
2016-08-16 03:30:11 -04:00
errors = r . errors . collect { | attr , messages | [ attr . to_s , messages ] }
2009-03-20 11:07:49 -04:00
2016-09-16 12:44:05 -04:00
assert_includes errors , [ " title " , " is Empty " ]
assert_includes errors , [ " content " , " is Empty " ]
2009-03-20 11:07:49 -04:00
end
def test_multiple_errors_per_attr_iteration_with_full_error_composition
r = Reply . new
2010-05-08 16:27:49 -04:00
r . title = " "
r . content = " "
r . valid?
2009-03-20 11:07:49 -04:00
errors = r . errors . to_a
2010-05-08 16:27:49 -04:00
assert_equal " Content is Empty " , errors [ 0 ]
assert_equal " Title is Empty " , errors [ 1 ]
2009-03-20 11:07:49 -04:00
assert_equal 2 , r . errors . count
end
2009-12-31 07:44:15 -05:00
def test_errors_on_nested_attributes_expands_name
t = Topic . new
t . errors [ " replies.name " ] << " can't be blank "
assert_equal [ " Replies name can't be blank " ] , t . errors . full_messages
end
2009-03-20 11:07:49 -04:00
def test_errors_on_base
r = Reply . new
r . content = " Mismatch "
2010-05-08 16:27:49 -04:00
r . valid?
2010-06-23 08:41:28 -04:00
r . errors . add ( :base , " Reply is not dignifying " )
2009-03-20 11:07:49 -04:00
2010-06-23 08:41:28 -04:00
errors = r . errors . to_a . inject ( [ ] ) { | result , error | result + [ error ] }
2009-03-20 11:07:49 -04:00
2009-03-20 13:36:22 -04:00
assert_equal [ " Reply is not dignifying " ] , r . errors [ :base ]
2009-03-20 11:07:49 -04:00
2016-09-16 12:44:05 -04:00
assert_includes errors , " Title is Empty "
assert_includes errors , " Reply is not dignifying "
2009-03-20 11:07:49 -04:00
assert_equal 2 , r . errors . count
end
2010-06-23 08:41:28 -04:00
def test_errors_on_base_with_symbol_message
r = Reply . new
r . content = " Mismatch "
r . valid?
r . errors . add ( :base , :invalid )
errors = r . errors . to_a . inject ( [ ] ) { | result , error | result + [ error ] }
assert_equal [ " is invalid " ] , r . errors [ :base ]
2016-09-16 12:44:05 -04:00
assert_includes errors , " Title is Empty "
assert_includes errors , " is invalid "
2010-06-23 08:41:28 -04:00
assert_equal 2 , r . errors . count
end
2010-04-09 10:48:24 -04:00
def test_errors_empty_after_errors_on_check
t = Topic . new
assert t . errors [ :id ] . empty?
assert t . errors . empty?
end
2009-03-20 11:07:49 -04:00
def test_validates_each
hits = 0
Topic . validates_each ( :title , :content , [ :title , :content ] ) do | record , attr |
2016-08-06 12:38:23 -04:00
record . errors . add attr , " gotcha "
2009-03-20 11:07:49 -04:00
hits += 1
end
t = Topic . new ( " title " = > " valid " , " content " = > " whatever " )
2010-05-08 16:27:49 -04:00
assert t . invalid?
2009-03-20 11:07:49 -04:00
assert_equal 4 , hits
assert_equal %w( gotcha gotcha ) , t . errors [ :title ]
assert_equal %w( gotcha gotcha ) , t . errors [ :content ]
end
2010-05-08 16:27:49 -04:00
2009-08-05 12:44:44 -04:00
def test_validates_each_custom_reader
hits = 0
CustomReader . validates_each ( :title , :content , [ :title , :content ] ) do | record , attr |
2016-08-06 12:38:23 -04:00
record . errors . add attr , " gotcha "
2009-08-05 12:44:44 -04:00
hits += 1
end
t = CustomReader . new ( " title " = > " valid " , " content " = > " whatever " )
2010-05-08 16:27:49 -04:00
assert t . invalid?
2009-08-05 12:44:44 -04:00
assert_equal 4 , hits
assert_equal %w( gotcha gotcha ) , t . errors [ :title ]
assert_equal %w( gotcha gotcha ) , t . errors [ :content ]
2014-03-10 09:22:22 -04:00
ensure
CustomReader . clear_validators!
2009-08-05 12:44:44 -04:00
end
2009-03-20 11:07:49 -04:00
def test_validate_block
2010-12-19 15:50:18 -05:00
Topic . validate { errors . add ( " title " , " will never be valid " ) }
t = Topic . new ( " title " = > " Title " , " content " = > " whatever " )
assert t . invalid?
assert t . errors [ :title ] . any?
assert_equal [ " will never be valid " ] , t . errors [ " title " ]
end
def test_validate_block_with_params
2009-03-20 11:07:49 -04:00
Topic . validate { | topic | topic . errors . add ( " title " , " will never be valid " ) }
2010-05-08 16:27:49 -04:00
t = Topic . new ( " title " = > " Title " , " content " = > " whatever " )
assert t . invalid?
2009-03-20 11:07:49 -04:00
assert t . errors [ :title ] . any?
assert_equal [ " will never be valid " ] , t . errors [ " title " ]
end
def test_invalid_validator
2009-09-08 11:10:14 -04:00
Topic . validate :i_dont_exist
2013-05-09 14:58:55 -04:00
assert_raises ( NoMethodError ) do
2010-05-08 16:27:49 -04:00
t = Topic . new
t . valid?
end
2009-03-20 11:07:49 -04:00
end
2014-07-17 17:02:54 -04:00
def test_invalid_options_to_validate
2014-09-23 01:36:15 -04:00
error = assert_raises ( ArgumentError ) do
2014-07-17 17:37:03 -04:00
# A common mistake -- we meant to call 'validates'
2014-07-17 17:02:54 -04:00
Topic . validate :title , presence : true
end
2016-08-06 12:38:23 -04:00
message = " Unknown key: :presence. Valid keys are: :on, :if, :unless, :prepend. Perhaps you meant to call `validates` instead of `validate`? "
2014-12-21 02:58:20 -05:00
assert_equal message , error . message
2014-12-12 17:47:38 -05:00
end
def test_callback_options_to_validate
klass = Class . new ( Topic ) do
attr_reader :call_sequence
def initialize ( * )
super
@call_sequence = [ ]
end
private
def validator_a
@call_sequence << :a
end
def validator_b
@call_sequence << :b
end
def validator_c
@call_sequence << :c
end
end
assert_nothing_raised do
2016-08-16 03:30:11 -04:00
klass . validate :validator_a , if : - > { true }
2014-12-12 17:47:38 -05:00
klass . validate :validator_b , prepend : true
2016-08-16 03:30:11 -04:00
klass . validate :validator_c , unless : - > { true }
2014-12-12 17:47:38 -05:00
end
t = klass . new
assert_predicate t , :valid?
assert_equal [ :b , :a ] , t . call_sequence
2014-07-17 17:02:54 -04:00
end
2010-06-26 05:57:43 -04:00
def test_errors_conversions
Topic . validates_presence_of %w( title content )
t = Topic . new
assert t . invalid?
xml = t . errors . to_xml
assert_match %r{ <errors> } , xml
assert_match %r{ <error>Title can't be blank</error> } , xml
assert_match %r{ <error>Content can't be blank</error> } , xml
2010-08-14 01:13:00 -04:00
2012-02-07 13:53:03 -05:00
hash = { }
2010-11-28 08:36:40 -05:00
hash [ :title ] = [ " can't be blank " ]
hash [ :content ] = [ " can't be blank " ]
2010-07-30 16:47:26 -04:00
assert_equal t . errors . to_json , hash . to_json
2009-03-20 11:07:49 -04:00
end
def test_validation_order
2010-05-08 16:27:49 -04:00
Topic . validates_presence_of :title
2013-05-01 20:10:06 -04:00
Topic . validates_length_of :title , minimum : 2
2009-03-20 11:07:49 -04:00
2010-05-08 16:27:49 -04:00
t = Topic . new ( " title " = > " " )
assert t . invalid?
assert_equal " can't be blank " , t . errors [ " title " ] . first
2009-03-19 01:42:08 -04:00
Topic . validates_presence_of :title , :author_name
2016-08-16 03:30:11 -04:00
Topic . validate { errors . add ( " author_email_address " , " will never be valid " ) }
2013-05-01 20:10:06 -04:00
Topic . validates_length_of :title , :content , minimum : 2
2009-03-19 01:42:08 -04:00
2016-08-06 12:38:23 -04:00
t = Topic . new title : " "
2010-05-08 16:27:49 -04:00
assert t . invalid?
2009-03-19 01:42:08 -04:00
2010-05-08 17:03:45 -04:00
assert_equal :title , key = t . errors . keys [ 0 ]
assert_equal " can't be blank " , t . errors [ key ] [ 0 ]
2016-08-06 12:38:23 -04:00
assert_equal " is too short (minimum is 2 characters) " , t . errors [ key ] [ 1 ]
2010-05-08 17:03:45 -04:00
assert_equal :author_name , key = t . errors . keys [ 1 ]
assert_equal " can't be blank " , t . errors [ key ] [ 0 ]
assert_equal :author_email_address , key = t . errors . keys [ 2 ]
2016-08-06 12:38:23 -04:00
assert_equal " will never be valid " , t . errors [ key ] [ 0 ]
2010-05-08 17:03:45 -04:00
assert_equal :content , key = t . errors . keys [ 3 ]
2016-08-06 12:38:23 -04:00
assert_equal " is too short (minimum is 2 characters) " , t . errors [ key ] [ 0 ]
2009-03-20 11:07:49 -04:00
end
2013-03-15 02:25:03 -04:00
def test_validation_with_if_and_on
2016-08-16 03:30:11 -04:00
Topic . validates_presence_of :title , if : Proc . new { | x | x . author_name = " bad " ; true } , on : :update
2011-04-28 17:24:37 -04:00
2013-05-01 20:10:06 -04:00
t = Topic . new ( title : " " )
2011-04-28 17:24:37 -04:00
# If block should not fire
assert t . valid?
assert t . author_name . nil?
# If block should fire
assert t . invalid? ( :update )
assert t . author_name == " bad "
end
2009-03-20 11:07:49 -04:00
def test_invalid_should_be_the_opposite_of_valid
Topic . validates_presence_of :title
t = Topic . new
assert t . invalid?
2009-03-21 14:29:15 -04:00
assert t . errors [ :title ] . any?
2009-03-20 11:07:49 -04:00
2016-08-06 12:38:23 -04:00
t . title = " Things are going to change "
2009-03-20 11:07:49 -04:00
assert ! t . invalid?
end
2009-03-20 13:36:22 -04:00
2010-01-17 06:15:52 -05:00
def test_validation_with_message_as_proc
2013-05-01 20:10:06 -04:00
Topic . validates_presence_of ( :title , message : proc { " no blanks here " . upcase } )
2010-01-17 06:15:52 -05:00
t = Topic . new
2010-05-08 16:27:49 -04:00
assert t . invalid?
2010-05-16 09:37:57 -04:00
assert_equal [ " NO BLANKS HERE " ] , t . errors [ :title ]
2010-01-17 06:15:52 -05:00
end
2010-02-18 10:28:48 -05:00
def test_list_of_validators_for_model
Topic . validates_presence_of :title
2013-05-01 20:10:06 -04:00
Topic . validates_length_of :title , minimum : 2
2010-02-18 10:28:48 -05:00
assert_equal 2 , Topic . validators . count
assert_equal [ :presence , :length ] , Topic . validators . map ( & :kind )
end
def test_list_of_validators_on_an_attribute
Topic . validates_presence_of :title , :content
2013-05-01 20:10:06 -04:00
Topic . validates_length_of :title , minimum : 2
2010-02-18 10:28:48 -05:00
assert_equal 2 , Topic . validators_on ( :title ) . count
assert_equal [ :presence , :length ] , Topic . validators_on ( :title ) . map ( & :kind )
assert_equal 1 , Topic . validators_on ( :content ) . count
assert_equal [ :presence ] , Topic . validators_on ( :content ) . map ( & :kind )
end
def test_accessing_instance_of_validator_on_an_attribute
2013-05-01 20:10:06 -04:00
Topic . validates_length_of :title , minimum : 10
2010-02-18 10:28:48 -05:00
assert_equal 10 , Topic . validators_on ( :title ) . first . options [ :minimum ]
end
2010-05-11 06:28:42 -04:00
2011-02-05 23:27:02 -05:00
def test_list_of_validators_on_multiple_attributes
2013-05-01 20:10:06 -04:00
Topic . validates :title , length : { minimum : 10 }
Topic . validates :author_name , presence : true , format : / a /
2011-02-05 23:27:02 -05:00
validators = Topic . validators_on ( :title , :author_name )
assert_equal [
ActiveModel :: Validations :: FormatValidator ,
ActiveModel :: Validations :: LengthValidator ,
ActiveModel :: Validations :: PresenceValidator
2014-10-27 12:28:53 -04:00
] , validators . map ( & :class ) . sort_by ( & :to_s )
2011-02-05 23:27:02 -05:00
end
def test_list_of_validators_will_be_empty_when_empty
2013-05-01 20:10:06 -04:00
Topic . validates :title , length : { minimum : 10 }
2011-02-05 23:27:02 -05:00
assert_equal [ ] , Topic . validators_on ( :author_name )
end
2010-05-11 06:28:42 -04:00
def test_validations_on_the_instance_level
2014-06-28 12:57:06 -04:00
Topic . validates :title , :author_name , presence : true
Topic . validates :content , length : { minimum : 10 }
2010-05-11 06:28:42 -04:00
2014-06-28 12:57:06 -04:00
topic = Topic . new
assert topic . invalid?
assert_equal 3 , topic . errors . size
2010-05-11 06:28:42 -04:00
2016-08-06 12:38:23 -04:00
topic . title = " Some Title "
topic . author_name = " Some Author "
topic . content = " Some Content Whose Length is more than 10. "
2014-06-28 12:57:06 -04:00
assert topic . valid?
2010-05-11 06:28:42 -04:00
end
2011-08-17 10:26:00 -04:00
2014-03-23 07:09:55 -04:00
def test_validate
2014-06-28 12:57:06 -04:00
Topic . validate do
validates_presence_of :title , :author_name
validates_length_of :content , minimum : 10
end
2014-03-23 07:09:55 -04:00
2014-06-28 12:57:06 -04:00
topic = Topic . new
assert_empty topic . errors
2014-03-23 07:09:55 -04:00
2014-06-28 12:57:06 -04:00
topic . validate
assert_not_empty topic . errors
2014-03-23 07:09:55 -04:00
end
2015-02-20 12:50:49 -05:00
def test_validate_with_bang
Topic . validates :title , presence : true
assert_raise ( ActiveModel :: ValidationError ) do
Topic . new . validate!
end
end
def test_validate_with_bang_and_context
Topic . validates :title , presence : true , on : :context
assert_raise ( ActiveModel :: ValidationError ) do
Topic . new . validate! ( :context )
end
t = Topic . new ( title : " Valid title " )
assert t . validate! ( :context )
end
2011-08-17 10:26:00 -04:00
def test_strict_validation_in_validates
2013-05-01 20:10:06 -04:00
Topic . validates :title , strict : true , presence : true
2011-08-17 10:26:00 -04:00
assert_raises ActiveModel :: StrictValidationFailed do
Topic . new . valid?
end
end
def test_strict_validation_not_fails
2013-05-01 20:10:06 -04:00
Topic . validates :title , strict : true , presence : true
assert Topic . new ( title : " hello " ) . valid?
2011-08-17 10:26:00 -04:00
end
def test_strict_validation_particular_validator
2013-05-01 20:10:06 -04:00
Topic . validates :title , presence : { strict : true }
2011-08-17 10:26:00 -04:00
assert_raises ActiveModel :: StrictValidationFailed do
Topic . new . valid?
end
end
def test_strict_validation_in_custom_validator_helper
2013-05-01 20:10:06 -04:00
Topic . validates_presence_of :title , strict : true
2011-08-17 10:26:00 -04:00
assert_raises ActiveModel :: StrictValidationFailed do
Topic . new . valid?
end
end
2012-08-06 06:45:27 -04:00
def test_strict_validation_custom_exception
2013-05-01 20:10:06 -04:00
Topic . validates_presence_of :title , strict : CustomStrictValidationException
2012-08-06 06:45:27 -04:00
assert_raises CustomStrictValidationException do
Topic . new . valid?
end
end
2011-08-17 10:26:00 -04:00
def test_validates_with_bang
2013-05-01 20:10:06 -04:00
Topic . validates! :title , presence : true
2011-08-17 10:26:00 -04:00
assert_raises ActiveModel :: StrictValidationFailed do
Topic . new . valid?
end
end
2012-01-17 05:18:58 -05:00
Don't enable validations when passing false hash values to ActiveModel.validates
Passing a falsey option value for a validator currently causes that validator to
be enabled, just like "true":
ActiveModel.validates :foo, :presence => false
This is rather counterintuitive, and makes it inconvenient to wrap `validates` in
methods which may conditionally enable different validators.
As an example, one is currently forced to write:
def has_slug(source_field, options={:unique => true})
slugger = Proc.new { |r| r[:slug] = self.class.sluggify(r[source_field]) if r[:slug].blank? }
before_validation slugger
validations = { :presence => true, :slug => true }
if options[:unique]
validations[:uniqueness] = true
end
validates :slug, validations
end
because the following reasonable-looking alternative fails to work as expected:
def has_slug(source_field, options={:unique => true})
slugger = Proc.new { |r| r[:slug] = self.class.sluggify(r[source_field]) if r[:slug].blank? }
before_validation slugger
validates :slug, :presence => true, :slug => true, :uniqueness => options[:unique]
end
(This commit includes a test, and all activemodel and activerecord tests pass as before.)
2012-05-28 09:39:09 -04:00
def test_validates_with_false_hash_value
2013-05-01 20:10:06 -04:00
Topic . validates :title , presence : false
Don't enable validations when passing false hash values to ActiveModel.validates
Passing a falsey option value for a validator currently causes that validator to
be enabled, just like "true":
ActiveModel.validates :foo, :presence => false
This is rather counterintuitive, and makes it inconvenient to wrap `validates` in
methods which may conditionally enable different validators.
As an example, one is currently forced to write:
def has_slug(source_field, options={:unique => true})
slugger = Proc.new { |r| r[:slug] = self.class.sluggify(r[source_field]) if r[:slug].blank? }
before_validation slugger
validations = { :presence => true, :slug => true }
if options[:unique]
validations[:uniqueness] = true
end
validates :slug, validations
end
because the following reasonable-looking alternative fails to work as expected:
def has_slug(source_field, options={:unique => true})
slugger = Proc.new { |r| r[:slug] = self.class.sluggify(r[source_field]) if r[:slug].blank? }
before_validation slugger
validates :slug, :presence => true, :slug => true, :uniqueness => options[:unique]
end
(This commit includes a test, and all activemodel and activerecord tests pass as before.)
2012-05-28 09:39:09 -04:00
assert Topic . new . valid?
end
2012-02-01 08:28:31 -05:00
def test_strict_validation_error_message
2013-05-01 20:10:06 -04:00
Topic . validates :title , strict : true , presence : true
2012-02-01 08:28:31 -05:00
exception = assert_raises ( ActiveModel :: StrictValidationFailed ) do
Topic . new . valid?
end
assert_equal " Title can't be blank " , exception . message
end
2012-01-17 05:18:58 -05:00
def test_does_not_modify_options_argument
2013-05-01 20:10:06 -04:00
options = { presence : true }
2012-01-17 05:18:58 -05:00
Topic . validates :title , options
2013-05-01 20:10:06 -04:00
assert_equal ( { presence : true } , options )
2012-01-17 05:18:58 -05:00
end
2012-05-15 12:38:29 -04:00
2012-05-12 09:03:21 -04:00
def test_dup_validity_is_independent
Topic . validates_presence_of :title
2013-03-15 02:25:03 -04:00
topic = Topic . new ( " title " = > " Literature " )
2012-05-12 09:03:21 -04:00
topic . valid?
duped = topic . dup
duped . title = nil
assert duped . invalid?
topic . title = nil
2016-08-06 12:38:23 -04:00
duped . title = " Mathematics "
2012-05-12 09:03:21 -04:00
assert topic . invalid?
assert duped . valid?
end
2016-03-09 05:06:45 -05:00
def test_validation_with_message_as_proc_that_takes_a_record_as_a_parameter
Topic . validates_presence_of ( :title , message : proc { | record | " You have failed me for the last time, #{ record . author_name } . " } )
2016-08-06 12:38:23 -04:00
t = Topic . new ( author_name : " Admiral " )
2016-03-09 05:06:45 -05:00
assert t . invalid?
assert_equal [ " You have failed me for the last time, Admiral. " ] , t . errors [ :title ]
end
2016-04-05 04:48:44 -04:00
def test_validation_with_message_as_proc_that_takes_record_and_data_as_a_parameters
Topic . validates_presence_of ( :title , message : proc { | record , data | " #{ data [ :attribute ] } is missing. You have failed me for the last time, #{ record . author_name } . " } )
2016-08-06 12:38:23 -04:00
t = Topic . new ( author_name : " Admiral " )
2016-04-05 04:48:44 -04:00
assert t . invalid?
assert_equal [ " Title is missing. You have failed me for the last time, Admiral. " ] , t . errors [ :title ]
end
2009-09-08 11:10:14 -04:00
end