diff options
Diffstat (limited to 'activemodel/test')
28 files changed, 550 insertions, 447 deletions
diff --git a/activemodel/test/cases/attribute_assignment_test.rb b/activemodel/test/cases/attribute_assignment_test.rb new file mode 100644 index 0000000000..64a85e01eb --- /dev/null +++ b/activemodel/test/cases/attribute_assignment_test.rb @@ -0,0 +1,107 @@ +require "cases/helper" +require "active_support/hash_with_indifferent_access" + +class AttributeAssignmentTest < ActiveModel::TestCase + class Model + include ActiveModel::AttributeAssignment + + attr_accessor :name, :description + + def initialize(attributes = {}) + assign_attributes(attributes) + end + + def broken_attribute=(value) + raise ErrorFromAttributeWriter + end + + protected + + attr_writer :metadata + end + + class ErrorFromAttributeWriter < StandardError + end + + class ProtectedParams < ActiveSupport::HashWithIndifferentAccess + def permit! + @permitted = true + end + + def permitted? + @permitted ||= false + end + + def dup + super.tap do |duplicate| + duplicate.instance_variable_set :@permitted, permitted? + end + end + end + + test "simple assignment" do + model = Model.new + + model.assign_attributes(name: "hello", description: "world") + assert_equal "hello", model.name + assert_equal "world", model.description + end + + test "assign non-existing attribute" do + model = Model.new + error = assert_raises(ActiveModel::UnknownAttributeError) do + model.assign_attributes(hz: 1) + end + + assert_equal model, error.record + assert_equal "hz", error.attribute + end + + test "assign private attribute" do + model = Model.new + assert_raises(ActiveModel::UnknownAttributeError) do + model.assign_attributes(metadata: { a: 1 }) + end + end + + test "does not swallow errors raised in an attribute writer" do + assert_raises(ErrorFromAttributeWriter) do + Model.new(broken_attribute: 1) + end + end + + test "an ArgumentError is raised if a non-hash-like obejct is passed" do + assert_raises(ArgumentError) do + Model.new(1) + end + end + + test "forbidden attributes cannot be used for mass assignment" do + params = ProtectedParams.new(name: "Guille", description: "m") + + assert_raises(ActiveModel::ForbiddenAttributesError) do + Model.new(params) + end + end + + test "permitted attributes can be used for mass assignment" do + params = ProtectedParams.new(name: "Guille", description: "desc") + params.permit! + model = Model.new(params) + + assert_equal "Guille", model.name + assert_equal "desc", model.description + end + + test "regular hash should still be used for mass assignment" do + model = Model.new(name: "Guille", description: "m") + + assert_equal "Guille", model.name + assert_equal "m", model.description + end + + test "assigning no attributes should not raise, even if the hash is un-permitted" do + model = Model.new + assert_nil model.assign_attributes(ProtectedParams.new({})) + end +end diff --git a/activemodel/test/cases/callbacks_test.rb b/activemodel/test/cases/callbacks_test.rb index 5fede098d1..85455c112c 100644 --- a/activemodel/test/cases/callbacks_test.rb +++ b/activemodel/test/cases/callbacks_test.rb @@ -7,6 +7,7 @@ class CallbacksTest < ActiveModel::TestCase model.callbacks << :before_around_create yield model.callbacks << :after_around_create + false end end @@ -24,16 +25,22 @@ class CallbacksTest < ActiveModel::TestCase after_create do |model| model.callbacks << :after_create + false end after_create "@callbacks << :final_callback" - def initialize(valid=true) - @callbacks, @valid = [], valid + def initialize(options = {}) + @callbacks = [] + @valid = options[:valid] + @before_create_returns = options.fetch(:before_create_returns, true) + @before_create_throws = options[:before_create_throws] end def before_create @callbacks << :before_create + throw(@before_create_throws) if @before_create_throws + @before_create_returns end def create @@ -51,14 +58,28 @@ class CallbacksTest < ActiveModel::TestCase :after_around_create, :after_create, :final_callback] end - test "after callbacks are always appended" do + test "the callback chain is not halted when around or after callbacks return false" do model = ModelCallbacks.new model.create assert_equal model.callbacks.last, :final_callback end + test "the callback chain is halted when a before callback returns false (deprecated)" do + model = ModelCallbacks.new(before_create_returns: false) + assert_deprecated do + model.create + assert_equal model.callbacks.last, :before_create + end + end + + test "the callback chain is halted when a callback throws :abort" do + model = ModelCallbacks.new(before_create_throws: :abort) + model.create + assert_equal model.callbacks, [:before_create] + end + test "after callbacks are not executed if the block returns false" do - model = ModelCallbacks.new(false) + model = ModelCallbacks.new(valid: false) model.create assert_equal model.callbacks, [ :before_create, :before_around_create, :create, :after_around_create] diff --git a/activemodel/test/cases/dirty_test.rb b/activemodel/test/cases/dirty_test.rb index db2cd885e2..d17a12ad12 100644 --- a/activemodel/test/cases/dirty_test.rb +++ b/activemodel/test/cases/dirty_test.rb @@ -45,10 +45,6 @@ class DirtyTest < ActiveModel::TestCase def reload clear_changes_information end - - def deprecated_reload - reset_changes - end end setup do @@ -141,6 +137,19 @@ class DirtyTest < ActiveModel::TestCase assert_equal [nil, "Jericho Cane"], @model.previous_changes['name'] end + test "setting new attributes should not affect previous changes" do + @model.name = "Jericho Cane" + @model.save + @model.name = "DudeFella ManGuy" + assert_equal [nil, "Jericho Cane"], @model.name_previous_change + end + + test "saving should preserve model's previous changed status" do + @model.name = "Jericho Cane" + @model.save + assert @model.name_previously_changed? + end + test "previous value is preserved when changed after save" do assert_equal({}, @model.changed_attributes) @model.name = "Paul" @@ -181,23 +190,6 @@ class DirtyTest < ActiveModel::TestCase assert_equal ActiveSupport::HashWithIndifferentAccess.new, @model.changed_attributes end - test "reset_changes is deprecated" do - @model.name = 'Dmitry' - @model.name_changed? - @model.save - @model.name = 'Bob' - - assert_equal [nil, 'Dmitry'], @model.previous_changes['name'] - assert_equal 'Dmitry', @model.changed_attributes['name'] - - assert_deprecated do - @model.deprecated_reload - end - - assert_equal ActiveSupport::HashWithIndifferentAccess.new, @model.previous_changes - assert_equal ActiveSupport::HashWithIndifferentAccess.new, @model.changed_attributes - end - test "restore_attributes should restore all previous data" do @model.name = 'Dmitry' @model.color = 'Red' diff --git a/activemodel/test/cases/errors_test.rb b/activemodel/test/cases/errors_test.rb index efedd9055f..f6d171bec6 100644 --- a/activemodel/test/cases/errors_test.rb +++ b/activemodel/test/cases/errors_test.rb @@ -29,28 +29,28 @@ class ErrorsTest < ActiveModel::TestCase def test_delete errors = ActiveModel::Errors.new(self) - errors[:foo] = 'omg' + errors[:foo] << 'omg' errors.delete(:foo) assert_empty errors[:foo] end def test_include? errors = ActiveModel::Errors.new(self) - errors[:foo] = 'omg' + errors[:foo] << 'omg' assert errors.include?(:foo), 'errors should include :foo' end def test_dup errors = ActiveModel::Errors.new(self) - errors[:foo] = 'bar' + errors[:foo] << 'bar' errors_dup = errors.dup - errors_dup[:bar] = 'omg' + errors_dup[:bar] << 'omg' assert_not_same errors_dup.messages, errors.messages end def test_has_key? errors = ActiveModel::Errors.new(self) - errors[:foo] = 'omg' + errors[:foo] << 'omg' assert_equal true, errors.has_key?(:foo), 'errors should have key :foo' end @@ -61,7 +61,7 @@ class ErrorsTest < ActiveModel::TestCase def test_key? errors = ActiveModel::Errors.new(self) - errors[:foo] = 'omg' + errors[:foo] << 'omg' assert_equal true, errors.key?(:foo), 'errors should have key :foo' end @@ -81,37 +81,41 @@ class ErrorsTest < ActiveModel::TestCase test "get returns the errors for the provided key" do errors = ActiveModel::Errors.new(self) - errors[:foo] = "omg" + errors[:foo] << "omg" - assert_equal ["omg"], errors.get(:foo) + assert_deprecated do + assert_equal ["omg"], errors.get(:foo) + end end test "sets the error with the provided key" do errors = ActiveModel::Errors.new(self) - errors.set(:foo, "omg") + assert_deprecated do + errors.set(:foo, "omg") + end assert_equal({ foo: "omg" }, errors.messages) end test "error access is indifferent" do errors = ActiveModel::Errors.new(self) - errors[:foo] = "omg" + errors[:foo] << "omg" assert_equal ["omg"], errors["foo"] end test "values returns an array of messages" do errors = ActiveModel::Errors.new(self) - errors.set(:foo, "omg") - errors.set(:baz, "zomg") + errors.messages[:foo] = "omg" + errors.messages[:baz] = "zomg" assert_equal ["omg", "zomg"], errors.values end test "keys returns the error keys" do errors = ActiveModel::Errors.new(self) - errors.set(:foo, "omg") - errors.set(:baz, "zomg") + errors.messages[:foo] << "omg" + errors.messages[:baz] << "zomg" assert_equal [:foo, :baz], errors.keys end @@ -133,7 +137,9 @@ class ErrorsTest < ActiveModel::TestCase test "assign error" do person = Person.new - person.errors[:name] = 'should not be nil' + assert_deprecated do + person.errors[:name] = 'should not be nil' + end assert_equal ["should not be nil"], person.errors[:name] end @@ -143,6 +149,12 @@ class ErrorsTest < ActiveModel::TestCase assert_equal ["cannot be blank"], person.errors[:name] end + test "add an error message on a specific attribute with a defined type" do + person = Person.new + person.errors.add(:name, :blank, message: "cannot be blank") + assert_equal ["cannot be blank"], person.errors[:name] + end + test "add an error with a symbol" do person = Person.new person.errors.add(:name, :blank) @@ -206,6 +218,12 @@ class ErrorsTest < ActiveModel::TestCase assert_equal 1, person.errors.size end + test "count calculates the number of error messages" do + person = Person.new + person.errors.add(:name, "cannot be blank") + assert_equal 1, person.errors.count + end + test "to_a returns the list of errors with complete messages containing the attribute names" do person = Person.new person.errors.add(:name, "cannot be blank") @@ -281,46 +299,115 @@ class ErrorsTest < ActiveModel::TestCase test "add_on_empty generates message" do person = Person.new - person.errors.expects(:generate_message).with(:name, :empty, {}) - person.errors.add_on_empty :name + assert_called_with(person.errors, :generate_message, [:name, :empty, {}]) do + assert_deprecated do + person.errors.add_on_empty :name + end + end end test "add_on_empty generates message for multiple attributes" do person = Person.new - person.errors.expects(:generate_message).with(:name, :empty, {}) - person.errors.expects(:generate_message).with(:age, :empty, {}) - person.errors.add_on_empty [:name, :age] + expected_calls = [ [:name, :empty, {}], [:age, :empty, {}] ] + assert_called_with(person.errors, :generate_message, expected_calls) do + assert_deprecated do + person.errors.add_on_empty [:name, :age] + end + end end test "add_on_empty generates message with custom default message" do person = Person.new - person.errors.expects(:generate_message).with(:name, :empty, { message: 'custom' }) - person.errors.add_on_empty :name, message: 'custom' + assert_called_with(person.errors, :generate_message, [:name, :empty, { message: 'custom' }]) do + assert_deprecated do + person.errors.add_on_empty :name, message: 'custom' + end + end end test "add_on_empty generates message with empty string value" do person = Person.new person.name = '' - person.errors.expects(:generate_message).with(:name, :empty, {}) - person.errors.add_on_empty :name + assert_called_with(person.errors, :generate_message, [:name, :empty, {}]) do + assert_deprecated do + person.errors.add_on_empty :name + end + end end test "add_on_blank generates message" do person = Person.new - person.errors.expects(:generate_message).with(:name, :blank, {}) - person.errors.add_on_blank :name + assert_called_with(person.errors, :generate_message, [:name, :blank, {}]) do + assert_deprecated do + person.errors.add_on_blank :name + end + end end test "add_on_blank generates message for multiple attributes" do person = Person.new - person.errors.expects(:generate_message).with(:name, :blank, {}) - person.errors.expects(:generate_message).with(:age, :blank, {}) - person.errors.add_on_blank [:name, :age] + expected_calls = [ [:name, :blank, {}], [:age, :blank, {}] ] + assert_called_with(person.errors, :generate_message, expected_calls) do + assert_deprecated do + person.errors.add_on_blank [:name, :age] + end + end end test "add_on_blank generates message with custom default message" do person = Person.new - person.errors.expects(:generate_message).with(:name, :blank, { message: 'custom' }) - person.errors.add_on_blank :name, message: 'custom' + assert_called_with(person.errors, :generate_message, [:name, :blank, { message: 'custom' }]) do + assert_deprecated do + person.errors.add_on_blank :name, message: 'custom' + end + end + end + + test "details returns added error detail" do + person = Person.new + person.errors.add(:name, :invalid) + assert_equal({ name: [{ error: :invalid }] }, person.errors.details) + end + + test "details returns added error detail with custom option" do + person = Person.new + person.errors.add(:name, :greater_than, count: 5) + assert_equal({ name: [{ error: :greater_than, count: 5 }] }, person.errors.details) + end + + test "details do not include message option" do + person = Person.new + person.errors.add(:name, :invalid, message: "is bad") + assert_equal({ name: [{ error: :invalid }] }, person.errors.details) + end + + test "dup duplicates details" do + errors = ActiveModel::Errors.new(Person.new) + errors.add(:name, :invalid) + errors_dup = errors.dup + errors_dup.add(:name, :taken) + assert_not_equal errors_dup.details, errors.details + end + + test "delete removes details on given attribute" do + errors = ActiveModel::Errors.new(Person.new) + errors.add(:name, :invalid) + errors.delete(:name) + assert_empty errors.details[:name] + end + + test "delete returns the deleted messages" do + errors = ActiveModel::Errors.new(Person.new) + errors.add(:name, :invalid) + assert_equal ["is invalid"], errors.delete(:name) + end + + test "clear removes details" do + person = Person.new + person.errors.add(:name, :invalid) + + assert_equal 1, person.errors.details.count + person.errors.clear + assert person.errors.details.empty? end end diff --git a/activemodel/test/cases/helper.rb b/activemodel/test/cases/helper.rb index 4ce6103593..0cd80be66f 100644 --- a/activemodel/test/cases/helper.rb +++ b/activemodel/test/cases/helper.rb @@ -11,10 +11,17 @@ ActiveSupport::Deprecation.debug = true I18n.enforce_available_locales = false require 'active_support/testing/autorun' +require 'active_support/testing/method_call_assertions' -require 'mocha/setup' # FIXME: stop using mocha +# Skips the current run on Rubinius using Minitest::Assertions#skip +def rubinius_skip(message = '') + skip message if RUBY_ENGINE == 'rbx' +end +# Skips the current run on JRuby using Minitest::Assertions#skip +def jruby_skip(message = '') + skip message if defined?(JRUBY_VERSION) +end -# FIXME: we have tests that depend on run order, we should fix that and -# remove this method call. -require 'active_support/test_case' -ActiveSupport::TestCase.test_order = :sorted +class ActiveModel::TestCase + include ActiveSupport::Testing::MethodCallAssertions +end diff --git a/activemodel/test/cases/model_test.rb b/activemodel/test/cases/model_test.rb index ee0fa26546..3017f3541b 100644 --- a/activemodel/test/cases/model_test.rb +++ b/activemodel/test/cases/model_test.rb @@ -70,6 +70,8 @@ class ModelTest < ActiveModel::TestCase end def test_mixin_initializer_when_args_dont_exist - assert_raises(NoMethodError) { SimpleModel.new(hello: 'world') } + assert_raises(ActiveModel::UnknownAttributeError) do + SimpleModel.new(hello: 'world') + end end end diff --git a/activemodel/test/cases/serialization_test.rb b/activemodel/test/cases/serialization_test.rb index 4ae41aa19c..8d3165cd78 100644 --- a/activemodel/test/cases/serialization_test.rb +++ b/activemodel/test/cases/serialization_test.rb @@ -16,6 +16,14 @@ class SerializationTest < ActiveModel::TestCase instance_values.except("address", "friends") end + def method_missing(method_name, *args) + if method_name == :bar + 'i_am_bar' + else + super + end + end + def foo 'i_am_foo' end @@ -58,23 +66,22 @@ class SerializationTest < ActiveModel::TestCase end def test_method_serializable_hash_should_work_with_methods_option - expected = {"name"=>"David", "gender"=>"male", "foo"=>"i_am_foo", "email"=>"david@example.com"} - assert_equal expected, @user.serializable_hash(methods: [:foo]) + expected = {"name"=>"David", "gender"=>"male", "foo"=>"i_am_foo", "bar"=>"i_am_bar", "email"=>"david@example.com"} + assert_equal expected, @user.serializable_hash(methods: [:foo, :bar]) end def test_method_serializable_hash_should_work_with_only_and_methods - expected = {"foo"=>"i_am_foo"} - assert_equal expected, @user.serializable_hash(only: [], methods: [:foo]) + expected = {"foo"=>"i_am_foo", "bar"=>"i_am_bar"} + assert_equal expected, @user.serializable_hash(only: [], methods: [:foo, :bar]) end def test_method_serializable_hash_should_work_with_except_and_methods - expected = {"gender"=>"male", "foo"=>"i_am_foo"} - assert_equal expected, @user.serializable_hash(except: [:name, :email], methods: [:foo]) + expected = {"gender"=>"male", "foo"=>"i_am_foo", "bar"=>"i_am_bar"} + assert_equal expected, @user.serializable_hash(except: [:name, :email], methods: [:foo, :bar]) end - def test_should_not_call_methods_that_dont_respond - expected = {"name"=>"David", "gender"=>"male", "email"=>"david@example.com"} - assert_equal expected, @user.serializable_hash(methods: [:bar]) + def test_should_raise_NoMethodError_for_non_existing_method + assert_raise(NoMethodError) { @user.serializable_hash(methods: [:nada]) } end def test_should_use_read_attribute_for_serialization diff --git a/activemodel/test/cases/serializers/json_serialization_test.rb b/activemodel/test/cases/serializers/json_serialization_test.rb index e2eb91eeb0..d765a47636 100644 --- a/activemodel/test/cases/serializers/json_serialization_test.rb +++ b/activemodel/test/cases/serializers/json_serialization_test.rb @@ -195,4 +195,8 @@ class JsonSerializationTest < ActiveModel::TestCase assert_no_match %r{"awesome":}, json assert_no_match %r{"preferences":}, json end + + test "Class.model_name should be json encodable" do + assert_match %r{"Contact"}, Contact.model_name.to_json + end end diff --git a/activemodel/test/cases/serializers/xml_serialization_test.rb b/activemodel/test/cases/serializers/xml_serialization_test.rb deleted file mode 100644 index 22fca5bd17..0000000000 --- a/activemodel/test/cases/serializers/xml_serialization_test.rb +++ /dev/null @@ -1,250 +0,0 @@ -require 'cases/helper' -require 'models/contact' -require 'active_support/core_ext/object/instance_variables' -require 'ostruct' - -module Admin - class Contact < ::Contact - end -end - -class Customer < Struct.new(:name) -end - -class Address - include ActiveModel::Serializers::Xml - - attr_accessor :street, :city, :state, :zip, :apt_number - - def attributes - instance_values - end -end - -class SerializableContact < Contact - def serializable_hash(options={}) - super(options.merge(only: [:name, :age])) - end -end - -class XmlSerializationTest < ActiveModel::TestCase - def setup - @contact = Contact.new - @contact.name = 'aaron stack' - @contact.age = 25 - @contact.created_at = Time.utc(2006, 8, 1) - @contact.awesome = false - customer = Customer.new - customer.name = "John" - @contact.preferences = customer - @contact.address = Address.new - @contact.address.city = "Springfield" - @contact.address.apt_number = 35 - @contact.friends = [Contact.new, Contact.new] - @contact.contact = SerializableContact.new - end - - test "should serialize default root" do - xml = @contact.to_xml - assert_match %r{^<contact>}, xml - assert_match %r{</contact>$}, xml - end - - test "should serialize namespaced root" do - xml = Admin::Contact.new(@contact.attributes).to_xml - assert_match %r{^<contact>}, xml - assert_match %r{</contact>$}, xml - end - - test "should serialize default root with namespace" do - xml = @contact.to_xml namespace: "http://xml.rubyonrails.org/contact" - assert_match %r{^<contact xmlns="http://xml.rubyonrails.org/contact">}, xml - assert_match %r{</contact>$}, xml - end - - test "should serialize custom root" do - xml = @contact.to_xml root: 'xml_contact' - assert_match %r{^<xml-contact>}, xml - assert_match %r{</xml-contact>$}, xml - end - - test "should allow undasherized tags" do - xml = @contact.to_xml root: 'xml_contact', dasherize: false - assert_match %r{^<xml_contact>}, xml - assert_match %r{</xml_contact>$}, xml - assert_match %r{<created_at}, xml - end - - test "should allow camelized tags" do - xml = @contact.to_xml root: 'xml_contact', camelize: true - assert_match %r{^<XmlContact>}, xml - assert_match %r{</XmlContact>$}, xml - assert_match %r{<CreatedAt}, xml - end - - test "should allow lower-camelized tags" do - xml = @contact.to_xml root: 'xml_contact', camelize: :lower - assert_match %r{^<xmlContact>}, xml - assert_match %r{</xmlContact>$}, xml - assert_match %r{<createdAt}, xml - end - - test "should use serializable hash" do - @contact = SerializableContact.new - @contact.name = 'aaron stack' - @contact.age = 25 - - xml = @contact.to_xml - assert_match %r{<name>aaron stack</name>}, xml - assert_match %r{<age type="integer">25</age>}, xml - assert_no_match %r{<awesome>}, xml - end - - test "should allow skipped types" do - xml = @contact.to_xml skip_types: true - assert_match %r{<age>25</age>}, xml - end - - test "should include yielded additions" do - xml_output = @contact.to_xml do |xml| - xml.creator "David" - end - assert_match %r{<creator>David</creator>}, xml_output - end - - test "should serialize string" do - assert_match %r{<name>aaron stack</name>}, @contact.to_xml - end - - test "should serialize nil" do - assert_match %r{<pseudonyms nil="true"/>}, @contact.to_xml(methods: :pseudonyms) - end - - test "should serialize integer" do - assert_match %r{<age type="integer">25</age>}, @contact.to_xml - end - - test "should serialize datetime" do - assert_match %r{<created-at type="dateTime">2006-08-01T00:00:00Z</created-at>}, @contact.to_xml - end - - test "should serialize boolean" do - assert_match %r{<awesome type="boolean">false</awesome>}, @contact.to_xml - end - - test "should serialize array" do - assert_match %r{<social type="array">\s*<social>twitter</social>\s*<social>github</social>\s*</social>}, @contact.to_xml(methods: :social) - end - - test "should serialize hash" do - assert_match %r{<network>\s*<git type="symbol">github</git>\s*</network>}, @contact.to_xml(methods: :network) - end - - test "should serialize yaml" do - assert_match %r{<preferences type="yaml">--- !ruby/struct:Customer(\s*)\nname: John\n</preferences>}, @contact.to_xml - end - - test "should call proc on object" do - proc = Proc.new { |options| options[:builder].tag!('nationality', 'unknown') } - xml = @contact.to_xml(procs: [ proc ]) - assert_match %r{<nationality>unknown</nationality>}, xml - end - - test "should supply serializable to second proc argument" do - proc = Proc.new { |options, record| options[:builder].tag!('name-reverse', record.name.reverse) } - xml = @contact.to_xml(procs: [ proc ]) - assert_match %r{<name-reverse>kcats noraa</name-reverse>}, xml - end - - test "should serialize string correctly when type passed" do - xml = @contact.to_xml type: 'Contact' - assert_match %r{<contact type="Contact">}, xml - assert_match %r{<name>aaron stack</name>}, xml - end - - test "include option with singular association" do - xml = @contact.to_xml include: :address, indent: 0 - assert xml.include?(@contact.address.to_xml(indent: 0, skip_instruct: true)) - end - - test "include option with plural association" do - xml = @contact.to_xml include: :friends, indent: 0 - assert_match %r{<friends type="array">}, xml - assert_match %r{<friend type="Contact">}, xml - end - - class FriendList - def initialize(friends) - @friends = friends - end - - def to_ary - @friends - end - end - - test "include option with ary" do - @contact.friends = FriendList.new(@contact.friends) - xml = @contact.to_xml include: :friends, indent: 0 - assert_match %r{<friends type="array">}, xml - assert_match %r{<friend type="Contact">}, xml - end - - test "multiple includes" do - xml = @contact.to_xml indent: 0, skip_instruct: true, include: [ :address, :friends ] - assert xml.include?(@contact.address.to_xml(indent: 0, skip_instruct: true)) - assert_match %r{<friends type="array">}, xml - assert_match %r{<friend type="Contact">}, xml - end - - test "include with options" do - xml = @contact.to_xml indent: 0, skip_instruct: true, include: { address: { only: :city } } - assert xml.include?(%(><address><city>Springfield</city></address>)) - end - - test "propagates skip_types option to included associations" do - xml = @contact.to_xml include: :friends, indent: 0, skip_types: true - assert_match %r{<friends>}, xml - assert_match %r{<friend>}, xml - end - - test "propagates skip-types option to included associations and attributes" do - xml = @contact.to_xml skip_types: true, include: :address, indent: 0 - assert_match %r{<address>}, xml - assert_match %r{<apt-number>}, xml - end - - test "propagates camelize option to included associations and attributes" do - xml = @contact.to_xml camelize: true, include: :address, indent: 0 - assert_match %r{<Address>}, xml - assert_match %r{<AptNumber type="integer">}, xml - end - - test "propagates dasherize option to included associations and attributes" do - xml = @contact.to_xml dasherize: false, include: :address, indent: 0 - assert_match %r{<apt_number type="integer">}, xml - end - - test "don't propagate skip_types if skip_types is defined at the included association level" do - xml = @contact.to_xml skip_types: true, include: { address: { skip_types: false } }, indent: 0 - assert_match %r{<address>}, xml - assert_match %r{<apt-number type="integer">}, xml - end - - test "don't propagate camelize if camelize is defined at the included association level" do - xml = @contact.to_xml camelize: true, include: { address: { camelize: false } }, indent: 0 - assert_match %r{<address>}, xml - assert_match %r{<apt-number type="integer">}, xml - end - - test "don't propagate dasherize if dasherize is defined at the included association level" do - xml = @contact.to_xml dasherize: false, include: { address: { dasherize: true } }, indent: 0 - assert_match %r{<address>}, xml - assert_match %r{<apt-number type="integer">}, xml - end - - test "association with sti" do - xml = @contact.to_xml(include: :contact) - assert xml.include?(%(<contact type="SerializableContact">)) - end -end diff --git a/activemodel/test/cases/validations/absence_validation_test.rb b/activemodel/test/cases/validations/absence_validation_test.rb index ebfe1cf4e4..9cbc77dfb5 100644 --- a/activemodel/test/cases/validations/absence_validation_test.rb +++ b/activemodel/test/cases/validations/absence_validation_test.rb @@ -1,4 +1,3 @@ -# encoding: utf-8 require 'cases/helper' require 'models/topic' require 'models/person' diff --git a/activemodel/test/cases/validations/acceptance_validation_test.rb b/activemodel/test/cases/validations/acceptance_validation_test.rb index e78aa1adaf..d3995ad5af 100644 --- a/activemodel/test/cases/validations/acceptance_validation_test.rb +++ b/activemodel/test/cases/validations/acceptance_validation_test.rb @@ -1,4 +1,3 @@ -# encoding: utf-8 require 'cases/helper' require 'models/topic' @@ -51,6 +50,20 @@ class AcceptanceValidationTest < ActiveModel::TestCase assert t.valid? end + def test_terms_of_service_agreement_with_multiple_accept_values + Topic.validates_acceptance_of(:terms_of_service, accept: [1, "I concur."]) + + t = Topic.new("title" => "We should be confirmed", "terms_of_service" => "") + assert t.invalid? + assert_equal ["must be accepted"], t.errors[:terms_of_service] + + t.terms_of_service = 1 + assert t.valid? + + t.terms_of_service = "I concur." + assert t.valid? + end + def test_validates_acceptance_of_for_ruby_class Person.validates_acceptance_of :karma @@ -65,4 +78,10 @@ class AcceptanceValidationTest < ActiveModel::TestCase ensure Person.clear_validators! end + + def test_validates_acceptance_of_true + Topic.validates_acceptance_of(:terms_of_service) + + assert Topic.new(terms_of_service: true).valid? + end end diff --git a/activemodel/test/cases/validations/callbacks_test.rb b/activemodel/test/cases/validations/callbacks_test.rb index 6cd0f4ed4d..75eb18e795 100644 --- a/activemodel/test/cases/validations/callbacks_test.rb +++ b/activemodel/test/cases/validations/callbacks_test.rb @@ -1,4 +1,3 @@ -# encoding: utf-8 require 'cases/helper' class Dog @@ -30,16 +29,34 @@ class DogWithTwoValidators < Dog before_validation { self.history << 'before_validation_marker2' } end -class DogValidatorReturningFalse < Dog +class DogDeprecatedBeforeValidatorReturningFalse < Dog before_validation { false } before_validation { self.history << 'before_validation_marker2' } end +class DogBeforeValidatorThrowingAbort < Dog + before_validation { throw :abort } + before_validation { self.history << 'before_validation_marker2' } +end + +class DogAfterValidatorReturningFalse < Dog + after_validation { false } + after_validation { self.history << 'after_validation_marker' } +end + class DogWithMissingName < Dog before_validation { self.history << 'before_validation_marker' } validates_presence_of :name end +class DogValidatorWithOnCondition < Dog + before_validation :set_before_validation_marker, on: :create + after_validation :set_after_validation_marker, on: :create + + def set_before_validation_marker; self.history << 'before_validation_marker'; end + def set_after_validation_marker; self.history << 'after_validation_marker' ; end +end + class DogValidatorWithIfCondition < Dog before_validation :set_before_validation_marker1, if: -> { true } before_validation :set_before_validation_marker2, if: -> { false } @@ -63,6 +80,24 @@ class CallbacksWithMethodNamesShouldBeCalled < ActiveModel::TestCase assert_equal ["before_validation_marker1", "after_validation_marker1"], d.history end + def test_on_condition_is_respected_for_validation_with_matching_context + d = DogValidatorWithOnCondition.new + d.valid?(:create) + assert_equal ["before_validation_marker", "after_validation_marker"], d.history + end + + def test_on_condition_is_respected_for_validation_without_matching_context + d = DogValidatorWithOnCondition.new + d.valid?(:save) + assert_equal [], d.history + end + + def test_on_condition_is_respected_for_validation_without_context + d = DogValidatorWithOnCondition.new + d.valid? + assert_equal [], d.history + end + def test_before_validation_and_after_validation_callbacks_should_be_called d = DogWithMethodCallbacks.new d.valid? @@ -81,13 +116,28 @@ class CallbacksWithMethodNamesShouldBeCalled < ActiveModel::TestCase assert_equal ['before_validation_marker1', 'before_validation_marker2'], d.history end - def test_further_callbacks_should_not_be_called_if_before_validation_returns_false - d = DogValidatorReturningFalse.new + def test_further_callbacks_should_not_be_called_if_before_validation_throws_abort + d = DogBeforeValidatorThrowingAbort.new output = d.valid? assert_equal [], d.history assert_equal false, output end + def test_deprecated_further_callbacks_should_not_be_called_if_before_validation_returns_false + d = DogDeprecatedBeforeValidatorReturningFalse.new + assert_deprecated do + output = d.valid? + assert_equal [], d.history + assert_equal false, output + end + end + + def test_further_callbacks_should_be_called_if_after_validation_returns_false + d = DogAfterValidatorReturningFalse.new + d.valid? + assert_equal ['after_validation_marker'], d.history + end + def test_validation_test_should_be_done d = DogWithMissingName.new output = d.valid? diff --git a/activemodel/test/cases/validations/conditional_validation_test.rb b/activemodel/test/cases/validations/conditional_validation_test.rb index 1261937b56..296d3b4407 100644 --- a/activemodel/test/cases/validations/conditional_validation_test.rb +++ b/activemodel/test/cases/validations/conditional_validation_test.rb @@ -1,4 +1,3 @@ -# encoding: utf-8 require 'cases/helper' require 'models/topic' diff --git a/activemodel/test/cases/validations/confirmation_validation_test.rb b/activemodel/test/cases/validations/confirmation_validation_test.rb index 65a2a1eb49..c1431548f7 100644 --- a/activemodel/test/cases/validations/confirmation_validation_test.rb +++ b/activemodel/test/cases/validations/confirmation_validation_test.rb @@ -1,4 +1,3 @@ -# encoding: utf-8 require 'cases/helper' require 'models/topic' diff --git a/activemodel/test/cases/validations/exclusion_validation_test.rb b/activemodel/test/cases/validations/exclusion_validation_test.rb index 1ce41f9bc9..005bc15df5 100644 --- a/activemodel/test/cases/validations/exclusion_validation_test.rb +++ b/activemodel/test/cases/validations/exclusion_validation_test.rb @@ -1,5 +1,5 @@ -# encoding: utf-8 require 'cases/helper' +require 'active_support/core_ext/numeric/time' require 'models/topic' require 'models/person' @@ -65,6 +65,22 @@ class ExclusionValidationTest < ActiveModel::TestCase assert t.valid? end + def test_validates_exclusion_of_with_range + Topic.validates_exclusion_of :content, in: ("a".."g") + + assert Topic.new(content: 'g').invalid? + assert Topic.new(content: 'h').valid? + end + + def test_validates_exclusion_of_with_time_range + Topic.validates_exclusion_of :created_at, in: 6.days.ago..2.days.ago + + assert Topic.new(created_at: 5.days.ago).invalid? + assert Topic.new(created_at: 3.days.ago).invalid? + assert Topic.new(created_at: 7.days.ago).valid? + assert Topic.new(created_at: 1.day.ago).valid? + end + def test_validates_inclusion_of_with_symbol Person.validates_exclusion_of :karma, in: :reserved_karmas diff --git a/activemodel/test/cases/validations/format_validation_test.rb b/activemodel/test/cases/validations/format_validation_test.rb index 0f91b73cd7..86bbbe6ebe 100644 --- a/activemodel/test/cases/validations/format_validation_test.rb +++ b/activemodel/test/cases/validations/format_validation_test.rb @@ -1,4 +1,3 @@ -# encoding: utf-8 require 'cases/helper' require 'models/topic' diff --git a/activemodel/test/cases/validations/i18n_generate_message_validation_test.rb b/activemodel/test/cases/validations/i18n_generate_message_validation_test.rb index 3eeb80a48b..da63df9152 100644 --- a/activemodel/test/cases/validations/i18n_generate_message_validation_test.rb +++ b/activemodel/test/cases/validations/i18n_generate_message_validation_test.rb @@ -62,7 +62,7 @@ class I18nGenerateMessageValidationTest < ActiveModel::TestCase assert_equal 'custom message', @person.errors.generate_message(:title, :empty, message: 'custom message') end - # add_on_blank: generate_message(attr, :blank, message: custom_message) + # validates_presence_of: generate_message(attr, :blank, message: custom_message) def test_generate_message_blank_with_default_message assert_equal "can't be blank", @person.errors.generate_message(:title, :blank) end diff --git a/activemodel/test/cases/validations/i18n_validation_test.rb b/activemodel/test/cases/validations/i18n_validation_test.rb index 96084a32ba..09d7226b5a 100644 --- a/activemodel/test/cases/validations/i18n_validation_test.rb +++ b/activemodel/test/cases/validations/i18n_validation_test.rb @@ -1,5 +1,3 @@ -# -*- coding: utf-8 -*- - require "cases/helper" require 'models/person' @@ -32,8 +30,9 @@ class I18nValidationTest < ActiveModel::TestCase def test_errors_full_messages_translates_human_attribute_name_for_model_attributes @person.errors.add(:name, 'not found') - Person.expects(:human_attribute_name).with(:name, default: 'Name').returns("Person's name") - assert_equal ["Person's name not found"], @person.errors.full_messages + assert_called_with(Person, :human_attribute_name, [:name, default: 'Name'], returns: "Person's name") do + assert_equal ["Person's name not found"], @person.errors.full_messages + end end def test_errors_full_messages_uses_format @@ -56,176 +55,175 @@ class I18nValidationTest < ActiveModel::TestCase [ "given option that is not reserved", { format: "jpg" }, { format: "jpg" }] ] - # validates_confirmation_of w/ mocha - COMMON_CASES.each do |name, validation_options, generate_message_options| test "validates_confirmation_of on generated message #{name}" do Person.validates_confirmation_of :title, validation_options @person.title_confirmation = 'foo' - @person.errors.expects(:generate_message).with(:title_confirmation, :confirmation, generate_message_options.merge(attribute: 'Title')) - @person.valid? + call = [:title_confirmation, :confirmation, generate_message_options.merge(attribute: 'Title')] + assert_called_with(@person.errors, :generate_message, call) do + @person.valid? + end end end - # validates_acceptance_of w/ mocha - COMMON_CASES.each do |name, validation_options, generate_message_options| test "validates_acceptance_of on generated message #{name}" do Person.validates_acceptance_of :title, validation_options.merge(allow_nil: false) - @person.errors.expects(:generate_message).with(:title, :accepted, generate_message_options) - @person.valid? + call = [:title, :accepted, generate_message_options] + assert_called_with(@person.errors, :generate_message, call) do + @person.valid? + end end end - # validates_presence_of w/ mocha - COMMON_CASES.each do |name, validation_options, generate_message_options| test "validates_presence_of on generated message #{name}" do Person.validates_presence_of :title, validation_options - @person.errors.expects(:generate_message).with(:title, :blank, generate_message_options) - @person.valid? + call = [:title, :blank, generate_message_options] + assert_called_with(@person.errors, :generate_message, call) do + @person.valid? + end end end - # validates_length_of :within too short w/ mocha - COMMON_CASES.each do |name, validation_options, generate_message_options| - test "validates_length_of for :withing on generated message when too short #{name}" do + test "validates_length_of for :within on generated message when too short #{name}" do Person.validates_length_of :title, validation_options.merge(within: 3..5) - @person.errors.expects(:generate_message).with(:title, :too_short, generate_message_options.merge(count: 3)) - @person.valid? + call = [:title, :too_short, generate_message_options.merge(count: 3)] + assert_called_with(@person.errors, :generate_message, call) do + @person.valid? + end end end - # validates_length_of :within too long w/ mocha - COMMON_CASES.each do |name, validation_options, generate_message_options| test "validates_length_of for :too_long generated message #{name}" do Person.validates_length_of :title, validation_options.merge(within: 3..5) @person.title = 'this title is too long' - @person.errors.expects(:generate_message).with(:title, :too_long, generate_message_options.merge(count: 5)) - @person.valid? + call = [:title, :too_long, generate_message_options.merge(count: 5)] + assert_called_with(@person.errors, :generate_message, call) do + @person.valid? + end end end - # validates_length_of :is w/ mocha - COMMON_CASES.each do |name, validation_options, generate_message_options| test "validates_length_of for :is on generated message #{name}" do Person.validates_length_of :title, validation_options.merge(is: 5) - @person.errors.expects(:generate_message).with(:title, :wrong_length, generate_message_options.merge(count: 5)) - @person.valid? + call = [:title, :wrong_length, generate_message_options.merge(count: 5)] + assert_called_with(@person.errors, :generate_message, call) do + @person.valid? + end end end - # validates_format_of w/ mocha - COMMON_CASES.each do |name, validation_options, generate_message_options| test "validates_format_of on generated message #{name}" do Person.validates_format_of :title, validation_options.merge(with: /\A[1-9][0-9]*\z/) @person.title = '72x' - @person.errors.expects(:generate_message).with(:title, :invalid, generate_message_options.merge(value: '72x')) - @person.valid? + call = [:title, :invalid, generate_message_options.merge(value: '72x')] + assert_called_with(@person.errors, :generate_message, call) do + @person.valid? + end end end - # validates_inclusion_of w/ mocha - COMMON_CASES.each do |name, validation_options, generate_message_options| test "validates_inclusion_of on generated message #{name}" do Person.validates_inclusion_of :title, validation_options.merge(in: %w(a b c)) @person.title = 'z' - @person.errors.expects(:generate_message).with(:title, :inclusion, generate_message_options.merge(value: 'z')) - @person.valid? + call = [:title, :inclusion, generate_message_options.merge(value: 'z')] + assert_called_with(@person.errors, :generate_message, call) do + @person.valid? + end end end - # validates_inclusion_of using :within w/ mocha - COMMON_CASES.each do |name, validation_options, generate_message_options| test "validates_inclusion_of using :within on generated message #{name}" do Person.validates_inclusion_of :title, validation_options.merge(within: %w(a b c)) @person.title = 'z' - @person.errors.expects(:generate_message).with(:title, :inclusion, generate_message_options.merge(value: 'z')) - @person.valid? + call = [:title, :inclusion, generate_message_options.merge(value: 'z')] + assert_called_with(@person.errors, :generate_message, call) do + @person.valid? + end end end - # validates_exclusion_of w/ mocha - COMMON_CASES.each do |name, validation_options, generate_message_options| test "validates_exclusion_of generated message #{name}" do Person.validates_exclusion_of :title, validation_options.merge(in: %w(a b c)) @person.title = 'a' - @person.errors.expects(:generate_message).with(:title, :exclusion, generate_message_options.merge(value: 'a')) - @person.valid? + call = [:title, :exclusion, generate_message_options.merge(value: 'a')] + assert_called_with(@person.errors, :generate_message, call) do + @person.valid? + end end end - # validates_exclusion_of using :within w/ mocha - COMMON_CASES.each do |name, validation_options, generate_message_options| test "validates_exclusion_of using :within generated message #{name}" do Person.validates_exclusion_of :title, validation_options.merge(within: %w(a b c)) @person.title = 'a' - @person.errors.expects(:generate_message).with(:title, :exclusion, generate_message_options.merge(value: 'a')) - @person.valid? + call = [:title, :exclusion, generate_message_options.merge(value: 'a')] + assert_called_with(@person.errors, :generate_message, call) do + @person.valid? + end end end - # validates_numericality_of without :only_integer w/ mocha - COMMON_CASES.each do |name, validation_options, generate_message_options| test "validates_numericality_of generated message #{name}" do Person.validates_numericality_of :title, validation_options @person.title = 'a' - @person.errors.expects(:generate_message).with(:title, :not_a_number, generate_message_options.merge(value: 'a')) - @person.valid? + call = [:title, :not_a_number, generate_message_options.merge(value: 'a')] + assert_called_with(@person.errors, :generate_message, call) do + @person.valid? + end end end - # validates_numericality_of with :only_integer w/ mocha - COMMON_CASES.each do |name, validation_options, generate_message_options| test "validates_numericality_of for :only_integer on generated message #{name}" do Person.validates_numericality_of :title, validation_options.merge(only_integer: true) @person.title = '0.0' - @person.errors.expects(:generate_message).with(:title, :not_an_integer, generate_message_options.merge(value: '0.0')) - @person.valid? + call = [:title, :not_an_integer, generate_message_options.merge(value: '0.0')] + assert_called_with(@person.errors, :generate_message, call) do + @person.valid? + end end end - # validates_numericality_of :odd w/ mocha - COMMON_CASES.each do |name, validation_options, generate_message_options| test "validates_numericality_of for :odd on generated message #{name}" do Person.validates_numericality_of :title, validation_options.merge(only_integer: true, odd: true) @person.title = 0 - @person.errors.expects(:generate_message).with(:title, :odd, generate_message_options.merge(value: 0)) - @person.valid? + call = [:title, :odd, generate_message_options.merge(value: 0)] + assert_called_with(@person.errors, :generate_message, call) do + @person.valid? + end end end - # validates_numericality_of :less_than w/ mocha - COMMON_CASES.each do |name, validation_options, generate_message_options| test "validates_numericality_of for :less_than on generated message #{name}" do Person.validates_numericality_of :title, validation_options.merge(only_integer: true, less_than: 0) @person.title = 1 - @person.errors.expects(:generate_message).with(:title, :less_than, generate_message_options.merge(value: 1, count: 0)) - @person.valid? + call = [:title, :less_than, generate_message_options.merge(value: 1, count: 0)] + assert_called_with(@person.errors, :generate_message, call) do + @person.valid? + end end end - - # To make things DRY this macro is defined to define 3 tests for every validation case. + # To make things DRY this macro is created to define 3 tests for every validation case. def self.set_expectations_for_validation(validation, error_type, &block_that_sets_validation) if error_type == :confirmation attribute = :title_confirmation else attribute = :title end - # test "validates_confirmation_of finds custom model key translation when blank" + test "#{validation} finds custom model key translation when #{error_type}" do I18n.backend.store_translations 'en', activemodel: { errors: { models: { person: { attributes: { attribute => { error_type => 'custom message' } } } } } } I18n.backend.store_translations 'en', errors: { messages: { error_type => 'global message'}} @@ -235,7 +233,6 @@ class I18nValidationTest < ActiveModel::TestCase assert_equal ['custom message'], @person.errors[attribute] end - # test "validates_confirmation_of finds custom model key translation with interpolation when blank" test "#{validation} finds custom model key translation with interpolation when #{error_type}" do I18n.backend.store_translations 'en', activemodel: { errors: { models: { person: { attributes: { attribute => { error_type => 'custom message with %{extra}' } } } } } } I18n.backend.store_translations 'en', errors: { messages: {error_type => 'global message'} } @@ -245,7 +242,6 @@ class I18nValidationTest < ActiveModel::TestCase assert_equal ['custom message with extra information'], @person.errors[attribute] end - # test "validates_confirmation_of finds global default key translation when blank" test "#{validation} finds global default key translation when #{error_type}" do I18n.backend.store_translations 'en', errors: { messages: {error_type => 'global message'} } @@ -255,27 +251,19 @@ class I18nValidationTest < ActiveModel::TestCase end end - # validates_confirmation_of w/o mocha - set_expectations_for_validation "validates_confirmation_of", :confirmation do |person, options_to_merge| Person.validates_confirmation_of :title, options_to_merge person.title_confirmation = 'foo' end - # validates_acceptance_of w/o mocha - set_expectations_for_validation "validates_acceptance_of", :accepted do |person, options_to_merge| Person.validates_acceptance_of :title, options_to_merge.merge(allow_nil: false) end - # validates_presence_of w/o mocha - set_expectations_for_validation "validates_presence_of", :blank do |person, options_to_merge| Person.validates_presence_of :title, options_to_merge end - # validates_length_of :within w/o mocha - set_expectations_for_validation "validates_length_of", :too_short do |person, options_to_merge| Person.validates_length_of :title, options_to_merge.merge(within: 3..5) end @@ -285,61 +273,43 @@ class I18nValidationTest < ActiveModel::TestCase person.title = "too long" end - # validates_length_of :is w/o mocha - set_expectations_for_validation "validates_length_of", :wrong_length do |person, options_to_merge| Person.validates_length_of :title, options_to_merge.merge(is: 5) end - # validates_format_of w/o mocha - set_expectations_for_validation "validates_format_of", :invalid do |person, options_to_merge| Person.validates_format_of :title, options_to_merge.merge(with: /\A[1-9][0-9]*\z/) end - # validates_inclusion_of w/o mocha - set_expectations_for_validation "validates_inclusion_of", :inclusion do |person, options_to_merge| Person.validates_inclusion_of :title, options_to_merge.merge(in: %w(a b c)) end - # validates_exclusion_of w/o mocha - set_expectations_for_validation "validates_exclusion_of", :exclusion do |person, options_to_merge| Person.validates_exclusion_of :title, options_to_merge.merge(in: %w(a b c)) person.title = 'a' end - # validates_numericality_of without :only_integer w/o mocha - set_expectations_for_validation "validates_numericality_of", :not_a_number do |person, options_to_merge| Person.validates_numericality_of :title, options_to_merge person.title = 'a' end - # validates_numericality_of with :only_integer w/o mocha - set_expectations_for_validation "validates_numericality_of", :not_an_integer do |person, options_to_merge| Person.validates_numericality_of :title, options_to_merge.merge(only_integer: true) person.title = '1.0' end - # validates_numericality_of :odd w/o mocha - set_expectations_for_validation "validates_numericality_of", :odd do |person, options_to_merge| Person.validates_numericality_of :title, options_to_merge.merge(only_integer: true, odd: true) person.title = 0 end - # validates_numericality_of :less_than w/o mocha - set_expectations_for_validation "validates_numericality_of", :less_than do |person, options_to_merge| Person.validates_numericality_of :title, options_to_merge.merge(only_integer: true, less_than: 0) person.title = 1 end - # test with validates_with - def test_validations_with_message_symbol_must_translate I18n.backend.store_translations 'en', errors: { messages: { custom_error: "I am a custom error" } } Person.validates_presence_of :title, message: :custom_error diff --git a/activemodel/test/cases/validations/inclusion_validation_test.rb b/activemodel/test/cases/validations/inclusion_validation_test.rb index 3a8f3080e1..55d1fb4dcb 100644 --- a/activemodel/test/cases/validations/inclusion_validation_test.rb +++ b/activemodel/test/cases/validations/inclusion_validation_test.rb @@ -1,4 +1,3 @@ -# encoding: utf-8 require 'cases/helper' require 'active_support/all' diff --git a/activemodel/test/cases/validations/length_validation_test.rb b/activemodel/test/cases/validations/length_validation_test.rb index 046ffcb16f..ee901b75fb 100644 --- a/activemodel/test/cases/validations/length_validation_test.rb +++ b/activemodel/test/cases/validations/length_validation_test.rb @@ -1,4 +1,3 @@ -# encoding: utf-8 require 'cases/helper' require 'models/topic' @@ -320,8 +319,33 @@ class LengthValidationTest < ActiveModel::TestCase 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+/) } + assert_deprecated do + Topic.validates_length_of( + :content, + minimum: 5, + too_short: "Your essay must be at least %{count} words.", + tokenizer: lambda {|str| str.scan(/\w+/) }, + ) + end + t = Topic.new(content: "this content should be long enough") + assert t.valid? + + t.content = "not long enough" + assert t.invalid? + assert t.errors[:content].any? + assert_equal ["Your essay must be at least 5 words."], t.errors[:content] + end + + + def test_validates_length_of_with_symbol + assert_deprecated do + Topic.validates_length_of( + :content, + minimum: 5, + too_short: "Your essay must be at least %{count} words.", + tokenizer: :my_word_tokenizer, + ) + end t = Topic.new(content: "this content should be long enough") assert t.valid? diff --git a/activemodel/test/cases/validations/numericality_validation_test.rb b/activemodel/test/cases/validations/numericality_validation_test.rb index 3834d327ea..05432abaff 100644 --- a/activemodel/test/cases/validations/numericality_validation_test.rb +++ b/activemodel/test/cases/validations/numericality_validation_test.rb @@ -1,4 +1,3 @@ -# encoding: utf-8 require 'cases/helper' require 'models/topic' @@ -59,7 +58,7 @@ class NumericalityValidationTest < ActiveModel::TestCase def test_validates_numericality_of_with_integer_only_and_proc_as_value Topic.send(:define_method, :allow_only_integers?, lambda { false }) - Topic.validates_numericality_of :approved, only_integer: Proc.new {|topic| topic.allow_only_integers? } + Topic.validates_numericality_of :approved, only_integer: Proc.new(&:allow_only_integers?) invalid!(NIL + BLANK + JUNK) valid!(FLOATS + INTEGERS + BIGDECIMAL + INFINITY) @@ -130,7 +129,7 @@ class NumericalityValidationTest < ActiveModel::TestCase def test_validates_numericality_with_proc Topic.send(:define_method, :min_approved, lambda { 5 }) - Topic.validates_numericality_of :approved, greater_than_or_equal_to: Proc.new {|topic| topic.min_approved } + Topic.validates_numericality_of :approved, greater_than_or_equal_to: Proc.new(&:min_approved) invalid!([3, 4]) valid!([5, 6]) diff --git a/activemodel/test/cases/validations/presence_validation_test.rb b/activemodel/test/cases/validations/presence_validation_test.rb index ecf16d1e16..59b9db0795 100644 --- a/activemodel/test/cases/validations/presence_validation_test.rb +++ b/activemodel/test/cases/validations/presence_validation_test.rb @@ -1,4 +1,3 @@ -# encoding: utf-8 require 'cases/helper' require 'models/topic' diff --git a/activemodel/test/cases/validations/validates_test.rb b/activemodel/test/cases/validations/validates_test.rb index 699a872e42..04101f3545 100644 --- a/activemodel/test/cases/validations/validates_test.rb +++ b/activemodel/test/cases/validations/validates_test.rb @@ -1,9 +1,7 @@ -# encoding: utf-8 require 'cases/helper' require 'models/person' require 'models/topic' require 'models/person_with_validator' -require 'validators/email_validator' require 'validators/namespace/email_validator' class ValidatesTest < ActiveModel::TestCase diff --git a/activemodel/test/cases/validations/validations_context_test.rb b/activemodel/test/cases/validations/validations_context_test.rb index 005bf118c6..150dce379f 100644 --- a/activemodel/test/cases/validations/validations_context_test.rb +++ b/activemodel/test/cases/validations/validations_context_test.rb @@ -1,4 +1,3 @@ -# encoding: utf-8 require 'cases/helper' require 'models/topic' diff --git a/activemodel/test/cases/validations/with_validation_test.rb b/activemodel/test/cases/validations/with_validation_test.rb index 736c2deea8..03c7943308 100644 --- a/activemodel/test/cases/validations/with_validation_test.rb +++ b/activemodel/test/cases/validations/with_validation_test.rb @@ -1,4 +1,3 @@ -# encoding: utf-8 require 'cases/helper' require 'models/topic' @@ -98,12 +97,14 @@ class ValidatesWithTest < ActiveModel::TestCase test "passes all configuration options to the validator class" do topic = Topic.new - validator = mock() - validator.expects(:new).with(foo: :bar, if: "1 == 1", class: Topic).returns(validator) - validator.expects(:validate).with(topic) + validator = Minitest::Mock.new + validator.expect(:new, validator, [{foo: :bar, if: "1 == 1", class: Topic}]) + validator.expect(:validate, nil, [topic]) + validator.expect(:is_a?, false, [Symbol]) Topic.validates_with(validator, if: "1 == 1", foo: :bar) assert topic.valid? + validator.verify end test "validates_with with options" do diff --git a/activemodel/test/cases/validations_test.rb b/activemodel/test/cases/validations_test.rb index de71bb6f42..f0317ad219 100644 --- a/activemodel/test/cases/validations_test.rb +++ b/activemodel/test/cases/validations_test.rb @@ -1,4 +1,3 @@ -# encoding: utf-8 require 'cases/helper' require 'models/topic' @@ -171,10 +170,45 @@ class ValidationsTest < ActiveModel::TestCase # A common mistake -- we meant to call 'validates' Topic.validate :title, presence: true end - message = 'Unknown key: :presence. Valid keys are: :on, :if, :unless. Perhaps you meant to call `validates` instead of `validate`?' + message = 'Unknown key: :presence. Valid keys are: :on, :if, :unless, :prepend. Perhaps you meant to call `validates` instead of `validate`?' assert_equal message, error.message end + def test_callback_options_to_validate + klass = Class.new(Topic) do + attr_reader :call_sequence + + def initialize(*) + super + @call_sequence = [] + end + + private + def validator_a + @call_sequence << :a + end + + def validator_b + @call_sequence << :b + end + + def validator_c + @call_sequence << :c + end + end + + assert_nothing_raised do + klass.validate :validator_a, if: ->{ true } + klass.validate :validator_b, prepend: true + klass.validate :validator_c, unless: ->{ true } + end + + t = klass.new + + assert_predicate t, :valid? + assert_equal [:b, :a], t.call_sequence + end + def test_errors_conversions Topic.validates_presence_of %w(title content) t = Topic.new @@ -282,7 +316,7 @@ class ValidationsTest < ActiveModel::TestCase ActiveModel::Validations::FormatValidator, ActiveModel::Validations::LengthValidator, ActiveModel::Validations::PresenceValidator - ], validators.map { |v| v.class }.sort_by { |c| c.to_s } + ], validators.map(&:class).sort_by(&:to_s) end def test_list_of_validators_will_be_empty_when_empty @@ -317,6 +351,25 @@ class ValidationsTest < ActiveModel::TestCase assert_not_empty topic.errors end + def test_validate_with_bang + Topic.validates :title, presence: true + + assert_raise(ActiveModel::ValidationError) do + Topic.new.validate! + end + end + + def test_validate_with_bang_and_context + Topic.validates :title, presence: true, on: :context + + assert_raise(ActiveModel::ValidationError) do + Topic.new.validate!(:context) + end + + t = Topic.new(title: "Valid title") + assert t.validate!(:context) + end + def test_strict_validation_in_validates Topic.validates :title, strict: true, presence: true assert_raises ActiveModel::StrictValidationFailed do diff --git a/activemodel/test/models/contact.rb b/activemodel/test/models/contact.rb index bcfd267a34..113ab0bc1f 100644 --- a/activemodel/test/models/contact.rb +++ b/activemodel/test/models/contact.rb @@ -4,7 +4,6 @@ class Contact include ActiveModel::Validations include ActiveModel::Serializers::JSON - include ActiveModel::Serializers::Xml attr_accessor :id, :name, :age, :created_at, :awesome, :preferences attr_accessor :address, :friends, :contact diff --git a/activemodel/test/models/topic.rb b/activemodel/test/models/topic.rb index 1411a093e9..fed50bc361 100644 --- a/activemodel/test/models/topic.rb +++ b/activemodel/test/models/topic.rb @@ -37,4 +37,8 @@ class Topic errors.add attr, "is missing" unless send(attr) end + def my_word_tokenizer(str) + str.scan(/\w+/) + end + end |