aboutsummaryrefslogtreecommitdiffstats
path: root/activemodel/test
diff options
context:
space:
mode:
Diffstat (limited to 'activemodel/test')
-rw-r--r--activemodel/test/cases/attribute_assignment_test.rb107
-rw-r--r--activemodel/test/cases/callbacks_test.rb29
-rw-r--r--activemodel/test/cases/dirty_test.rb34
-rw-r--r--activemodel/test/cases/errors_test.rb149
-rw-r--r--activemodel/test/cases/helper.rb17
-rw-r--r--activemodel/test/cases/model_test.rb4
-rw-r--r--activemodel/test/cases/serialization_test.rb25
-rw-r--r--activemodel/test/cases/serializers/json_serialization_test.rb4
-rw-r--r--activemodel/test/cases/serializers/xml_serialization_test.rb250
-rw-r--r--activemodel/test/cases/validations/absence_validation_test.rb1
-rw-r--r--activemodel/test/cases/validations/acceptance_validation_test.rb21
-rw-r--r--activemodel/test/cases/validations/callbacks_test.rb58
-rw-r--r--activemodel/test/cases/validations/conditional_validation_test.rb1
-rw-r--r--activemodel/test/cases/validations/confirmation_validation_test.rb1
-rw-r--r--activemodel/test/cases/validations/exclusion_validation_test.rb18
-rw-r--r--activemodel/test/cases/validations/format_validation_test.rb1
-rw-r--r--activemodel/test/cases/validations/i18n_generate_message_validation_test.rb2
-rw-r--r--activemodel/test/cases/validations/i18n_validation_test.rb162
-rw-r--r--activemodel/test/cases/validations/inclusion_validation_test.rb1
-rw-r--r--activemodel/test/cases/validations/length_validation_test.rb30
-rw-r--r--activemodel/test/cases/validations/numericality_validation_test.rb5
-rw-r--r--activemodel/test/cases/validations/presence_validation_test.rb1
-rw-r--r--activemodel/test/cases/validations/validates_test.rb2
-rw-r--r--activemodel/test/cases/validations/validations_context_test.rb1
-rw-r--r--activemodel/test/cases/validations/with_validation_test.rb9
-rw-r--r--activemodel/test/cases/validations_test.rb59
-rw-r--r--activemodel/test/models/contact.rb1
-rw-r--r--activemodel/test/models/topic.rb4
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