Upgraded Rails and RSpec
[monkeycharger.git] / vendor / rails / activerecord / test / validations_test.rb
blob03d5da499024ec431931d35ca790c4fc455138fa
1 require 'abstract_unit'
2 require 'fixtures/topic'
3 require 'fixtures/reply'
4 require 'fixtures/person'
5 require 'fixtures/developer'
7 # The following methods in Topic are used in test_conditional_validation_*
8 class Topic
9   def condition_is_true
10     return true
11   end
13   def condition_is_true_but_its_not
14     return false
15   end
16 end
18 class ProtectedPerson < ActiveRecord::Base
19   set_table_name 'people'
20   attr_accessor :addon
21   attr_protected :first_name
22 end
24 class UniqueReply < Reply
25   validates_uniqueness_of :content, :scope => 'parent_id'
26 end
28 class SillyUniqueReply < UniqueReply
29 end
31 class Topic < ActiveRecord::Base
32   has_many :unique_replies, :dependent => :destroy, :foreign_key => "parent_id"
33   has_many :silly_unique_replies, :dependent => :destroy, :foreign_key => "parent_id"
34 end
36 class Wizard < ActiveRecord::Base
37   self.abstract_class = true
39   validates_uniqueness_of :name
40 end
42 class IneptWizard < Wizard
43   validates_uniqueness_of :city
44 end
46 class Conjurer < IneptWizard
47 end
49 class Thaumaturgist < IneptWizard
50 end
52 class ValidationsTest < Test::Unit::TestCase
53   fixtures :topics, :developers
55   def setup
56     Topic.write_inheritable_attribute(:validate, nil)
57     Topic.write_inheritable_attribute(:validate_on_create, nil)
58     Topic.write_inheritable_attribute(:validate_on_update, nil)
59   end
61   def test_single_field_validation
62     r = Reply.new
63     r.title = "There's no content!"
64     assert !r.valid?, "A reply without content shouldn't be saveable"
66     r.content = "Messa content!"
67     assert r.valid?, "A reply with content should be saveable"
68   end
70   def test_single_attr_validation_and_error_msg
71     r = Reply.new
72     r.title = "There's no content!"
73     assert !r.valid?
74     assert r.errors.invalid?("content"), "A reply without content should mark that attribute as invalid"
75     assert_equal "Empty", r.errors.on("content"), "A reply without content should contain an error"
76     assert_equal 1, r.errors.count
77   end
79   def test_double_attr_validation_and_error_msg
80     r = Reply.new
81     assert !r.valid?
83     assert r.errors.invalid?("title"), "A reply without title should mark that attribute as invalid"
84     assert_equal "Empty", r.errors.on("title"), "A reply without title should contain an error"
86     assert r.errors.invalid?("content"), "A reply without content should mark that attribute as invalid"
87     assert_equal "Empty", r.errors.on("content"), "A reply without content should contain an error"
89     assert_equal 2, r.errors.count
90   end
92   def test_error_on_create
93     r = Reply.new
94     r.title = "Wrong Create"
95     assert !r.valid?
96     assert r.errors.invalid?("title"), "A reply with a bad title should mark that attribute as invalid"
97     assert_equal "is Wrong Create", r.errors.on("title"), "A reply with a bad content should contain an error"
98   end
100   def test_error_on_update
101     r = Reply.new
102     r.title = "Bad"
103     r.content = "Good"
104     assert r.save, "First save should be successful"
106     r.title = "Wrong Update"
107     assert !r.save, "Second save should fail"
109     assert r.errors.invalid?("title"), "A reply with a bad title should mark that attribute as invalid"
110     assert_equal "is Wrong Update", r.errors.on("title"), "A reply with a bad content should contain an error"
111   end
113   def test_invalid_record_exception
114     assert_raises(ActiveRecord::RecordInvalid) { Reply.create! }
115     assert_raises(ActiveRecord::RecordInvalid) { Reply.new.save! }
117     begin
118       r = Reply.new
119       r.save!
120       flunk
121     rescue ActiveRecord::RecordInvalid => invalid
122       assert_equal r, invalid.record
123     end
124   end
126   def test_exception_on_create_bang_many
127     assert_raises(ActiveRecord::RecordInvalid) do
128       Reply.create!([ { "title" => "OK" }, { "title" => "Wrong Create" }])
129     end
130   end
132   def test_scoped_create_without_attributes
133     Reply.with_scope(:create => {}) do
134       assert_raises(ActiveRecord::RecordInvalid) { Reply.create! }
135     end
136   end
138   def test_create_with_exceptions_using_scope_for_protected_attributes
139     assert_nothing_raised do
140       ProtectedPerson.with_scope( :create => { :first_name => "Mary" } ) do
141         person = ProtectedPerson.create! :addon => "Addon"
142         assert_equal person.first_name, "Mary", "scope should ignore attr_protected"
143       end
144     end
145   end
147   def test_create_with_exceptions_using_scope_and_empty_attributes
148     assert_nothing_raised do
149       ProtectedPerson.with_scope( :create => { :first_name => "Mary" } ) do        
150         person = ProtectedPerson.create!
151         assert_equal person.first_name, "Mary", "should be ok when no attributes are passed to create!"
152       end
153     end
154  end
156   def test_single_error_per_attr_iteration
157     r = Reply.new
158     r.save
160     errors = []
161     r.errors.each { |attr, msg| errors << [attr, msg] }
163     assert errors.include?(["title", "Empty"])
164     assert errors.include?(["content", "Empty"])
165   end
167   def test_multiple_errors_per_attr_iteration_with_full_error_composition
168     r = Reply.new
169     r.title   = "Wrong Create"
170     r.content = "Mismatch"
171     r.save
173     errors = []
174     r.errors.each_full { |error| errors << error }
176     assert_equal "Title is Wrong Create", errors[0]
177     assert_equal "Title is Content Mismatch", errors[1]
178     assert_equal 2, r.errors.count
179   end
181   def test_errors_on_base
182     r = Reply.new
183     r.content = "Mismatch"
184     r.save
185     r.errors.add_to_base "Reply is not dignifying"
187     errors = []
188     r.errors.each_full { |error| errors << error }
190     assert_equal "Reply is not dignifying", r.errors.on_base
192     assert errors.include?("Title Empty")
193     assert errors.include?("Reply is not dignifying")
194     assert_equal 2, r.errors.count
195   end
197   def test_create_without_validation
198     reply = Reply.new
199     assert !reply.save
200     assert reply.save(false)
201   end
203   def test_create_without_validation_bang
204     count = Reply.count
205     assert_nothing_raised { Reply.new.save_without_validation! }
206     assert count+1, Reply.count
207   end
209   def test_validates_each
210     perform = true
211     hits = 0
212     Topic.validates_each(:title, :content, [:title, :content]) do |record, attr|
213       if perform
214         record.errors.add attr, 'gotcha'
215         hits += 1
216       end
217     end
218     t = Topic.new("title" => "valid", "content" => "whatever")
219     assert !t.save
220     assert_equal 4, hits
221     assert_equal %w(gotcha gotcha), t.errors.on(:title)
222     assert_equal %w(gotcha gotcha), t.errors.on(:content)
223   ensure
224     perform = false
225   end
227   def test_no_title_confirmation
228     Topic.validates_confirmation_of(:title)
230     t = Topic.new(:author_name => "Plutarch")
231     assert t.valid?
233     t.title_confirmation = "Parallel Lives"
234     assert !t.valid?
236     t.title_confirmation = nil
237     t.title = "Parallel Lives"
238     assert t.valid?
240     t.title_confirmation = "Parallel Lives"
241     assert t.valid?
242   end
244   def test_title_confirmation
245     Topic.validates_confirmation_of(:title)
247     t = Topic.create("title" => "We should be confirmed","title_confirmation" => "")
248     assert !t.save
250     t.title_confirmation = "We should be confirmed"
251     assert t.save
252   end
254   def test_terms_of_service_agreement_no_acceptance
255     Topic.validates_acceptance_of(:terms_of_service, :on => :create)
257     t = Topic.create("title" => "We should not be confirmed")
258     assert t.save
259   end
261   def test_terms_of_service_agreement
262     Topic.validates_acceptance_of(:terms_of_service, :on => :create)
264     t = Topic.create("title" => "We should be confirmed","terms_of_service" => "")
265     assert !t.save
266     assert_equal "must be accepted", t.errors.on(:terms_of_service)
268     t.terms_of_service = "1"
269     assert t.save
270   end
273   def test_eula
274     Topic.validates_acceptance_of(:eula, :message => "must be abided", :on => :create)
276     t = Topic.create("title" => "We should be confirmed","eula" => "")
277     assert !t.save
278     assert_equal "must be abided", t.errors.on(:eula)
280     t.eula = "1"
281     assert t.save
282   end
284   def test_terms_of_service_agreement_with_accept_value
285     Topic.validates_acceptance_of(:terms_of_service, :on => :create, :accept => "I agree.")
287     t = Topic.create("title" => "We should be confirmed", "terms_of_service" => "")
288     assert !t.save
289     assert_equal "must be accepted", t.errors.on(:terms_of_service)
291     t.terms_of_service = "I agree."
292     assert t.save
293   end
295   def test_validate_presences
296     Topic.validates_presence_of(:title, :content)
298     t = Topic.create
299     assert !t.save
300     assert_equal "can't be blank", t.errors.on(:title)
301     assert_equal "can't be blank", t.errors.on(:content)
303     t.title = "something"
304     t.content  = "   "
306     assert !t.save
307     assert_equal "can't be blank", t.errors.on(:content)
309     t.content = "like stuff"
311     assert t.save
312   end
314   def test_validate_uniqueness
315     Topic.validates_uniqueness_of(:title)
317     t = Topic.new("title" => "I'm unique!")
318     assert t.save, "Should save t as unique"
320     t.content = "Remaining unique"
321     assert t.save, "Should still save t as unique"
323     t2 = Topic.new("title" => "I'm unique!")
324     assert !t2.valid?, "Shouldn't be valid"
325     assert !t2.save, "Shouldn't save t2 as unique"
326     assert_equal "has already been taken", t2.errors.on(:title)
328     t2.title = "Now Im really also unique"
329     assert t2.save, "Should now save t2 as unique"
330   end
332   def test_validate_uniqueness_with_scope
333     Reply.validates_uniqueness_of(:content, :scope => "parent_id")
335     t = Topic.create("title" => "I'm unique!")
337     r1 = t.replies.create "title" => "r1", "content" => "hello world"
338     assert r1.valid?, "Saving r1"
340     r2 = t.replies.create "title" => "r2", "content" => "hello world"
341     assert !r2.valid?, "Saving r2 first time"
343     r2.content = "something else"
344     assert r2.save, "Saving r2 second time"
346     t2 = Topic.create("title" => "I'm unique too!")
347     r3 = t2.replies.create "title" => "r3", "content" => "hello world"
348     assert r3.valid?, "Saving r3"
349   end
351   def test_validate_uniqueness_scoped_to_defining_class
352     t = Topic.create("title" => "What, me worry?")
354     r1 = t.unique_replies.create "title" => "r1", "content" => "a barrel of fun"
355     assert r1.valid?, "Saving r1"
357     r2 = t.silly_unique_replies.create "title" => "r2", "content" => "a barrel of fun"
358     assert !r2.valid?, "Saving r2"
360     # Should succeed as validates_uniqueness_of only applies to
361     # UniqueReply and it's subclasses
362     r3 = t.replies.create "title" => "r2", "content" => "a barrel of fun"
363     assert r3.valid?, "Saving r3"
364   end
366   def test_validate_uniqueness_with_scope_array
367     Reply.validates_uniqueness_of(:author_name, :scope => [:author_email_address, :parent_id])
369     t = Topic.create("title" => "The earth is actually flat!")
371     r1 = t.replies.create "author_name" => "jeremy", "author_email_address" => "jeremy@rubyonrails.com", "title" => "You're crazy!", "content" => "Crazy reply"
372     assert r1.valid?, "Saving r1"
373     
374     r2 = t.replies.create "author_name" => "jeremy", "author_email_address" => "jeremy@rubyonrails.com", "title" => "You're crazy!", "content" => "Crazy reply again..."
375     assert !r2.valid?, "Saving r2. Double reply by same author." 
376     
377     r2.author_email_address = "jeremy_alt_email@rubyonrails.com"
378     assert r2.save, "Saving r2 the second time." 
379     
380     r3 = t.replies.create "author_name" => "jeremy", "author_email_address" => "jeremy_alt_email@rubyonrails.com", "title" => "You're wrong", "content" => "It's cubic"
381     assert !r3.valid?, "Saving r3"
382     
383     r3.author_name = "jj"
384     assert r3.save, "Saving r3 the second time."
385     
386     r3.author_name = "jeremy"
387     assert !r3.save, "Saving r3 the third time."
388   end
390   def test_validate_case_insensitive_uniqueness
391     Topic.validates_uniqueness_of(:title, :parent_id, :case_sensitive => false, :allow_nil => true)
393     t = Topic.new("title" => "I'm unique!", :parent_id => 2)
394     assert t.save, "Should save t as unique"
396     t.content = "Remaining unique"
397     assert t.save, "Should still save t as unique"
399     t2 = Topic.new("title" => "I'm UNIQUE!", :parent_id => 1)
400     assert !t2.valid?, "Shouldn't be valid"
401     assert !t2.save, "Shouldn't save t2 as unique"
402     assert t2.errors.on(:title)
403     assert t2.errors.on(:parent_id)
404     assert_equal "has already been taken", t2.errors.on(:title)
406     t2.title = "I'm truly UNIQUE!"
407     assert !t2.valid?, "Shouldn't be valid"
408     assert !t2.save, "Shouldn't save t2 as unique"
409     assert_nil t2.errors.on(:title)
410     assert t2.errors.on(:parent_id)
412     t2.parent_id = 3
413     assert t2.save, "Should now save t2 as unique"
415     t2.parent_id = nil
416     t2.title = nil
417     assert t2.valid?, "should validate with nil"
418     assert t2.save, "should save with nil"
419   end
421   def test_validate_straight_inheritance_uniqueness
422     w1 = IneptWizard.create(:name => "Rincewind", :city => "Ankh-Morpork")
423     assert w1.valid?, "Saving w1"
425     # Should use validation from base class (which is abstract)
426     w2 = IneptWizard.new(:name => "Rincewind", :city => "Quirm")
427     assert !w2.valid?, "w2 shouldn't be valid"
428     assert w2.errors.on(:name), "Should have errors for name"
429     assert_equal "has already been taken", w2.errors.on(:name), "Should have uniqueness message for name"
431     w3 = Conjurer.new(:name => "Rincewind", :city => "Quirm")
432     assert !w3.valid?, "w3 shouldn't be valid"
433     assert w3.errors.on(:name), "Should have errors for name"
434     assert_equal "has already been taken", w3.errors.on(:name), "Should have uniqueness message for name"
436     w4 = Conjurer.create(:name => "The Amazing Bonko", :city => "Quirm")
437     assert w4.valid?, "Saving w4"
439     w5 = Thaumaturgist.new(:name => "The Amazing Bonko", :city => "Lancre")
440     assert !w5.valid?, "w5 shouldn't be valid"
441     assert w5.errors.on(:name), "Should have errors for name"
442     assert_equal "has already been taken", w5.errors.on(:name), "Should have uniqueness message for name"
444     w6 = Thaumaturgist.new(:name => "Mustrum Ridcully", :city => "Quirm")
445     assert !w6.valid?, "w6 shouldn't be valid"
446     assert w6.errors.on(:city), "Should have errors for city"
447     assert_equal "has already been taken", w6.errors.on(:city), "Should have uniqueness message for city"
448   end
450   def test_validate_format
451     Topic.validates_format_of(:title, :content, :with => /^Validation\smacros \w+!$/, :message => "is bad data")
453     t = Topic.create("title" => "i'm incorrect", "content" => "Validation macros rule!")
454     assert !t.valid?, "Shouldn't be valid"
455     assert !t.save, "Shouldn't save because it's invalid"
456     assert_equal "is bad data", t.errors.on(:title)
457     assert_nil t.errors.on(:content)
459     t.title = "Validation macros rule!"
461     assert t.save
462     assert_nil t.errors.on(:title)
464     assert_raise(ArgumentError) { Topic.validates_format_of(:title, :content) }
465   end
466   
467   # testing ticket #3142
468   def test_validate_format_numeric
469     Topic.validates_format_of(:title, :content, :with => /^[1-9][0-9]*$/, :message => "is bad data")
471     t = Topic.create("title" => "72x", "content" => "6789")
472     assert !t.valid?, "Shouldn't be valid"
473     assert !t.save, "Shouldn't save because it's invalid"
474     assert_equal "is bad data", t.errors.on(:title)
475     assert_nil t.errors.on(:content)
477     t.title = "-11"
478     assert !t.valid?, "Shouldn't be valid"
480     t.title = "03"
481     assert !t.valid?, "Shouldn't be valid"
483     t.title = "z44"
484     assert !t.valid?, "Shouldn't be valid"
486     t.title = "5v7"
487     assert !t.valid?, "Shouldn't be valid"
489     t.title = "1"
491     assert t.save
492     assert_nil t.errors.on(:title)
493   end
495   def test_validates_inclusion_of
496     Topic.validates_inclusion_of( :title, :in => %w( a b c d e f g ) )
498     assert !Topic.create("title" => "a!", "content" => "abc").valid?
499     assert !Topic.create("title" => "a b", "content" => "abc").valid?
500     assert !Topic.create("title" => nil, "content" => "def").valid?
502     t = Topic.create("title" => "a", "content" => "I know you are but what am I?")
503     assert t.valid?
504     t.title = "uhoh"
505     assert !t.valid?
506     assert t.errors.on(:title)
507     assert_equal "is not included in the list", t.errors["title"]
509     assert_raise(ArgumentError) { Topic.validates_inclusion_of( :title, :in => nil ) }
510     assert_raise(ArgumentError) { Topic.validates_inclusion_of( :title, :in => 0) }
512     assert_nothing_raised(ArgumentError) { Topic.validates_inclusion_of( :title, :in => "hi!" ) }
513     assert_nothing_raised(ArgumentError) { Topic.validates_inclusion_of( :title, :in => {} ) }
514     assert_nothing_raised(ArgumentError) { Topic.validates_inclusion_of( :title, :in => [] ) }
515   end
517   def test_validates_inclusion_of_with_allow_nil
518     Topic.validates_inclusion_of( :title, :in => %w( a b c d e f g ), :allow_nil=>true )
520     assert !Topic.create("title" => "a!", "content" => "abc").valid?
521     assert !Topic.create("title" => "", "content" => "abc").valid?
522     assert Topic.create("title" => nil, "content" => "abc").valid?
523   end
525   def test_numericality_with_getter_method
526     Developer.validates_numericality_of( :salary )
527     developer = Developer.new("name" => "michael", "salary" => nil)
528     developer.instance_eval("def salary; read_attribute('salary') ? read_attribute('salary') : 100000; end")
529     assert developer.valid?
530   end
532   def test_validates_length_of_with_allow_nil
533     Topic.validates_length_of( :title, :is => 5, :allow_nil=>true )
535     assert !Topic.create("title" => "ab").valid?
536     assert !Topic.create("title" => "").valid?
537     assert Topic.create("title" => nil).valid?
538     assert Topic.create("title" => "abcde").valid?
539   end
541   def test_validates_length_of_with_allow_blank
542     Topic.validates_length_of( :title, :is => 5, :allow_blank=>true )
544     assert !Topic.create("title" => "ab").valid?
545     assert Topic.create("title" => "").valid?
546     assert Topic.create("title" => nil).valid?
547     assert Topic.create("title" => "abcde").valid?
548   end
550   def test_validates_inclusion_of_with_formatted_message
551     Topic.validates_inclusion_of( :title, :in => %w( a b c d e f g ), :message => "option %s is not in the list" )
553     assert Topic.create("title" => "a", "content" => "abc").valid?
555     t = Topic.create("title" => "uhoh", "content" => "abc")
556     assert !t.valid?
557     assert t.errors.on(:title)
558     assert_equal "option uhoh is not in the list", t.errors["title"]
559   end
561   def test_numericality_with_allow_nil_and_getter_method
562     Developer.validates_numericality_of( :salary, :allow_nil => true)
563     developer = Developer.new("name" => "michael", "salary" => nil)
564     developer.instance_eval("def salary; read_attribute('salary') ? read_attribute('salary') : 100000; end")
565     assert developer.valid?
566   end
568   def test_validates_exclusion_of
569     Topic.validates_exclusion_of( :title, :in => %w( abe monkey ) )
571     assert Topic.create("title" => "something", "content" => "abc").valid?
572     assert !Topic.create("title" => "monkey", "content" => "abc").valid?
573   end
575   def test_validates_exclusion_of_with_formatted_message
576     Topic.validates_exclusion_of( :title, :in => %w( abe monkey ), :message => "option %s is restricted" )
578     assert Topic.create("title" => "something", "content" => "abc")
580     t = Topic.create("title" => "monkey")
581     assert !t.valid?
582     assert t.errors.on(:title)
583     assert_equal "option monkey is restricted", t.errors["title"]
584   end
586   def test_validates_length_of_using_minimum
587     Topic.validates_length_of :title, :minimum => 5
589     t = Topic.create("title" => "valid", "content" => "whatever")
590     assert t.valid?
592     t.title = "not"
593     assert !t.valid?
594     assert t.errors.on(:title)
595     assert_equal "is too short (minimum is 5 characters)", t.errors["title"]
597     t.title = ""
598     assert !t.valid?
599     assert t.errors.on(:title)
600     assert_equal "is too short (minimum is 5 characters)", t.errors["title"]
602     t.title = nil
603     assert !t.valid?
604     assert t.errors.on(:title)
605     assert_equal "is too short (minimum is 5 characters)", t.errors["title"]
606   end
608   def test_optionally_validates_length_of_using_minimum
609     Topic.validates_length_of :title, :minimum => 5, :allow_nil => true
611     t = Topic.create("title" => "valid", "content" => "whatever")
612     assert t.valid?
614     t.title = nil
615     assert t.valid?
616   end
618   def test_validates_length_of_using_maximum
619     Topic.validates_length_of :title, :maximum => 5
621     t = Topic.create("title" => "valid", "content" => "whatever")
622     assert t.valid?
624     t.title = "notvalid"
625     assert !t.valid?
626     assert t.errors.on(:title)
627     assert_equal "is too long (maximum is 5 characters)", t.errors["title"]
629     t.title = ""
630     assert t.valid?
632     t.title = nil
633     assert !t.valid?
634   end
636   def test_optionally_validates_length_of_using_maximum
637     Topic.validates_length_of :title, :maximum => 5, :allow_nil => true
639     t = Topic.create("title" => "valid", "content" => "whatever")
640     assert t.valid?
642     t.title = nil
643     assert t.valid?
644   end
646   def test_validates_length_of_using_within
647     Topic.validates_length_of(:title, :content, :within => 3..5)
649     t = Topic.new("title" => "a!", "content" => "I'm ooooooooh so very long")
650     assert !t.valid?
651     assert_equal "is too short (minimum is 3 characters)", t.errors.on(:title)
652     assert_equal "is too long (maximum is 5 characters)", t.errors.on(:content)
654     t.title = nil
655     t.content = nil
656     assert !t.valid?
657     assert_equal "is too short (minimum is 3 characters)", t.errors.on(:title)
658     assert_equal "is too short (minimum is 3 characters)", t.errors.on(:content)
660     t.title = "abe"
661     t.content  = "mad"
662     assert t.valid?
663   end
665   def test_optionally_validates_length_of_using_within
666     Topic.validates_length_of :title, :content, :within => 3..5, :allow_nil => true
668     t = Topic.create('title' => 'abc', 'content' => 'abcd')
669     assert t.valid?
671     t.title = nil
672     assert t.valid?
673   end
675   def test_optionally_validates_length_of_using_within_on_create
676     Topic.validates_length_of :title, :content, :within => 5..10, :on => :create, :too_long => "my string is too long: %d"
678     t = Topic.create("title" => "thisisnotvalid", "content" => "whatever")
679     assert !t.save
680     assert t.errors.on(:title)
681     assert_equal "my string is too long: 10", t.errors[:title]
683     t.title = "butthisis"
684     assert t.save
686     t.title = "few"
687     assert t.save
689     t.content = "andthisislong"
690     assert t.save
692     t.content = t.title = "iamfine"
693     assert t.save
694   end
696   def test_optionally_validates_length_of_using_within_on_update
697     Topic.validates_length_of :title, :content, :within => 5..10, :on => :update, :too_short => "my string is too short: %d"
699     t = Topic.create("title" => "vali", "content" => "whatever")
700     assert !t.save
701     assert t.errors.on(:title)
703     t.title = "not"
704     assert !t.save
705     assert t.errors.on(:title)
706     assert_equal "my string is too short: 5", t.errors[:title]
708     t.title = "valid"
709     t.content = "andthisistoolong"
710     assert !t.save
711     assert t.errors.on(:content)
713     t.content = "iamfine"
714     assert t.save
715   end
717   def test_validates_length_of_using_is
718     Topic.validates_length_of :title, :is => 5
720     t = Topic.create("title" => "valid", "content" => "whatever")
721     assert t.valid?
723     t.title = "notvalid"
724     assert !t.valid?
725     assert t.errors.on(:title)
726     assert_equal "is the wrong length (should be 5 characters)", t.errors["title"]
728     t.title = ""
729     assert !t.valid?
731     t.title = nil
732     assert !t.valid?
733   end
735   def test_optionally_validates_length_of_using_is
736     Topic.validates_length_of :title, :is => 5, :allow_nil => true
738     t = Topic.create("title" => "valid", "content" => "whatever")
739     assert t.valid?
741     t.title = nil
742     assert t.valid?
743   end
745   def test_validates_length_of_using_bignum
746     bigmin = 2 ** 30
747     bigmax = 2 ** 32
748     bigrange = bigmin...bigmax
749     assert_nothing_raised do
750       Topic.validates_length_of :title, :is => bigmin + 5
751       Topic.validates_length_of :title, :within => bigrange
752       Topic.validates_length_of :title, :in => bigrange
753       Topic.validates_length_of :title, :minimum => bigmin
754       Topic.validates_length_of :title, :maximum => bigmax
755     end
756   end
758   def test_validates_length_with_globally_modified_error_message
759     ActiveRecord::Errors.default_error_messages[:too_short] = 'tu est trops petit hombre %d'
760     Topic.validates_length_of :title, :minimum => 10
761     t = Topic.create(:title => 'too short')
762     assert !t.valid?
764     assert_equal 'tu est trops petit hombre 10', t.errors['title']
765   end
767   def test_validates_size_of_association
768     assert_nothing_raised { Topic.validates_size_of :replies, :minimum => 1 }
769     t = Topic.new('title' => 'noreplies', 'content' => 'whatever')
770     assert !t.save
771     assert t.errors.on(:replies)
772     reply = t.replies.build('title' => 'areply', 'content' => 'whateveragain')
773     assert t.valid?
774   end
776   def test_validates_length_of_nasty_params
777     assert_raise(ArgumentError) { Topic.validates_length_of(:title, :minimum=>6, :maximum=>9) }
778     assert_raise(ArgumentError) { Topic.validates_length_of(:title, :within=>6, :maximum=>9) }
779     assert_raise(ArgumentError) { Topic.validates_length_of(:title, :within=>6, :minimum=>9) }
780     assert_raise(ArgumentError) { Topic.validates_length_of(:title, :within=>6, :is=>9) }
781     assert_raise(ArgumentError) { Topic.validates_length_of(:title, :minimum=>"a") }
782     assert_raise(ArgumentError) { Topic.validates_length_of(:title, :maximum=>"a") }
783     assert_raise(ArgumentError) { Topic.validates_length_of(:title, :within=>"a") }
784     assert_raise(ArgumentError) { Topic.validates_length_of(:title, :is=>"a") }
785   end
787   def test_validates_length_of_custom_errors_for_minimum_with_message
788     Topic.validates_length_of( :title, :minimum=>5, :message=>"boo %d" )
789     t = Topic.create("title" => "uhoh", "content" => "whatever")
790     assert !t.valid?
791     assert t.errors.on(:title)
792     assert_equal "boo 5", t.errors["title"]
793   end
795   def test_validates_length_of_custom_errors_for_minimum_with_too_short
796     Topic.validates_length_of( :title, :minimum=>5, :too_short=>"hoo %d" )
797     t = Topic.create("title" => "uhoh", "content" => "whatever")
798     assert !t.valid?
799     assert t.errors.on(:title)
800     assert_equal "hoo 5", t.errors["title"]
801   end
803   def test_validates_length_of_custom_errors_for_maximum_with_message
804     Topic.validates_length_of( :title, :maximum=>5, :message=>"boo %d" )
805     t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
806     assert !t.valid?
807     assert t.errors.on(:title)
808     assert_equal "boo 5", t.errors["title"]
809   end
811   def test_validates_length_of_custom_errors_for_maximum_with_too_long
812     Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d" )
813     t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
814     assert !t.valid?
815     assert t.errors.on(:title)
816     assert_equal "hoo 5", t.errors["title"]
817   end
819   def test_validates_length_of_custom_errors_for_is_with_message
820     Topic.validates_length_of( :title, :is=>5, :message=>"boo %d" )
821     t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
822     assert !t.valid?
823     assert t.errors.on(:title)
824     assert_equal "boo 5", t.errors["title"]
825   end
827   def test_validates_length_of_custom_errors_for_is_with_wrong_length
828     Topic.validates_length_of( :title, :is=>5, :wrong_length=>"hoo %d" )
829     t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
830     assert !t.valid?
831     assert t.errors.on(:title)
832     assert_equal "hoo 5", t.errors["title"]
833   end
835   def kcode_scope(kcode)
836     orig_kcode = $KCODE
837     $KCODE = kcode
838     begin
839       yield
840     ensure
841       $KCODE = orig_kcode
842     end
843   end
845   def test_validates_length_of_using_minimum_utf8
846     kcode_scope('UTF8') do
847       Topic.validates_length_of :title, :minimum => 5
849       t = Topic.create("title" => "一二三四五", "content" => "whatever")
850       assert t.valid?
852       t.title = "一二三四"
853       assert !t.valid?
854       assert t.errors.on(:title)
855       assert_equal "is too short (minimum is 5 characters)", t.errors["title"]
856     end
857   end
859   def test_validates_length_of_using_maximum_utf8
860     kcode_scope('UTF8') do
861       Topic.validates_length_of :title, :maximum => 5
863       t = Topic.create("title" => "一二三四五", "content" => "whatever")
864       assert t.valid?
866       t.title = "一二34五六"
867       assert !t.valid?
868       assert t.errors.on(:title)
869       assert_equal "is too long (maximum is 5 characters)", t.errors["title"]
870     end
871   end
873   def test_validates_length_of_using_within_utf8
874     kcode_scope('UTF8') do
875       Topic.validates_length_of(:title, :content, :within => 3..5)
877       t = Topic.new("title" => "一二", "content" => "12三四五六七")
878       assert !t.valid?
879       assert_equal "is too short (minimum is 3 characters)", t.errors.on(:title)
880       assert_equal "is too long (maximum is 5 characters)", t.errors.on(:content)
881       t.title = "一二三"
882       t.content  = "12三"
883       assert t.valid?
884     end
885   end
887   def test_optionally_validates_length_of_using_within_utf8
888     kcode_scope('UTF8') do
889       Topic.validates_length_of :title, :content, :within => 3..5, :allow_nil => true
891       t = Topic.create('title' => '一二三', 'content' => '一二三四五')
892       assert t.valid?
894       t.title = nil
895       assert t.valid?
896     end
897   end
899   def test_optionally_validates_length_of_using_within_on_create_utf8
900     kcode_scope('UTF8') do
901       Topic.validates_length_of :title, :content, :within => 5..10, :on => :create, :too_long => "長すぎます: %d"
903       t = Topic.create("title" => "一二三四五六七八九十A", "content" => "whatever")
904       assert !t.save
905       assert t.errors.on(:title)
906       assert_equal "長すぎます: 10", t.errors[:title]
908       t.title = "一二三四五六七八九"
909       assert t.save
911       t.title = "一二3"
912       assert t.save
914       t.content = "一二三四五六七八九十"
915       assert t.save
917       t.content = t.title = "一二三四五六"
918       assert t.save
919     end
920   end
922   def test_optionally_validates_length_of_using_within_on_update_utf8
923     kcode_scope('UTF8') do    
924       Topic.validates_length_of :title, :content, :within => 5..10, :on => :update, :too_short => "短すぎます: %d"
926       t = Topic.create("title" => "一二三4", "content" => "whatever")
927       assert !t.save
928       assert t.errors.on(:title)
930       t.title = "1二三4"
931       assert !t.save
932       assert t.errors.on(:title)
933       assert_equal "短すぎます: 5", t.errors[:title]
935       t.title = "valid"
936       t.content = "一二三四五六七八九十A"
937       assert !t.save
938       assert t.errors.on(:content)
940       t.content = "一二345"
941       assert t.save
942     end
943   end
945   def test_validates_length_of_using_is_utf8
946     kcode_scope('UTF8') do
947       Topic.validates_length_of :title, :is => 5
949       t = Topic.create("title" => "一二345", "content" => "whatever")
950       assert t.valid?
952       t.title = "一二345六"
953       assert !t.valid?
954       assert t.errors.on(:title)
955       assert_equal "is the wrong length (should be 5 characters)", t.errors["title"]
956     end
957   end
959   def test_validates_size_of_association_utf8
960     kcode_scope('UTF8') do
961       assert_nothing_raised { Topic.validates_size_of :replies, :minimum => 1 }
962       t = Topic.new('title' => 'あいうえお', 'content' => 'かきくけこ')
963       assert !t.save
964       assert t.errors.on(:replies)
965       t.replies.build('title' => 'あいうえお', 'content' => 'かきくけこ')
966       assert t.valid?
967     end
968   end
970   def test_validates_associated_many
971     Topic.validates_associated( :replies )
972     t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
973     t.replies << [r = Reply.new("title" => "A reply"), r2 = Reply.new("title" => "Another reply", "content" => "non-empty"), r3 = Reply.new("title" => "Yet another reply"), r4 = Reply.new("title" => "The last reply", "content" => "non-empty")]
974     assert !t.valid?
975     assert t.errors.on(:replies)
976     assert_equal 1, r.errors.count  # make sure all associated objects have been validated
977     assert_equal 0, r2.errors.count
978     assert_equal 1, r3.errors.count
979     assert_equal 0, r4.errors.count
980     r.content = r3.content = "non-empty"
981     assert t.valid?
982   end
984   def test_validates_associated_one
985     Reply.validates_associated( :topic )
986     Topic.validates_presence_of( :content )
987     r = Reply.new("title" => "A reply", "content" => "with content!")
988     r.topic = Topic.create("title" => "uhohuhoh")
989     assert !r.valid?
990     assert r.errors.on(:topic)
991     r.topic.content = "non-empty"
992     assert r.valid?
993   end
995   def test_validate_block
996     Topic.validate { |topic| topic.errors.add("title", "will never be valid") }
997     t = Topic.create("title" => "Title", "content" => "whatever")
998     assert !t.valid?
999     assert t.errors.on(:title)
1000     assert_equal "will never be valid", t.errors["title"]
1001   end
1003   def test_invalid_validator
1004     Topic.validate 3
1005     assert_raise(ActiveRecord::ActiveRecordError) { t = Topic.create }
1006   end
1008   def test_throw_away_typing
1009     d = Developer.new("name" => "David", "salary" => "100,000")
1010     assert !d.valid?
1011     assert_equal 100, d.salary
1012     assert_equal "100,000", d.salary_before_type_cast
1013   end
1015   def test_validates_acceptance_of_with_custom_error_using_quotes
1016     Developer.validates_acceptance_of :salary, :message=> "This string contains 'single' and \"double\" quotes"
1017     d = Developer.new
1018     d.salary = "0"
1019     assert !d.valid?
1020     assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:salary).last
1021   end
1023   def test_validates_confirmation_of_with_custom_error_using_quotes
1024     Developer.validates_confirmation_of :name, :message=> "confirm 'single' and \"double\" quotes"
1025     d = Developer.new
1026     d.name = "John"
1027     d.name_confirmation = "Johnny"
1028     assert !d.valid?
1029     assert_equal "confirm 'single' and \"double\" quotes", d.errors.on(:name)
1030   end
1032   def test_validates_format_of_with_custom_error_using_quotes
1033     Developer.validates_format_of :name, :with => /^(A-Z*)$/, :message=> "format 'single' and \"double\" quotes"
1034     d = Developer.new
1035     d.name = d.name_confirmation = "John 32"
1036     assert !d.valid?
1037     assert_equal "format 'single' and \"double\" quotes", d.errors.on(:name)
1038   end
1040   def test_validates_inclusion_of_with_custom_error_using_quotes
1041     Developer.validates_inclusion_of :salary, :in => 1000..80000, :message=> "This string contains 'single' and \"double\" quotes"
1042     d = Developer.new
1043     d.salary = "90,000"
1044     assert !d.valid?
1045     assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:salary).last
1046   end
1048   def test_validates_length_of_with_custom_too_long_using_quotes
1049     Developer.validates_length_of :name, :maximum => 4, :too_long=> "This string contains 'single' and \"double\" quotes"
1050     d = Developer.new
1051     d.name = "Jeffrey"
1052     assert !d.valid?
1053     assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:name).last
1054   end
1056   def test_validates_length_of_with_custom_too_short_using_quotes
1057     Developer.validates_length_of :name, :minimum => 4, :too_short=> "This string contains 'single' and \"double\" quotes"
1058     d = Developer.new
1059     d.name = "Joe"
1060     assert !d.valid?
1061     assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:name).last
1062   end
1064   def test_validates_length_of_with_custom_message_using_quotes
1065     Developer.validates_length_of :name, :minimum => 4, :message=> "This string contains 'single' and \"double\" quotes"
1066     d = Developer.new
1067     d.name = "Joe"
1068     assert !d.valid?
1069     assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:name).last
1070   end
1072   def test_validates_presence_of_with_custom_message_using_quotes
1073     Developer.validates_presence_of :non_existent, :message=> "This string contains 'single' and \"double\" quotes"
1074     d = Developer.new
1075     d.name = "Joe"
1076     assert !d.valid?
1077     assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:non_existent)
1078   end
1080   def test_validates_uniqueness_of_with_custom_message_using_quotes
1081     Developer.validates_uniqueness_of :name, :message=> "This string contains 'single' and \"double\" quotes"
1082     d = Developer.new
1083     d.name = "David"
1084     assert !d.valid?
1085     assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:name).last
1086   end
1088   def test_validates_associated_with_custom_message_using_quotes
1089     Reply.validates_associated :topic, :message=> "This string contains 'single' and \"double\" quotes"
1090     Topic.validates_presence_of :content
1091     r = Reply.create("title" => "A reply", "content" => "with content!")
1092     r.topic = Topic.create("title" => "uhohuhoh")
1093     assert !r.valid?
1094     assert_equal "This string contains 'single' and \"double\" quotes", r.errors.on(:topic).last
1095   end
1097   def test_if_validation_using_method_true
1098     # When the method returns true
1099     Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d", :if => :condition_is_true )
1100     t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1101     assert !t.valid?
1102     assert t.errors.on(:title)
1103     assert_equal "hoo 5", t.errors["title"]
1104   end
1106   def test_unless_validation_using_method_true
1107     # When the method returns true
1108     Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d", :unless => :condition_is_true )
1109     t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1110     assert t.valid?
1111     assert !t.errors.on(:title)
1112   end
1114   def test_if_validation_using_method_false
1115     # When the method returns false
1116     Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d", :if => :condition_is_true_but_its_not )
1117     t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1118     assert t.valid?
1119     assert !t.errors.on(:title)
1120   end
1122   def test_unless_validation_using_method_false
1123     # When the method returns false
1124     Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d", :unless => :condition_is_true_but_its_not )
1125     t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1126     assert !t.valid?
1127     assert t.errors.on(:title)
1128     assert_equal "hoo 5", t.errors["title"]
1129   end
1131   def test_if_validation_using_string_true
1132     # When the evaluated string returns true
1133     Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d", :if => "a = 1; a == 1" )
1134     t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1135     assert !t.valid?
1136     assert t.errors.on(:title)
1137     assert_equal "hoo 5", t.errors["title"]
1138   end
1140   def test_unless_validation_using_string_true
1141     # When the evaluated string returns true
1142     Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d", :unless => "a = 1; a == 1" )
1143     t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1144     assert t.valid?
1145     assert !t.errors.on(:title)
1146   end
1148   def test_if_validation_using_string_false
1149     # When the evaluated string returns false
1150     Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d", :if => "false")
1151     t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1152     assert t.valid?
1153     assert !t.errors.on(:title)
1154   end
1156   def test_unless_validation_using_string_false
1157     # When the evaluated string returns false
1158     Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d", :unless => "false")
1159     t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1160     assert !t.valid?
1161     assert t.errors.on(:title)
1162     assert_equal "hoo 5", t.errors["title"]
1163   end
1165   def test_if_validation_using_block_true
1166     # When the block returns true
1167     Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d",
1168       :if => Proc.new { |r| r.content.size > 4 } )
1169     t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1170     assert !t.valid?
1171     assert t.errors.on(:title)
1172     assert_equal "hoo 5", t.errors["title"]
1173   end
1175   def test_unless_validation_using_block_true
1176     # When the block returns true
1177     Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d",
1178       :unless => Proc.new { |r| r.content.size > 4 } )
1179     t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1180     assert t.valid?
1181     assert !t.errors.on(:title)
1182   end
1184   def test_if_validation_using_block_false
1185     # When the block returns false
1186     Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d",
1187       :if => Proc.new { |r| r.title != "uhohuhoh"} )
1188     t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1189     assert t.valid?
1190     assert !t.errors.on(:title)
1191   end
1193   def test_unless_validation_using_block_false
1194     # When the block returns false
1195     Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %d",
1196       :unless => Proc.new { |r| r.title != "uhohuhoh"} )
1197     t = Topic.create("title" => "uhohuhoh", "content" => "whatever")
1198     assert !t.valid?
1199     assert t.errors.on(:title)
1200     assert_equal "hoo 5", t.errors["title"]
1201   end
1203   def test_validates_associated_missing
1204     Reply.validates_presence_of(:topic)
1205     r = Reply.create("title" => "A reply", "content" => "with content!")
1206     assert !r.valid?
1207     assert r.errors.on(:topic)
1208     
1209     r.topic = Topic.find :first
1210     assert r.valid?
1211   end
1213   def test_errors_to_xml
1214     r = Reply.new :title => "Wrong Create"
1215     assert !r.valid?
1216     xml = r.errors.to_xml(:skip_instruct => true)
1217     assert_equal "<errors>", xml.first(8)
1218     assert xml.include?("<error>Title is Wrong Create</error>")
1219     assert xml.include?("<error>Content Empty</error>")
1220   end
1222  def test_validation_order
1223     Topic.validates_presence_of :title
1224     Topic.validates_length_of :title, :minimum => 2
1226     t = Topic.new("title" => "")
1227     assert !t.valid?
1228     assert_equal "can't be blank", t.errors.on("title").first
1229  end
1231   # previous implementation of validates_presence_of eval'd the 
1232   # string with the wrong binding, this regression test is to 
1233   # ensure that it works correctly
1234   def test_validation_with_if_as_string
1235     Topic.validates_presence_of(:title)
1236     Topic.validates_presence_of(:author_name, :if => "title.to_s.match('important')")
1238     t = Topic.new
1239     assert !t.valid?, "A topic without a title should not be valid"
1240     assert !t.errors.invalid?("author_name"), "A topic without an 'important' title should not require an author"
1242     t.title = "Just a title"
1243     assert t.valid?, "A topic with a basic title should be valid"
1245     t.title = "A very important title"
1246     assert !t.valid?, "A topic with an important title, but without an author, should not be valid"
1247     assert t.errors.invalid?("author_name"), "A topic with an 'important' title should require an author"
1249     t.author_name = "Hubert J. Farnsworth"
1250     assert t.valid?, "A topic with an important title and author should be valid"
1251   end
1255 class ValidatesNumericalityTest < Test::Unit::TestCase
1256   NIL = [nil]
1257   BLANK = ["", " ", " \t \r \n"]
1258   BIGDECIMAL_STRINGS = %w(12345678901234567890.1234567890) # 30 significent digits
1259   FLOAT_STRINGS = %w(0.0 +0.0 -0.0 10.0 10.5 -10.5 -0.0001 -090.1 90.1e1 -90.1e5 -90.1e-5 90e-5)
1260   INTEGER_STRINGS = %w(0 +0 -0 10 +10 -10 0090 -090)
1261   FLOATS = [0.0, 10.0, 10.5, -10.5, -0.0001] + FLOAT_STRINGS
1262   INTEGERS = [0, 10, -10] + INTEGER_STRINGS
1263   BIGDECIMAL = BIGDECIMAL_STRINGS.collect! { |bd| BigDecimal.new(bd) }
1264   JUNK = ["not a number", "42 not a number", "0xdeadbeef", "00-1", "--3", "+-3", "+3-1", "-+019.0", "12.12.13.12", "123\nnot a number"]
1266   def setup
1267     Topic.write_inheritable_attribute(:validate, nil)
1268     Topic.write_inheritable_attribute(:validate_on_create, nil)
1269     Topic.write_inheritable_attribute(:validate_on_update, nil)
1270   end
1272   def test_default_validates_numericality_of
1273     Topic.validates_numericality_of :approved
1275     invalid!(NIL + BLANK + JUNK)
1276     valid!(FLOATS + INTEGERS + BIGDECIMAL)
1277   end
1279   def test_validates_numericality_of_with_nil_allowed
1280     Topic.validates_numericality_of :approved, :allow_nil => true
1282     invalid!(BLANK + JUNK)
1283     valid!(NIL + FLOATS + INTEGERS + BIGDECIMAL)
1284   end
1286   def test_validates_numericality_of_with_integer_only
1287     Topic.validates_numericality_of :approved, :only_integer => true
1289     invalid!(NIL + BLANK + JUNK + FLOATS + BIGDECIMAL)
1290     valid!(INTEGERS)
1291   end
1293   def test_validates_numericality_of_with_integer_only_and_nil_allowed
1294     Topic.validates_numericality_of :approved, :only_integer => true, :allow_nil => true
1296     invalid!(BLANK + JUNK + FLOATS + BIGDECIMAL)
1297     valid!(NIL + INTEGERS)
1298   end
1300   def test_validates_numericality_with_greater_than
1301     Topic.validates_numericality_of :approved, :greater_than => 10
1303     invalid!([-10, 10], 'must be greater than 10')
1304     valid!([11])
1305   end
1307   def test_validates_numericality_with_greater_than_or_equal
1308     Topic.validates_numericality_of :approved, :greater_than_or_equal_to => 10
1310     invalid!([-9, 9], 'must be greater than or equal to 10')
1311     valid!([10])
1312   end
1314   def test_validates_numericality_with_equal_to
1315     Topic.validates_numericality_of :approved, :equal_to => 10
1317     invalid!([-10, 11], 'must be equal to 10')
1318     valid!([10])
1319   end
1321   def test_validates_numericality_with_less_than
1322     Topic.validates_numericality_of :approved, :less_than => 10
1324     invalid!([10], 'must be less than 10')
1325     valid!([-9, 9])
1326   end
1328   def test_validates_numericality_with_less_than_or_equal_to
1329     Topic.validates_numericality_of :approved, :less_than_or_equal_to => 10
1331     invalid!([11], 'must be less than or equal to 10')
1332     valid!([-10, 10])
1333   end
1335   def test_validates_numericality_with_odd
1336     Topic.validates_numericality_of :approved, :odd => true
1338     invalid!([-2, 2], 'must be odd')
1339     valid!([-1, 1])
1340   end
1342   def test_validates_numericality_with_even
1343     Topic.validates_numericality_of :approved, :even => true
1345     invalid!([-1, 1], 'must be even')
1346     valid!([-2, 2])
1347   end
1349   def test_validates_numericality_with_greater_than_less_than_and_even
1350     Topic.validates_numericality_of :approved, :greater_than => 1, :less_than => 4, :even => true
1352     invalid!([1, 3, 4])
1353     valid!([2])
1354   end
1356   private
1357     def invalid!(values, error=nil)
1358       with_each_topic_approved_value(values) do |topic, value|
1359         assert !topic.valid?, "#{value.inspect} not rejected as a number"
1360         assert topic.errors.on(:approved)
1361         assert_equal error, topic.errors.on(:approved) if error
1362       end
1363     end
1365     def valid!(values)
1366       with_each_topic_approved_value(values) do |topic, value|
1367         assert topic.valid?, "#{value.inspect} not accepted as a number"
1368       end
1369     end
1371     def with_each_topic_approved_value(values)
1372       topic = Topic.new("title" => "numeric test", "content" => "whatever")
1373       values.each do |value|
1374         topic.approved = value
1375         yield topic, value
1376       end
1377     end