diff options
Diffstat (limited to 'activerecord/test')
15 files changed, 716 insertions, 1756 deletions
diff --git a/activerecord/test/cases/associations/eager_load_nested_include_test.rb b/activerecord/test/cases/associations/eager_load_nested_include_test.rb index cb7fe9698b..f313a75233 100644 --- a/activerecord/test/cases/associations/eager_load_nested_include_test.rb +++ b/activerecord/test/cases/associations/eager_load_nested_include_test.rb @@ -7,7 +7,7 @@ require 'models/categorization' require 'active_support/core_ext/array/random_access' module Remembered - extend ActiveSupport::DependencyModule + extend ActiveSupport::Concern included do after_create :remember diff --git a/activerecord/test/cases/associations/has_one_associations_test.rb b/activerecord/test/cases/associations/has_one_associations_test.rb index 1ddb3f49bf..7140de77ea 100644 --- a/activerecord/test/cases/associations/has_one_associations_test.rb +++ b/activerecord/test/cases/associations/has_one_associations_test.rb @@ -183,7 +183,7 @@ class HasOneAssociationsTest < ActiveRecord::TestCase account = firm.build_account assert !account.save - assert_equal "can't be empty", account.errors.on("credit_limit") + assert_equal ["can't be empty"], account.errors["credit_limit"] end def test_build_association_twice_without_saving_affects_nothing @@ -219,7 +219,7 @@ class HasOneAssociationsTest < ActiveRecord::TestCase assert_equal account, firm.account assert !account.save assert_equal account, firm.account - assert_equal "can't be empty", account.errors.on("credit_limit") + assert_equal ["can't be empty"], account.errors["credit_limit"] end def test_create diff --git a/activerecord/test/cases/autosave_association_test.rb b/activerecord/test/cases/autosave_association_test.rb index f95b2c0079..ddca5e962d 100644 --- a/activerecord/test/cases/autosave_association_test.rb +++ b/activerecord/test/cases/autosave_association_test.rb @@ -61,7 +61,7 @@ class TestDefaultAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCas assert !firm.account.valid? assert !firm.valid? assert !firm.save - assert_equal "is invalid", firm.errors.on("account") + assert_equal ["is invalid"], firm.errors["account"] end def test_save_succeeds_for_invalid_has_one_with_validate_false @@ -160,7 +160,7 @@ class TestDefaultAutosaveAssociationOnABelongsToAssociation < ActiveRecord::Test assert !log.developer.valid? assert !log.valid? assert !log.save - assert_equal "is invalid", log.errors.on("developer") + assert_equal ["is invalid"], log.errors["developer"] end def test_save_succeeds_for_invalid_belongs_to_with_validate_false @@ -650,16 +650,16 @@ class TestAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCase def test_should_automatically_validate_the_associated_model @pirate.ship.name = '' - assert !@pirate.valid? - assert !@pirate.errors.on(:ship_name).blank? + assert @pirate.invalid? + assert @pirate.errors[:ship_name].any? end def test_should_merge_errors_on_the_associated_models_onto_the_parent_even_if_it_is_not_valid @pirate.ship.name = nil @pirate.catchphrase = nil - assert !@pirate.valid? - assert !@pirate.errors.on(:ship_name).blank? - assert !@pirate.errors.on(:catchphrase).blank? + assert @pirate.invalid? + assert @pirate.errors[:ship_name].any? + assert @pirate.errors[:catchphrase].any? end def test_should_still_allow_to_bypass_validations_on_the_associated_model @@ -740,16 +740,16 @@ class TestAutosaveAssociationOnABelongsToAssociation < ActiveRecord::TestCase def test_should_automatically_validate_the_associated_model @ship.pirate.catchphrase = '' - assert !@ship.valid? - assert !@ship.errors.on(:pirate_catchphrase).blank? + assert @ship.invalid? + assert @ship.errors[:pirate_catchphrase].any? end def test_should_merge_errors_on_the_associated_model_onto_the_parent_even_if_it_is_not_valid @ship.name = nil @ship.pirate.catchphrase = nil - assert !@ship.valid? - assert !@ship.errors.on(:name).blank? - assert !@ship.errors.on(:pirate_catchphrase).blank? + assert @ship.invalid? + assert @ship.errors[:name].any? + assert @ship.errors[:pirate_catchphrase].any? end def test_should_still_allow_to_bypass_validations_on_the_associated_model @@ -811,16 +811,16 @@ module AutosaveAssociationOnACollectionAssociationTests @pirate.send(@association_name).each { |child| child.name = '' } assert !@pirate.valid? - assert_equal "can't be blank", @pirate.errors.on("#{@association_name}_name") - assert @pirate.errors.on(@association_name).blank? + assert_equal ["can't be blank"], @pirate.errors["#{@association_name}_name"] + assert @pirate.errors[@association_name].empty? end def test_should_not_use_default_invalid_error_on_associated_models @pirate.send(@association_name).build(:name => '') assert !@pirate.valid? - assert_equal "can't be blank", @pirate.errors.on("#{@association_name}_name") - assert @pirate.errors.on(@association_name).blank? + assert_equal ["can't be blank"], @pirate.errors["#{@association_name}_name"] + assert @pirate.errors[@association_name].empty? end def test_should_merge_errors_on_the_associated_models_onto_the_parent_even_if_it_is_not_valid @@ -828,8 +828,8 @@ module AutosaveAssociationOnACollectionAssociationTests @pirate.catchphrase = nil assert !@pirate.valid? - assert_equal "can't be blank", @pirate.errors.on("#{@association_name}_name") - assert !@pirate.errors.on(:catchphrase).blank? + assert_equal ["can't be blank"], @pirate.errors["#{@association_name}_name"] + assert @pirate.errors[:catchphrase].any? end def test_should_allow_to_bypass_validations_on_the_associated_models_on_update diff --git a/activerecord/test/cases/helper.rb b/activerecord/test/cases/helper.rb index f82784836e..c30186286d 100644 --- a/activerecord/test/cases/helper.rb +++ b/activerecord/test/cases/helper.rb @@ -63,7 +63,7 @@ end class ActiveSupport::TestCase include ActiveRecord::TestFixtures - include ActiveRecord::Testing::RepairHelper + include ActiveModel::ValidationsRepairHelper self.fixture_path = FIXTURES_ROOT self.use_instantiated_fixtures = false diff --git a/activerecord/test/cases/repair_helper.rb b/activerecord/test/cases/repair_helper.rb index 686bfee46d..80d04010d6 100644 --- a/activerecord/test/cases/repair_helper.rb +++ b/activerecord/test/cases/repair_helper.rb @@ -1,7 +1,7 @@ module ActiveRecord module Testing module RepairHelper - extend ActiveSupport::DependencyModule + extend ActiveSupport::Concern module Toolbox def self.record_validations(*model_classes) diff --git a/activerecord/test/cases/schema_dumper_test.rb b/activerecord/test/cases/schema_dumper_test.rb index f9ad7f3ba3..4f02be3c06 100644 --- a/activerecord/test/cases/schema_dumper_test.rb +++ b/activerecord/test/cases/schema_dumper_test.rb @@ -190,4 +190,9 @@ class SchemaDumperTest < ActiveRecord::TestCase output = stream.string assert_match %r{:precision => 3,[[:space:]]+:scale => 2,[[:space:]]+:default => 2.78}, output end + + def test_schema_dump_keeps_large_precision_integer_columns_as_decimal + output = standard_dump + assert_match %r{t.decimal\s+"atoms_in_universe",\s+:precision => 55,\s+:scale => 0}, output + end end diff --git a/activerecord/test/cases/validations/association_validation_test.rb b/activerecord/test/cases/validations/association_validation_test.rb new file mode 100644 index 0000000000..b1203c12ed --- /dev/null +++ b/activerecord/test/cases/validations/association_validation_test.rb @@ -0,0 +1,101 @@ +# encoding: utf-8 +require "cases/helper" +require 'models/topic' +require 'models/reply' +require 'models/owner' + +class AssociationValidationTest < ActiveRecord::TestCase + fixtures :topics, :owners + + repair_validations(Topic) + + def test_validates_size_of_association + repair_validations(Owner) do + assert_nothing_raised { Owner.validates_size_of :pets, :minimum => 1 } + o = Owner.new('name' => 'nopets') + assert !o.save + assert o.errors[:pets].any? + pet = o.pets.build('name' => 'apet') + assert o.valid? + end + end + + def test_validates_size_of_association_using_within + repair_validations(Owner) do + assert_nothing_raised { Owner.validates_size_of :pets, :within => 1..2 } + o = Owner.new('name' => 'nopets') + assert !o.save + assert o.errors[:pets].any? + + pet = o.pets.build('name' => 'apet') + assert o.valid? + + 2.times { o.pets.build('name' => 'apet') } + assert !o.save + assert o.errors[:pets].any? + end + end + + def test_validates_associated_many + Topic.validates_associated( :replies ) + t = Topic.create("title" => "uhohuhoh", "content" => "whatever") + 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")] + assert !t.valid? + assert t.errors[:replies].any? + assert_equal 1, r.errors.count # make sure all associated objects have been validated + assert_equal 0, r2.errors.count + assert_equal 1, r3.errors.count + assert_equal 0, r4.errors.count + r.content = r3.content = "non-empty" + assert t.valid? + end + + def test_validates_associated_one + repair_validations(Reply) do + Reply.validates_associated( :topic ) + Topic.validates_presence_of( :content ) + r = Reply.new("title" => "A reply", "content" => "with content!") + r.topic = Topic.create("title" => "uhohuhoh") + assert !r.valid? + assert r.errors[:topic].any? + r.topic.content = "non-empty" + assert r.valid? + end + end + + def test_validates_associated_with_custom_message_using_quotes + repair_validations(Reply) do + Reply.validates_associated :topic, :message=> "This string contains 'single' and \"double\" quotes" + Topic.validates_presence_of :content + r = Reply.create("title" => "A reply", "content" => "with content!") + r.topic = Topic.create("title" => "uhohuhoh") + assert !r.valid? + assert_equal ["This string contains 'single' and \"double\" quotes"], r.errors[:topic] + end + end + + def test_validates_associated_missing + repair_validations(Reply) do + Reply.validates_presence_of(:topic) + r = Reply.create("title" => "A reply", "content" => "with content!") + assert !r.valid? + assert r.errors[:topic].any? + + r.topic = Topic.find :first + assert r.valid? + end + end + + def test_validates_size_of_association_utf8 + repair_validations(Owner) do + with_kcode('UTF8') do + assert_nothing_raised { Owner.validates_size_of :pets, :minimum => 1 } + o = Owner.new('name' => 'あいうえおかきくけこ') + assert !o.save + assert o.errors[:pets].any? + o.pets.build('name' => 'あいうえおかきくけこ') + assert o.valid? + end + end + end +end diff --git a/activerecord/test/cases/validations/i18n_generate_message_validation_test.rb b/activerecord/test/cases/validations/i18n_generate_message_validation_test.rb new file mode 100644 index 0000000000..29c10de4fe --- /dev/null +++ b/activerecord/test/cases/validations/i18n_generate_message_validation_test.rb @@ -0,0 +1,166 @@ +require "cases/helper" +require 'models/topic' +require 'models/reply' + +class I18nGenerateMessageValidationTest < Test::Unit::TestCase + def setup + reset_callbacks Topic + @topic = Topic.new + I18n.backend.store_translations :'en', { + :activerecord => { + :errors => { + :messages => { + :taken => "has already been taken", + } + } + } + } + end + + def reset_callbacks(*models) + models.each do |model| + model.instance_variable_set("@validate_callbacks", ActiveSupport::Callbacks::CallbackChain.new) + model.instance_variable_set("@validate_on_create_callbacks", ActiveSupport::Callbacks::CallbackChain.new) + model.instance_variable_set("@validate_on_update_callbacks", ActiveSupport::Callbacks::CallbackChain.new) + end + end + + # validates_inclusion_of: generate_message(attr_name, :inclusion, :default => configuration[:message], :value => value) + def test_generate_message_inclusion_with_default_message + assert_equal 'is not included in the list', @topic.errors.generate_message(:title, :inclusion, :default => nil, :value => 'title') + end + + def test_generate_message_inclusion_with_custom_message + assert_equal 'custom message title', @topic.errors.generate_message(:title, :inclusion, :default => 'custom message {{value}}', :value => 'title') + end + + # validates_exclusion_of: generate_message(attr_name, :exclusion, :default => configuration[:message], :value => value) + def test_generate_message_exclusion_with_default_message + assert_equal 'is reserved', @topic.errors.generate_message(:title, :exclusion, :default => nil, :value => 'title') + end + + def test_generate_message_exclusion_with_custom_message + assert_equal 'custom message title', @topic.errors.generate_message(:title, :exclusion, :default => 'custom message {{value}}', :value => 'title') + end + + # validates_associated: generate_message(attr_name, :invalid, :default => configuration[:message], :value => value) + # validates_format_of: generate_message(attr_name, :invalid, :default => configuration[:message], :value => value) + def test_generate_message_invalid_with_default_message + assert_equal 'is invalid', @topic.errors.generate_message(:title, :invalid, :default => nil, :value => 'title') + end + + def test_generate_message_invalid_with_custom_message + assert_equal 'custom message title', @topic.errors.generate_message(:title, :invalid, :default => 'custom message {{value}}', :value => 'title') + end + + # validates_confirmation_of: generate_message(attr_name, :confirmation, :default => configuration[:message]) + def test_generate_message_confirmation_with_default_message + assert_equal "doesn't match confirmation", @topic.errors.generate_message(:title, :confirmation, :default => nil) + end + + def test_generate_message_confirmation_with_custom_message + assert_equal 'custom message', @topic.errors.generate_message(:title, :confirmation, :default => 'custom message') + end + + # validates_acceptance_of: generate_message(attr_name, :accepted, :default => configuration[:message]) + def test_generate_message_accepted_with_default_message + assert_equal "must be accepted", @topic.errors.generate_message(:title, :accepted, :default => nil) + end + + def test_generate_message_accepted_with_custom_message + assert_equal 'custom message', @topic.errors.generate_message(:title, :accepted, :default => 'custom message') + end + + # add_on_empty: generate_message(attr, :empty, :default => custom_message) + def test_generate_message_empty_with_default_message + assert_equal "can't be empty", @topic.errors.generate_message(:title, :empty, :default => nil) + end + + def test_generate_message_empty_with_custom_message + assert_equal 'custom message', @topic.errors.generate_message(:title, :empty, :default => 'custom message') + end + + # add_on_blank: generate_message(attr, :blank, :default => custom_message) + def test_generate_message_blank_with_default_message + assert_equal "can't be blank", @topic.errors.generate_message(:title, :blank, :default => nil) + end + + def test_generate_message_blank_with_custom_message + assert_equal 'custom message', @topic.errors.generate_message(:title, :blank, :default => 'custom message') + end + + # validates_length_of: generate_message(attr, :too_long, :default => options[:too_long], :count => option_value.end) + def test_generate_message_too_long_with_default_message + assert_equal "is too long (maximum is 10 characters)", @topic.errors.generate_message(:title, :too_long, :default => nil, :count => 10) + end + + def test_generate_message_too_long_with_custom_message + assert_equal 'custom message 10', @topic.errors.generate_message(:title, :too_long, :default => 'custom message {{count}}', :count => 10) + end + + # validates_length_of: generate_message(attr, :too_short, :default => options[:too_short], :count => option_value.begin) + def test_generate_message_too_short_with_default_message + assert_equal "is too short (minimum is 10 characters)", @topic.errors.generate_message(:title, :too_short, :default => nil, :count => 10) + end + + def test_generate_message_too_short_with_custom_message + assert_equal 'custom message 10', @topic.errors.generate_message(:title, :too_short, :default => 'custom message {{count}}', :count => 10) + end + + # validates_length_of: generate_message(attr, key, :default => custom_message, :count => option_value) + def test_generate_message_wrong_length_with_default_message + assert_equal "is the wrong length (should be 10 characters)", @topic.errors.generate_message(:title, :wrong_length, :default => nil, :count => 10) + end + + def test_generate_message_wrong_length_with_custom_message + assert_equal 'custom message 10', @topic.errors.generate_message(:title, :wrong_length, :default => 'custom message {{count}}', :count => 10) + end + + # validates_numericality_of: generate_message(attr_name, :not_a_number, :value => raw_value, :default => configuration[:message]) + def test_generate_message_not_a_number_with_default_message + assert_equal "is not a number", @topic.errors.generate_message(:title, :not_a_number, :default => nil, :value => 'title') + end + + def test_generate_message_not_a_number_with_custom_message + assert_equal 'custom message title', @topic.errors.generate_message(:title, :not_a_number, :default => 'custom message {{value}}', :value => 'title') + end + + # validates_numericality_of: generate_message(attr_name, option, :value => raw_value, :default => configuration[:message]) + def test_generate_message_greater_than_with_default_message + assert_equal "must be greater than 10", @topic.errors.generate_message(:title, :greater_than, :default => nil, :value => 'title', :count => 10) + end + + def test_generate_message_greater_than_or_equal_to_with_default_message + assert_equal "must be greater than or equal to 10", @topic.errors.generate_message(:title, :greater_than_or_equal_to, :default => nil, :value => 'title', :count => 10) + end + + def test_generate_message_equal_to_with_default_message + assert_equal "must be equal to 10", @topic.errors.generate_message(:title, :equal_to, :default => nil, :value => 'title', :count => 10) + end + + def test_generate_message_less_than_with_default_message + assert_equal "must be less than 10", @topic.errors.generate_message(:title, :less_than, :default => nil, :value => 'title', :count => 10) + end + + def test_generate_message_less_than_or_equal_to_with_default_message + assert_equal "must be less than or equal to 10", @topic.errors.generate_message(:title, :less_than_or_equal_to, :default => nil, :value => 'title', :count => 10) + end + + def test_generate_message_odd_with_default_message + assert_equal "must be odd", @topic.errors.generate_message(:title, :odd, :default => nil, :value => 'title', :count => 10) + end + + def test_generate_message_even_with_default_message + assert_equal "must be even", @topic.errors.generate_message(:title, :even, :default => nil, :value => 'title', :count => 10) + end + + # validates_uniqueness_of: generate_message(attr_name, :taken, :default => configuration[:message]) + def test_generate_message_taken_with_default_message + assert_equal "has already been taken", @topic.errors.generate_message(:title, :taken, :default => nil, :value => 'title') + end + + def test_generate_message_taken_with_custom_message + assert_equal 'custom message title', @topic.errors.generate_message(:title, :taken, :default => 'custom message {{value}}', :value => 'title') + end + +end diff --git a/activerecord/test/cases/validations_i18n_test.rb b/activerecord/test/cases/validations/i18n_validation_test.rb index 20d1369a64..0303147e50 100644 --- a/activerecord/test/cases/validations_i18n_test.rb +++ b/activerecord/test/cases/validations/i18n_validation_test.rb @@ -2,7 +2,7 @@ require "cases/helper" require 'models/topic' require 'models/reply' -class ActiveRecordValidationsI18nTests < ActiveSupport::TestCase +class I18nValidationTest < ActiveRecord::TestCase def setup reset_callbacks Topic @topic = Topic.new @@ -54,7 +54,6 @@ class ActiveRecordValidationsI18nTests < ActiveSupport::TestCase # ActiveRecord::Errors def test_errors_generate_message_translates_custom_model_attribute_key - I18n.expects(:translate).with( :topic, { :count => 1, @@ -122,6 +121,55 @@ class ActiveRecordValidationsI18nTests < ActiveSupport::TestCase end + # validates_uniqueness_of w/ mocha + + def test_validates_uniqueness_of_generates_message + Topic.validates_uniqueness_of :title + @topic.title = unique_topic.title + @topic.errors.expects(:generate_message).with(:title, :taken, {:default => nil, :value => 'unique!'}) + @topic.valid? + end + + def test_validates_uniqueness_of_generates_message_with_custom_default_message + Topic.validates_uniqueness_of :title, :message => 'custom' + @topic.title = unique_topic.title + @topic.errors.expects(:generate_message).with(:title, :taken, {:default => 'custom', :value => 'unique!'}) + @topic.valid? + end + + # validates_associated w/ mocha + + def test_validates_associated_generates_message + Topic.validates_associated :replies + replied_topic.errors.expects(:generate_message).with(:replies, :invalid, {:value => replied_topic.replies, :default => nil}) + replied_topic.valid? + end + + def test_validates_associated_generates_message_with_custom_default_message + Topic.validates_associated :replies + replied_topic.errors.expects(:generate_message).with(:replies, :invalid, {:value => replied_topic.replies, :default => nil}) + replied_topic.valid? + end + + # validates_associated w/o mocha + + def test_validates_associated_finds_custom_model_key_translation + I18n.backend.store_translations 'en', :activerecord => {:errors => {:models => {:topic => {:attributes => {:replies => {:invalid => 'custom message'}}}}}} + I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:invalid => 'global message'}}} + + Topic.validates_associated :replies + replied_topic.valid? + assert_equal ['custom message'], replied_topic.errors[:replies] + end + + def test_validates_associated_finds_global_default_translation + I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:invalid => 'global message'}}} + + Topic.validates_associated :replies + replied_topic.valid? + assert_equal ['global message'], replied_topic.errors[:replies] + end + def test_errors_add_on_empty_generates_message @topic.errors.expects(:generate_message).with(:title, :empty, {:default => nil}) @topic.errors.add_on_empty :title @@ -143,7 +191,7 @@ class ActiveRecordValidationsI18nTests < ActiveSupport::TestCase end def test_errors_full_messages_translates_human_attribute_name_for_model_attributes - @topic.errors.instance_variable_set :@errors, { 'title' => ['empty'] } + @topic.errors.add('title', 'empty') I18n.expects(:translate).with(:"topic.title", :default => ['Title'], :scope => [:activerecord, :attributes], :count => 1).returns('Title') @topic.errors.full_messages :locale => 'en' end @@ -260,22 +308,6 @@ class ActiveRecordValidationsI18nTests < ActiveSupport::TestCase @topic.valid? end - # validates_uniqueness_of w/ mocha - - def test_validates_uniqueness_of_generates_message - Topic.validates_uniqueness_of :title - @topic.title = unique_topic.title - @topic.errors.expects(:generate_message).with(:title, :taken, {:default => nil, :value => 'unique!'}) - @topic.valid? - end - - def test_validates_uniqueness_of_generates_message_with_custom_default_message - Topic.validates_uniqueness_of :title, :message => 'custom' - @topic.title = unique_topic.title - @topic.errors.expects(:generate_message).with(:title, :taken, {:default => 'custom', :value => 'unique!'}) - @topic.valid? - end - # validates_format_of w/ mocha def test_validates_format_of_generates_message @@ -388,20 +420,6 @@ class ActiveRecordValidationsI18nTests < ActiveSupport::TestCase @topic.valid? end - # validates_associated w/ mocha - - def test_validates_associated_generates_message - Topic.validates_associated :replies - replied_topic.errors.expects(:generate_message).with(:replies, :invalid, {:value => replied_topic.replies, :default => nil}) - replied_topic.valid? - end - - def test_validates_associated_generates_message_with_custom_default_message - Topic.validates_associated :replies - replied_topic.errors.expects(:generate_message).with(:replies, :invalid, {:value => replied_topic.replies, :default => nil}) - replied_topic.valid? - end - # validates_confirmation_of w/o mocha def test_validates_confirmation_of_finds_custom_model_key_translation @@ -411,7 +429,7 @@ class ActiveRecordValidationsI18nTests < ActiveSupport::TestCase Topic.validates_confirmation_of :title @topic.title_confirmation = 'foo' @topic.valid? - assert_equal 'custom message', @topic.errors.on(:title) + assert_equal ['custom message'], @topic.errors[:title] end def test_validates_confirmation_of_finds_global_default_translation @@ -420,7 +438,7 @@ class ActiveRecordValidationsI18nTests < ActiveSupport::TestCase Topic.validates_confirmation_of :title @topic.title_confirmation = 'foo' @topic.valid? - assert_equal 'global message', @topic.errors.on(:title) + assert_equal ['global message'], @topic.errors[:title] end # validates_acceptance_of w/o mocha @@ -431,7 +449,7 @@ class ActiveRecordValidationsI18nTests < ActiveSupport::TestCase Topic.validates_acceptance_of :title, :allow_nil => false @topic.valid? - assert_equal 'custom message', @topic.errors.on(:title) + assert_equal ['custom message'], @topic.errors[:title] end def test_validates_acceptance_of_finds_global_default_translation @@ -439,7 +457,7 @@ class ActiveRecordValidationsI18nTests < ActiveSupport::TestCase Topic.validates_acceptance_of :title, :allow_nil => false @topic.valid? - assert_equal 'global message', @topic.errors.on(:title) + assert_equal ['global message'], @topic.errors[:title] end # validates_presence_of w/o mocha @@ -450,7 +468,7 @@ class ActiveRecordValidationsI18nTests < ActiveSupport::TestCase Topic.validates_presence_of :title @topic.valid? - assert_equal 'custom message', @topic.errors.on(:title) + assert_equal ['custom message'], @topic.errors[:title] end def test_validates_presence_of_finds_global_default_translation @@ -458,7 +476,7 @@ class ActiveRecordValidationsI18nTests < ActiveSupport::TestCase Topic.validates_presence_of :title @topic.valid? - assert_equal 'global message', @topic.errors.on(:title) + assert_equal ['global message'], @topic.errors[:title] end # validates_length_of :within w/o mocha @@ -469,7 +487,7 @@ class ActiveRecordValidationsI18nTests < ActiveSupport::TestCase Topic.validates_length_of :title, :within => 3..5 @topic.valid? - assert_equal 'custom message', @topic.errors.on(:title) + assert_equal ['custom message'], @topic.errors[:title] end def test_validates_length_of_within_finds_global_default_translation @@ -477,7 +495,7 @@ class ActiveRecordValidationsI18nTests < ActiveSupport::TestCase Topic.validates_length_of :title, :within => 3..5 @topic.valid? - assert_equal 'global message', @topic.errors.on(:title) + assert_equal ['global message'], @topic.errors[:title] end # validates_length_of :is w/o mocha @@ -488,7 +506,7 @@ class ActiveRecordValidationsI18nTests < ActiveSupport::TestCase Topic.validates_length_of :title, :is => 5 @topic.valid? - assert_equal 'custom message', @topic.errors.on(:title) + assert_equal ['custom message'], @topic.errors[:title] end def test_validates_length_of_is_finds_global_default_translation @@ -496,18 +514,16 @@ class ActiveRecordValidationsI18nTests < ActiveSupport::TestCase Topic.validates_length_of :title, :is => 5 @topic.valid? - assert_equal 'global message', @topic.errors.on(:title) + assert_equal ['global message'], @topic.errors[:title] end - # validates_uniqueness_of w/o mocha - def test_validates_length_of_is_finds_custom_model_key_translation I18n.backend.store_translations 'en', :activerecord => {:errors => {:models => {:topic => {:attributes => {:title => {:wrong_length => 'custom message'}}}}}} I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:wrong_length => 'global message'}}} Topic.validates_length_of :title, :is => 5 @topic.valid? - assert_equal 'custom message', @topic.errors.on(:title) + assert_equal ['custom message'], @topic.errors[:title] end def test_validates_length_of_is_finds_global_default_translation @@ -515,7 +531,7 @@ class ActiveRecordValidationsI18nTests < ActiveSupport::TestCase Topic.validates_length_of :title, :is => 5 @topic.valid? - assert_equal 'global message', @topic.errors.on(:title) + assert_equal ['global message'], @topic.errors[:title] end @@ -527,7 +543,7 @@ class ActiveRecordValidationsI18nTests < ActiveSupport::TestCase Topic.validates_format_of :title, :with => /^[1-9][0-9]*$/ @topic.valid? - assert_equal 'custom message', @topic.errors.on(:title) + assert_equal ['custom message'], @topic.errors[:title] end def test_validates_format_of_finds_global_default_translation @@ -535,7 +551,7 @@ class ActiveRecordValidationsI18nTests < ActiveSupport::TestCase Topic.validates_format_of :title, :with => /^[1-9][0-9]*$/ @topic.valid? - assert_equal 'global message', @topic.errors.on(:title) + assert_equal ['global message'], @topic.errors[:title] end # validates_inclusion_of w/o mocha @@ -546,7 +562,7 @@ class ActiveRecordValidationsI18nTests < ActiveSupport::TestCase Topic.validates_inclusion_of :title, :in => %w(a b c) @topic.valid? - assert_equal 'custom message', @topic.errors.on(:title) + assert_equal ['custom message'], @topic.errors[:title] end def test_validates_inclusion_of_finds_global_default_translation @@ -554,7 +570,7 @@ class ActiveRecordValidationsI18nTests < ActiveSupport::TestCase Topic.validates_inclusion_of :title, :in => %w(a b c) @topic.valid? - assert_equal 'global message', @topic.errors.on(:title) + assert_equal ['global message'], @topic.errors[:title] end # validates_exclusion_of w/o mocha @@ -566,7 +582,7 @@ class ActiveRecordValidationsI18nTests < ActiveSupport::TestCase Topic.validates_exclusion_of :title, :in => %w(a b c) @topic.title = 'a' @topic.valid? - assert_equal 'custom message', @topic.errors.on(:title) + assert_equal ['custom message'], @topic.errors[:title] end def test_validates_exclusion_of_finds_global_default_translation @@ -575,7 +591,7 @@ class ActiveRecordValidationsI18nTests < ActiveSupport::TestCase Topic.validates_exclusion_of :title, :in => %w(a b c) @topic.title = 'a' @topic.valid? - assert_equal 'global message', @topic.errors.on(:title) + assert_equal ['global message'], @topic.errors[:title] end # validates_numericality_of without :only_integer w/o mocha @@ -587,7 +603,7 @@ class ActiveRecordValidationsI18nTests < ActiveSupport::TestCase Topic.validates_numericality_of :title @topic.title = 'a' @topic.valid? - assert_equal 'custom message', @topic.errors.on(:title) + assert_equal ['custom message'], @topic.errors[:title] end def test_validates_numericality_of_finds_global_default_translation @@ -596,7 +612,7 @@ class ActiveRecordValidationsI18nTests < ActiveSupport::TestCase Topic.validates_numericality_of :title, :only_integer => true @topic.title = 'a' @topic.valid? - assert_equal 'global message', @topic.errors.on(:title) + assert_equal ['global message'], @topic.errors[:title] end # validates_numericality_of with :only_integer w/o mocha @@ -608,7 +624,7 @@ class ActiveRecordValidationsI18nTests < ActiveSupport::TestCase Topic.validates_numericality_of :title, :only_integer => true @topic.title = 'a' @topic.valid? - assert_equal 'custom message', @topic.errors.on(:title) + assert_equal ['custom message'], @topic.errors[:title] end def test_validates_numericality_of_only_integer_finds_global_default_translation @@ -617,7 +633,7 @@ class ActiveRecordValidationsI18nTests < ActiveSupport::TestCase Topic.validates_numericality_of :title, :only_integer => true @topic.title = 'a' @topic.valid? - assert_equal 'global message', @topic.errors.on(:title) + assert_equal ['global message'], @topic.errors[:title] end # validates_numericality_of :odd w/o mocha @@ -629,7 +645,7 @@ class ActiveRecordValidationsI18nTests < ActiveSupport::TestCase Topic.validates_numericality_of :title, :only_integer => true, :odd => true @topic.title = 0 @topic.valid? - assert_equal 'custom message', @topic.errors.on(:title) + assert_equal ['custom message'], @topic.errors[:title] end def test_validates_numericality_of_odd_finds_global_default_translation @@ -638,7 +654,7 @@ class ActiveRecordValidationsI18nTests < ActiveSupport::TestCase Topic.validates_numericality_of :title, :only_integer => true, :odd => true @topic.title = 0 @topic.valid? - assert_equal 'global message', @topic.errors.on(:title) + assert_equal ['global message'], @topic.errors[:title] end # validates_numericality_of :less_than w/o mocha @@ -650,7 +666,7 @@ class ActiveRecordValidationsI18nTests < ActiveSupport::TestCase Topic.validates_numericality_of :title, :only_integer => true, :less_than => 0 @topic.title = 1 @topic.valid? - assert_equal 'custom message', @topic.errors.on(:title) + assert_equal ['custom message'], @topic.errors[:title] end def test_validates_numericality_of_less_than_finds_global_default_translation @@ -659,27 +675,7 @@ class ActiveRecordValidationsI18nTests < ActiveSupport::TestCase Topic.validates_numericality_of :title, :only_integer => true, :less_than => 0 @topic.title = 1 @topic.valid? - assert_equal 'global message', @topic.errors.on(:title) - end - - - # validates_associated w/o mocha - - def test_validates_associated_finds_custom_model_key_translation - I18n.backend.store_translations 'en', :activerecord => {:errors => {:models => {:topic => {:attributes => {:replies => {:invalid => 'custom message'}}}}}} - I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:invalid => 'global message'}}} - - Topic.validates_associated :replies - replied_topic.valid? - assert_equal 'custom message', replied_topic.errors.on(:replies) - end - - def test_validates_associated_finds_global_default_translation - I18n.backend.store_translations 'en', :activerecord => {:errors => {:messages => {:invalid => 'global message'}}} - - Topic.validates_associated :replies - replied_topic.valid? - assert_equal 'global message', replied_topic.errors.on(:replies) + assert_equal ['global message'], @topic.errors[:title] end def test_validations_with_message_symbol_must_translate @@ -687,7 +683,7 @@ class ActiveRecordValidationsI18nTests < ActiveSupport::TestCase Topic.validates_presence_of :title, :message => :custom_error @topic.title = nil @topic.valid? - assert_equal "I am a custom error", @topic.errors.on(:title) + assert_equal ["I am a custom error"], @topic.errors[:title] end def test_validates_with_message_symbol_must_translate_per_attribute @@ -695,7 +691,7 @@ class ActiveRecordValidationsI18nTests < ActiveSupport::TestCase Topic.validates_presence_of :title, :message => :custom_error @topic.title = nil @topic.valid? - assert_equal "I am a custom error", @topic.errors.on(:title) + assert_equal ["I am a custom error"], @topic.errors[:title] end def test_validates_with_message_symbol_must_translate_per_model @@ -703,195 +699,13 @@ class ActiveRecordValidationsI18nTests < ActiveSupport::TestCase Topic.validates_presence_of :title, :message => :custom_error @topic.title = nil @topic.valid? - assert_equal "I am a custom error", @topic.errors.on(:title) + assert_equal ["I am a custom error"], @topic.errors[:title] end def test_validates_with_message_string Topic.validates_presence_of :title, :message => "I am a custom error" @topic.title = nil @topic.valid? - assert_equal "I am a custom error", @topic.errors.on(:title) - end - -end - -class ActiveRecordValidationsGenerateMessageI18nTests < Test::Unit::TestCase - def setup - reset_callbacks Topic - @topic = Topic.new - I18n.backend.store_translations :'en', { - :activerecord => { - :errors => { - :messages => { - :inclusion => "is not included in the list", - :exclusion => "is reserved", - :invalid => "is invalid", - :confirmation => "doesn't match confirmation", - :accepted => "must be accepted", - :empty => "can't be empty", - :blank => "can't be blank", - :too_long => "is too long (maximum is {{count}} characters)", - :too_short => "is too short (minimum is {{count}} characters)", - :wrong_length => "is the wrong length (should be {{count}} characters)", - :taken => "has already been taken", - :not_a_number => "is not a number", - :greater_than => "must be greater than {{count}}", - :greater_than_or_equal_to => "must be greater than or equal to {{count}}", - :equal_to => "must be equal to {{count}}", - :less_than => "must be less than {{count}}", - :less_than_or_equal_to => "must be less than or equal to {{count}}", - :odd => "must be odd", - :even => "must be even" - } - } - } - } - end - - def reset_callbacks(*models) - models.each do |model| - model.instance_variable_set("@validate_callbacks", ActiveSupport::Callbacks::CallbackChain.new) - model.instance_variable_set("@validate_on_create_callbacks", ActiveSupport::Callbacks::CallbackChain.new) - model.instance_variable_set("@validate_on_update_callbacks", ActiveSupport::Callbacks::CallbackChain.new) - end - end - - # validates_inclusion_of: generate_message(attr_name, :inclusion, :default => configuration[:message], :value => value) - def test_generate_message_inclusion_with_default_message - assert_equal 'is not included in the list', @topic.errors.generate_message(:title, :inclusion, :default => nil, :value => 'title') - end - - def test_generate_message_inclusion_with_custom_message - assert_equal 'custom message title', @topic.errors.generate_message(:title, :inclusion, :default => 'custom message {{value}}', :value => 'title') - end - - # validates_exclusion_of: generate_message(attr_name, :exclusion, :default => configuration[:message], :value => value) - def test_generate_message_exclusion_with_default_message - assert_equal 'is reserved', @topic.errors.generate_message(:title, :exclusion, :default => nil, :value => 'title') - end - - def test_generate_message_exclusion_with_custom_message - assert_equal 'custom message title', @topic.errors.generate_message(:title, :exclusion, :default => 'custom message {{value}}', :value => 'title') - end - - # validates_associated: generate_message(attr_name, :invalid, :default => configuration[:message], :value => value) - # validates_format_of: generate_message(attr_name, :invalid, :default => configuration[:message], :value => value) - def test_generate_message_invalid_with_default_message - assert_equal 'is invalid', @topic.errors.generate_message(:title, :invalid, :default => nil, :value => 'title') - end - - def test_generate_message_invalid_with_custom_message - assert_equal 'custom message title', @topic.errors.generate_message(:title, :invalid, :default => 'custom message {{value}}', :value => 'title') - end - - # validates_confirmation_of: generate_message(attr_name, :confirmation, :default => configuration[:message]) - def test_generate_message_confirmation_with_default_message - assert_equal "doesn't match confirmation", @topic.errors.generate_message(:title, :confirmation, :default => nil) - end - - def test_generate_message_confirmation_with_custom_message - assert_equal 'custom message', @topic.errors.generate_message(:title, :confirmation, :default => 'custom message') - end - - # validates_acceptance_of: generate_message(attr_name, :accepted, :default => configuration[:message]) - def test_generate_message_accepted_with_default_message - assert_equal "must be accepted", @topic.errors.generate_message(:title, :accepted, :default => nil) - end - - def test_generate_message_accepted_with_custom_message - assert_equal 'custom message', @topic.errors.generate_message(:title, :accepted, :default => 'custom message') - end - - # add_on_empty: generate_message(attr, :empty, :default => custom_message) - def test_generate_message_empty_with_default_message - assert_equal "can't be empty", @topic.errors.generate_message(:title, :empty, :default => nil) - end - - def test_generate_message_empty_with_custom_message - assert_equal 'custom message', @topic.errors.generate_message(:title, :empty, :default => 'custom message') - end - - # add_on_blank: generate_message(attr, :blank, :default => custom_message) - def test_generate_message_blank_with_default_message - assert_equal "can't be blank", @topic.errors.generate_message(:title, :blank, :default => nil) - end - - def test_generate_message_blank_with_custom_message - assert_equal 'custom message', @topic.errors.generate_message(:title, :blank, :default => 'custom message') - end - - # validates_length_of: generate_message(attr, :too_long, :default => options[:too_long], :count => option_value.end) - def test_generate_message_too_long_with_default_message - assert_equal "is too long (maximum is 10 characters)", @topic.errors.generate_message(:title, :too_long, :default => nil, :count => 10) - end - - def test_generate_message_too_long_with_custom_message - assert_equal 'custom message 10', @topic.errors.generate_message(:title, :too_long, :default => 'custom message {{count}}', :count => 10) - end - - # validates_length_of: generate_message(attr, :too_short, :default => options[:too_short], :count => option_value.begin) - def test_generate_message_too_short_with_default_message - assert_equal "is too short (minimum is 10 characters)", @topic.errors.generate_message(:title, :too_short, :default => nil, :count => 10) - end - - def test_generate_message_too_short_with_custom_message - assert_equal 'custom message 10', @topic.errors.generate_message(:title, :too_short, :default => 'custom message {{count}}', :count => 10) - end - - # validates_length_of: generate_message(attr, key, :default => custom_message, :count => option_value) - def test_generate_message_wrong_length_with_default_message - assert_equal "is the wrong length (should be 10 characters)", @topic.errors.generate_message(:title, :wrong_length, :default => nil, :count => 10) - end - - def test_generate_message_wrong_length_with_custom_message - assert_equal 'custom message 10', @topic.errors.generate_message(:title, :wrong_length, :default => 'custom message {{count}}', :count => 10) - end - - # validates_uniqueness_of: generate_message(attr_name, :taken, :default => configuration[:message]) - def test_generate_message_taken_with_default_message - assert_equal "has already been taken", @topic.errors.generate_message(:title, :taken, :default => nil, :value => 'title') - end - - def test_generate_message_taken_with_custom_message - assert_equal 'custom message title', @topic.errors.generate_message(:title, :taken, :default => 'custom message {{value}}', :value => 'title') - end - - # validates_numericality_of: generate_message(attr_name, :not_a_number, :value => raw_value, :default => configuration[:message]) - def test_generate_message_not_a_number_with_default_message - assert_equal "is not a number", @topic.errors.generate_message(:title, :not_a_number, :default => nil, :value => 'title') - end - - def test_generate_message_not_a_number_with_custom_message - assert_equal 'custom message title', @topic.errors.generate_message(:title, :not_a_number, :default => 'custom message {{value}}', :value => 'title') - end - - # validates_numericality_of: generate_message(attr_name, option, :value => raw_value, :default => configuration[:message]) - def test_generate_message_greater_than_with_default_message - assert_equal "must be greater than 10", @topic.errors.generate_message(:title, :greater_than, :default => nil, :value => 'title', :count => 10) - end - - def test_generate_message_greater_than_or_equal_to_with_default_message - assert_equal "must be greater than or equal to 10", @topic.errors.generate_message(:title, :greater_than_or_equal_to, :default => nil, :value => 'title', :count => 10) + assert_equal ["I am a custom error"], @topic.errors[:title] end - - def test_generate_message_equal_to_with_default_message - assert_equal "must be equal to 10", @topic.errors.generate_message(:title, :equal_to, :default => nil, :value => 'title', :count => 10) - end - - def test_generate_message_less_than_with_default_message - assert_equal "must be less than 10", @topic.errors.generate_message(:title, :less_than, :default => nil, :value => 'title', :count => 10) - end - - def test_generate_message_less_than_or_equal_to_with_default_message - assert_equal "must be less than or equal to 10", @topic.errors.generate_message(:title, :less_than_or_equal_to, :default => nil, :value => 'title', :count => 10) - end - - def test_generate_message_odd_with_default_message - assert_equal "must be odd", @topic.errors.generate_message(:title, :odd, :default => nil, :value => 'title', :count => 10) - end - - def test_generate_message_even_with_default_message - assert_equal "must be even", @topic.errors.generate_message(:title, :even, :default => nil, :value => 'title', :count => 10) - end - end diff --git a/activerecord/test/cases/validations/uniqueness_validation_test.rb b/activerecord/test/cases/validations/uniqueness_validation_test.rb new file mode 100644 index 0000000000..961db51d1d --- /dev/null +++ b/activerecord/test/cases/validations/uniqueness_validation_test.rb @@ -0,0 +1,278 @@ +# encoding: utf-8 +require "cases/helper" +require 'models/topic' +require 'models/reply' +require 'models/warehouse_thing' +require 'models/guid' +require 'models/event' + +# The following methods in Topic are used in test_conditional_validation_* +class Topic + has_many :unique_replies, :dependent => :destroy, :foreign_key => "parent_id" + has_many :silly_unique_replies, :dependent => :destroy, :foreign_key => "parent_id" +end + +class UniqueReply < Reply + validates_uniqueness_of :content, :scope => 'parent_id' +end + +class SillyUniqueReply < UniqueReply +end + +class Wizard < ActiveRecord::Base + self.abstract_class = true + + validates_uniqueness_of :name +end + +class IneptWizard < Wizard + validates_uniqueness_of :city +end + +class Conjurer < IneptWizard +end + +class Thaumaturgist < IneptWizard +end + +class UniquenessValidationTest < ActiveRecord::TestCase + fixtures :topics, 'warehouse-things' + + repair_validations(Topic) + + def test_validate_uniqueness + Topic.validates_uniqueness_of(:title) + + t = Topic.new("title" => "I'm unique!") + assert t.save, "Should save t as unique" + + t.content = "Remaining unique" + assert t.save, "Should still save t as unique" + + t2 = Topic.new("title" => "I'm unique!") + assert !t2.valid?, "Shouldn't be valid" + assert !t2.save, "Shouldn't save t2 as unique" + assert_equal ["has already been taken"], t2.errors[:title] + + t2.title = "Now Im really also unique" + assert t2.save, "Should now save t2 as unique" + end + + def test_validates_uniquness_with_newline_chars + Topic.validates_uniqueness_of(:title, :case_sensitive => false) + + t = Topic.new("title" => "new\nline") + assert t.save, "Should save t as unique" + end + + def test_validate_uniqueness_with_scope + repair_validations(Reply) do + Reply.validates_uniqueness_of(:content, :scope => "parent_id") + + t = Topic.create("title" => "I'm unique!") + + r1 = t.replies.create "title" => "r1", "content" => "hello world" + assert r1.valid?, "Saving r1" + + r2 = t.replies.create "title" => "r2", "content" => "hello world" + assert !r2.valid?, "Saving r2 first time" + + r2.content = "something else" + assert r2.save, "Saving r2 second time" + + t2 = Topic.create("title" => "I'm unique too!") + r3 = t2.replies.create "title" => "r3", "content" => "hello world" + assert r3.valid?, "Saving r3" + end + end + + def test_validate_uniqueness_scoped_to_defining_class + t = Topic.create("title" => "What, me worry?") + + r1 = t.unique_replies.create "title" => "r1", "content" => "a barrel of fun" + assert r1.valid?, "Saving r1" + + r2 = t.silly_unique_replies.create "title" => "r2", "content" => "a barrel of fun" + assert !r2.valid?, "Saving r2" + + # Should succeed as validates_uniqueness_of only applies to + # UniqueReply and its subclasses + r3 = t.replies.create "title" => "r2", "content" => "a barrel of fun" + assert r3.valid?, "Saving r3" + end + + def test_validate_uniqueness_with_scope_array + repair_validations(Reply) do + Reply.validates_uniqueness_of(:author_name, :scope => [:author_email_address, :parent_id]) + + t = Topic.create("title" => "The earth is actually flat!") + + r1 = t.replies.create "author_name" => "jeremy", "author_email_address" => "jeremy@rubyonrails.com", "title" => "You're crazy!", "content" => "Crazy reply" + assert r1.valid?, "Saving r1" + + r2 = t.replies.create "author_name" => "jeremy", "author_email_address" => "jeremy@rubyonrails.com", "title" => "You're crazy!", "content" => "Crazy reply again..." + assert !r2.valid?, "Saving r2. Double reply by same author." + + r2.author_email_address = "jeremy_alt_email@rubyonrails.com" + assert r2.save, "Saving r2 the second time." + + r3 = t.replies.create "author_name" => "jeremy", "author_email_address" => "jeremy_alt_email@rubyonrails.com", "title" => "You're wrong", "content" => "It's cubic" + assert !r3.valid?, "Saving r3" + + r3.author_name = "jj" + assert r3.save, "Saving r3 the second time." + + r3.author_name = "jeremy" + assert !r3.save, "Saving r3 the third time." + end + end + + def test_validate_case_insensitive_uniqueness + Topic.validates_uniqueness_of(:title, :parent_id, :case_sensitive => false, :allow_nil => true) + + t = Topic.new("title" => "I'm unique!", :parent_id => 2) + assert t.save, "Should save t as unique" + + t.content = "Remaining unique" + assert t.save, "Should still save t as unique" + + t2 = Topic.new("title" => "I'm UNIQUE!", :parent_id => 1) + assert !t2.valid?, "Shouldn't be valid" + assert !t2.save, "Shouldn't save t2 as unique" + assert t2.errors[:title].any? + assert t2.errors[:parent_id].any? + assert_equal ["has already been taken"], t2.errors[:title] + + t2.title = "I'm truly UNIQUE!" + assert !t2.valid?, "Shouldn't be valid" + assert !t2.save, "Shouldn't save t2 as unique" + assert t2.errors[:title].empty? + assert t2.errors[:parent_id].any? + + t2.parent_id = 4 + assert t2.save, "Should now save t2 as unique" + + t2.parent_id = nil + t2.title = nil + assert t2.valid?, "should validate with nil" + assert t2.save, "should save with nil" + + with_kcode('UTF8') do + t_utf8 = Topic.new("title" => "Я тоже уникальный!") + assert t_utf8.save, "Should save t_utf8 as unique" + + # If database hasn't UTF-8 character set, this test fails + if Topic.find(t_utf8, :select => 'LOWER(title) AS title').title == "я тоже уникальный!" + t2_utf8 = Topic.new("title" => "я тоже УНИКАЛЬНЫЙ!") + assert !t2_utf8.valid?, "Shouldn't be valid" + assert !t2_utf8.save, "Shouldn't save t2_utf8 as unique" + end + end + end + + def test_validate_case_sensitive_uniqueness + Topic.validates_uniqueness_of(:title, :case_sensitive => true, :allow_nil => true) + + t = Topic.new("title" => "I'm unique!") + assert t.save, "Should save t as unique" + + t.content = "Remaining unique" + assert t.save, "Should still save t as unique" + + t2 = Topic.new("title" => "I'M UNIQUE!") + assert t2.valid?, "Should be valid" + assert t2.save, "Should save t2 as unique" + assert t2.errors[:title].empty? + assert t2.errors[:parent_id].empty? + assert_not_equal ["has already been taken"], t2.errors[:title] + + t3 = Topic.new("title" => "I'M uNiQUe!") + assert t3.valid?, "Should be valid" + assert t3.save, "Should save t2 as unique" + assert t3.errors[:title].empty? + assert t3.errors[:parent_id].empty? + assert_not_equal ["has already been taken"], t3.errors[:title] + end + + def test_validate_case_sensitive_uniqueness_with_attribute_passed_as_integer + Topic.validates_uniqueness_of(:title, :case_sensitve => true) + t = Topic.create!('title' => 101) + + t2 = Topic.new('title' => 101) + assert !t2.valid? + assert t2.errors[:title] + end + + def test_validate_uniqueness_with_non_standard_table_names + i1 = WarehouseThing.create(:value => 1000) + assert !i1.valid?, "i1 should not be valid" + assert i1.errors[:value].any?, "Should not be empty" + end + + def test_validates_uniqueness_inside_with_scope + Topic.validates_uniqueness_of(:title) + + Topic.with_scope(:find => { :conditions => { :author_name => "David" } }) do + t1 = Topic.new("title" => "I'm unique!", "author_name" => "Mary") + assert t1.save + t2 = Topic.new("title" => "I'm unique!", "author_name" => "David") + assert !t2.valid? + end + end + + def test_validate_uniqueness_with_columns_which_are_sql_keywords + repair_validations(Guid) do + Guid.validates_uniqueness_of :key + g = Guid.new + g.key = "foo" + assert_nothing_raised { !g.valid? } + end + end + + def test_validate_uniqueness_with_limit + # Event.title is limited to 5 characters + e1 = Event.create(:title => "abcde") + assert e1.valid?, "Could not create an event with a unique, 5 character title" + e2 = Event.create(:title => "abcdefgh") + assert !e2.valid?, "Created an event whose title, with limit taken into account, is not unique" + end + + def test_validate_straight_inheritance_uniqueness + w1 = IneptWizard.create(:name => "Rincewind", :city => "Ankh-Morpork") + assert w1.valid?, "Saving w1" + + # Should use validation from base class (which is abstract) + w2 = IneptWizard.new(:name => "Rincewind", :city => "Quirm") + assert !w2.valid?, "w2 shouldn't be valid" + assert w2.errors[:name].any?, "Should have errors for name" + assert_equal ["has already been taken"], w2.errors[:name], "Should have uniqueness message for name" + + w3 = Conjurer.new(:name => "Rincewind", :city => "Quirm") + assert !w3.valid?, "w3 shouldn't be valid" + assert w3.errors[:name].any?, "Should have errors for name" + assert_equal ["has already been taken"], w3.errors[:name], "Should have uniqueness message for name" + + w4 = Conjurer.create(:name => "The Amazing Bonko", :city => "Quirm") + assert w4.valid?, "Saving w4" + + w5 = Thaumaturgist.new(:name => "The Amazing Bonko", :city => "Lancre") + assert !w5.valid?, "w5 shouldn't be valid" + assert w5.errors[:name].any?, "Should have errors for name" + assert_equal ["has already been taken"], w5.errors[:name], "Should have uniqueness message for name" + + w6 = Thaumaturgist.new(:name => "Mustrum Ridcully", :city => "Quirm") + assert !w6.valid?, "w6 shouldn't be valid" + assert w6.errors[:city].any?, "Should have errors for city" + assert_equal ["has already been taken"], w6.errors[:city], "Should have uniqueness message for city" + end + + def test_validates_uniqueness_of_with_custom_message_using_quotes + repair_validations(Developer) do + Developer.validates_uniqueness_of :name, :message=> "This string contains 'single' and \"double\" quotes" + d = Developer.new + d.name = "David" + assert !d.valid? + assert_equal ["This string contains 'single' and \"double\" quotes"], d.errors[:name] + end + end +end diff --git a/activerecord/test/cases/validations_test.rb b/activerecord/test/cases/validations_test.rb index c20f5ae63e..a4e874e5e6 100644 --- a/activerecord/test/cases/validations_test.rb +++ b/activerecord/test/cases/validations_test.rb @@ -10,94 +10,43 @@ require 'models/owner' require 'models/pet' require 'models/event' -# The following methods in Topic are used in test_conditional_validation_* -class Topic - has_many :unique_replies, :dependent => :destroy, :foreign_key => "parent_id" - has_many :silly_unique_replies, :dependent => :destroy, :foreign_key => "parent_id" - - def condition_is_true - true - end - - def condition_is_true_but_its_not - false - end -end - class ProtectedPerson < ActiveRecord::Base set_table_name 'people' attr_accessor :addon attr_protected :first_name end -class UniqueReply < Reply - validates_uniqueness_of :content, :scope => 'parent_id' -end - -class SillyUniqueReply < UniqueReply -end - -class Wizard < ActiveRecord::Base - self.abstract_class = true +class DeprecatedPerson < ActiveRecord::Base + set_table_name 'people' - validates_uniqueness_of :name -end + protected -class IneptWizard < Wizard - validates_uniqueness_of :city -end + def validate + errors[:name] << "always invalid" + end -class Conjurer < IneptWizard -end + def validate_on_create + errors[:name] << "invalid on create" + end -class Thaumaturgist < IneptWizard + def validate_on_update + errors[:name] << "invalid on update" + end end - class ValidationsTest < ActiveRecord::TestCase - fixtures :topics, :developers, 'warehouse-things' + fixtures :topics, :developers # Most of the tests mess with the validations of Topic, so lets repair it all the time. # Other classes we mess with will be dealt with in the specific tests repair_validations(Topic) - def test_single_field_validation - r = Reply.new - r.title = "There's no content!" - assert !r.valid?, "A reply without content shouldn't be saveable" - - r.content = "Messa content!" - assert r.valid?, "A reply with content should be saveable" - end - - def test_single_attr_validation_and_error_msg - r = Reply.new - r.title = "There's no content!" - assert !r.valid? - assert r.errors.invalid?("content"), "A reply without content should mark that attribute as invalid" - assert_equal "Empty", r.errors.on("content"), "A reply without content should contain an error" - assert_equal 1, r.errors.count - end - - def test_double_attr_validation_and_error_msg - r = Reply.new - assert !r.valid? - - assert r.errors.invalid?("title"), "A reply without title should mark that attribute as invalid" - assert_equal "Empty", r.errors.on("title"), "A reply without title should contain an error" - - assert r.errors.invalid?("content"), "A reply without content should mark that attribute as invalid" - assert_equal "Empty", r.errors.on("content"), "A reply without content should contain an error" - - assert_equal 2, r.errors.count - end - def test_error_on_create r = Reply.new r.title = "Wrong Create" assert !r.valid? - assert r.errors.invalid?("title"), "A reply with a bad title should mark that attribute as invalid" - assert_equal "is Wrong Create", r.errors.on("title"), "A reply with a bad content should contain an error" + assert r.errors[:title].any?, "A reply with a bad title should mark that attribute as invalid" + assert_equal ["is Wrong Create"], r.errors[:title], "A reply with a bad content should contain an error" end def test_error_on_update @@ -109,8 +58,8 @@ class ValidationsTest < ActiveRecord::TestCase r.title = "Wrong Update" assert !r.save, "Second save should fail" - assert r.errors.invalid?("title"), "A reply with a bad title should mark that attribute as invalid" - assert_equal "is Wrong Update", r.errors.on("title"), "A reply with a bad content should contain an error" + assert r.errors[:title].any?, "A reply with a bad title should mark that attribute as invalid" + assert_equal ["is Wrong Update"], r.errors[:title], "A reply with a bad content should contain an error" end def test_invalid_record_exception @@ -172,47 +121,6 @@ class ValidationsTest < ActiveRecord::TestCase end end - def test_single_error_per_attr_iteration - r = Reply.new - r.save - - errors = [] - r.errors.each { |attr, msg| errors << [attr, msg] } - - assert errors.include?(["title", "Empty"]) - assert errors.include?(["content", "Empty"]) - end - - def test_multiple_errors_per_attr_iteration_with_full_error_composition - r = Reply.new - r.title = "Wrong Create" - r.content = "Mismatch" - r.save - - errors = [] - r.errors.each_full { |error| errors << error } - - assert_equal "Title is Wrong Create", errors[0] - assert_equal "Title is Content Mismatch", errors[1] - assert_equal 2, r.errors.count - end - - def test_errors_on_base - r = Reply.new - r.content = "Mismatch" - r.save - r.errors.add_to_base "Reply is not dignifying" - - errors = [] - r.errors.each_full { |error| errors << error } - - assert_equal "Reply is not dignifying", r.errors.on_base - - assert errors.include?("Title Empty") - assert errors.include?("Reply is not dignifying") - assert_equal 2, r.errors.count - end - def test_create_without_validation reply = Reply.new assert !reply.save @@ -225,96 +133,6 @@ class ValidationsTest < ActiveRecord::TestCase assert count+1, Reply.count end - def test_validates_each - hits = 0 - Topic.validates_each(:title, :content, [:title, :content]) do |record, attr| - record.errors.add attr, 'gotcha' - hits += 1 - end - t = Topic.new("title" => "valid", "content" => "whatever") - assert !t.save - assert_equal 4, hits - assert_equal %w(gotcha gotcha), t.errors.on(:title) - assert_equal %w(gotcha gotcha), t.errors.on(:content) - end - - def test_no_title_confirmation - Topic.validates_confirmation_of(:title) - - t = Topic.new(:author_name => "Plutarch") - assert t.valid? - - t.title_confirmation = "Parallel Lives" - assert !t.valid? - - t.title_confirmation = nil - t.title = "Parallel Lives" - assert t.valid? - - t.title_confirmation = "Parallel Lives" - assert t.valid? - end - - def test_title_confirmation - Topic.validates_confirmation_of(:title) - - t = Topic.create("title" => "We should be confirmed","title_confirmation" => "") - assert !t.save - - t.title_confirmation = "We should be confirmed" - assert t.save - end - - def test_terms_of_service_agreement_no_acceptance - Topic.validates_acceptance_of(:terms_of_service, :on => :create) - - t = Topic.create("title" => "We should not be confirmed") - assert t.save - end - - def test_terms_of_service_agreement - Topic.validates_acceptance_of(:terms_of_service, :on => :create) - - t = Topic.create("title" => "We should be confirmed","terms_of_service" => "") - assert !t.save - assert_equal "must be accepted", t.errors.on(:terms_of_service) - - t.terms_of_service = "1" - assert t.save - end - - - def test_eula - Topic.validates_acceptance_of(:eula, :message => "must be abided", :on => :create) - - t = Topic.create("title" => "We should be confirmed","eula" => "") - assert !t.save - assert_equal "must be abided", t.errors.on(:eula) - - t.eula = "1" - assert t.save - end - - def test_terms_of_service_agreement_with_accept_value - Topic.validates_acceptance_of(:terms_of_service, :on => :create, :accept => "I agree.") - - t = Topic.create("title" => "We should be confirmed", "terms_of_service" => "") - assert !t.save - assert_equal "must be accepted", t.errors.on(:terms_of_service) - - t.terms_of_service = "I agree." - assert t.save - end - - def test_validates_acceptance_of_as_database_column - repair_validations(Reply) do - Reply.validates_acceptance_of(:author_name) - - reply = Reply.create("author_name" => "Dan Brown") - assert_equal "Dan Brown", reply["author_name"] - end - end - def test_validates_acceptance_of_with_non_existant_table Object.const_set :IncorporealModel, Class.new(ActiveRecord::Base) @@ -323,868 +141,6 @@ class ValidationsTest < ActiveRecord::TestCase end end - def test_validate_presences - Topic.validates_presence_of(:title, :content) - - t = Topic.create - assert !t.save - assert_equal "can't be blank", t.errors.on(:title) - assert_equal "can't be blank", t.errors.on(:content) - - t.title = "something" - t.content = " " - - assert !t.save - assert_equal "can't be blank", t.errors.on(:content) - - t.content = "like stuff" - - assert t.save - end - - def test_validate_uniqueness - Topic.validates_uniqueness_of(:title) - - t = Topic.new("title" => "I'm unique!") - assert t.save, "Should save t as unique" - - t.content = "Remaining unique" - assert t.save, "Should still save t as unique" - - t2 = Topic.new("title" => "I'm unique!") - assert !t2.valid?, "Shouldn't be valid" - assert !t2.save, "Shouldn't save t2 as unique" - assert_equal "has already been taken", t2.errors.on(:title) - - t2.title = "Now Im really also unique" - assert t2.save, "Should now save t2 as unique" - end - - def test_validates_uniquness_with_newline_chars - Topic.validates_uniqueness_of(:title, :case_sensitive => false) - - t = Topic.new("title" => "new\nline") - assert t.save, "Should save t as unique" - end - - def test_validate_uniqueness_with_scope - repair_validations(Reply) do - Reply.validates_uniqueness_of(:content, :scope => "parent_id") - - t = Topic.create("title" => "I'm unique!") - - r1 = t.replies.create "title" => "r1", "content" => "hello world" - assert r1.valid?, "Saving r1" - - r2 = t.replies.create "title" => "r2", "content" => "hello world" - assert !r2.valid?, "Saving r2 first time" - - r2.content = "something else" - assert r2.save, "Saving r2 second time" - - t2 = Topic.create("title" => "I'm unique too!") - r3 = t2.replies.create "title" => "r3", "content" => "hello world" - assert r3.valid?, "Saving r3" - end - end - - def test_validate_uniqueness_scoped_to_defining_class - t = Topic.create("title" => "What, me worry?") - - r1 = t.unique_replies.create "title" => "r1", "content" => "a barrel of fun" - assert r1.valid?, "Saving r1" - - r2 = t.silly_unique_replies.create "title" => "r2", "content" => "a barrel of fun" - assert !r2.valid?, "Saving r2" - - # Should succeed as validates_uniqueness_of only applies to - # UniqueReply and its subclasses - r3 = t.replies.create "title" => "r2", "content" => "a barrel of fun" - assert r3.valid?, "Saving r3" - end - - def test_validate_uniqueness_with_scope_array - repair_validations(Reply) do - Reply.validates_uniqueness_of(:author_name, :scope => [:author_email_address, :parent_id]) - - t = Topic.create("title" => "The earth is actually flat!") - - r1 = t.replies.create "author_name" => "jeremy", "author_email_address" => "jeremy@rubyonrails.com", "title" => "You're crazy!", "content" => "Crazy reply" - assert r1.valid?, "Saving r1" - - r2 = t.replies.create "author_name" => "jeremy", "author_email_address" => "jeremy@rubyonrails.com", "title" => "You're crazy!", "content" => "Crazy reply again..." - assert !r2.valid?, "Saving r2. Double reply by same author." - - r2.author_email_address = "jeremy_alt_email@rubyonrails.com" - assert r2.save, "Saving r2 the second time." - - r3 = t.replies.create "author_name" => "jeremy", "author_email_address" => "jeremy_alt_email@rubyonrails.com", "title" => "You're wrong", "content" => "It's cubic" - assert !r3.valid?, "Saving r3" - - r3.author_name = "jj" - assert r3.save, "Saving r3 the second time." - - r3.author_name = "jeremy" - assert !r3.save, "Saving r3 the third time." - end - end - - def test_validate_case_insensitive_uniqueness - Topic.validates_uniqueness_of(:title, :parent_id, :case_sensitive => false, :allow_nil => true) - - t = Topic.new("title" => "I'm unique!", :parent_id => 2) - assert t.save, "Should save t as unique" - - t.content = "Remaining unique" - assert t.save, "Should still save t as unique" - - t2 = Topic.new("title" => "I'm UNIQUE!", :parent_id => 1) - assert !t2.valid?, "Shouldn't be valid" - assert !t2.save, "Shouldn't save t2 as unique" - assert t2.errors.on(:title) - assert t2.errors.on(:parent_id) - assert_equal "has already been taken", t2.errors.on(:title) - - t2.title = "I'm truly UNIQUE!" - assert !t2.valid?, "Shouldn't be valid" - assert !t2.save, "Shouldn't save t2 as unique" - assert_nil t2.errors.on(:title) - assert t2.errors.on(:parent_id) - - t2.parent_id = 4 - assert t2.save, "Should now save t2 as unique" - - t2.parent_id = nil - t2.title = nil - assert t2.valid?, "should validate with nil" - assert t2.save, "should save with nil" - - with_kcode('UTF8') do - t_utf8 = Topic.new("title" => "Я тоже уникальный!") - assert t_utf8.save, "Should save t_utf8 as unique" - - # If database hasn't UTF-8 character set, this test fails - if Topic.find(t_utf8, :select => 'LOWER(title) AS title').title == "я тоже уникальный!" - t2_utf8 = Topic.new("title" => "я тоже УНИКАЛЬНЫЙ!") - assert !t2_utf8.valid?, "Shouldn't be valid" - assert !t2_utf8.save, "Shouldn't save t2_utf8 as unique" - end - end - end - - def test_validate_case_sensitive_uniqueness - Topic.validates_uniqueness_of(:title, :case_sensitive => true, :allow_nil => true) - - t = Topic.new("title" => "I'm unique!") - assert t.save, "Should save t as unique" - - t.content = "Remaining unique" - assert t.save, "Should still save t as unique" - - t2 = Topic.new("title" => "I'M UNIQUE!") - assert t2.valid?, "Should be valid" - assert t2.save, "Should save t2 as unique" - assert !t2.errors.on(:title) - assert !t2.errors.on(:parent_id) - assert_not_equal "has already been taken", t2.errors.on(:title) - - t3 = Topic.new("title" => "I'M uNiQUe!") - assert t3.valid?, "Should be valid" - assert t3.save, "Should save t2 as unique" - assert !t3.errors.on(:title) - assert !t3.errors.on(:parent_id) - assert_not_equal "has already been taken", t3.errors.on(:title) - end - - def test_validate_case_sensitive_uniqueness_with_attribute_passed_as_integer - Topic.validates_uniqueness_of(:title, :case_sensitve => true) - t = Topic.create!('title' => 101) - - t2 = Topic.new('title' => 101) - assert !t2.valid? - assert t2.errors.on(:title) - end - - def test_validate_uniqueness_with_non_standard_table_names - i1 = WarehouseThing.create(:value => 1000) - assert !i1.valid?, "i1 should not be valid" - assert i1.errors.on(:value), "Should not be empty" - end - - def test_validates_uniqueness_inside_with_scope - Topic.validates_uniqueness_of(:title) - - Topic.with_scope(:find => { :conditions => { :author_name => "David" } }) do - t1 = Topic.new("title" => "I'm unique!", "author_name" => "Mary") - assert t1.save - t2 = Topic.new("title" => "I'm unique!", "author_name" => "David") - assert !t2.valid? - end - end - - def test_validate_uniqueness_with_columns_which_are_sql_keywords - repair_validations(Guid) do - Guid.validates_uniqueness_of :key - g = Guid.new - g.key = "foo" - assert_nothing_raised { !g.valid? } - end - end - - def test_validate_uniqueness_with_limit - # Event.title is limited to 5 characters - e1 = Event.create(:title => "abcde") - assert e1.valid?, "Could not create an event with a unique, 5 character title" - e2 = Event.create(:title => "abcdefgh") - assert !e2.valid?, "Created an event whose title, with limit taken into account, is not unique" - end - - def test_validate_straight_inheritance_uniqueness - w1 = IneptWizard.create(:name => "Rincewind", :city => "Ankh-Morpork") - assert w1.valid?, "Saving w1" - - # Should use validation from base class (which is abstract) - w2 = IneptWizard.new(:name => "Rincewind", :city => "Quirm") - assert !w2.valid?, "w2 shouldn't be valid" - assert w2.errors.on(:name), "Should have errors for name" - assert_equal "has already been taken", w2.errors.on(:name), "Should have uniqueness message for name" - - w3 = Conjurer.new(:name => "Rincewind", :city => "Quirm") - assert !w3.valid?, "w3 shouldn't be valid" - assert w3.errors.on(:name), "Should have errors for name" - assert_equal "has already been taken", w3.errors.on(:name), "Should have uniqueness message for name" - - w4 = Conjurer.create(:name => "The Amazing Bonko", :city => "Quirm") - assert w4.valid?, "Saving w4" - - w5 = Thaumaturgist.new(:name => "The Amazing Bonko", :city => "Lancre") - assert !w5.valid?, "w5 shouldn't be valid" - assert w5.errors.on(:name), "Should have errors for name" - assert_equal "has already been taken", w5.errors.on(:name), "Should have uniqueness message for name" - - w6 = Thaumaturgist.new(:name => "Mustrum Ridcully", :city => "Quirm") - assert !w6.valid?, "w6 shouldn't be valid" - assert w6.errors.on(:city), "Should have errors for city" - assert_equal "has already been taken", w6.errors.on(:city), "Should have uniqueness message for city" - end - - def test_validate_format - Topic.validates_format_of(:title, :content, :with => /^Validation\smacros \w+!$/, :message => "is bad data") - - t = Topic.create("title" => "i'm incorrect", "content" => "Validation macros rule!") - assert !t.valid?, "Shouldn't be valid" - assert !t.save, "Shouldn't save because it's invalid" - assert_equal "is bad data", t.errors.on(:title) - assert_nil t.errors.on(:content) - - t.title = "Validation macros rule!" - - assert t.save - assert_nil t.errors.on(:title) - - assert_raise(ArgumentError) { Topic.validates_format_of(:title, :content) } - end - - def test_validate_format_with_allow_blank - Topic.validates_format_of(:title, :with => /^Validation\smacros \w+!$/, :allow_blank=>true) - assert !Topic.create("title" => "Shouldn't be valid").valid? - assert Topic.create("title" => "").valid? - assert Topic.create("title" => nil).valid? - assert Topic.create("title" => "Validation macros rule!").valid? - end - - # testing ticket #3142 - def test_validate_format_numeric - Topic.validates_format_of(:title, :content, :with => /^[1-9][0-9]*$/, :message => "is bad data") - - t = Topic.create("title" => "72x", "content" => "6789") - assert !t.valid?, "Shouldn't be valid" - assert !t.save, "Shouldn't save because it's invalid" - assert_equal "is bad data", t.errors.on(:title) - assert_nil t.errors.on(:content) - - t.title = "-11" - assert !t.valid?, "Shouldn't be valid" - - t.title = "03" - assert !t.valid?, "Shouldn't be valid" - - t.title = "z44" - assert !t.valid?, "Shouldn't be valid" - - t.title = "5v7" - assert !t.valid?, "Shouldn't be valid" - - t.title = "1" - - assert t.save - assert_nil t.errors.on(:title) - end - - def test_validate_format_with_formatted_message - Topic.validates_format_of(:title, :with => /^Valid Title$/, :message => "can't be {{value}}") - t = Topic.create(:title => 'Invalid title') - assert_equal "can't be Invalid title", t.errors.on(:title) - end - - def test_validates_inclusion_of - Topic.validates_inclusion_of( :title, :in => %w( a b c d e f g ) ) - - assert !Topic.create("title" => "a!", "content" => "abc").valid? - assert !Topic.create("title" => "a b", "content" => "abc").valid? - assert !Topic.create("title" => nil, "content" => "def").valid? - - t = Topic.create("title" => "a", "content" => "I know you are but what am I?") - assert t.valid? - t.title = "uhoh" - assert !t.valid? - assert t.errors.on(:title) - assert_equal "is not included in the list", t.errors["title"] - - assert_raise(ArgumentError) { Topic.validates_inclusion_of( :title, :in => nil ) } - assert_raise(ArgumentError) { Topic.validates_inclusion_of( :title, :in => 0) } - - assert_nothing_raised(ArgumentError) { Topic.validates_inclusion_of( :title, :in => "hi!" ) } - assert_nothing_raised(ArgumentError) { Topic.validates_inclusion_of( :title, :in => {} ) } - assert_nothing_raised(ArgumentError) { Topic.validates_inclusion_of( :title, :in => [] ) } - end - - def test_validates_inclusion_of_with_allow_nil - Topic.validates_inclusion_of( :title, :in => %w( a b c d e f g ), :allow_nil=>true ) - - assert !Topic.create("title" => "a!", "content" => "abc").valid? - assert !Topic.create("title" => "", "content" => "abc").valid? - assert Topic.create("title" => nil, "content" => "abc").valid? - end - - def test_numericality_with_getter_method - repair_validations(Developer) do - Developer.validates_numericality_of( :salary ) - developer = Developer.new("name" => "michael", "salary" => nil) - developer.instance_eval("def salary; read_attribute('salary') ? read_attribute('salary') : 100000; end") - assert developer.valid? - end - end - - def test_validates_length_of_with_allow_nil - Topic.validates_length_of( :title, :is => 5, :allow_nil=>true ) - - assert !Topic.create("title" => "ab").valid? - assert !Topic.create("title" => "").valid? - assert Topic.create("title" => nil).valid? - assert Topic.create("title" => "abcde").valid? - end - - def test_validates_length_of_with_allow_blank - Topic.validates_length_of( :title, :is => 5, :allow_blank=>true ) - - assert !Topic.create("title" => "ab").valid? - assert Topic.create("title" => "").valid? - assert Topic.create("title" => nil).valid? - assert Topic.create("title" => "abcde").valid? - end - - def test_validates_inclusion_of_with_formatted_message - Topic.validates_inclusion_of( :title, :in => %w( a b c d e f g ), :message => "option {{value}} is not in the list" ) - - assert Topic.create("title" => "a", "content" => "abc").valid? - - t = Topic.create("title" => "uhoh", "content" => "abc") - assert !t.valid? - assert t.errors.on(:title) - assert_equal "option uhoh is not in the list", t.errors["title"] - end - - def test_numericality_with_allow_nil_and_getter_method - repair_validations(Developer) do - Developer.validates_numericality_of( :salary, :allow_nil => true) - developer = Developer.new("name" => "michael", "salary" => nil) - developer.instance_eval("def salary; read_attribute('salary') ? read_attribute('salary') : 100000; end") - assert developer.valid? - end - end - - def test_validates_exclusion_of - Topic.validates_exclusion_of( :title, :in => %w( abe monkey ) ) - - assert Topic.create("title" => "something", "content" => "abc").valid? - assert !Topic.create("title" => "monkey", "content" => "abc").valid? - end - - def test_validates_exclusion_of_with_formatted_message - Topic.validates_exclusion_of( :title, :in => %w( abe monkey ), :message => "option {{value}} is restricted" ) - - assert Topic.create("title" => "something", "content" => "abc") - - t = Topic.create("title" => "monkey") - assert !t.valid? - assert t.errors.on(:title) - assert_equal "option monkey is restricted", t.errors["title"] - end - - def test_validates_length_of_using_minimum - Topic.validates_length_of :title, :minimum => 5 - - t = Topic.create("title" => "valid", "content" => "whatever") - assert t.valid? - - t.title = "not" - assert !t.valid? - assert t.errors.on(:title) - assert_equal "is too short (minimum is 5 characters)", t.errors["title"] - - t.title = "" - assert !t.valid? - assert t.errors.on(:title) - assert_equal "is too short (minimum is 5 characters)", t.errors["title"] - - t.title = nil - assert !t.valid? - assert t.errors.on(:title) - assert_equal "is too short (minimum is 5 characters)", t.errors["title"] - end - - def test_optionally_validates_length_of_using_minimum - Topic.validates_length_of :title, :minimum => 5, :allow_nil => true - - t = Topic.create("title" => "valid", "content" => "whatever") - assert t.valid? - - t.title = nil - assert t.valid? - end - - def test_validates_length_of_using_maximum - Topic.validates_length_of :title, :maximum => 5 - - t = Topic.create("title" => "valid", "content" => "whatever") - assert t.valid? - - t.title = "notvalid" - assert !t.valid? - assert t.errors.on(:title) - assert_equal "is too long (maximum is 5 characters)", t.errors["title"] - - t.title = "" - assert t.valid? - - t.title = nil - assert !t.valid? - end - - def test_optionally_validates_length_of_using_maximum - Topic.validates_length_of :title, :maximum => 5, :allow_nil => true - - t = Topic.create("title" => "valid", "content" => "whatever") - assert t.valid? - - t.title = nil - assert t.valid? - end - - def test_validates_length_of_using_within - Topic.validates_length_of(:title, :content, :within => 3..5) - - t = Topic.new("title" => "a!", "content" => "I'm ooooooooh so very long") - assert !t.valid? - assert_equal "is too short (minimum is 3 characters)", t.errors.on(:title) - assert_equal "is too long (maximum is 5 characters)", t.errors.on(:content) - - t.title = nil - t.content = nil - assert !t.valid? - assert_equal "is too short (minimum is 3 characters)", t.errors.on(:title) - assert_equal "is too short (minimum is 3 characters)", t.errors.on(:content) - - t.title = "abe" - t.content = "mad" - assert t.valid? - end - - def test_optionally_validates_length_of_using_within - Topic.validates_length_of :title, :content, :within => 3..5, :allow_nil => true - - t = Topic.create('title' => 'abc', 'content' => 'abcd') - assert t.valid? - - t.title = nil - assert t.valid? - end - - def test_optionally_validates_length_of_using_within_on_create - Topic.validates_length_of :title, :content, :within => 5..10, :on => :create, :too_long => "my string is too long: {{count}}" - - t = Topic.create("title" => "thisisnotvalid", "content" => "whatever") - assert !t.save - assert t.errors.on(:title) - assert_equal "my string is too long: 10", t.errors[:title] - - t.title = "butthisis" - assert t.save - - t.title = "few" - assert t.save - - t.content = "andthisislong" - assert t.save - - t.content = t.title = "iamfine" - assert t.save - end - - def test_optionally_validates_length_of_using_within_on_update - Topic.validates_length_of :title, :content, :within => 5..10, :on => :update, :too_short => "my string is too short: {{count}}" - - t = Topic.create("title" => "vali", "content" => "whatever") - assert !t.save - assert t.errors.on(:title) - - t.title = "not" - assert !t.save - assert t.errors.on(:title) - assert_equal "my string is too short: 5", t.errors[:title] - - t.title = "valid" - t.content = "andthisistoolong" - assert !t.save - assert t.errors.on(:content) - - t.content = "iamfine" - assert t.save - end - - def test_validates_length_of_using_is - Topic.validates_length_of :title, :is => 5 - - t = Topic.create("title" => "valid", "content" => "whatever") - assert t.valid? - - t.title = "notvalid" - assert !t.valid? - assert t.errors.on(:title) - assert_equal "is the wrong length (should be 5 characters)", t.errors["title"] - - t.title = "" - assert !t.valid? - - t.title = nil - assert !t.valid? - end - - def test_optionally_validates_length_of_using_is - Topic.validates_length_of :title, :is => 5, :allow_nil => true - - t = Topic.create("title" => "valid", "content" => "whatever") - assert t.valid? - - t.title = nil - assert t.valid? - end - - def test_validates_length_of_using_bignum - bigmin = 2 ** 30 - bigmax = 2 ** 32 - bigrange = bigmin...bigmax - assert_nothing_raised do - Topic.validates_length_of :title, :is => bigmin + 5 - Topic.validates_length_of :title, :within => bigrange - Topic.validates_length_of :title, :in => bigrange - Topic.validates_length_of :title, :minimum => bigmin - Topic.validates_length_of :title, :maximum => bigmax - end - end - - def test_validates_length_with_globally_modified_error_message - ActiveSupport::Deprecation.silence do - ActiveRecord::Errors.default_error_messages[:too_short] = 'tu est trops petit hombre {{count}}' - end - Topic.validates_length_of :title, :minimum => 10 - t = Topic.create(:title => 'too short') - assert !t.valid? - - assert_equal 'tu est trops petit hombre 10', t.errors['title'] - end - - def test_validates_size_of_association - repair_validations(Owner) do - assert_nothing_raised { Owner.validates_size_of :pets, :minimum => 1 } - o = Owner.new('name' => 'nopets') - assert !o.save - assert o.errors.on(:pets) - pet = o.pets.build('name' => 'apet') - assert o.valid? - end - end - - def test_validates_size_of_association_using_within - repair_validations(Owner) do - assert_nothing_raised { Owner.validates_size_of :pets, :within => 1..2 } - o = Owner.new('name' => 'nopets') - assert !o.save - assert o.errors.on(:pets) - - pet = o.pets.build('name' => 'apet') - assert o.valid? - - 2.times { o.pets.build('name' => 'apet') } - assert !o.save - assert o.errors.on(:pets) - end - end - - def test_validates_length_of_nasty_params - assert_raise(ArgumentError) { Topic.validates_length_of(:title, :minimum=>6, :maximum=>9) } - assert_raise(ArgumentError) { Topic.validates_length_of(:title, :within=>6, :maximum=>9) } - assert_raise(ArgumentError) { Topic.validates_length_of(:title, :within=>6, :minimum=>9) } - assert_raise(ArgumentError) { Topic.validates_length_of(:title, :within=>6, :is=>9) } - assert_raise(ArgumentError) { Topic.validates_length_of(:title, :minimum=>"a") } - assert_raise(ArgumentError) { Topic.validates_length_of(:title, :maximum=>"a") } - assert_raise(ArgumentError) { Topic.validates_length_of(:title, :within=>"a") } - assert_raise(ArgumentError) { Topic.validates_length_of(:title, :is=>"a") } - end - - def test_validates_length_of_custom_errors_for_minimum_with_message - Topic.validates_length_of( :title, :minimum=>5, :message=>"boo {{count}}" ) - t = Topic.create("title" => "uhoh", "content" => "whatever") - assert !t.valid? - assert t.errors.on(:title) - assert_equal "boo 5", t.errors["title"] - end - - def test_validates_length_of_custom_errors_for_minimum_with_too_short - Topic.validates_length_of( :title, :minimum=>5, :too_short=>"hoo {{count}}" ) - t = Topic.create("title" => "uhoh", "content" => "whatever") - assert !t.valid? - assert t.errors.on(:title) - assert_equal "hoo 5", t.errors["title"] - end - - def test_validates_length_of_custom_errors_for_maximum_with_message - Topic.validates_length_of( :title, :maximum=>5, :message=>"boo {{count}}" ) - t = Topic.create("title" => "uhohuhoh", "content" => "whatever") - assert !t.valid? - assert t.errors.on(:title) - assert_equal "boo 5", t.errors["title"] - end - - def test_validates_length_of_custom_errors_for_in - Topic.validates_length_of(:title, :in => 10..20, :message => "hoo {{count}}") - t = Topic.create("title" => "uhohuhoh", "content" => "whatever") - assert !t.valid? - assert t.errors.on(:title) - assert_equal "hoo 10", t.errors["title"] - - t = Topic.create("title" => "uhohuhohuhohuhohuhohuhohuhohuhoh", "content" => "whatever") - assert !t.valid? - assert t.errors.on(:title) - assert_equal "hoo 20", t.errors["title"] - end - - def test_validates_length_of_custom_errors_for_maximum_with_too_long - Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}" ) - t = Topic.create("title" => "uhohuhoh", "content" => "whatever") - assert !t.valid? - assert t.errors.on(:title) - assert_equal "hoo 5", t.errors["title"] - end - - def test_validates_length_of_custom_errors_for_is_with_message - Topic.validates_length_of( :title, :is=>5, :message=>"boo {{count}}" ) - t = Topic.create("title" => "uhohuhoh", "content" => "whatever") - assert !t.valid? - assert t.errors.on(:title) - assert_equal "boo 5", t.errors["title"] - end - - def test_validates_length_of_custom_errors_for_is_with_wrong_length - Topic.validates_length_of( :title, :is=>5, :wrong_length=>"hoo {{count}}" ) - t = Topic.create("title" => "uhohuhoh", "content" => "whatever") - assert !t.valid? - assert t.errors.on(:title) - assert_equal "hoo 5", t.errors["title"] - end - - def test_validates_length_of_using_minimum_utf8 - with_kcode('UTF8') do - Topic.validates_length_of :title, :minimum => 5 - - t = Topic.create("title" => "一二三四五", "content" => "whatever") - assert t.valid? - - t.title = "一二三四" - assert !t.valid? - assert t.errors.on(:title) - assert_equal "is too short (minimum is 5 characters)", t.errors["title"] - end - end - - def test_validates_length_of_using_maximum_utf8 - with_kcode('UTF8') do - Topic.validates_length_of :title, :maximum => 5 - - t = Topic.create("title" => "一二三四五", "content" => "whatever") - assert t.valid? - - t.title = "一二34五六" - assert !t.valid? - assert t.errors.on(:title) - assert_equal "is too long (maximum is 5 characters)", t.errors["title"] - end - end - - def test_validates_length_of_using_within_utf8 - with_kcode('UTF8') do - Topic.validates_length_of(:title, :content, :within => 3..5) - - t = Topic.new("title" => "一二", "content" => "12三四五六七") - assert !t.valid? - assert_equal "is too short (minimum is 3 characters)", t.errors.on(:title) - assert_equal "is too long (maximum is 5 characters)", t.errors.on(:content) - t.title = "一二三" - t.content = "12三" - assert t.valid? - end - end - - def test_optionally_validates_length_of_using_within_utf8 - with_kcode('UTF8') do - Topic.validates_length_of :title, :within => 3..5, :allow_nil => true - - t = Topic.create(:title => "一二三四五") - assert t.valid?, t.errors.inspect - - t = Topic.create(:title => "一二三") - assert t.valid?, t.errors.inspect - - t.title = nil - assert t.valid?, t.errors.inspect - end - end - - def test_optionally_validates_length_of_using_within_on_create_utf8 - with_kcode('UTF8') do - Topic.validates_length_of :title, :within => 5..10, :on => :create, :too_long => "長すぎます: {{count}}" - - t = Topic.create("title" => "一二三四五六七八九十A", "content" => "whatever") - assert !t.save - assert t.errors.on(:title) - assert_equal "長すぎます: 10", t.errors[:title] - - t.title = "一二三四五六七八九" - assert t.save - - t.title = "一二3" - assert t.save - - t.content = "一二三四五六七八九十" - assert t.save - - t.content = t.title = "一二三四五六" - assert t.save - end - end - - def test_optionally_validates_length_of_using_within_on_update_utf8 - with_kcode('UTF8') do - Topic.validates_length_of :title, :within => 5..10, :on => :update, :too_short => "短すぎます: {{count}}" - - t = Topic.create("title" => "一二三4", "content" => "whatever") - assert !t.save - assert t.errors.on(:title) - - t.title = "1二三4" - assert !t.save - assert t.errors.on(:title) - assert_equal "短すぎます: 5", t.errors[:title] - - t.title = "一二三四五六七八九十A" - assert !t.save - assert t.errors.on(:title) - - t.title = "一二345" - assert t.save - end - end - - def test_validates_length_of_using_is_utf8 - with_kcode('UTF8') do - Topic.validates_length_of :title, :is => 5 - - t = Topic.create("title" => "一二345", "content" => "whatever") - assert t.valid? - - t.title = "一二345六" - assert !t.valid? - assert t.errors.on(:title) - assert_equal "is the wrong length (should be 5 characters)", t.errors["title"] - end - end - - def test_validates_length_of_with_block - Topic.validates_length_of :content, :minimum => 5, :too_short=>"Your essay must be at least {{count}} words.", - :tokenizer => lambda {|str| str.scan(/\w+/) } - t = Topic.create!(:content => "this content should be long enough") - assert t.valid? - - t.content = "not long enough" - assert !t.valid? - assert t.errors.on(:content) - assert_equal "Your essay must be at least 5 words.", t.errors[:content] - end - - def test_validates_size_of_association_utf8 - repair_validations(Owner) do - with_kcode('UTF8') do - assert_nothing_raised { Owner.validates_size_of :pets, :minimum => 1 } - o = Owner.new('name' => 'あいうえおかきくけこ') - assert !o.save - assert o.errors.on(:pets) - o.pets.build('name' => 'あいうえおかきくけこ') - assert o.valid? - end - end - end - - def test_validates_associated_many - Topic.validates_associated( :replies ) - t = Topic.create("title" => "uhohuhoh", "content" => "whatever") - 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")] - assert !t.valid? - assert t.errors.on(:replies) - assert_equal 1, r.errors.count # make sure all associated objects have been validated - assert_equal 0, r2.errors.count - assert_equal 1, r3.errors.count - assert_equal 0, r4.errors.count - r.content = r3.content = "non-empty" - assert t.valid? - end - - def test_validates_associated_one - repair_validations(Reply) do - Reply.validates_associated( :topic ) - Topic.validates_presence_of( :content ) - r = Reply.new("title" => "A reply", "content" => "with content!") - r.topic = Topic.create("title" => "uhohuhoh") - assert !r.valid? - assert r.errors.on(:topic) - r.topic.content = "non-empty" - assert r.valid? - end - end - - def test_validate_block - Topic.validate { |topic| topic.errors.add("title", "will never be valid") } - t = Topic.create("title" => "Title", "content" => "whatever") - assert !t.valid? - assert t.errors.on(:title) - assert_equal "will never be valid", t.errors["title"] - end - - def test_invalid_validator - Topic.validate 3 - assert_raise(ArgumentError) { t = Topic.create } - end - def test_throw_away_typing d = Developer.new("name" => "David", "salary" => "100,000") assert !d.valid? @@ -1192,411 +148,40 @@ class ValidationsTest < ActiveRecord::TestCase assert_equal "100,000", d.salary_before_type_cast end - def test_validates_acceptance_of_with_custom_error_using_quotes - repair_validations(Developer) do - Developer.validates_acceptance_of :salary, :message=> "This string contains 'single' and \"double\" quotes" - d = Developer.new - d.salary = "0" - assert !d.valid? - assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:salary).last - end - end - - def test_validates_confirmation_of_with_custom_error_using_quotes - repair_validations(Developer) do - Developer.validates_confirmation_of :name, :message=> "confirm 'single' and \"double\" quotes" - d = Developer.new - d.name = "John" - d.name_confirmation = "Johnny" - assert !d.valid? - assert_equal "confirm 'single' and \"double\" quotes", d.errors.on(:name) - end - end - - def test_validates_format_of_with_custom_error_using_quotes - repair_validations(Developer) do - Developer.validates_format_of :name, :with => /^(A-Z*)$/, :message=> "format 'single' and \"double\" quotes" - d = Developer.new - d.name = d.name_confirmation = "John 32" - assert !d.valid? - assert_equal "format 'single' and \"double\" quotes", d.errors.on(:name) - end - end - - def test_validates_inclusion_of_with_custom_error_using_quotes - repair_validations(Developer) do - Developer.validates_inclusion_of :salary, :in => 1000..80000, :message=> "This string contains 'single' and \"double\" quotes" - d = Developer.new - d.salary = "90,000" - assert !d.valid? - assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:salary).last - end - end - - def test_validates_length_of_with_custom_too_long_using_quotes - repair_validations(Developer) do - Developer.validates_length_of :name, :maximum => 4, :too_long=> "This string contains 'single' and \"double\" quotes" - d = Developer.new - d.name = "Jeffrey" - assert !d.valid? - assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:name) - end - end - - def test_validates_length_of_with_custom_too_short_using_quotes - repair_validations(Developer) do - Developer.validates_length_of :name, :minimum => 4, :too_short=> "This string contains 'single' and \"double\" quotes" - d = Developer.new - d.name = "Joe" - assert !d.valid? - assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:name) - end - end - - def test_validates_length_of_with_custom_message_using_quotes - repair_validations(Developer) do - Developer.validates_length_of :name, :minimum => 4, :message=> "This string contains 'single' and \"double\" quotes" - d = Developer.new - d.name = "Joe" - assert !d.valid? - assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:name) - end - end - - def test_validates_presence_of_with_custom_message_using_quotes - repair_validations(Developer) do - Developer.validates_presence_of :non_existent, :message=> "This string contains 'single' and \"double\" quotes" - d = Developer.new - d.name = "Joe" - assert !d.valid? - assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:non_existent) - end - end - - def test_validates_uniqueness_of_with_custom_message_using_quotes - repair_validations(Developer) do - Developer.validates_uniqueness_of :name, :message=> "This string contains 'single' and \"double\" quotes" - d = Developer.new - d.name = "David" - assert !d.valid? - assert_equal "This string contains 'single' and \"double\" quotes", d.errors.on(:name) - end - end - - def test_validates_associated_with_custom_message_using_quotes - repair_validations(Reply) do - Reply.validates_associated :topic, :message=> "This string contains 'single' and \"double\" quotes" - Topic.validates_presence_of :content - r = Reply.create("title" => "A reply", "content" => "with content!") - r.topic = Topic.create("title" => "uhohuhoh") - assert !r.valid? - assert_equal "This string contains 'single' and \"double\" quotes", r.errors.on(:topic) + def test_validates_length_with_globally_modified_error_message + ActiveSupport::Deprecation.silence do + ActiveRecord::Errors.default_error_messages[:too_short] = 'tu est trops petit hombre {{count}}' end - end - def test_if_validation_using_method_true - # When the method returns true - Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}", :if => :condition_is_true ) - t = Topic.create("title" => "uhohuhoh", "content" => "whatever") - assert !t.valid? - assert t.errors.on(:title) - assert_equal "hoo 5", t.errors["title"] - end - - def test_unless_validation_using_method_true - # When the method returns true - Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}", :unless => :condition_is_true ) - t = Topic.create("title" => "uhohuhoh", "content" => "whatever") - assert t.valid? - assert !t.errors.on(:title) - end - - def test_if_validation_using_method_false - # When the method returns false - Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}", :if => :condition_is_true_but_its_not ) - t = Topic.create("title" => "uhohuhoh", "content" => "whatever") - assert t.valid? - assert !t.errors.on(:title) - end - - def test_unless_validation_using_method_false - # When the method returns false - Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}", :unless => :condition_is_true_but_its_not ) - t = Topic.create("title" => "uhohuhoh", "content" => "whatever") - assert !t.valid? - assert t.errors.on(:title) - assert_equal "hoo 5", t.errors["title"] - end - - def test_if_validation_using_string_true - # When the evaluated string returns true - Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}", :if => "a = 1; a == 1" ) - t = Topic.create("title" => "uhohuhoh", "content" => "whatever") - assert !t.valid? - assert t.errors.on(:title) - assert_equal "hoo 5", t.errors["title"] - end - - def test_unless_validation_using_string_true - # When the evaluated string returns true - Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}", :unless => "a = 1; a == 1" ) - t = Topic.create("title" => "uhohuhoh", "content" => "whatever") - assert t.valid? - assert !t.errors.on(:title) - end - - def test_if_validation_using_string_false - # When the evaluated string returns false - Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}", :if => "false") - t = Topic.create("title" => "uhohuhoh", "content" => "whatever") - assert t.valid? - assert !t.errors.on(:title) - end - - def test_unless_validation_using_string_false - # When the evaluated string returns false - Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}", :unless => "false") - t = Topic.create("title" => "uhohuhoh", "content" => "whatever") - assert !t.valid? - assert t.errors.on(:title) - assert_equal "hoo 5", t.errors["title"] - end - - def test_if_validation_using_block_true - # When the block returns true - Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}", - :if => Proc.new { |r| r.content.size > 4 } ) - t = Topic.create("title" => "uhohuhoh", "content" => "whatever") + Topic.validates_length_of :title, :minimum => 10 + t = Topic.create(:title => 'too short') assert !t.valid? - assert t.errors.on(:title) - assert_equal "hoo 5", t.errors["title"] - end - - def test_unless_validation_using_block_true - # When the block returns true - Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}", - :unless => Proc.new { |r| r.content.size > 4 } ) - t = Topic.create("title" => "uhohuhoh", "content" => "whatever") - assert t.valid? - assert !t.errors.on(:title) - end - def test_if_validation_using_block_false - # When the block returns false - Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}", - :if => Proc.new { |r| r.title != "uhohuhoh"} ) - t = Topic.create("title" => "uhohuhoh", "content" => "whatever") - assert t.valid? - assert !t.errors.on(:title) + assert_equal ['tu est trops petit hombre 10'], t.errors[:title] end - def test_unless_validation_using_block_false - # When the block returns false - Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo {{count}}", - :unless => Proc.new { |r| r.title != "uhohuhoh"} ) - t = Topic.create("title" => "uhohuhoh", "content" => "whatever") - assert !t.valid? - assert t.errors.on(:title) - assert_equal "hoo 5", t.errors["title"] - end - - def test_validates_associated_missing + def test_validates_acceptance_of_as_database_column repair_validations(Reply) do - Reply.validates_presence_of(:topic) - r = Reply.create("title" => "A reply", "content" => "with content!") - assert !r.valid? - assert r.errors.on(:topic) + Reply.validates_acceptance_of(:author_name) - r.topic = Topic.find :first - assert r.valid? + reply = Reply.create("author_name" => "Dan Brown") + assert_equal "Dan Brown", reply["author_name"] end end - def test_errors_to_xml - r = Reply.new :title => "Wrong Create" - assert !r.valid? - xml = r.errors.to_xml(:skip_instruct => true) - assert_equal "<errors>", xml.first(8) - assert xml.include?("<error>Title is Wrong Create</error>") - assert xml.include?("<error>Content Empty</error>") - end - - def test_validation_order - Topic.validates_presence_of :title - Topic.validates_length_of :title, :minimum => 2 - - t = Topic.new("title" => "") - assert !t.valid? - assert_equal "can't be blank", t.errors.on("title").first - end - - def test_invalid_should_be_the_opposite_of_valid - Topic.validates_presence_of :title - - t = Topic.new - assert t.invalid? - assert t.errors.invalid?(:title) - - t.title = 'Things are going to change' - assert !t.invalid? - end - - # previous implementation of validates_presence_of eval'd the - # string with the wrong binding, this regression test is to - # ensure that it works correctly - def test_validation_with_if_as_string - Topic.validates_presence_of(:title) - Topic.validates_presence_of(:author_name, :if => "title.to_s.match('important')") - - t = Topic.new - assert !t.valid?, "A topic without a title should not be valid" - assert !t.errors.invalid?("author_name"), "A topic without an 'important' title should not require an author" - - t.title = "Just a title" - assert t.valid?, "A topic with a basic title should be valid" - - t.title = "A very important title" - assert !t.valid?, "A topic with an important title, but without an author, should not be valid" - assert t.errors.invalid?("author_name"), "A topic with an 'important' title should require an author" - - t.author_name = "Hubert J. Farnsworth" - assert t.valid?, "A topic with an important title and author should be valid" - end -end - - -class ValidatesNumericalityTest < ActiveRecord::TestCase - NIL = [nil] - BLANK = ["", " ", " \t \r \n"] - BIGDECIMAL_STRINGS = %w(12345678901234567890.1234567890) # 30 significent digits - 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) - INTEGER_STRINGS = %w(0 +0 -0 10 +10 -10 0090 -090) - FLOATS = [0.0, 10.0, 10.5, -10.5, -0.0001] + FLOAT_STRINGS - INTEGERS = [0, 10, -10] + INTEGER_STRINGS - BIGDECIMAL = BIGDECIMAL_STRINGS.collect! { |bd| BigDecimal.new(bd) } - 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"] - INFINITY = [1.0/0.0] + def test_deprecated_validation_instance_methods + tom = DeprecatedPerson.new - repair_validations(Topic) - - def test_default_validates_numericality_of - Topic.validates_numericality_of :approved - - invalid!(NIL + BLANK + JUNK) - valid!(FLOATS + INTEGERS + BIGDECIMAL + INFINITY) - end - - def test_validates_numericality_of_with_nil_allowed - Topic.validates_numericality_of :approved, :allow_nil => true - - invalid!(JUNK) - valid!(NIL + BLANK + FLOATS + INTEGERS + BIGDECIMAL + INFINITY) - end - - def test_validates_numericality_of_with_integer_only - Topic.validates_numericality_of :approved, :only_integer => true - - invalid!(NIL + BLANK + JUNK + FLOATS + BIGDECIMAL + INFINITY) - valid!(INTEGERS) - end - - def test_validates_numericality_of_with_integer_only_and_nil_allowed - Topic.validates_numericality_of :approved, :only_integer => true, :allow_nil => true - - invalid!(JUNK + FLOATS + BIGDECIMAL + INFINITY) - valid!(NIL + BLANK + INTEGERS) - end - - def test_validates_numericality_with_greater_than - Topic.validates_numericality_of :approved, :greater_than => 10 - - invalid!([-10, 10], 'must be greater than 10') - valid!([11]) - end - - def test_validates_numericality_with_greater_than_or_equal - Topic.validates_numericality_of :approved, :greater_than_or_equal_to => 10 - - invalid!([-9, 9], 'must be greater than or equal to 10') - valid!([10]) - end - - def test_validates_numericality_with_equal_to - Topic.validates_numericality_of :approved, :equal_to => 10 - - invalid!([-10, 11] + INFINITY, 'must be equal to 10') - valid!([10]) - end - - def test_validates_numericality_with_less_than - Topic.validates_numericality_of :approved, :less_than => 10 - - invalid!([10], 'must be less than 10') - valid!([-9, 9]) - end - - def test_validates_numericality_with_less_than_or_equal_to - Topic.validates_numericality_of :approved, :less_than_or_equal_to => 10 - - invalid!([11], 'must be less than or equal to 10') - valid!([-10, 10]) - end - - def test_validates_numericality_with_odd - Topic.validates_numericality_of :approved, :odd => true - - invalid!([-2, 2], 'must be odd') - valid!([-1, 1]) - end - - def test_validates_numericality_with_even - Topic.validates_numericality_of :approved, :even => true - - invalid!([-1, 1], 'must be even') - valid!([-2, 2]) - end - - def test_validates_numericality_with_greater_than_less_than_and_even - Topic.validates_numericality_of :approved, :greater_than => 1, :less_than => 4, :even => true - - invalid!([1, 3, 4]) - valid!([2]) - end - - def test_validates_numericality_with_numeric_message - Topic.validates_numericality_of :approved, :less_than => 4, :message => "smaller than {{count}}" - topic = Topic.new("title" => "numeric test", "approved" => 10) - - assert !topic.valid? - assert_equal "smaller than 4", topic.errors.on(:approved) - - Topic.validates_numericality_of :approved, :greater_than => 4, :message => "greater than {{count}}" - topic = Topic.new("title" => "numeric test", "approved" => 1) - - assert !topic.valid? - assert_equal "greater than 4", topic.errors.on(:approved) - end - - private - def invalid!(values, error=nil) - with_each_topic_approved_value(values) do |topic, value| - assert !topic.valid?, "#{value.inspect} not rejected as a number" - assert topic.errors.on(:approved) - assert_equal error, topic.errors.on(:approved) if error - end + assert_deprecated do + assert tom.invalid? + assert_equal ["always invalid", "invalid on create"], tom.errors[:name] end - def valid!(values) - with_each_topic_approved_value(values) do |topic, value| - assert topic.valid?, "#{value.inspect} not accepted as a number" - end - end + tom.save(false) - def with_each_topic_approved_value(values) - topic = Topic.new("title" => "numeric test", "content" => "whatever") - values.each do |value| - topic.approved = value - yield topic, value - end + assert_deprecated do + assert tom.invalid? + assert_equal ["always invalid", "invalid on update"], tom.errors[:name] end + end end diff --git a/activerecord/test/models/company.rb b/activerecord/test/models/company.rb index eb68153bbe..4c2514b0b3 100644 --- a/activerecord/test/models/company.rb +++ b/activerecord/test/models/company.rb @@ -145,10 +145,13 @@ class Account < ActiveRecord::Base true end + validate :check_empty_credit_limit + protected - def validate - errors.add_on_empty "credit_limit" - end + + def check_empty_credit_limit + errors.add_on_empty "credit_limit" + end private diff --git a/activerecord/test/models/company_in_module.rb b/activerecord/test/models/company_in_module.rb index 3c34efbe16..8b84c2fb5e 100644 --- a/activerecord/test/models/company_in_module.rb +++ b/activerecord/test/models/company_in_module.rb @@ -54,10 +54,13 @@ module MyApplication i.belongs_to :nested_unqualified_billing_firm, :class_name => 'Nested::Firm' end + validate :check_empty_credit_limit + protected - def validate - errors.add_on_empty "credit_limit" - end + + def check_empty_credit_limit + errors.add_on_empty "credit_limit" + end end end end diff --git a/activerecord/test/models/reply.rb b/activerecord/test/models/reply.rb index 1c990acab6..616c07687c 100644 --- a/activerecord/test/models/reply.rb +++ b/activerecord/test/models/reply.rb @@ -11,26 +11,30 @@ class Reply < Topic attr_accessible :title, :author_name, :author_email_address, :written_on, :content, :last_read - def validate - errors.add("title", "Empty") unless attribute_present? "title" + validate :check_empty_title + validate_on_create :check_content_mismatch + validate_on_update :check_wrong_update + + def check_empty_title + errors[:title] << "Empty" unless attribute_present?("title") end def errors_on_empty_content - errors.add("content", "Empty") unless attribute_present? "content" + errors[:content] << "Empty" unless attribute_present?("content") end - def validate_on_create + def check_content_mismatch if attribute_present?("title") && attribute_present?("content") && content == "Mismatch" - errors.add("title", "is Content Mismatch") + errors[:title] << "is Content Mismatch" end end def title_is_wrong_create - errors.add("title", "is Wrong Create") if attribute_present?("title") && title == "Wrong Create" + errors[:title] << "is Wrong Create" if attribute_present?("title") && title == "Wrong Create" end - def validate_on_update - errors.add("title", "is Wrong Update") if attribute_present?("title") && title == "Wrong Update" + def check_wrong_update + errors[:title] << "is Wrong Update" if attribute_present?("title") && title == "Wrong Update" end end diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb index 6e8813d8ab..b2aaccb352 100644 --- a/activerecord/test/schema/schema.rb +++ b/activerecord/test/schema/schema.rb @@ -273,6 +273,7 @@ ActiveRecord::Schema.define do t.decimal :my_house_population, :precision => 2, :scale => 0 t.decimal :decimal_number_with_default, :precision => 3, :scale => 2, :default => 2.78 t.float :temperature + t.decimal :atoms_in_universe, :precision => 55, :scale => 0 end create_table :orders, :force => true do |t| |