1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00
rails--rails/activemodel/test/cases/errors_test.rb
Sean Griffin ebe2dd9396 Ensure that instances of ActiveModel::Errors can be marshalled
This commit is a backport of b3dfd7d.

We now use default procs inside of the errors object, which gets
included by default when marshaling anything that includes
`ActiveModel::Validations`. This means that Active Record objects cannot
be marshalled. We strip and apply the default proc ourselves. This will
ensure the objects are YAML serializable as well, since YAML falls back
to marshal implementations now. This is less important, however, as the
errors aren't included when dumping Active Record objects.

Fixes #25165
2016-05-30 14:06:19 -04:00

439 lines
13 KiB
Ruby

require "cases/helper"
class ErrorsTest < ActiveModel::TestCase
class Person
extend ActiveModel::Naming
def initialize
@errors = ActiveModel::Errors.new(self)
end
attr_accessor :name, :age
attr_reader :errors
def validate!
errors.add(:name, :blank, message: "cannot be nil") if name == nil
end
def read_attribute_for_validation(attr)
send(attr)
end
def self.human_attribute_name(attr, options = {})
attr
end
def self.lookup_ancestors
[self]
end
end
def test_delete
errors = ActiveModel::Errors.new(self)
errors[:foo] << 'omg'
errors.delete(:foo)
assert_empty errors[:foo]
end
def test_include?
errors = ActiveModel::Errors.new(self)
errors[:foo] << 'omg'
assert errors.include?(:foo), 'errors should include :foo'
end
def test_dup
errors = ActiveModel::Errors.new(self)
errors[:foo] << 'bar'
errors_dup = errors.dup
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'
assert_equal true, errors.has_key?(:foo), 'errors should have key :foo'
end
def test_has_no_key
errors = ActiveModel::Errors.new(self)
assert_equal false, errors.has_key?(:name), 'errors should not have key :name'
end
def test_key?
errors = ActiveModel::Errors.new(self)
errors[:foo] << 'omg'
assert_equal true, errors.key?(:foo), 'errors should have key :foo'
end
def test_no_key
errors = ActiveModel::Errors.new(self)
assert_equal false, errors.key?(:name), 'errors should not have key :name'
end
test "clear errors" do
person = Person.new
person.validate!
assert_equal 1, person.errors.count
person.errors.clear
assert person.errors.empty?
end
test "get returns the errors for the provided key" do
errors = ActiveModel::Errors.new(self)
errors[:foo] << "omg"
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)
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"
assert_equal ["omg"], errors["foo"]
end
test "values returns an array of messages" do
errors = ActiveModel::Errors.new(self)
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.messages[:foo] << "omg"
errors.messages[:baz] << "zomg"
assert_equal [:foo, :baz], errors.keys
end
test "detecting whether there are errors with empty?, blank?, include?" do
person = Person.new
person.errors[:foo]
assert person.errors.empty?
assert person.errors.blank?
assert !person.errors.include?(:foo)
end
test "include? does not add a key to messages hash" do
person = Person.new
person.errors.include?(:foo)
assert_not person.errors.messages.key?(:foo)
end
test "adding errors using conditionals with Person#validate!" do
person = Person.new
person.validate!
assert_equal ["name cannot be nil"], person.errors.full_messages
assert_equal ["cannot be nil"], person.errors[:name]
end
test "assign error" do
person = Person.new
assert_deprecated do
person.errors[:name] = 'should not be nil'
end
assert_equal ["should not be nil"], person.errors[:name]
end
test "add an error message on a specific attribute" do
person = Person.new
person.errors.add(:name, "cannot be blank")
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)
message = person.errors.generate_message(:name, :blank)
assert_equal [message], person.errors[:name]
end
test "add an error with a proc" do
person = Person.new
message = Proc.new { "cannot be blank" }
person.errors.add(:name, message)
assert_equal ["cannot be blank"], person.errors[:name]
end
test "added? detects if a specific error was added to the object" do
person = Person.new
person.errors.add(:name, "cannot be blank")
assert person.errors.added?(:name, "cannot be blank")
end
test "added? handles symbol message" do
person = Person.new
person.errors.add(:name, :blank)
assert person.errors.added?(:name, :blank)
end
test "added? handles proc messages" do
person = Person.new
message = Proc.new { "cannot be blank" }
person.errors.add(:name, message)
assert person.errors.added?(:name, message)
end
test "added? defaults message to :invalid" do
person = Person.new
person.errors.add(:name)
assert person.errors.added?(:name)
end
test "added? matches the given message when several errors are present for the same attribute" do
person = Person.new
person.errors.add(:name, "cannot be blank")
person.errors.add(:name, "is invalid")
assert person.errors.added?(:name, "cannot be blank")
end
test "added? returns false when no errors are present" do
person = Person.new
assert !person.errors.added?(:name)
end
test "added? returns false when checking a nonexisting error and other errors are present for the given attribute" do
person = Person.new
person.errors.add(:name, "is invalid")
assert !person.errors.added?(:name, "cannot be blank")
end
test "size calculates the number of error messages" do
person = Person.new
person.errors.add(:name, "cannot be blank")
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")
person.errors.add(:name, "cannot be nil")
assert_equal ["name cannot be blank", "name cannot be nil"], person.errors.to_a
end
test "to_hash returns the error messages hash" do
person = Person.new
person.errors.add(:name, "cannot be blank")
assert_equal({ name: ["cannot be blank"] }, person.errors.to_hash)
end
test "full_messages creates a list of error messages with the attribute name included" do
person = Person.new
person.errors.add(:name, "cannot be blank")
person.errors.add(:name, "cannot be nil")
assert_equal ["name cannot be blank", "name cannot be nil"], person.errors.full_messages
end
test "full_messages_for contains all the error messages for the given attribute" do
person = Person.new
person.errors.add(:name, "cannot be blank")
person.errors.add(:name, "cannot be nil")
assert_equal ["name cannot be blank", "name cannot be nil"], person.errors.full_messages_for(:name)
end
test "full_messages_for does not contain error messages from other attributes" do
person = Person.new
person.errors.add(:name, "cannot be blank")
person.errors.add(:email, "cannot be blank")
assert_equal ["name cannot be blank"], person.errors.full_messages_for(:name)
end
test "full_messages_for returns an empty list in case there are no errors for the given attribute" do
person = Person.new
person.errors.add(:name, "cannot be blank")
assert_equal [], person.errors.full_messages_for(:email)
end
test "full_message returns the given message when attribute is :base" do
person = Person.new
assert_equal "press the button", person.errors.full_message(:base, "press the button")
end
test "full_message returns the given message with the attribute name included" do
person = Person.new
assert_equal "name cannot be blank", person.errors.full_message(:name, "cannot be blank")
assert_equal "name_test cannot be blank", person.errors.full_message(:name_test, "cannot be blank")
end
test "as_json creates a json formatted representation of the errors hash" do
person = Person.new
person.validate!
assert_equal({ name: ["cannot be nil"] }, person.errors.as_json)
end
test "as_json with :full_messages option creates a json formatted representation of the errors containing complete messages" do
person = Person.new
person.validate!
assert_equal({ name: ["name cannot be nil"] }, person.errors.as_json(full_messages: true))
end
test "generate_message works without i18n_scope" do
person = Person.new
assert !Person.respond_to?(:i18n_scope)
assert_nothing_raised {
person.errors.generate_message(:name, :blank)
}
end
test "add_on_empty generates message" do
person = Person.new
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
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
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 = ''
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
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
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
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
test "copy errors" do
errors = ActiveModel::Errors.new(Person.new)
errors.add(:name, :invalid)
person = Person.new
person.errors.copy!(errors)
assert_equal [:name], person.errors.messages.keys
assert_equal [:name], person.errors.details.keys
end
test "errors are marshalable" do
errors = ActiveModel::Errors.new(Person.new)
errors.add(:name, :invalid)
serialized = Marshal.load(Marshal.dump(errors))
assert_equal errors.messages, serialized.messages
assert_equal errors.details, serialized.details
end
end