diff options
Diffstat (limited to 'activerecord/test/cases/base_test.rb')
-rw-r--r-- | activerecord/test/cases/base_test.rb | 341 |
1 files changed, 207 insertions, 134 deletions
diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index 56b417562b..7c7c1fbfbd 100644 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -1,4 +1,7 @@ +# encoding: utf-8 + require "cases/helper" +require 'active_support/concurrency/latch' require 'models/post' require 'models/author' require 'models/topic' @@ -75,22 +78,6 @@ end class BasicsTest < ActiveRecord::TestCase fixtures :topics, :companies, :developers, :projects, :computers, :accounts, :minimalistics, 'warehouse-things', :authors, :categorizations, :categories, :posts - def setup - ActiveRecord::Base.time_zone_aware_attributes = false - ActiveRecord::Base.default_timezone = :local - Time.zone = nil - end - - def test_generated_methods_modules - modules = Computer.ancestors - assert modules.include?(Computer::GeneratedFeatureMethods) - assert_equal(Computer::GeneratedFeatureMethods, Computer.generated_feature_methods) - assert(modules.index(Computer.generated_attribute_methods) > modules.index(Computer.generated_feature_methods), - "generated_attribute_methods must be higher in inheritance hierarchy than generated_feature_methods") - assert_not_equal Computer.generated_feature_methods, Post.generated_feature_methods - assert(modules.index(Computer.generated_attribute_methods) < modules.index(ActiveRecord::Base.ancestors[1])) - end - def test_column_names_are_escaped conn = ActiveRecord::Base.connection classname = conn.class.name[/[^:]*$/] @@ -134,6 +121,10 @@ class BasicsTest < ActiveRecord::TestCase assert_equal 1, Topic.limit(1).to_a.length end + def test_limit_should_take_value_from_latest_limit + assert_equal 1, Topic.limit(2).limit(1).to_a.length + end + def test_invalid_limit assert_raises(ArgumentError) do Topic.limit("asdfadf").to_a @@ -169,19 +160,11 @@ class BasicsTest < ActiveRecord::TestCase end def test_preserving_date_objects - if current_adapter?(:SybaseAdapter) - # Sybase ctlib does not (yet?) support the date type; use datetime instead. - assert_kind_of( - Time, Topic.find(1).last_read, - "The last_read attribute should be of the Time class" - ) - else - # Oracle enhanced adapter allows to define Date attributes in model class (see topic.rb) - assert_kind_of( - Date, Topic.find(1).last_read, - "The last_read attribute should be of the Date class" - ) - end + # Oracle enhanced adapter allows to define Date attributes in model class (see topic.rb) + assert_kind_of( + Date, Topic.find(1).last_read, + "The last_read attribute should be of the Date class" + ) end def test_previously_changed @@ -221,7 +204,7 @@ class BasicsTest < ActiveRecord::TestCase ) # For adapters which support microsecond resolution. - if current_adapter?(:PostgreSQLAdapter, :SQLite3Adapter) + if current_adapter?(:PostgreSQLAdapter, :SQLite3Adapter) || mysql_56? assert_equal 11, Topic.find(1).written_on.sec assert_equal 223300, Topic.find(1).written_on.usec assert_equal 9900, Topic.find(2).written_on.usec @@ -231,7 +214,7 @@ class BasicsTest < ActiveRecord::TestCase def test_preserving_time_objects_with_local_time_conversion_to_default_timezone_utc with_env_tz 'America/New_York' do - with_active_record_default_timezone :utc do + with_timezone_config default: :utc do time = Time.local(2000) topic = Topic.create('written_on' => time) saved_time = Topic.find(topic.id).reload.written_on @@ -244,7 +227,7 @@ class BasicsTest < ActiveRecord::TestCase def test_preserving_time_objects_with_time_with_zone_conversion_to_default_timezone_utc with_env_tz 'America/New_York' do - with_active_record_default_timezone :utc do + with_timezone_config default: :utc do Time.use_zone 'Central Time (US & Canada)' do time = Time.zone.local(2000) topic = Topic.create('written_on' => time) @@ -259,18 +242,20 @@ class BasicsTest < ActiveRecord::TestCase def test_preserving_time_objects_with_utc_time_conversion_to_default_timezone_local with_env_tz 'America/New_York' do - time = Time.utc(2000) - topic = Topic.create('written_on' => time) - saved_time = Topic.find(topic.id).reload.written_on - assert_equal time, saved_time - assert_equal [0, 0, 0, 1, 1, 2000, 6, 1, false, "UTC"], time.to_a - assert_equal [0, 0, 19, 31, 12, 1999, 5, 365, false, "EST"], saved_time.to_a + with_timezone_config default: :local do + time = Time.utc(2000) + topic = Topic.create('written_on' => time) + saved_time = Topic.find(topic.id).reload.written_on + assert_equal time, saved_time + assert_equal [0, 0, 0, 1, 1, 2000, 6, 1, false, "UTC"], time.to_a + assert_equal [0, 0, 19, 31, 12, 1999, 5, 365, false, "EST"], saved_time.to_a + end end end def test_preserving_time_objects_with_time_with_zone_conversion_to_default_timezone_local with_env_tz 'America/New_York' do - with_active_record_default_timezone :local do + with_timezone_config default: :local do Time.use_zone 'Central Time (US & Canada)' do time = Time.zone.local(2000) topic = Topic.create('written_on' => time) @@ -328,7 +313,7 @@ class BasicsTest < ActiveRecord::TestCase def test_load topics = Topic.all.merge!(:order => 'id').to_a - assert_equal(4, topics.size) + assert_equal(5, topics.size) assert_equal(topics(:first).title, topics.first.title) end @@ -487,28 +472,28 @@ class BasicsTest < ActiveRecord::TestCase end end - # Oracle, and Sybase do not have a TIME datatype. - unless current_adapter?(:OracleAdapter, :SybaseAdapter) + # Oracle does not have a TIME datatype. + unless current_adapter?(:OracleAdapter) def test_utc_as_time_zone - Topic.default_timezone = :utc - attributes = { "bonus_time" => "5:42:00AM" } - topic = Topic.find(1) - topic.attributes = attributes - assert_equal Time.utc(2000, 1, 1, 5, 42, 0), topic.bonus_time - Topic.default_timezone = :local + with_timezone_config default: :utc do + attributes = { "bonus_time" => "5:42:00AM" } + topic = Topic.find(1) + topic.attributes = attributes + assert_equal Time.utc(2000, 1, 1, 5, 42, 0), topic.bonus_time + end end def test_utc_as_time_zone_and_new - Topic.default_timezone = :utc - attributes = { "bonus_time(1i)"=>"2000", - "bonus_time(2i)"=>"1", - "bonus_time(3i)"=>"1", - "bonus_time(4i)"=>"10", - "bonus_time(5i)"=>"35", - "bonus_time(6i)"=>"50" } - topic = Topic.new(attributes) - assert_equal Time.utc(2000, 1, 1, 10, 35, 50), topic.bonus_time - Topic.default_timezone = :local + with_timezone_config default: :utc do + attributes = { "bonus_time(1i)"=>"2000", + "bonus_time(2i)"=>"1", + "bonus_time(3i)"=>"1", + "bonus_time(4i)"=>"10", + "bonus_time(5i)"=>"35", + "bonus_time(6i)"=>"50" } + topic = Topic.new(attributes) + assert_equal Time.utc(2000, 1, 1, 10, 35, 50), topic.bonus_time + end end end @@ -522,12 +507,7 @@ class BasicsTest < ActiveRecord::TestCase topic = Topic.find(topic.id) assert_nil topic.last_read - # Sybase adapter does not allow nulls in boolean columns - if current_adapter?(:SybaseAdapter) - assert topic.approved == false - else - assert_nil topic.approved - end + assert_nil topic.approved end def test_equality @@ -540,6 +520,7 @@ class BasicsTest < ActiveRecord::TestCase def test_equality_of_new_records assert_not_equal Topic.new, Topic.new + assert_equal false, Topic.new == Topic.new end def test_equality_of_destroyed_records @@ -551,23 +532,94 @@ class BasicsTest < ActiveRecord::TestCase assert_equal topic_2, topic_1 end + def test_equality_with_blank_ids + one = Subscriber.new(:id => '') + two = Subscriber.new(:id => '') + assert_equal one, two + end + + def test_equality_of_relation_and_collection_proxy + car = Car.create! + car.bulbs.build + car.save + + assert car.bulbs == Bulb.where(car_id: car.id), 'CollectionProxy should be comparable with Relation' + assert Bulb.where(car_id: car.id) == car.bulbs, 'Relation should be comparable with CollectionProxy' + end + + def test_equality_of_relation_and_array + car = Car.create! + car.bulbs.build + car.save + + assert Bulb.where(car_id: car.id) == car.bulbs.to_a, 'Relation should be comparable with Array' + end + + def test_equality_of_relation_and_association_relation + car = Car.create! + car.bulbs.build + car.save + + assert_equal Bulb.where(car_id: car.id), car.bulbs.includes(:car), 'Relation should be comparable with AssociationRelation' + assert_equal car.bulbs.includes(:car), Bulb.where(car_id: car.id), 'AssociationRelation should be comparable with Relation' + end + + def test_equality_of_collection_proxy_and_association_relation + car = Car.create! + car.bulbs.build + car.save + + assert_equal car.bulbs, car.bulbs.includes(:car), 'CollectionProxy should be comparable with AssociationRelation' + assert_equal car.bulbs.includes(:car), car.bulbs, 'AssociationRelation should be comparable with CollectionProxy' + end + def test_hashing assert_equal [ Topic.find(1) ], [ Topic.find(2).topic ] & [ Topic.find(1) ] end - def test_comparison + def test_successful_comparison_of_like_class_records topic_1 = Topic.create! topic_2 = Topic.create! assert_equal [topic_2, topic_1].sort, [topic_1, topic_2] end + def test_failed_comparison_of_unlike_class_records + assert_raises ArgumentError do + [ topics(:first), posts(:welcome) ].sort + end + end + + def test_create_without_prepared_statement + topic = Topic.connection.unprepared_statement do + Topic.create(:title => 'foo') + end + + assert_equal topic, Topic.find(topic.id) + end + + def test_destroy_without_prepared_statement + topic = Topic.create(title: 'foo') + Topic.connection.unprepared_statement do + Topic.find(topic.id).destroy + end + + assert_equal nil, Topic.find_by_id(topic.id) + end + def test_comparison_with_different_objects topic = Topic.create category = Category.create(:name => "comparison") assert_nil topic <=> category end + def test_comparison_with_different_objects_in_array + topic = Topic.create + assert_raises(ArgumentError) do + [1, topic].sort + end + end + def test_readonly_attributes assert_equal Set.new([ 'title' , 'comments_count' ]), ReadonlyTitlePost.readonly_attributes @@ -581,10 +633,24 @@ class BasicsTest < ActiveRecord::TestCase assert_equal "changed", post.body end - def test_attr_readonly_is_class_level_setting - post = ReadonlyTitlePost.new - assert_raise(NoMethodError) { post._attr_readonly = [:title] } - assert_deprecated { post._attr_readonly } + def test_unicode_column_name + Weird.reset_column_information + weird = Weird.create(:なまえ => 'たこ焼き仮面') + assert_equal 'たこ焼き仮面', weird.なまえ + end + + unless current_adapter?(:PostgreSQLAdapter) + def test_respect_internal_encoding + old_default_internal = Encoding.default_internal + silence_warnings { Encoding.default_internal = "EUC-JP" } + + Weird.reset_column_information + + assert_equal ["EUC-JP"], Weird.columns.map {|c| c.name.encoding.name }.uniq + ensure + silence_warnings { Encoding.default_internal = old_default_internal } + Weird.reset_column_information + end end def test_non_valid_identifier_column_name @@ -606,20 +672,22 @@ class BasicsTest < ActiveRecord::TestCase end def test_attributes_on_dummy_time - # Oracle, and Sybase do not have a TIME datatype. - return true if current_adapter?(:OracleAdapter, :SybaseAdapter) + # Oracle does not have a TIME datatype. + return true if current_adapter?(:OracleAdapter) - attributes = { - "bonus_time" => "5:42:00AM" - } - topic = Topic.find(1) - topic.attributes = attributes - assert_equal Time.local(2000, 1, 1, 5, 42, 0), topic.bonus_time + with_timezone_config default: :local do + attributes = { + "bonus_time" => "5:42:00AM" + } + topic = Topic.find(1) + topic.attributes = attributes + assert_equal Time.local(2000, 1, 1, 5, 42, 0), topic.bonus_time + end end def test_attributes_on_dummy_time_with_invalid_time - # Oracle, and Sybase do not have a TIME datatype. - return true if current_adapter?(:OracleAdapter, :SybaseAdapter) + # Oracle does not have a TIME datatype. + return true if current_adapter?(:OracleAdapter) attributes = { "bonus_time" => "not a time" @@ -706,8 +774,14 @@ class BasicsTest < ActiveRecord::TestCase assert_equal("c", duped_topic.title) end + DeveloperSalary = Struct.new(:amount) def test_dup_with_aggregate_of_same_name_as_attribute - dev = DeveloperWithAggregate.find(1) + developer_with_aggregate = Class.new(ActiveRecord::Base) do + self.table_name = 'developers' + composed_of :salary, :class_name => 'BasicsTest::DeveloperSalary', :mapping => [%w(salary amount)] + end + + dev = developer_with_aggregate.find(1) assert_kind_of DeveloperSalary, dev.salary dup = nil @@ -802,19 +876,18 @@ class BasicsTest < ActiveRecord::TestCase # TODO: extend defaults tests to other databases! if current_adapter?(:PostgreSQLAdapter) def test_default - tz = Default.default_timezone - Default.default_timezone = :local - default = Default.new - Default.default_timezone = tz - - # fixed dates / times - assert_equal Date.new(2004, 1, 1), default.fixed_date - assert_equal Time.local(2004, 1,1,0,0,0,0), default.fixed_time - - # char types - assert_equal 'Y', default.char1 - assert_equal 'a varchar field', default.char2 - assert_equal 'a text field', default.char3 + with_timezone_config default: :local do + default = Default.new + + # fixed dates / times + assert_equal Date.new(2004, 1, 1), default.fixed_date + assert_equal Time.local(2004, 1,1,0,0,0,0), default.fixed_time + + # char types + assert_equal 'Y', default.char1 + assert_equal 'a varchar field', default.char2 + assert_equal 'a text field', default.char3 + end end class Geometric < ActiveRecord::Base; end @@ -1086,7 +1159,7 @@ class BasicsTest < ActiveRecord::TestCase k = Class.new(ak) k.table_name = "projects" orig_name = k.sequence_name - return skip "sequences not supported by db" unless orig_name + skip "sequences not supported by db" unless orig_name assert_equal k.reset_sequence_name, orig_name end @@ -1228,38 +1301,6 @@ class BasicsTest < ActiveRecord::TestCase assert_no_queries { assert true } end - def test_silence_sets_log_level_to_error_in_block - original_logger = ActiveRecord::Base.logger - - assert_deprecated do - log = StringIO.new - ActiveRecord::Base.logger = ActiveSupport::Logger.new(log) - ActiveRecord::Base.logger.level = Logger::DEBUG - ActiveRecord::Base.silence do - ActiveRecord::Base.logger.warn "warn" - ActiveRecord::Base.logger.error "error" - end - assert_equal "error\n", log.string - end - ensure - ActiveRecord::Base.logger = original_logger - end - - def test_silence_sets_log_level_back_to_level_before_yield - original_logger = ActiveRecord::Base.logger - - assert_deprecated do - log = StringIO.new - ActiveRecord::Base.logger = ActiveSupport::Logger.new(log) - ActiveRecord::Base.logger.level = Logger::WARN - ActiveRecord::Base.silence do - end - assert_equal Logger::WARN, ActiveRecord::Base.logger.level - end - ensure - ActiveRecord::Base.logger = original_logger - end - def test_benchmark_with_log_level original_logger = ActiveRecord::Base.logger log = StringIO.new @@ -1290,9 +1331,11 @@ class BasicsTest < ActiveRecord::TestCase end def test_compute_type_nonexistent_constant - assert_raises NameError do + e = assert_raises NameError do ActiveRecord::Base.send :compute_type, 'NonexistentModel' end + assert_equal 'uninitialized constant ActiveRecord::Base::NonexistentModel', e.message + assert_equal 'ActiveRecord::Base::NonexistentModel', e.name end def test_compute_type_no_method_error @@ -1354,6 +1397,37 @@ class BasicsTest < ActiveRecord::TestCase assert_equal 1, post.comments.length end + if Process.respond_to?(:fork) && !in_memory_db? + def test_marshal_between_processes + # Define a new model to ensure there are no caches + if self.class.const_defined?("Post", false) + flunk "there should be no post constant" + end + + self.class.const_set("Post", Class.new(ActiveRecord::Base) { + has_many :comments + }) + + rd, wr = IO.pipe + rd.binmode + wr.binmode + + ActiveRecord::Base.connection_handler.clear_all_connections! + + fork do + rd.close + post = Post.new + post.comments.build + wr.write Marshal.dump(post) + wr.close + end + + wr.close + assert Marshal.load rd.read + rd.close + end + end + def test_marshalling_new_record_round_trip_with_associations post = Post.new post.comments.build @@ -1496,21 +1570,20 @@ class BasicsTest < ActiveRecord::TestCase orig_handler = klass.connection_handler new_handler = ActiveRecord::ConnectionAdapters::ConnectionHandler.new after_handler = nil - is_set = false + latch1 = ActiveSupport::Concurrency::Latch.new + latch2 = ActiveSupport::Concurrency::Latch.new t = Thread.new do klass.connection_handler = new_handler - is_set = true - Thread.stop + latch1.release + latch2.await after_handler = klass.connection_handler end - while(!is_set) - Thread.pass - end + latch1.await klass.connection_handler = orig_handler - t.wakeup + latch2.release t.join assert_equal after_handler, new_handler |