diff options
Diffstat (limited to 'activemodel/test/cases')
19 files changed, 587 insertions, 83 deletions
diff --git a/activemodel/test/cases/attribute_methods_test.rb b/activemodel/test/cases/attribute_methods_test.rb index 022c6716bd..67471ed497 100644 --- a/activemodel/test/cases/attribute_methods_test.rb +++ b/activemodel/test/cases/attribute_methods_test.rb @@ -3,8 +3,6 @@ require 'cases/helper' class ModelWithAttributes include ActiveModel::AttributeMethods - attribute_method_suffix '' - class << self define_method(:bar) do 'original bar' @@ -24,14 +22,31 @@ end class ModelWithAttributes2 include ActiveModel::AttributeMethods + attr_accessor :attributes + attribute_method_suffix '_test' + +private + def attribute(name) + attributes[name.to_s] + end + + alias attribute_test attribute + + def private_method + "<3 <3" + end + +protected + + def protected_method + "O_o O_o" + end end class ModelWithAttributesWithSpaces include ActiveModel::AttributeMethods - attribute_method_suffix '' - def attributes { :'foo bar' => 'value of foo bar'} end @@ -45,8 +60,6 @@ end class ModelWithWeirdNamesAttributes include ActiveModel::AttributeMethods - attribute_method_suffix '' - class << self define_method(:'c?d') do 'original c?d' @@ -76,9 +89,31 @@ class AttributeMethodsTest < ActiveModel::TestCase assert_equal "value of foo", ModelWithAttributes.new.foo end + test '#define_attribute_method does not generate attribute method if already defined in attribute module' do + klass = Class.new(ModelWithAttributes) + klass.generated_attribute_methods.module_eval do + def foo + '<3' + end + end + klass.define_attribute_method(:foo) + + assert_equal '<3', klass.new.foo + end + + test '#define_attribute_method generates a method that is already defined on the host' do + klass = Class.new(ModelWithAttributes) do + def foo + super + end + end + klass.define_attribute_method(:foo) + + assert_equal 'value of foo', klass.new.foo + end + test '#define_attribute_method generates attribute method with invalid identifier characters' do ModelWithWeirdNamesAttributes.define_attribute_method(:'a?b') - ModelWithWeirdNamesAttributes.define_attribute_method(:'a?b') assert_respond_to ModelWithWeirdNamesAttributes.new, :'a?b' assert_equal "value of a?b", ModelWithWeirdNamesAttributes.new.send('a?b') @@ -130,4 +165,64 @@ class AttributeMethodsTest < ActiveModel::TestCase assert !ModelWithAttributes.new.respond_to?(:foo) assert_raises(NoMethodError) { ModelWithAttributes.new.foo } end + + test 'acessing a suffixed attribute' do + m = ModelWithAttributes2.new + m.attributes = { 'foo' => 'bar' } + + assert_equal 'bar', m.foo + assert_equal 'bar', m.foo_test + end + + test 'explicitly specifying an empty prefix/suffix is deprecated' do + klass = Class.new(ModelWithAttributes) + + assert_deprecated { klass.attribute_method_suffix '' } + assert_deprecated { klass.attribute_method_prefix '' } + + klass.define_attribute_methods([:foo]) + + assert_equal 'value of foo', klass.new.foo + end + + test 'should not interfere with method_missing if the attr has a private/protected method' do + m = ModelWithAttributes2.new + m.attributes = { 'private_method' => '<3', 'protected_method' => 'O_o' } + + # dispatches to the *method*, not the attribute + assert_equal '<3 <3', m.send(:private_method) + assert_equal 'O_o O_o', m.send(:protected_method) + + # sees that a method is already defined, so doesn't intervene + assert_raises(NoMethodError) { m.private_method } + assert_raises(NoMethodError) { m.protected_method } + end + + test 'should not interfere with respond_to? if the attribute has a private/protected method' do + m = ModelWithAttributes2.new + m.attributes = { 'private_method' => '<3', 'protected_method' => 'O_o' } + + assert !m.respond_to?(:private_method) + assert m.respond_to?(:private_method, true) + + # This is messed up, but it's how Ruby works at the moment. Apparently it will be changed + # in the future. + assert m.respond_to?(:protected_method) + assert m.respond_to?(:protected_method, true) + end + + test 'should use attribute_missing to dispatch a missing attribute' do + m = ModelWithAttributes2.new + m.attributes = { 'foo' => 'bar' } + + def m.attribute_missing(match, *args, &block) + match + end + + match = m.foo_test + + assert_equal 'foo', match.attr_name + assert_equal 'attribute_test', match.target + assert_equal 'foo_test', match.method_name + end end diff --git a/activemodel/test/cases/conversion_test.rb b/activemodel/test/cases/conversion_test.rb index 7669bf5f65..24552bcaf2 100644 --- a/activemodel/test/cases/conversion_test.rb +++ b/activemodel/test/cases/conversion_test.rb @@ -1,5 +1,6 @@ require 'cases/helper' require 'models/contact' +require 'models/helicopter' class ConversionTest < ActiveModel::TestCase test "to_model default implementation returns self" do @@ -22,4 +23,10 @@ class ConversionTest < ActiveModel::TestCase test "to_param default implementation returns a string of ids for persisted records" do assert_equal "1", Contact.new(:id => 1).to_param end -end
\ No newline at end of file + + test "to_path default implementation returns a string giving a relative path" do + assert_equal "contacts/contact", Contact.new.to_partial_path + assert_equal "helicopters/helicopter", Helicopter.new.to_partial_path, + "ActiveModel::Conversion#to_partial_path caching should be class-specific" + end +end diff --git a/activemodel/test/cases/dirty_test.rb b/activemodel/test/cases/dirty_test.rb index 858ae9cb69..98244a6290 100644 --- a/activemodel/test/cases/dirty_test.rb +++ b/activemodel/test/cases/dirty_test.rb @@ -106,4 +106,13 @@ class DirtyTest < ActiveModel::TestCase assert_equal [nil, "Jericho Cane"], @model.previous_changes['name'] end + test "changing the same attribute multiple times retains the correct original value" do + @model.name = "Otto" + @model.save + @model.name = "DudeFella ManGuy" + @model.name = "Mr. Manfredgensonton" + assert_equal ["Otto", "Mr. Manfredgensonton"], @model.name_change + assert_equal @model.name_was, "Otto" + end + end diff --git a/activemodel/test/cases/errors_test.rb b/activemodel/test/cases/errors_test.rb index a24cac40ad..4c76bb43a8 100644 --- a/activemodel/test/cases/errors_test.rb +++ b/activemodel/test/cases/errors_test.rb @@ -33,11 +33,18 @@ class ErrorsTest < ActiveModel::TestCase assert errors.include?(:foo), 'errors should include :foo' end + def test_has_key? + errors = ActiveModel::Errors.new(self) + errors[:foo] = 'omg' + assert errors.has_key?(:foo), 'errors should have key :foo' + end + test "should return true if no errors" do person = Person.new person.errors[:foo] assert person.errors.empty? assert person.errors.blank? + assert !person.errors.include?(:foo) end test "method validate! should work" do @@ -45,7 +52,6 @@ class ErrorsTest < ActiveModel::TestCase person.validate! assert_equal ["name can not be nil"], person.errors.full_messages assert_equal ["can not be nil"], person.errors[:name] - end test 'should be able to assign error' do @@ -71,7 +77,6 @@ class ErrorsTest < ActiveModel::TestCase person.errors.add(:name, "can not be blank") person.errors.add(:name, "can not be nil") assert_equal ["name can not be blank", "name can not be nil"], person.errors.to_a - end test 'to_hash should return an ordered hash' do @@ -79,4 +84,33 @@ class ErrorsTest < ActiveModel::TestCase person.errors.add(:name, "can not be blank") assert_instance_of ActiveSupport::OrderedHash, person.errors.to_hash end + + test 'full_messages should return an array of error messages, with the attribute name included' do + person = Person.new + person.errors.add(:name, "can not be blank") + person.errors.add(:name, "can not be nil") + assert_equal ["name can not be blank", "name can not be nil"], person.errors.to_a + end + + test 'full_message should return the given message if attribute equals :base' do + person = Person.new + assert_equal "press the button", person.errors.full_message(:base, "press the button") + end + + test 'full_message should return the given message with the attribute name included' do + person = Person.new + assert_equal "name can not be blank", person.errors.full_message(:name, "can not be blank") + end + + test 'should return a JSON hash representation of the errors' do + person = Person.new + person.errors.add(:name, "can not be blank") + person.errors.add(:name, "can not be nil") + person.errors.add(:email, "is invalid") + hash = person.errors.as_json + assert_equal ["can not be blank", "can not be nil"], hash[:name] + assert_equal ["is invalid"], hash[:email] + end + end + diff --git a/activemodel/test/cases/helper.rb b/activemodel/test/cases/helper.rb index 01f0158678..2e860272a4 100644 --- a/activemodel/test/cases/helper.rb +++ b/activemodel/test/cases/helper.rb @@ -10,5 +10,4 @@ require 'active_support/core_ext/string/access' # Show backtraces for deprecated behavior for quicker cleanup. ActiveSupport::Deprecation.debug = true -require 'rubygems' require 'test/unit' diff --git a/activemodel/test/cases/mass_assignment_security/black_list_test.rb b/activemodel/test/cases/mass_assignment_security/black_list_test.rb index ed168bc016..0ec7f8719c 100644 --- a/activemodel/test/cases/mass_assignment_security/black_list_test.rb +++ b/activemodel/test/cases/mass_assignment_security/black_list_test.rb @@ -16,13 +16,5 @@ class BlackListTest < ActiveModel::TestCase assert_equal false, @black_list.deny?('first_name') end - test "sanitize attributes" do - original_attributes = { 'first_name' => 'allowed', 'admin' => 'denied', 'admin(1)' => 'denied' } - attributes = @black_list.sanitize(original_attributes) - - assert attributes.key?('first_name'), "Allowed key shouldn't be rejected" - assert !attributes.key?('admin'), "Denied key should be rejected" - assert !attributes.key?('admin(1)'), "Multi-parameter key should be detected" - end end diff --git a/activemodel/test/cases/mass_assignment_security/sanitizer_test.rb b/activemodel/test/cases/mass_assignment_security/sanitizer_test.rb index 9a73a5ad91..676937b5e1 100644 --- a/activemodel/test/cases/mass_assignment_security/sanitizer_test.rb +++ b/activemodel/test/cases/mass_assignment_security/sanitizer_test.rb @@ -3,36 +3,49 @@ require 'logger' require 'active_support/core_ext/object/inclusion' class SanitizerTest < ActiveModel::TestCase + attr_accessor :logger - class SanitizingAuthorizer - include ActiveModel::MassAssignmentSecurity::Sanitizer - - attr_accessor :logger - + class Authorizer < ActiveModel::MassAssignmentSecurity::PermissionSet def deny?(key) - key.in?(['admin']) + ['admin', 'id'].include?(key) end - end def setup - @sanitizer = SanitizingAuthorizer.new + @logger_sanitizer = ActiveModel::MassAssignmentSecurity::LoggerSanitizer.new(self) + @strict_sanitizer = ActiveModel::MassAssignmentSecurity::StrictSanitizer.new(self) + @authorizer = Authorizer.new end test "sanitize attributes" do original_attributes = { 'first_name' => 'allowed', 'admin' => 'denied' } - attributes = @sanitizer.sanitize(original_attributes) + attributes = @logger_sanitizer.sanitize(original_attributes, @authorizer) assert attributes.key?('first_name'), "Allowed key shouldn't be rejected" assert !attributes.key?('admin'), "Denied key should be rejected" end - test "debug mass assignment removal" do + test "debug mass assignment removal with LoggerSanitizer" do original_attributes = { 'first_name' => 'allowed', 'admin' => 'denied' } log = StringIO.new - @sanitizer.logger = Logger.new(log) - @sanitizer.sanitize(original_attributes) + self.logger = Logger.new(log) + @logger_sanitizer.sanitize(original_attributes, @authorizer) assert_match(/admin/, log.string, "Should log removed attributes: #{log.string}") end + test "debug mass assignment removal with StrictSanitizer" do + original_attributes = { 'first_name' => 'allowed', 'admin' => 'denied' } + assert_raise ActiveModel::MassAssignmentSecurity::Error do + @strict_sanitizer.sanitize(original_attributes, @authorizer) + end + end + + test "mass assignment insensitive attributes" do + original_attributes = {'id' => 1, 'first_name' => 'allowed'} + + assert_nothing_raised do + @strict_sanitizer.sanitize(original_attributes, @authorizer) + end + end + end diff --git a/activemodel/test/cases/mass_assignment_security/white_list_test.rb b/activemodel/test/cases/mass_assignment_security/white_list_test.rb index aa3596ad2a..737b55492a 100644 --- a/activemodel/test/cases/mass_assignment_security/white_list_test.rb +++ b/activemodel/test/cases/mass_assignment_security/white_list_test.rb @@ -16,13 +16,4 @@ class WhiteListTest < ActiveModel::TestCase assert_equal true, @white_list.deny?('admin') end - test "sanitize attributes" do - original_attributes = { 'first_name' => 'allowed', 'admin' => 'denied', 'admin(1)' => 'denied' } - attributes = @white_list.sanitize(original_attributes) - - assert attributes.key?('first_name'), "Allowed key shouldn't be rejected" - assert !attributes.key?('admin'), "Denied key should be rejected" - assert !attributes.key?('admin(1)'), "Multi-parameter key should be detected" - end - end diff --git a/activemodel/test/cases/mass_assignment_security_test.rb b/activemodel/test/cases/mass_assignment_security_test.rb index 43a12eed61..be07e59a2f 100644 --- a/activemodel/test/cases/mass_assignment_security_test.rb +++ b/activemodel/test/cases/mass_assignment_security_test.rb @@ -1,6 +1,15 @@ require "cases/helper" require 'models/mass_assignment_specific' + +class CustomSanitizer < ActiveModel::MassAssignmentSecurity::Sanitizer + + def process_removed_attributes(attrs) + raise StandardError + end + +end + class MassAssignmentSecurityTest < ActiveModel::TestCase def test_attribute_protection @@ -34,6 +43,20 @@ class MassAssignmentSecurityTest < ActiveModel::TestCase assert_equal expected, sanitized end + def test_attributes_accessible_with_roles_given_as_array + user = Account.new + expected = { "name" => "John Smith", "email" => "john@smith.com" } + sanitized = user.sanitize_for_mass_assignment(expected.merge("admin" => true)) + assert_equal expected, sanitized + end + + def test_attributes_accessible_with_admin_role_when_roles_given_as_array + user = Account.new + expected = { "name" => "John Smith", "email" => "john@smith.com", "admin" => true } + sanitized = user.sanitize_for_mass_assignment(expected.merge("super_powers" => true), :admin) + assert_equal expected, sanitized + end + def test_attributes_protected_by_default firm = Firm.new expected = { } @@ -76,4 +99,15 @@ class MassAssignmentSecurityTest < ActiveModel::TestCase assert_equal sanitized, { } end + def test_custom_sanitizer + user = User.new + User.mass_assignment_sanitizer = CustomSanitizer.new + assert_raise StandardError do + user.sanitize_for_mass_assignment("admin" => true) + end + ensure + User.mass_assignment_sanitizer = nil + + end + end diff --git a/activemodel/test/cases/naming_test.rb b/activemodel/test/cases/naming_test.rb index a7dde2c433..5f943729dd 100644 --- a/activemodel/test/cases/naming_test.rb +++ b/activemodel/test/cases/naming_test.rb @@ -26,7 +26,9 @@ class NamingTest < ActiveModel::TestCase end def test_partial_path - assert_equal 'post/track_backs/track_back', @model_name.partial_path + assert_deprecated(/#partial_path.*#to_partial_path/) do + assert_equal 'post/track_backs/track_back', @model_name.partial_path + end end def test_human @@ -56,7 +58,9 @@ class NamingWithNamespacedModelInIsolatedNamespaceTest < ActiveModel::TestCase end def test_partial_path - assert_equal 'blog/posts/post', @model_name.partial_path + assert_deprecated(/#partial_path.*#to_partial_path/) do + assert_equal 'blog/posts/post', @model_name.partial_path + end end def test_human @@ -98,7 +102,9 @@ class NamingWithNamespacedModelInSharedNamespaceTest < ActiveModel::TestCase end def test_partial_path - assert_equal 'blog/posts/post', @model_name.partial_path + assert_deprecated(/#partial_path.*#to_partial_path/) do + assert_equal 'blog/posts/post', @model_name.partial_path + end end def test_human @@ -114,6 +120,46 @@ class NamingWithNamespacedModelInSharedNamespaceTest < ActiveModel::TestCase end end +class NamingWithSuppliedModelNameTest < ActiveModel::TestCase + def setup + @model_name = ActiveModel::Name.new(Blog::Post, nil, 'Article') + end + + def test_singular + assert_equal 'article', @model_name.singular + end + + def test_plural + assert_equal 'articles', @model_name.plural + end + + def test_element + assert_equal 'article', @model_name.element + end + + def test_collection + assert_equal 'articles', @model_name.collection + end + + def test_partial_path + assert_deprecated(/#partial_path.*#to_partial_path/) do + assert_equal 'articles/article', @model_name.partial_path + end + end + + def test_human + assert_equal 'Article', @model_name.human + end + + def test_route_key + assert_equal 'articles', @model_name.route_key + end + + def test_param_key + assert_equal 'article', @model_name.param_key + end +end + class NamingHelpersTest < Test::Unit::TestCase def setup @klass = Contact @@ -171,4 +217,3 @@ class NamingHelpersTest < Test::Unit::TestCase ActiveModel::Naming.send(method, *args) end end - diff --git a/activemodel/test/cases/observing_test.rb b/activemodel/test/cases/observing_test.rb index 99b1f407ae..f6ec24ae57 100644 --- a/activemodel/test/cases/observing_test.rb +++ b/activemodel/test/cases/observing_test.rb @@ -17,6 +17,10 @@ class FooObserver < ActiveModel::Observer def on_spec(record) stub.event_with(record) if stub end + + def around_save(record) + yield :in_around_save + end end class Foo @@ -133,4 +137,12 @@ class ObserverTest < ActiveModel::TestCase foo = Foo.new Foo.send(:notify_observers, :whatever, foo) end + + test "update passes a block on to the observer" do + yielded_value = nil + FooObserver.instance.update(:around_save, Foo.new) do |val| + yielded_value = val + end + assert_equal :in_around_save, yielded_value + end end diff --git a/activemodel/test/cases/serialization_test.rb b/activemodel/test/cases/serialization_test.rb index 8cc1ccb1e7..1ec915d245 100644 --- a/activemodel/test/cases/serialization_test.rb +++ b/activemodel/test/cases/serialization_test.rb @@ -1,13 +1,19 @@ require "cases/helper" +require 'active_support/core_ext/object/instance_variables' class SerializationTest < ActiveModel::TestCase class User include ActiveModel::Serialization - attr_accessor :name, :email, :gender + attr_accessor :name, :email, :gender, :address, :friends + + def initialize(name, email, gender) + @name, @email, @gender = name, email, gender + @friends = [] + end def attributes - @attributes ||= {'name' => 'nil', 'email' => 'nil', 'gender' => 'nil'} + instance_values.except("address", "friends") end def foo @@ -15,11 +21,25 @@ class SerializationTest < ActiveModel::TestCase end end + class Address + include ActiveModel::Serialization + + attr_accessor :street, :city, :state, :zip + + def attributes + instance_values + end + end + setup do - @user = User.new - @user.name = 'David' - @user.email = 'david@example.com' - @user.gender = 'male' + @user = User.new('David', 'david@example.com', 'male') + @user.address = Address.new + @user.address.street = "123 Lane" + @user.address.city = "Springfield" + @user.address.state = "CA" + @user.address.zip = 11111 + @user.friends = [User.new('Joe', 'joe@example.com', 'male'), + User.new('Sue', 'sue@example.com', 'female')] end def test_method_serializable_hash_should_work @@ -42,4 +62,82 @@ class SerializationTest < ActiveModel::TestCase assert_equal expected , @user.serializable_hash(:methods => [:foo]) 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]) + 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]) + 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]) + end + + def test_should_use_read_attribute_for_serialization + def @user.read_attribute_for_serialization(n) + "Jon" + end + + expected = { "name" => "Jon" } + assert_equal expected, @user.serializable_hash(:only => :name) + end + + def test_include_option_with_singular_association + expected = {"name"=>"David", "gender"=>"male", "email"=>"david@example.com", + :address=>{"street"=>"123 Lane", "city"=>"Springfield", "state"=>"CA", "zip"=>11111}} + assert_equal expected , @user.serializable_hash(:include => :address) + end + + def test_include_option_with_plural_association + expected = {"email"=>"david@example.com", "gender"=>"male", "name"=>"David", + :friends=>[{"name"=>'Joe', "email"=>'joe@example.com', "gender"=>'male'}, + {"name"=>'Sue', "email"=>'sue@example.com', "gender"=>'female'}]} + assert_equal expected , @user.serializable_hash(:include => :friends) + end + + def test_include_option_with_empty_association + @user.friends = [] + expected = {"email"=>"david@example.com", "gender"=>"male", "name"=>"David", :friends=>[]} + assert_equal expected , @user.serializable_hash(:include => :friends) + end + + def test_multiple_includes + expected = {"email"=>"david@example.com", "gender"=>"male", "name"=>"David", + :address=>{"street"=>"123 Lane", "city"=>"Springfield", "state"=>"CA", "zip"=>11111}, + :friends=>[{"name"=>'Joe', "email"=>'joe@example.com', "gender"=>'male'}, + {"name"=>'Sue', "email"=>'sue@example.com', "gender"=>'female'}]} + assert_equal expected , @user.serializable_hash(:include => [:address, :friends]) + end + + def test_include_with_options + expected = {"email"=>"david@example.com", "gender"=>"male", "name"=>"David", + :address=>{"street"=>"123 Lane"}} + assert_equal expected , @user.serializable_hash(:include => {:address => {:only => "street"}}) + end + + def test_nested_include + @user.friends.first.friends = [@user] + expected = {"email"=>"david@example.com", "gender"=>"male", "name"=>"David", + :friends=>[{"name"=>'Joe', "email"=>'joe@example.com', "gender"=>'male', + :friends => [{"email"=>"david@example.com", "gender"=>"male", "name"=>"David"}]}, + {"name"=>'Sue', "email"=>'sue@example.com', "gender"=>'female', :friends => []}]} + assert_equal expected , @user.serializable_hash(:include => {:friends => {:include => :friends}}) + end + + def test_only_include + expected = {"name"=>"David", :friends => [{"name" => "Joe"}, {"name" => "Sue"}]} + assert_equal expected , @user.serializable_hash(:only => :name, :include => {:friends => {:only => :name}}) + end + + def test_except_include + expected = {"name"=>"David", "email"=>"david@example.com", + :friends => [{"name" => 'Joe', "email" => 'joe@example.com'}, + {"name" => "Sue", "email" => 'sue@example.com'}]} + assert_equal expected , @user.serializable_hash(:except => :gender, :include => {:friends => {:except => :gender}}) + end + end diff --git a/activemodel/test/cases/serializers/json_serialization_test.rb b/activemodel/test/cases/serializers/json_serialization_test.rb index 500a5c575f..a754d610b9 100644 --- a/activemodel/test/cases/serializers/json_serialization_test.rb +++ b/activemodel/test/cases/serializers/json_serialization_test.rb @@ -8,6 +8,12 @@ class Contact include ActiveModel::Serializers::JSON include ActiveModel::Validations + def attributes=(hash) + hash.each do |k, v| + instance_variable_set("@#{k}", v) + end + end + def attributes instance_values end unless method_defined?(:attributes) @@ -34,7 +40,7 @@ class JsonSerializationTest < ActiveModel::TestCase assert_match %r{"preferences":\{"shows":"anime"\}}, json end - test "should not include root in json" do + test "should not include root in json (class method)" do begin Contact.include_root_in_json = false json = @contact.to_json @@ -50,6 +56,23 @@ class JsonSerializationTest < ActiveModel::TestCase end end + test "should include root in json (option) even if the default is set to false" do + begin + Contact.include_root_in_json = false + json = @contact.to_json(:root => true) + assert_match %r{^\{"contact":\{}, json + ensure + Contact.include_root_in_json = true + end + end + + test "should not include root in json (option)" do + + json = @contact.to_json(:root => false) + + assert_no_match %r{^\{"contact":\{}, json + end + test "should include custom root in json" do json = @contact.to_json(:root => 'json_contact') @@ -135,6 +158,44 @@ class JsonSerializationTest < ActiveModel::TestCase end end + test "from_json should set the object's attributes" do + json = @contact.to_json + result = Contact.new.from_json(json) + + assert_equal result.name, @contact.name + assert_equal result.age, @contact.age + assert_equal Time.parse(result.created_at), @contact.created_at + assert_equal result.awesome, @contact.awesome + assert_equal result.preferences, @contact.preferences + end + + test "from_json should work without a root (method parameter)" do + json = @contact.to_json(:root => false) + result = Contact.new.from_json(json, false) + + assert_equal result.name, @contact.name + assert_equal result.age, @contact.age + assert_equal Time.parse(result.created_at), @contact.created_at + assert_equal result.awesome, @contact.awesome + assert_equal result.preferences, @contact.preferences + end + + test "from_json should work without a root (class attribute)" do + begin + Contact.include_root_in_json = false + json = @contact.to_json + result = Contact.new.from_json(json) + + assert_equal result.name, @contact.name + assert_equal result.age, @contact.age + assert_equal Time.parse(result.created_at), @contact.created_at + assert_equal result.awesome, @contact.awesome + assert_equal result.preferences, @contact.preferences + ensure + Contact.include_root_in_json = true + end + end + test "custom as_json should be honored when generating json" do def @contact.as_json(options); { :name => name, :created_at => created_at }; end json = @contact.to_json @@ -145,4 +206,14 @@ class JsonSerializationTest < ActiveModel::TestCase assert_no_match %r{"preferences":}, json end + test "custom as_json options should be extendible" do + def @contact.as_json(options = {}); super(options.merge(:only => [:name])); end + json = @contact.to_json + + assert_match %r{"name":"Konata Izumi"}, json + assert_no_match %r{"created_at":#{ActiveSupport::JSON.encode(Time.utc(2006, 8, 1))}}, json + assert_no_match %r{"awesome":}, json + assert_no_match %r{"preferences":}, json + end + end diff --git a/activemodel/test/cases/serializers/xml_serialization_test.rb b/activemodel/test/cases/serializers/xml_serialization_test.rb index 8f5c196850..fc73d9dcd8 100644 --- a/activemodel/test/cases/serializers/xml_serialization_test.rb +++ b/activemodel/test/cases/serializers/xml_serialization_test.rb @@ -7,9 +7,11 @@ class Contact extend ActiveModel::Naming include ActiveModel::Serializers::Xml + attr_accessor :address, :friends + def attributes - instance_values - end unless method_defined?(:attributes) + instance_values.except("address", "friends") + end end module Admin @@ -20,6 +22,23 @@ end class Customer < Struct.new(:name) end +class Address + extend ActiveModel::Naming + include ActiveModel::Serializers::Xml + + attr_accessor :street, :city, :state, :zip + + 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 @@ -30,6 +49,12 @@ class XmlSerializationTest < ActiveModel::TestCase customer = Customer.new customer.name = "John" @contact.preferences = customer + @contact.address = Address.new + @contact.address.street = "123 Lane" + @contact.address.city = "Springfield" + @contact.address.state = "CA" + @contact.address.zip = 11111 + @contact.friends = [Contact.new, Contact.new] end test "should serialize default root" do @@ -77,6 +102,17 @@ class XmlSerializationTest < ActiveModel::TestCase assert_match %r{<createdAt}, @xml end + test "should use serialiable 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 @@ -92,7 +128,7 @@ class XmlSerializationTest < ActiveModel::TestCase 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\"></pseudonyms>}, @contact.to_xml(:methods => :pseudonyms) end @@ -132,4 +168,39 @@ class XmlSerializationTest < ActiveModel::TestCase 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 + + 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 end diff --git a/activemodel/test/cases/validations/conditional_validation_test.rb b/activemodel/test/cases/validations/conditional_validation_test.rb index 3cb95b4a00..e06b04af19 100644 --- a/activemodel/test/cases/validations/conditional_validation_test.rb +++ b/activemodel/test/cases/validations/conditional_validation_test.rb @@ -11,7 +11,7 @@ class ConditionalValidationTest < ActiveModel::TestCase def test_if_validation_using_method_true # When the method returns true - Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %{count}", :if => :condition_is_true ) + Topic.validates_length_of( :title, :maximum => 5, :too_long => "hoo %{count}", :if => :condition_is_true ) t = Topic.new("title" => "uhohuhoh", "content" => "whatever") assert t.invalid? assert t.errors[:title].any? @@ -20,7 +20,7 @@ class ConditionalValidationTest < ActiveModel::TestCase def test_unless_validation_using_method_true # When the method returns true - Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %{count}", :unless => :condition_is_true ) + Topic.validates_length_of( :title, :maximum => 5, :too_long => "hoo %{count}", :unless => :condition_is_true ) t = Topic.new("title" => "uhohuhoh", "content" => "whatever") assert t.valid? assert t.errors[:title].empty? @@ -28,7 +28,7 @@ class ConditionalValidationTest < ActiveModel::TestCase def test_if_validation_using_method_false # When the method returns false - Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %{count}", :if => :condition_is_true_but_its_not ) + Topic.validates_length_of( :title, :maximum => 5, :too_long => "hoo %{count}", :if => :condition_is_true_but_its_not ) t = Topic.new("title" => "uhohuhoh", "content" => "whatever") assert t.valid? assert t.errors[:title].empty? @@ -36,7 +36,7 @@ class ConditionalValidationTest < ActiveModel::TestCase def test_unless_validation_using_method_false # When the method returns false - Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %{count}", :unless => :condition_is_true_but_its_not ) + Topic.validates_length_of( :title, :maximum => 5, :too_long => "hoo %{count}", :unless => :condition_is_true_but_its_not ) t = Topic.new("title" => "uhohuhoh", "content" => "whatever") assert t.invalid? assert t.errors[:title].any? @@ -45,7 +45,7 @@ class ConditionalValidationTest < ActiveModel::TestCase def test_if_validation_using_string_true # When the evaluated string returns true - Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %{count}", :if => "a = 1; a == 1" ) + Topic.validates_length_of( :title, :maximum => 5, :too_long => "hoo %{count}", :if => "a = 1; a == 1" ) t = Topic.new("title" => "uhohuhoh", "content" => "whatever") assert t.invalid? assert t.errors[:title].any? @@ -54,7 +54,7 @@ class ConditionalValidationTest < ActiveModel::TestCase def test_unless_validation_using_string_true # When the evaluated string returns true - Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %{count}", :unless => "a = 1; a == 1" ) + Topic.validates_length_of( :title, :maximum => 5, :too_long => "hoo %{count}", :unless => "a = 1; a == 1" ) t = Topic.new("title" => "uhohuhoh", "content" => "whatever") assert t.valid? assert t.errors[:title].empty? @@ -62,7 +62,7 @@ class ConditionalValidationTest < ActiveModel::TestCase def test_if_validation_using_string_false # When the evaluated string returns false - Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %{count}", :if => "false") + Topic.validates_length_of( :title, :maximum => 5, :too_long => "hoo %{count}", :if => "false") t = Topic.new("title" => "uhohuhoh", "content" => "whatever") assert t.valid? assert t.errors[:title].empty? @@ -70,7 +70,7 @@ class ConditionalValidationTest < ActiveModel::TestCase def test_unless_validation_using_string_false # When the evaluated string returns false - Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %{count}", :unless => "false") + Topic.validates_length_of( :title, :maximum => 5, :too_long => "hoo %{count}", :unless => "false") t = Topic.new("title" => "uhohuhoh", "content" => "whatever") assert t.invalid? assert t.errors[:title].any? @@ -79,7 +79,7 @@ class ConditionalValidationTest < ActiveModel::TestCase def test_if_validation_using_block_true # When the block returns true - Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %{count}", + Topic.validates_length_of( :title, :maximum => 5, :too_long => "hoo %{count}", :if => Proc.new { |r| r.content.size > 4 } ) t = Topic.new("title" => "uhohuhoh", "content" => "whatever") assert t.invalid? @@ -89,7 +89,7 @@ class ConditionalValidationTest < ActiveModel::TestCase def test_unless_validation_using_block_true # When the block returns true - Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %{count}", + Topic.validates_length_of( :title, :maximum => 5, :too_long => "hoo %{count}", :unless => Proc.new { |r| r.content.size > 4 } ) t = Topic.new("title" => "uhohuhoh", "content" => "whatever") assert t.valid? @@ -98,7 +98,7 @@ class ConditionalValidationTest < ActiveModel::TestCase def test_if_validation_using_block_false # When the block returns false - Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %{count}", + Topic.validates_length_of( :title, :maximum => 5, :too_long => "hoo %{count}", :if => Proc.new { |r| r.title != "uhohuhoh"} ) t = Topic.new("title" => "uhohuhoh", "content" => "whatever") assert t.valid? @@ -107,7 +107,7 @@ class ConditionalValidationTest < ActiveModel::TestCase def test_unless_validation_using_block_false # When the block returns false - Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %{count}", + Topic.validates_length_of( :title, :maximum => 5, :too_long => "hoo %{count}", :unless => Proc.new { |r| r.title != "uhohuhoh"} ) t = Topic.new("title" => "uhohuhoh", "content" => "whatever") assert t.invalid? diff --git a/activemodel/test/cases/validations/format_validation_test.rb b/activemodel/test/cases/validations/format_validation_test.rb index 73647efea5..2ce714fef0 100644 --- a/activemodel/test/cases/validations/format_validation_test.rb +++ b/activemodel/test/cases/validations/format_validation_test.rb @@ -27,7 +27,7 @@ class PresenceValidationTest < ActiveModel::TestCase end def test_validate_format_with_allow_blank - Topic.validates_format_of(:title, :with => /^Validation\smacros \w+!$/, :allow_blank=>true) + Topic.validates_format_of(:title, :with => /^Validation\smacros \w+!$/, :allow_blank => true) assert Topic.new("title" => "Shouldn't be valid").invalid? assert Topic.new("title" => "").valid? assert Topic.new("title" => nil).valid? diff --git a/activemodel/test/cases/validations/inclusion_validation_test.rb b/activemodel/test/cases/validations/inclusion_validation_test.rb index 92c473de0e..413da92de4 100644 --- a/activemodel/test/cases/validations/inclusion_validation_test.rb +++ b/activemodel/test/cases/validations/inclusion_validation_test.rb @@ -42,7 +42,7 @@ class InclusionValidationTest < ActiveModel::TestCase end def test_validates_inclusion_of_with_allow_nil - Topic.validates_inclusion_of( :title, :in => %w( a b c d e f g ), :allow_nil=>true ) + Topic.validates_inclusion_of( :title, :in => %w( a b c d e f g ), :allow_nil => true ) assert Topic.new("title" => "a!", "content" => "abc").invalid? assert Topic.new("title" => "", "content" => "abc").invalid? diff --git a/activemodel/test/cases/validations/length_validation_test.rb b/activemodel/test/cases/validations/length_validation_test.rb index f02514639b..44048a9c1d 100644 --- a/activemodel/test/cases/validations/length_validation_test.rb +++ b/activemodel/test/cases/validations/length_validation_test.rb @@ -11,7 +11,7 @@ class LengthValidationTest < ActiveModel::TestCase end def test_validates_length_of_with_allow_nil - Topic.validates_length_of( :title, :is => 5, :allow_nil=>true ) + Topic.validates_length_of( :title, :is => 5, :allow_nil => true ) assert Topic.new("title" => "ab").invalid? assert Topic.new("title" => "").invalid? @@ -20,7 +20,7 @@ class LengthValidationTest < ActiveModel::TestCase end def test_validates_length_of_with_allow_blank - Topic.validates_length_of( :title, :is => 5, :allow_blank=>true ) + Topic.validates_length_of( :title, :is => 5, :allow_blank => true ) assert Topic.new("title" => "ab").invalid? assert Topic.new("title" => "").valid? @@ -176,16 +176,16 @@ class LengthValidationTest < ActiveModel::TestCase end def test_validates_length_of_nasty_params - assert_raise(ArgumentError) { Topic.validates_length_of(:title, :is=>-6) } - assert_raise(ArgumentError) { Topic.validates_length_of(:title, :within=>6) } - assert_raise(ArgumentError) { Topic.validates_length_of(:title, :minimum=>"a") } - assert_raise(ArgumentError) { Topic.validates_length_of(:title, :maximum=>"a") } - assert_raise(ArgumentError) { Topic.validates_length_of(:title, :within=>"a") } - assert_raise(ArgumentError) { Topic.validates_length_of(:title, :is=>"a") } + assert_raise(ArgumentError) { Topic.validates_length_of(:title, :is => -6) } + assert_raise(ArgumentError) { Topic.validates_length_of(:title, :within => 6) } + assert_raise(ArgumentError) { Topic.validates_length_of(:title, :minimum => "a") } + assert_raise(ArgumentError) { Topic.validates_length_of(:title, :maximum => "a") } + assert_raise(ArgumentError) { Topic.validates_length_of(:title, :within => "a") } + assert_raise(ArgumentError) { Topic.validates_length_of(:title, :is => "a") } end def test_validates_length_of_custom_errors_for_minimum_with_message - Topic.validates_length_of( :title, :minimum=>5, :message=>"boo %{count}" ) + Topic.validates_length_of( :title, :minimum => 5, :message => "boo %{count}" ) t = Topic.new("title" => "uhoh", "content" => "whatever") assert t.invalid? assert t.errors[:title].any? @@ -193,7 +193,7 @@ class LengthValidationTest < ActiveModel::TestCase end def test_validates_length_of_custom_errors_for_minimum_with_too_short - Topic.validates_length_of( :title, :minimum=>5, :too_short=>"hoo %{count}" ) + Topic.validates_length_of( :title, :minimum => 5, :too_short => "hoo %{count}" ) t = Topic.new("title" => "uhoh", "content" => "whatever") assert t.invalid? assert t.errors[:title].any? @@ -201,7 +201,7 @@ class LengthValidationTest < ActiveModel::TestCase end def test_validates_length_of_custom_errors_for_maximum_with_message - Topic.validates_length_of( :title, :maximum=>5, :message=>"boo %{count}" ) + Topic.validates_length_of( :title, :maximum => 5, :message => "boo %{count}" ) t = Topic.new("title" => "uhohuhoh", "content" => "whatever") assert t.invalid? assert t.errors[:title].any? @@ -222,7 +222,7 @@ class LengthValidationTest < ActiveModel::TestCase end def test_validates_length_of_custom_errors_for_maximum_with_too_long - Topic.validates_length_of( :title, :maximum=>5, :too_long=>"hoo %{count}" ) + Topic.validates_length_of( :title, :maximum => 5, :too_long => "hoo %{count}" ) t = Topic.new("title" => "uhohuhoh", "content" => "whatever") assert t.invalid? assert t.errors[:title].any? @@ -244,7 +244,7 @@ class LengthValidationTest < ActiveModel::TestCase end def test_validates_length_of_custom_errors_for_is_with_message - Topic.validates_length_of( :title, :is=>5, :message=>"boo %{count}" ) + Topic.validates_length_of( :title, :is => 5, :message => "boo %{count}" ) t = Topic.new("title" => "uhohuhoh", "content" => "whatever") assert t.invalid? assert t.errors[:title].any? @@ -252,7 +252,7 @@ class LengthValidationTest < ActiveModel::TestCase end def test_validates_length_of_custom_errors_for_is_with_wrong_length - Topic.validates_length_of( :title, :is=>5, :wrong_length=>"hoo %{count}" ) + Topic.validates_length_of( :title, :is => 5, :wrong_length => "hoo %{count}" ) t = Topic.new("title" => "uhohuhoh", "content" => "whatever") assert t.invalid? assert t.errors[:title].any? @@ -331,7 +331,7 @@ 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.", + Topic.validates_length_of :content, :minimum => 5, :too_short => "Your essay must be at least %{count} words.", :tokenizer => lambda {|str| str.scan(/\w+/) } t = Topic.new(:content => "this content should be long enough") assert t.valid? diff --git a/activemodel/test/cases/validations_test.rb b/activemodel/test/cases/validations_test.rb index 0b50acf913..2f4376bd41 100644 --- a/activemodel/test/cases/validations_test.rb +++ b/activemodel/test/cases/validations_test.rb @@ -297,4 +297,37 @@ class ValidationsTest < ActiveModel::TestCase assert auto.valid? end + + def test_strict_validation_in_validates + Topic.validates :title, :strict => true, :presence => true + assert_raises ActiveModel::StrictValidationFailed do + Topic.new.valid? + end + end + + def test_strict_validation_not_fails + Topic.validates :title, :strict => true, :presence => true + assert Topic.new(:title => "hello").valid? + end + + def test_strict_validation_particular_validator + Topic.validates :title, :presence => {:strict => true} + assert_raises ActiveModel::StrictValidationFailed do + Topic.new.valid? + end + end + + def test_strict_validation_in_custom_validator_helper + Topic.validates_presence_of :title, :strict => true + assert_raises ActiveModel::StrictValidationFailed do + Topic.new.valid? + end + end + + def test_validates_with_bang + Topic.validates! :title, :presence => true + assert_raises ActiveModel::StrictValidationFailed do + Topic.new.valid? + end + end end |