aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/test
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord/test')
-rw-r--r--activerecord/test/active_record/connection_adapters/fake_adapter.rb9
-rw-r--r--activerecord/test/assets/test.txt1
-rw-r--r--activerecord/test/cases/adapter_test.rb255
-rw-r--r--activerecord/test/cases/adapters/mysql/case_sensitivity_test.rb35
-rw-r--r--activerecord/test/cases/adapters/mysql/connection_test.rb16
-rw-r--r--activerecord/test/cases/adapters/mysql/mysql_adapter_test.rb16
-rw-r--r--activerecord/test/cases/adapters/mysql/schema_test.rb8
-rw-r--r--activerecord/test/cases/adapters/mysql2/case_sensitivity_test.rb35
-rw-r--r--activerecord/test/cases/adapters/mysql2/connection_test.rb6
-rw-r--r--activerecord/test/cases/adapters/mysql2/explain_test.rb26
-rw-r--r--activerecord/test/cases/adapters/mysql2/schema_test.rb8
-rw-r--r--activerecord/test/cases/adapters/postgresql/connection_test.rb15
-rw-r--r--activerecord/test/cases/adapters/postgresql/explain_test.rb28
-rw-r--r--activerecord/test/cases/adapters/postgresql/hstore_test.rb89
-rw-r--r--activerecord/test/cases/adapters/postgresql/schema_test.rb59
-rw-r--r--activerecord/test/cases/adapters/postgresql/statement_pool_test.rb16
-rw-r--r--activerecord/test/cases/adapters/postgresql/utils_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/view_test.rb2
-rw-r--r--activerecord/test/cases/adapters/sqlite3/explain_test.rb26
-rw-r--r--activerecord/test/cases/adapters/sqlite3/quoting_test.rb4
-rw-r--r--activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb9
-rw-r--r--activerecord/test/cases/associations/belongs_to_associations_test.rb8
-rw-r--r--activerecord/test/cases/associations/cascaded_eager_loading_test.rb6
-rw-r--r--activerecord/test/cases/associations/eager_load_includes_full_sti_class_test.rb2
-rw-r--r--activerecord/test/cases/associations/eager_load_nested_include_test.rb1
-rw-r--r--activerecord/test/cases/associations/eager_test.rb157
-rw-r--r--activerecord/test/cases/associations/extension_test.rb16
-rw-r--r--activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb61
-rw-r--r--activerecord/test/cases/associations/has_many_associations_test.rb133
-rw-r--r--activerecord/test/cases/associations/has_many_through_associations_test.rb40
-rw-r--r--activerecord/test/cases/associations/has_one_associations_test.rb67
-rw-r--r--activerecord/test/cases/associations/inner_join_association_test.rb2
-rw-r--r--activerecord/test/cases/associations/join_model_test.rb4
-rw-r--r--activerecord/test/cases/associations/nested_through_associations_test.rb13
-rw-r--r--activerecord/test/cases/associations_test.rb23
-rw-r--r--activerecord/test/cases/attribute_methods/read_test.rb16
-rw-r--r--activerecord/test/cases/attribute_methods_test.rb120
-rw-r--r--activerecord/test/cases/autosave_association_test.rb21
-rw-r--r--activerecord/test/cases/base_test.rb276
-rw-r--r--activerecord/test/cases/binary_test.rb8
-rw-r--r--activerecord/test/cases/calculations_test.rb38
-rw-r--r--activerecord/test/cases/callbacks_test.rb14
-rw-r--r--activerecord/test/cases/column_test.rb29
-rw-r--r--activerecord/test/cases/connection_adapters/abstract_adapter_test.rb54
-rw-r--r--activerecord/test/cases/connection_adapters/connection_handler_test.rb5
-rw-r--r--activerecord/test/cases/connection_adapters/connection_specification_test.rb12
-rw-r--r--activerecord/test/cases/connection_adapters/schema_cache_test.rb44
-rw-r--r--activerecord/test/cases/connection_management_test.rb42
-rw-r--r--activerecord/test/cases/connection_pool_test.rb123
-rw-r--r--activerecord/test/cases/connection_specification/resolver_test.rb41
-rw-r--r--activerecord/test/cases/counter_cache_test.rb20
-rw-r--r--activerecord/test/cases/dup_test.rb8
-rw-r--r--activerecord/test/cases/explain_test.rb115
-rw-r--r--activerecord/test/cases/finder_test.rb31
-rw-r--r--activerecord/test/cases/fixtures_test.rb145
-rw-r--r--activerecord/test/cases/helper.rb23
-rw-r--r--activerecord/test/cases/identity_map/middleware_test.rb3
-rw-r--r--activerecord/test/cases/inclusion_test.rb113
-rw-r--r--activerecord/test/cases/inheritance_test.rb5
-rw-r--r--activerecord/test/cases/invertible_migration_test.rb12
-rw-r--r--activerecord/test/cases/locking_test.rb74
-rw-r--r--activerecord/test/cases/log_subscriber_test.rb3
-rw-r--r--activerecord/test/cases/mass_assignment_security_test.rb67
-rw-r--r--activerecord/test/cases/method_scoping_test.rb12
-rw-r--r--activerecord/test/cases/migration/change_schema_test.rb328
-rw-r--r--activerecord/test/cases/migration/column_attributes_test.rb188
-rw-r--r--activerecord/test/cases/migration/column_positioning_test.rb60
-rw-r--r--activerecord/test/cases/migration/command_recorder_test.rb18
-rw-r--r--activerecord/test/cases/migration/create_join_table_test.rb70
-rw-r--r--activerecord/test/cases/migration/helper.rb64
-rw-r--r--activerecord/test/cases/migration/index_test.rb176
-rw-r--r--activerecord/test/cases/migration/logger_test.rb37
-rw-r--r--activerecord/test/cases/migration/rename_column_test.rb194
-rw-r--r--activerecord/test/cases/migration/rename_table_test.rb72
-rw-r--r--activerecord/test/cases/migration/table_and_index_test.rb24
-rw-r--r--activerecord/test/cases/migration_test.rb2673
-rw-r--r--activerecord/test/cases/migrator_test.rb377
-rw-r--r--activerecord/test/cases/modules_test.rb2
-rw-r--r--activerecord/test/cases/multiple_db_test.rb1
-rw-r--r--activerecord/test/cases/named_scope_test.rb6
-rw-r--r--activerecord/test/cases/nested_attributes_test.rb8
-rw-r--r--activerecord/test/cases/pooled_connections_test.rb100
-rw-r--r--activerecord/test/cases/primary_keys_test.rb74
-rw-r--r--activerecord/test/cases/query_cache_test.rb38
-rw-r--r--activerecord/test/cases/reaper_test.rb81
-rw-r--r--activerecord/test/cases/reflection_test.rb41
-rw-r--r--activerecord/test/cases/relation_scoping_test.rb8
-rw-r--r--activerecord/test/cases/relation_test.rb23
-rw-r--r--activerecord/test/cases/relations_test.rb82
-rw-r--r--activerecord/test/cases/schema_dumper_test.rb25
-rw-r--r--activerecord/test/cases/serialization_test.rb9
-rw-r--r--activerecord/test/cases/session_store/session_test.rb10
-rw-r--r--activerecord/test/cases/store_test.rb43
-rw-r--r--activerecord/test/cases/test_case.rb63
-rw-r--r--activerecord/test/cases/timestamp_test.rb10
-rw-r--r--activerecord/test/cases/transaction_callbacks_test.rb4
-rw-r--r--activerecord/test/cases/transactions_test.rb3
-rw-r--r--activerecord/test/cases/unconnected_test.rb8
-rw-r--r--activerecord/test/cases/validations/association_validation_test.rb41
-rw-r--r--activerecord/test/cases/validations/uniqueness_validation_test.rb64
-rw-r--r--activerecord/test/cases/validations_test.rb2
-rw-r--r--activerecord/test/cases/yaml_serialization_test.rb42
-rw-r--r--activerecord/test/config.example.yml2
-rw-r--r--activerecord/test/fixtures/admin/randomly_named_a9.yml7
-rw-r--r--activerecord/test/fixtures/admin/randomly_named_b0.yml7
-rw-r--r--activerecord/test/fixtures/developers.yml2
-rw-r--r--activerecord/test/fixtures/dog_lovers.yml4
-rw-r--r--activerecord/test/fixtures/dogs.yml3
-rw-r--r--activerecord/test/fixtures/other_topics.yml42
-rw-r--r--activerecord/test/fixtures/randomly_named_a9.yml7
-rw-r--r--activerecord/test/fixtures/teapots.yml3
-rw-r--r--activerecord/test/migrations/broken/100_migration_that_raises_exception.rb10
-rw-r--r--activerecord/test/migrations/duplicate/3_foo.rb7
-rw-r--r--activerecord/test/migrations/duplicate_names/20080507052938_chunky.rb7
-rw-r--r--activerecord/test/migrations/duplicate_names/20080507053028_chunky.rb7
-rw-r--r--activerecord/test/migrations/interleaved/pass_1/3_interleaved_innocent_jointable.rb12
-rw-r--r--activerecord/test/migrations/interleaved/pass_2/1_interleaved_people_have_last_names.rb9
-rw-r--r--activerecord/test/migrations/interleaved/pass_2/3_interleaved_innocent_jointable.rb12
-rw-r--r--activerecord/test/migrations/interleaved/pass_3/1_interleaved_people_have_last_names.rb9
-rw-r--r--activerecord/test/migrations/interleaved/pass_3/2_interleaved_i_raise_on_down.rb8
-rw-r--r--activerecord/test/migrations/interleaved/pass_3/3_interleaved_innocent_jointable.rb12
-rw-r--r--activerecord/test/migrations/rename/1_we_need_things.rb11
-rw-r--r--activerecord/test/migrations/rename/2_rename_things.rb9
-rw-r--r--activerecord/test/migrations/to_copy_with_name_collision/1_people_have_hobbies.rb9
-rw-r--r--activerecord/test/migrations/valid_with_subdirectories/1_valid_people_have_last_names.rb (renamed from activerecord/test/migrations/duplicate/1_people_have_last_names.rb)4
-rw-r--r--activerecord/test/migrations/valid_with_subdirectories/sub/2_we_need_reminders.rb (renamed from activerecord/test/migrations/duplicate/2_we_need_reminders.rb)0
-rw-r--r--activerecord/test/migrations/valid_with_subdirectories/sub1/3_innocent_jointable.rb (renamed from activerecord/test/migrations/duplicate/3_innocent_jointable.rb)0
-rw-r--r--activerecord/test/models/admin/randomly_named_c1.rb3
-rw-r--r--activerecord/test/models/admin/user.rb4
-rw-r--r--activerecord/test/models/author.rb5
-rw-r--r--activerecord/test/models/bird.rb5
-rw-r--r--activerecord/test/models/car.rb2
-rw-r--r--activerecord/test/models/company.rb6
-rw-r--r--activerecord/test/models/country.rb2
-rw-r--r--activerecord/test/models/dashboard.rb4
-rw-r--r--activerecord/test/models/dog.rb4
-rw-r--r--activerecord/test/models/dog_lover.rb4
-rw-r--r--activerecord/test/models/joke.rb4
-rw-r--r--activerecord/test/models/keyboard.rb2
-rw-r--r--activerecord/test/models/legacy_thing.rb2
-rw-r--r--activerecord/test/models/liquid.rb2
-rw-r--r--activerecord/test/models/minivan.rb2
-rw-r--r--activerecord/test/models/mixed_case_monkey.rb2
-rw-r--r--activerecord/test/models/owner.rb2
-rw-r--r--activerecord/test/models/parrot.rb3
-rw-r--r--activerecord/test/models/person.rb7
-rw-r--r--activerecord/test/models/pet.rb2
-rw-r--r--activerecord/test/models/post.rb10
-rw-r--r--activerecord/test/models/randomly_named_c1.rb3
-rw-r--r--activerecord/test/models/speedometer.rb4
-rw-r--r--activerecord/test/models/string_key_object.rb2
-rw-r--r--activerecord/test/models/subscriber.rb2
-rw-r--r--activerecord/test/models/teapot.rb35
-rw-r--r--activerecord/test/models/topic.rb11
-rw-r--r--activerecord/test/models/toy.rb2
-rw-r--r--activerecord/test/models/treaty.rb2
-rw-r--r--activerecord/test/models/warehouse_thing.rb4
-rw-r--r--activerecord/test/schema/mysql2_specific_schema.rb13
-rw-r--r--activerecord/test/schema/mysql_specific_schema.rb11
-rw-r--r--activerecord/test/schema/schema.rb28
-rw-r--r--activerecord/test/support/connection.rb8
161 files changed, 5282 insertions, 2864 deletions
diff --git a/activerecord/test/active_record/connection_adapters/fake_adapter.rb b/activerecord/test/active_record/connection_adapters/fake_adapter.rb
index 1c2942170e..69dfd2503e 100644
--- a/activerecord/test/active_record/connection_adapters/fake_adapter.rb
+++ b/activerecord/test/active_record/connection_adapters/fake_adapter.rb
@@ -9,11 +9,16 @@ module ActiveRecord
class FakeAdapter < AbstractAdapter
attr_accessor :tables, :primary_keys
+ @columns = Hash.new { |h,k| h[k] = [] }
+ class << self
+ attr_reader :columns
+ end
+
def initialize(connection, logger)
super
@tables = []
@primary_keys = {}
- @columns = Hash.new { |h,k| h[k] = [] }
+ @columns = self.class.columns
end
def primary_key(table)
@@ -28,7 +33,7 @@ module ActiveRecord
options[:null])
end
- def columns(table_name, message)
+ def columns(table_name)
@columns[table_name]
end
end
diff --git a/activerecord/test/assets/test.txt b/activerecord/test/assets/test.txt
new file mode 100644
index 0000000000..6754f0612e
--- /dev/null
+++ b/activerecord/test/assets/test.txt
@@ -0,0 +1 @@
+%00
diff --git a/activerecord/test/cases/adapter_test.rb b/activerecord/test/cases/adapter_test.rb
index 94497e37c7..852fc0e26e 100644
--- a/activerecord/test/cases/adapter_test.rb
+++ b/activerecord/test/cases/adapter_test.rb
@@ -1,170 +1,163 @@
require "cases/helper"
-class AdapterTest < ActiveRecord::TestCase
- def setup
- @connection = ActiveRecord::Base.connection
- end
-
- def test_tables
- tables = @connection.tables
- assert tables.include?("accounts")
- assert tables.include?("authors")
- assert tables.include?("tasks")
- assert tables.include?("topics")
- end
+module ActiveRecord
+ class AdapterTest < ActiveRecord::TestCase
+ def setup
+ @connection = ActiveRecord::Base.connection
+ end
- def test_table_exists?
- assert @connection.table_exists?("accounts")
- assert !@connection.table_exists?("nonexistingtable")
- end
+ def test_tables
+ tables = @connection.tables
+ assert tables.include?("accounts")
+ assert tables.include?("authors")
+ assert tables.include?("tasks")
+ assert tables.include?("topics")
+ end
- def test_indexes
- idx_name = "accounts_idx"
-
- if @connection.respond_to?(:indexes)
- indexes = @connection.indexes("accounts")
- assert indexes.empty?
-
- @connection.add_index :accounts, :firm_id, :name => idx_name
- indexes = @connection.indexes("accounts")
- assert_equal "accounts", indexes.first.table
- # OpenBase does not have the concept of a named index
- # Indexes are merely properties of columns.
- assert_equal idx_name, indexes.first.name unless current_adapter?(:OpenBaseAdapter)
- assert !indexes.first.unique
- assert_equal ["firm_id"], indexes.first.columns
- else
- warn "#{@connection.class} does not respond to #indexes"
+ def test_table_exists?
+ assert @connection.table_exists?("accounts")
+ assert !@connection.table_exists?("nonexistingtable")
+ assert !@connection.table_exists?(nil)
end
- ensure
- @connection.remove_index(:accounts, :name => idx_name) rescue nil
- end
+ def test_indexes
+ idx_name = "accounts_idx"
+
+ if @connection.respond_to?(:indexes)
+ indexes = @connection.indexes("accounts")
+ assert indexes.empty?
+
+ @connection.add_index :accounts, :firm_id, :name => idx_name
+ indexes = @connection.indexes("accounts")
+ assert_equal "accounts", indexes.first.table
+ # OpenBase does not have the concept of a named index
+ # Indexes are merely properties of columns.
+ assert_equal idx_name, indexes.first.name unless current_adapter?(:OpenBaseAdapter)
+ assert !indexes.first.unique
+ assert_equal ["firm_id"], indexes.first.columns
+ else
+ warn "#{@connection.class} does not respond to #indexes"
+ end
- def test_current_database
- if @connection.respond_to?(:current_database)
- assert_equal ARTest.connection_config['arunit']['database'], @connection.current_database
+ ensure
+ @connection.remove_index(:accounts, :name => idx_name) rescue nil
end
- end
- if current_adapter?(:MysqlAdapter)
- def test_charset
- assert_not_nil @connection.charset
- assert_not_equal 'character_set_database', @connection.charset
- assert_equal @connection.show_variable('character_set_database'), @connection.charset
+ def test_current_database
+ if @connection.respond_to?(:current_database)
+ assert_equal ARTest.connection_config['arunit']['database'], @connection.current_database
+ end
end
- def test_collation
- assert_not_nil @connection.collation
- assert_not_equal 'collation_database', @connection.collation
- assert_equal @connection.show_variable('collation_database'), @connection.collation
- end
+ if current_adapter?(:MysqlAdapter)
+ def test_charset
+ assert_not_nil @connection.charset
+ assert_not_equal 'character_set_database', @connection.charset
+ assert_equal @connection.show_variable('character_set_database'), @connection.charset
+ end
- def test_show_nonexistent_variable_returns_nil
- assert_nil @connection.show_variable('foo_bar_baz')
- end
+ def test_collation
+ assert_not_nil @connection.collation
+ assert_not_equal 'collation_database', @connection.collation
+ assert_equal @connection.show_variable('collation_database'), @connection.collation
+ end
- def test_not_specifying_database_name_for_cross_database_selects
- begin
- assert_nothing_raised do
- ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations['arunit'].except(:database))
+ def test_show_nonexistent_variable_returns_nil
+ assert_nil @connection.show_variable('foo_bar_baz')
+ end
- config = ARTest.connection_config
- ActiveRecord::Base.connection.execute(
- "SELECT #{config['arunit']['database']}.pirates.*, #{config['arunit2']['database']}.courses.* " \
- "FROM #{config['arunit']['database']}.pirates, #{config['arunit2']['database']}.courses"
- )
+ def test_not_specifying_database_name_for_cross_database_selects
+ begin
+ assert_nothing_raised do
+ ActiveRecord::Model.establish_connection(ActiveRecord::Base.configurations['arunit'].except(:database))
+
+ config = ARTest.connection_config
+ ActiveRecord::Model.connection.execute(
+ "SELECT #{config['arunit']['database']}.pirates.*, #{config['arunit2']['database']}.courses.* " \
+ "FROM #{config['arunit']['database']}.pirates, #{config['arunit2']['database']}.courses"
+ )
+ end
+ ensure
+ ActiveRecord::Model.establish_connection 'arunit'
end
- ensure
- ActiveRecord::Base.establish_connection 'arunit'
end
end
- end
- def test_table_alias
- def @connection.test_table_alias_length() 10; end
- class << @connection
- alias_method :old_table_alias_length, :table_alias_length
- alias_method :table_alias_length, :test_table_alias_length
- end
+ def test_table_alias
+ def @connection.test_table_alias_length() 10; end
+ class << @connection
+ alias_method :old_table_alias_length, :table_alias_length
+ alias_method :table_alias_length, :test_table_alias_length
+ end
- assert_equal 'posts', @connection.table_alias_for('posts')
- assert_equal 'posts_comm', @connection.table_alias_for('posts_comments')
- assert_equal 'dbo_posts', @connection.table_alias_for('dbo.posts')
+ assert_equal 'posts', @connection.table_alias_for('posts')
+ assert_equal 'posts_comm', @connection.table_alias_for('posts_comments')
+ assert_equal 'dbo_posts', @connection.table_alias_for('dbo.posts')
- class << @connection
- remove_method :table_alias_length
- alias_method :table_alias_length, :old_table_alias_length
+ class << @connection
+ remove_method :table_alias_length
+ alias_method :table_alias_length, :old_table_alias_length
+ end
end
- end
- # test resetting sequences in odd tables in postgreSQL
- if ActiveRecord::Base.connection.respond_to?(:reset_pk_sequence!)
- require 'models/movie'
- require 'models/subscriber'
+ # test resetting sequences in odd tables in postgreSQL
+ if ActiveRecord::Base.connection.respond_to?(:reset_pk_sequence!)
+ require 'models/movie'
+ require 'models/subscriber'
- def test_reset_empty_table_with_custom_pk
- Movie.delete_all
- Movie.connection.reset_pk_sequence! 'movies'
- assert_equal 1, Movie.create(:name => 'fight club').id
- end
+ def test_reset_empty_table_with_custom_pk
+ Movie.delete_all
+ Movie.connection.reset_pk_sequence! 'movies'
+ assert_equal 1, Movie.create(:name => 'fight club').id
+ end
- if ActiveRecord::Base.connection.adapter_name != "FrontBase"
- def test_reset_table_with_non_integer_pk
- Subscriber.delete_all
- Subscriber.connection.reset_pk_sequence! 'subscribers'
- sub = Subscriber.new(:name => 'robert drake')
- sub.id = 'bob drake'
- assert_nothing_raised { sub.save! }
+ if ActiveRecord::Base.connection.adapter_name != "FrontBase"
+ def test_reset_table_with_non_integer_pk
+ Subscriber.delete_all
+ Subscriber.connection.reset_pk_sequence! 'subscribers'
+ sub = Subscriber.new(:name => 'robert drake')
+ sub.id = 'bob drake'
+ assert_nothing_raised { sub.save! }
+ end
end
end
- end
- def test_uniqueness_violations_are_translated_to_specific_exception
- @connection.execute "INSERT INTO subscribers(nick) VALUES('me')"
- assert_raises(ActiveRecord::RecordNotUnique) do
+ def test_uniqueness_violations_are_translated_to_specific_exception
@connection.execute "INSERT INTO subscribers(nick) VALUES('me')"
+ assert_raises(ActiveRecord::RecordNotUnique) do
+ @connection.execute "INSERT INTO subscribers(nick) VALUES('me')"
+ end
end
- end
- def test_foreign_key_violations_are_translated_to_specific_exception
- unless @connection.adapter_name == 'SQLite'
- assert_raises(ActiveRecord::InvalidForeignKey) do
- # Oracle adapter uses prefetched primary key values from sequence and passes them to connection adapter insert method
- if @connection.prefetch_primary_key?
- id_value = @connection.next_sequence_value(@connection.default_sequence_name("fk_test_has_fk", "id"))
- @connection.execute "INSERT INTO fk_test_has_fk (id, fk_id) VALUES (#{id_value},0)"
- else
- @connection.execute "INSERT INTO fk_test_has_fk (fk_id) VALUES (0)"
+ def test_foreign_key_violations_are_translated_to_specific_exception
+ unless @connection.adapter_name == 'SQLite'
+ assert_raises(ActiveRecord::InvalidForeignKey) do
+ # Oracle adapter uses prefetched primary key values from sequence and passes them to connection adapter insert method
+ if @connection.prefetch_primary_key?
+ id_value = @connection.next_sequence_value(@connection.default_sequence_name("fk_test_has_fk", "id"))
+ @connection.execute "INSERT INTO fk_test_has_fk (id, fk_id) VALUES (#{id_value},0)"
+ else
+ @connection.execute "INSERT INTO fk_test_has_fk (fk_id) VALUES (0)"
+ end
end
end
end
- end
- def test_disable_referential_integrity
- assert_nothing_raised do
- @connection.disable_referential_integrity do
- # Oracle adapter uses prefetched primary key values from sequence and passes them to connection adapter insert method
- if @connection.prefetch_primary_key?
- id_value = @connection.next_sequence_value(@connection.default_sequence_name("fk_test_has_fk", "id"))
- @connection.execute "INSERT INTO fk_test_has_fk (id, fk_id) VALUES (#{id_value},0)"
- else
- @connection.execute "INSERT INTO fk_test_has_fk (fk_id) VALUES (0)"
+ def test_disable_referential_integrity
+ assert_nothing_raised do
+ @connection.disable_referential_integrity do
+ # Oracle adapter uses prefetched primary key values from sequence and passes them to connection adapter insert method
+ if @connection.prefetch_primary_key?
+ id_value = @connection.next_sequence_value(@connection.default_sequence_name("fk_test_has_fk", "id"))
+ @connection.execute "INSERT INTO fk_test_has_fk (id, fk_id) VALUES (#{id_value},0)"
+ else
+ @connection.execute "INSERT INTO fk_test_has_fk (fk_id) VALUES (0)"
+ end
+ # should deleted created record as otherwise disable_referential_integrity will try to enable contraints after executed block
+ # and will fail (at least on Oracle)
+ @connection.execute "DELETE FROM fk_test_has_fk"
end
- # should deleted created record as otherwise disable_referential_integrity will try to enable contraints after executed block
- # and will fail (at least on Oracle)
- @connection.execute "DELETE FROM fk_test_has_fk"
end
end
end
-
- def test_deprecated_visitor_for
- visitor_klass = Class.new(Arel::Visitors::ToSql)
- Arel::Visitors::VISITORS['fuuu'] = visitor_klass
- pool = stub(:spec => stub(:config => { :adapter => 'fuuu' }))
- visitor = assert_deprecated {
- ActiveRecord::ConnectionAdapters::AbstractAdapter.visitor_for(pool)
- }
- assert visitor.is_a?(visitor_klass)
- end
end
diff --git a/activerecord/test/cases/adapters/mysql/case_sensitivity_test.rb b/activerecord/test/cases/adapters/mysql/case_sensitivity_test.rb
new file mode 100644
index 0000000000..97adb6b297
--- /dev/null
+++ b/activerecord/test/cases/adapters/mysql/case_sensitivity_test.rb
@@ -0,0 +1,35 @@
+require "cases/helper"
+require 'models/person'
+
+class MysqlCaseSensitivityTest < ActiveRecord::TestCase
+ class CollationTest < ActiveRecord::Base
+ validates_uniqueness_of :string_cs_column, :case_sensitive => false
+ validates_uniqueness_of :string_ci_column, :case_sensitive => false
+ end
+
+ def test_columns_include_collation_different_from_table
+ assert_equal 'utf8_bin', CollationTest.columns_hash['string_cs_column'].collation
+ assert_equal 'utf8_general_ci', CollationTest.columns_hash['string_ci_column'].collation
+ end
+
+ def test_case_sensitive
+ assert !CollationTest.columns_hash['string_ci_column'].case_sensitive?
+ assert CollationTest.columns_hash['string_cs_column'].case_sensitive?
+ end
+
+ def test_case_insensitive_comparison_for_ci_column
+ CollationTest.create!(:string_ci_column => 'A')
+ invalid = CollationTest.new(:string_ci_column => 'a')
+ queries = assert_sql { invalid.save }
+ ci_uniqueness_query = queries.detect { |q| q.match(/string_ci_column/) }
+ assert_no_match(/lower/i, ci_uniqueness_query)
+ end
+
+ def test_case_insensitive_comparison_for_cs_column
+ CollationTest.create!(:string_cs_column => 'A')
+ invalid = CollationTest.new(:string_cs_column => 'a')
+ queries = assert_sql { invalid.save }
+ cs_uniqueness_query = queries.detect { |q| q.match(/string_cs_column/) }
+ assert_match(/lower/i, cs_uniqueness_query)
+ end
+end
diff --git a/activerecord/test/cases/adapters/mysql/connection_test.rb b/activerecord/test/cases/adapters/mysql/connection_test.rb
index 2a89430da9..fa2ba8d592 100644
--- a/activerecord/test/cases/adapters/mysql/connection_test.rb
+++ b/activerecord/test/cases/adapters/mysql/connection_test.rb
@@ -3,13 +3,13 @@ require "cases/helper"
class MysqlConnectionTest < ActiveRecord::TestCase
def setup
super
- @connection = ActiveRecord::Base.connection
+ @connection = ActiveRecord::Model.connection
end
def test_mysql_reconnect_attribute_after_connection_with_reconnect_true
run_without_connection do |orig_connection|
- ActiveRecord::Base.establish_connection(orig_connection.merge({:reconnect => true}))
- assert ActiveRecord::Base.connection.raw_connection.reconnect
+ ActiveRecord::Model.establish_connection(orig_connection.merge({:reconnect => true}))
+ assert ActiveRecord::Model.connection.raw_connection.reconnect
end
end
@@ -25,8 +25,8 @@ class MysqlConnectionTest < ActiveRecord::TestCase
def test_mysql_reconnect_attribute_after_connection_with_reconnect_false
run_without_connection do |orig_connection|
- ActiveRecord::Base.establish_connection(orig_connection.merge({:reconnect => false}))
- assert !ActiveRecord::Base.connection.raw_connection.reconnect
+ ActiveRecord::Model.establish_connection(orig_connection.merge({:reconnect => false}))
+ assert !ActiveRecord::Model.connection.raw_connection.reconnect
end
end
@@ -114,7 +114,7 @@ class MysqlConnectionTest < ActiveRecord::TestCase
# Test that MySQL allows multiple results for stored procedures
if defined?(Mysql) && Mysql.const_defined?(:CLIENT_MULTI_RESULTS)
def test_multi_results
- rows = ActiveRecord::Base.connection.select_rows('CALL ten();')
+ rows = ActiveRecord::Model.connection.select_rows('CALL ten();')
assert_equal 10, rows[0][0].to_i, "ten() did not return 10 as expected: #{rows.inspect}"
assert @connection.active?, "Bad connection use by 'MysqlAdapter.select_rows'"
end
@@ -123,11 +123,11 @@ class MysqlConnectionTest < ActiveRecord::TestCase
private
def run_without_connection
- original_connection = ActiveRecord::Base.remove_connection
+ original_connection = ActiveRecord::Model.remove_connection
begin
yield original_connection
ensure
- ActiveRecord::Base.establish_connection(original_connection)
+ ActiveRecord::Model.establish_connection(original_connection)
end
end
end
diff --git a/activerecord/test/cases/adapters/mysql/mysql_adapter_test.rb b/activerecord/test/cases/adapters/mysql/mysql_adapter_test.rb
index 146b77a95c..7fe2c02c04 100644
--- a/activerecord/test/cases/adapters/mysql/mysql_adapter_test.rb
+++ b/activerecord/test/cases/adapters/mysql/mysql_adapter_test.rb
@@ -17,11 +17,7 @@ module ActiveRecord
end
def test_client_encoding
- if "<3".respond_to?(:encoding)
- assert_equal Encoding::UTF_8, @conn.client_encoding
- else
- assert_equal 'utf8', @conn.client_encoding
- end
+ assert_equal Encoding::UTF_8, @conn.client_encoding
end
def test_exec_insert_number
@@ -41,13 +37,11 @@ module ActiveRecord
value = result.rows.last.last
- if "<3".respond_to?(:encoding)
- # FIXME: this should probably be inside the mysql AR adapter?
- value.force_encoding(@conn.client_encoding)
+ # FIXME: this should probably be inside the mysql AR adapter?
+ value.force_encoding(@conn.client_encoding)
- # The strings in this file are utf-8, so transcode to utf-8
- value.encode!(Encoding::UTF_8)
- end
+ # The strings in this file are utf-8, so transcode to utf-8
+ value.encode!(Encoding::UTF_8)
assert_equal str, value
end
diff --git a/activerecord/test/cases/adapters/mysql/schema_test.rb b/activerecord/test/cases/adapters/mysql/schema_test.rb
index a2155d1dd1..29f885c6e7 100644
--- a/activerecord/test/cases/adapters/mysql/schema_test.rb
+++ b/activerecord/test/cases/adapters/mysql/schema_test.rb
@@ -13,8 +13,8 @@ module ActiveRecord
table = Post.table_name
@db_name = db
- @omgpost = Class.new(Post) do
- set_table_name "#{db}.#{table}"
+ @omgpost = Class.new(ActiveRecord::Base) do
+ self.table_name = "#{db}.#{table}"
def self.name; 'Post'; end
end
end
@@ -23,6 +23,10 @@ module ActiveRecord
assert @omgpost.find(:first)
end
+ def test_primary_key
+ assert_equal 'id', @omgpost.primary_key
+ end
+
def test_table_exists?
name = @omgpost.table_name
assert @connection.table_exists?(name), "#{name} table should exist"
diff --git a/activerecord/test/cases/adapters/mysql2/case_sensitivity_test.rb b/activerecord/test/cases/adapters/mysql2/case_sensitivity_test.rb
new file mode 100644
index 0000000000..6bcc113482
--- /dev/null
+++ b/activerecord/test/cases/adapters/mysql2/case_sensitivity_test.rb
@@ -0,0 +1,35 @@
+require "cases/helper"
+require 'models/person'
+
+class Mysql2CaseSensitivityTest < ActiveRecord::TestCase
+ class CollationTest < ActiveRecord::Base
+ validates_uniqueness_of :string_cs_column, :case_sensitive => false
+ validates_uniqueness_of :string_ci_column, :case_sensitive => false
+ end
+
+ def test_columns_include_collation_different_from_table
+ assert_equal 'utf8_bin', CollationTest.columns_hash['string_cs_column'].collation
+ assert_equal 'utf8_general_ci', CollationTest.columns_hash['string_ci_column'].collation
+ end
+
+ def test_case_sensitive
+ assert !CollationTest.columns_hash['string_ci_column'].case_sensitive?
+ assert CollationTest.columns_hash['string_cs_column'].case_sensitive?
+ end
+
+ def test_case_insensitive_comparison_for_ci_column
+ CollationTest.create!(:string_ci_column => 'A')
+ invalid = CollationTest.new(:string_ci_column => 'a')
+ queries = assert_sql { invalid.save }
+ ci_uniqueness_query = queries.detect { |q| q.match(/string_ci_column/) }
+ assert_no_match(/lower/i, ci_uniqueness_query)
+ end
+
+ def test_case_insensitive_comparison_for_cs_column
+ CollationTest.create!(:string_cs_column => 'A')
+ invalid = CollationTest.new(:string_cs_column => 'a')
+ queries = assert_sql { invalid.save }
+ cs_uniqueness_query = queries.detect { |q| q.match(/string_cs_column/)}
+ assert_match(/lower/i, cs_uniqueness_query)
+ end
+end
diff --git a/activerecord/test/cases/adapters/mysql2/connection_test.rb b/activerecord/test/cases/adapters/mysql2/connection_test.rb
index 26091c713b..8e2b9ca9a5 100644
--- a/activerecord/test/cases/adapters/mysql2/connection_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/connection_test.rb
@@ -3,7 +3,7 @@ require "cases/helper"
class MysqlConnectionTest < ActiveRecord::TestCase
def setup
super
- @connection = ActiveRecord::Base.connection
+ @connection = ActiveRecord::Model.connection
end
def test_no_automatic_reconnection_after_timeout
@@ -32,11 +32,11 @@ class MysqlConnectionTest < ActiveRecord::TestCase
private
def run_without_connection
- original_connection = ActiveRecord::Base.remove_connection
+ original_connection = ActiveRecord::Model.remove_connection
begin
yield original_connection
ensure
- ActiveRecord::Base.establish_connection(original_connection)
+ ActiveRecord::Model.establish_connection(original_connection)
end
end
end
diff --git a/activerecord/test/cases/adapters/mysql2/explain_test.rb b/activerecord/test/cases/adapters/mysql2/explain_test.rb
new file mode 100644
index 0000000000..68ed361aeb
--- /dev/null
+++ b/activerecord/test/cases/adapters/mysql2/explain_test.rb
@@ -0,0 +1,26 @@
+require "cases/helper"
+require 'models/developer'
+
+module ActiveRecord
+ module ConnectionAdapters
+ class Mysql2Adapter
+ class ExplainTest < ActiveRecord::TestCase
+ fixtures :developers
+
+ def test_explain_for_one_query
+ explain = Developer.where(:id => 1).explain
+ assert_match %(EXPLAIN for: SELECT `developers`.* FROM `developers` WHERE `developers`.`id` = 1), explain
+ assert_match %(developers | const), explain
+ end
+
+ def test_explain_with_eager_loading
+ explain = Developer.where(:id => 1).includes(:audit_logs).explain
+ assert_match %(EXPLAIN for: SELECT `developers`.* FROM `developers` WHERE `developers`.`id` = 1), explain
+ assert_match %(developers | const), explain
+ assert_match %(EXPLAIN for: SELECT `audit_logs`.* FROM `audit_logs` WHERE `audit_logs`.`developer_id` IN (1)), explain
+ assert_match %(audit_logs | ALL), explain
+ end
+ end
+ end
+ end
+end
diff --git a/activerecord/test/cases/adapters/mysql2/schema_test.rb b/activerecord/test/cases/adapters/mysql2/schema_test.rb
index 858d1da2dd..d5676bc522 100644
--- a/activerecord/test/cases/adapters/mysql2/schema_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/schema_test.rb
@@ -13,8 +13,8 @@ module ActiveRecord
table = Post.table_name
@db_name = db
- @omgpost = Class.new(Post) do
- set_table_name "#{db}.#{table}"
+ @omgpost = Class.new(ActiveRecord::Base) do
+ self.table_name = "#{db}.#{table}"
def self.name; 'Post'; end
end
end
@@ -23,6 +23,10 @@ module ActiveRecord
assert @omgpost.find(:first)
end
+ def test_primary_key
+ assert_equal 'id', @omgpost.primary_key
+ end
+
def test_table_exists?
name = @omgpost.table_name
assert @connection.table_exists?(name), "#{name} table should exist"
diff --git a/activerecord/test/cases/adapters/postgresql/connection_test.rb b/activerecord/test/cases/adapters/postgresql/connection_test.rb
index 21b97b3b39..4baec749ff 100644
--- a/activerecord/test/cases/adapters/postgresql/connection_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/connection_test.rb
@@ -2,6 +2,9 @@ require "cases/helper"
module ActiveRecord
class PostgresqlConnectionTest < ActiveRecord::TestCase
+ class NonExistentTable < ActiveRecord::Base
+ end
+
def setup
super
@connection = ActiveRecord::Base.connection
@@ -10,5 +13,17 @@ module ActiveRecord
def test_encoding
assert_not_nil @connection.encoding
end
+
+ # Ensure, we can set connection params using the example of Generic
+ # Query Optimizer (geqo). It is 'on' per default.
+ def test_connection_options
+ params = ActiveRecord::Base.connection_config.dup
+ params[:options] = "-c geqo=off"
+ NonExistentTable.establish_connection(params)
+
+ # Verify the connection param has been applied.
+ expect = NonExistentTable.connection.query('show geqo').first.first
+ assert_equal 'off', expect
+ end
end
end
diff --git a/activerecord/test/cases/adapters/postgresql/explain_test.rb b/activerecord/test/cases/adapters/postgresql/explain_test.rb
new file mode 100644
index 0000000000..0b61f61572
--- /dev/null
+++ b/activerecord/test/cases/adapters/postgresql/explain_test.rb
@@ -0,0 +1,28 @@
+require "cases/helper"
+require 'models/developer'
+
+module ActiveRecord
+ module ConnectionAdapters
+ class PostgreSQLAdapter
+ class ExplainTest < ActiveRecord::TestCase
+ fixtures :developers
+
+ def test_explain_for_one_query
+ explain = Developer.where(:id => 1).explain
+ assert_match %(EXPLAIN for: SELECT "developers".* FROM "developers" WHERE "developers"."id" = 1), explain
+ assert_match %(QUERY PLAN), explain
+ assert_match %(Index Scan using developers_pkey on developers), explain
+ end
+
+ def test_explain_with_eager_loading
+ explain = Developer.where(:id => 1).includes(:audit_logs).explain
+ assert_match %(QUERY PLAN), explain
+ assert_match %(EXPLAIN for: SELECT "developers".* FROM "developers" WHERE "developers"."id" = 1), explain
+ assert_match %(Index Scan using developers_pkey on developers), explain
+ assert_match %(EXPLAIN for: SELECT "audit_logs".* FROM "audit_logs" WHERE "audit_logs"."developer_id" IN (1)), explain
+ assert_match %(Seq Scan on audit_logs), explain
+ end
+ end
+ end
+ end
+end
diff --git a/activerecord/test/cases/adapters/postgresql/hstore_test.rb b/activerecord/test/cases/adapters/postgresql/hstore_test.rb
new file mode 100644
index 0000000000..33bf4478cc
--- /dev/null
+++ b/activerecord/test/cases/adapters/postgresql/hstore_test.rb
@@ -0,0 +1,89 @@
+require "cases/helper"
+
+class PostgresqlHstoreTest < ActiveRecord::TestCase
+ class Hstore < ActiveRecord::Base
+ self.table_name = 'hstores'
+ end
+
+ def setup
+ @connection = ActiveRecord::Base.connection
+ begin
+ @connection.transaction do
+ @connection.create_table('hstores') do |t|
+ t.hstore 'tags'
+ end
+ end
+ rescue ActiveRecord::StatementInvalid
+ return skip "do not test on PG without hstore"
+ end
+ end
+
+ def teardown
+ @connection.execute 'drop table if exists hstores'
+ end
+
+ def test_column
+ column = Hstore.columns.find { |c| c.name == 'tags' }
+ assert column
+ assert_equal :hstore, column.type
+ end
+
+ def test_type_cast_hstore
+ column = Hstore.columns.find { |c| c.name == 'tags' }
+ assert column
+
+ data = "\"1\"=>\"2\""
+ hash = column.class.cast_hstore data
+ assert_equal({'1' => '2'}, hash)
+ assert_equal({'1' => '2'}, column.type_cast(data))
+ end
+
+ def test_select
+ @connection.execute "insert into hstores (tags) VALUES ('1=>2')"
+ x = Hstore.find :first
+ assert_equal({'1' => '2'}, x.tags)
+ end
+
+ def test_select_multikey
+ @connection.execute "insert into hstores (tags) VALUES ('1=>2,2=>3')"
+ x = Hstore.find :first
+ assert_equal({'1' => '2', '2' => '3'}, x.tags)
+ end
+
+ def test_create
+ assert_cycle('a' => 'b', '1' => '2')
+ end
+
+ def test_quotes
+ assert_cycle('a' => 'b"ar', '1"foo' => '2')
+ end
+
+ def test_whitespace
+ assert_cycle('a b' => 'b ar', '1"foo' => '2')
+ end
+
+ def test_backslash
+ assert_cycle('a\\b' => 'b\\ar', '1"foo' => '2')
+ end
+
+ def test_comma
+ assert_cycle('a, b' => 'bar', '1"foo' => '2')
+ end
+
+ def test_arrow
+ assert_cycle('a=>b' => 'bar', '1"foo' => '2')
+ end
+
+ private
+ def assert_cycle hash
+ x = Hstore.create!(:tags => hash)
+ x.reload
+ assert_equal(hash, x.tags)
+
+ # make sure updates work
+ x.tags = hash
+ x.save!
+ x.reload
+ assert_equal(hash, x.tags)
+ end
+end
diff --git a/activerecord/test/cases/adapters/postgresql/schema_test.rb b/activerecord/test/cases/adapters/postgresql/schema_test.rb
index 76c73e9dfa..18670b4177 100644
--- a/activerecord/test/cases/adapters/postgresql/schema_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/schema_test.rb
@@ -10,32 +10,39 @@ class SchemaTest < ActiveRecord::TestCase
INDEX_A_NAME = 'a_index_things_on_name'
INDEX_B_NAME = 'b_index_things_on_different_columns_in_each_schema'
INDEX_C_NAME = 'c_index_full_text_search'
+ INDEX_D_NAME = 'd_index_things_on_description_desc'
INDEX_A_COLUMN = 'name'
INDEX_B_COLUMN_S1 = 'email'
INDEX_B_COLUMN_S2 = 'moment'
INDEX_C_COLUMN = %q{(to_tsvector('english', coalesce(things.name, '')))}
+ INDEX_D_COLUMN = 'description'
COLUMNS = [
'id integer',
'name character varying(50)',
'email character varying(50)',
+ 'description character varying(100)',
'moment timestamp without time zone default now()'
]
PK_TABLE_NAME = 'table_with_pk'
class Thing1 < ActiveRecord::Base
- set_table_name "test_schema.things"
+ self.table_name = "test_schema.things"
end
class Thing2 < ActiveRecord::Base
- set_table_name "test_schema2.things"
+ self.table_name = "test_schema2.things"
end
class Thing3 < ActiveRecord::Base
- set_table_name 'test_schema."things.table"'
+ self.table_name = 'test_schema."things.table"'
end
class Thing4 < ActiveRecord::Base
- set_table_name 'test_schema."Things"'
+ self.table_name = 'test_schema."Things"'
+ end
+
+ class Thing5 < ActiveRecord::Base
+ self.table_name = 'things'
end
def setup
@@ -50,6 +57,8 @@ class SchemaTest < ActiveRecord::TestCase
@connection.execute "CREATE INDEX #{INDEX_B_NAME} ON #{SCHEMA2_NAME}.#{TABLE_NAME} USING btree (#{INDEX_B_COLUMN_S2});"
@connection.execute "CREATE INDEX #{INDEX_C_NAME} ON #{SCHEMA_NAME}.#{TABLE_NAME} USING gin (#{INDEX_C_COLUMN});"
@connection.execute "CREATE INDEX #{INDEX_C_NAME} ON #{SCHEMA2_NAME}.#{TABLE_NAME} USING gin (#{INDEX_C_COLUMN});"
+ @connection.execute "CREATE INDEX #{INDEX_D_NAME} ON #{SCHEMA_NAME}.#{TABLE_NAME} USING btree (#{INDEX_D_COLUMN} DESC);"
+ @connection.execute "CREATE INDEX #{INDEX_D_NAME} ON #{SCHEMA2_NAME}.#{TABLE_NAME} USING btree (#{INDEX_D_COLUMN} DESC);"
@connection.execute "CREATE TABLE #{SCHEMA_NAME}.#{PK_TABLE_NAME} (id serial primary key)"
end
@@ -58,6 +67,18 @@ class SchemaTest < ActiveRecord::TestCase
@connection.execute "DROP SCHEMA #{SCHEMA_NAME} CASCADE"
end
+ def test_schema_change_with_prepared_stmt
+ altered = false
+ @connection.exec_query "select * from developers where id = $1", 'sql', [[nil, 1]]
+ @connection.exec_query "alter table developers add column zomg int", 'sql', []
+ altered = true
+ @connection.exec_query "select * from developers where id = $1", 'sql', [[nil, 1]]
+ ensure
+ # We are not using DROP COLUMN IF EXISTS because that syntax is only
+ # supported by pg 9.X
+ @connection.exec_query("alter table developers drop column zomg", 'sql', []) if altered
+ end
+
def test_table_exists?
[Thing1, Thing2, Thing3, Thing4].each do |klass|
name = klass.table_name
@@ -172,11 +193,15 @@ class SchemaTest < ActiveRecord::TestCase
end
def test_dump_indexes_for_schema_one
- do_dump_index_tests_for_schema(SCHEMA_NAME, INDEX_A_COLUMN, INDEX_B_COLUMN_S1)
+ do_dump_index_tests_for_schema(SCHEMA_NAME, INDEX_A_COLUMN, INDEX_B_COLUMN_S1, INDEX_D_COLUMN)
end
def test_dump_indexes_for_schema_two
- do_dump_index_tests_for_schema(SCHEMA2_NAME, INDEX_A_COLUMN, INDEX_B_COLUMN_S2)
+ do_dump_index_tests_for_schema(SCHEMA2_NAME, INDEX_A_COLUMN, INDEX_B_COLUMN_S2, INDEX_D_COLUMN)
+ end
+
+ def test_dump_indexes_for_schema_multiple_schemas_in_search_path
+ do_dump_index_tests_for_schema("public, #{SCHEMA_NAME}", INDEX_A_COLUMN, INDEX_B_COLUMN_S1, INDEX_D_COLUMN)
end
def test_with_uppercase_index_name
@@ -236,6 +261,21 @@ class SchemaTest < ActiveRecord::TestCase
end
end
+ def test_prepared_statements_with_multiple_schemas
+
+ @connection.schema_search_path = SCHEMA_NAME
+ Thing5.create(:id => 1, :name => "thing inside #{SCHEMA_NAME}", :email => "thing1@localhost", :moment => Time.now)
+
+ @connection.schema_search_path = SCHEMA2_NAME
+ Thing5.create(:id => 1, :name => "thing inside #{SCHEMA2_NAME}", :email => "thing1@localhost", :moment => Time.now)
+
+ @connection.schema_search_path = SCHEMA_NAME
+ assert_equal 1, Thing5.count
+
+ @connection.schema_search_path = SCHEMA2_NAME
+ assert_equal 1, Thing5.count
+ end
+
def test_schema_exists?
{
'public' => true,
@@ -261,13 +301,16 @@ class SchemaTest < ActiveRecord::TestCase
@connection.schema_search_path = "'$user', public"
end
- def do_dump_index_tests_for_schema(this_schema_name, first_index_column_name, second_index_column_name)
+ def do_dump_index_tests_for_schema(this_schema_name, first_index_column_name, second_index_column_name, third_index_column_name)
with_schema_search_path(this_schema_name) do
indexes = @connection.indexes(TABLE_NAME).sort_by {|i| i.name}
- assert_equal 2,indexes.size
+ assert_equal 3,indexes.size
do_dump_index_assertions_for_one_index(indexes[0], INDEX_A_NAME, first_index_column_name)
do_dump_index_assertions_for_one_index(indexes[1], INDEX_B_NAME, second_index_column_name)
+ do_dump_index_assertions_for_one_index(indexes[2], INDEX_D_NAME, third_index_column_name)
+
+ assert_equal :desc, indexes.select{|i| i.name == INDEX_D_NAME}[0].orders[INDEX_D_COLUMN]
end
end
diff --git a/activerecord/test/cases/adapters/postgresql/statement_pool_test.rb b/activerecord/test/cases/adapters/postgresql/statement_pool_test.rb
index a82c6f67d6..f1c4b85126 100644
--- a/activerecord/test/cases/adapters/postgresql/statement_pool_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/statement_pool_test.rb
@@ -2,6 +2,16 @@ require 'cases/helper'
module ActiveRecord::ConnectionAdapters
class PostgreSQLAdapter < AbstractAdapter
+ class InactivePGconn
+ def query(*args)
+ raise PGError
+ end
+
+ def status
+ PGconn::CONNECTION_BAD
+ end
+ end
+
class StatementPoolTest < ActiveRecord::TestCase
def test_cache_is_per_pid
return skip('must support fork') unless Process.respond_to?(:fork)
@@ -18,6 +28,12 @@ module ActiveRecord::ConnectionAdapters
Process.waitpid pid
assert $?.success?, 'process should exit successfully'
end
+
+ def test_dealloc_does_not_raise_on_inactive_connection
+ cache = StatementPool.new InactivePGconn.new, 10
+ cache['foo'] = 'bar'
+ assert_nothing_raised { cache.clear }
+ end
end
end
end
diff --git a/activerecord/test/cases/adapters/postgresql/utils_test.rb b/activerecord/test/cases/adapters/postgresql/utils_test.rb
index 5f08f79171..9e7b08ef34 100644
--- a/activerecord/test/cases/adapters/postgresql/utils_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/utils_test.rb
@@ -1,3 +1,5 @@
+require 'cases/helper'
+
class PostgreSQLUtilsTest < ActiveSupport::TestCase
include ActiveRecord::ConnectionAdapters::PostgreSQLAdapter::Utils
diff --git a/activerecord/test/cases/adapters/postgresql/view_test.rb b/activerecord/test/cases/adapters/postgresql/view_test.rb
index 303ba9245a..66e07b71a0 100644
--- a/activerecord/test/cases/adapters/postgresql/view_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/view_test.rb
@@ -14,7 +14,7 @@ class ViewTest < ActiveRecord::TestCase
]
class ThingView < ActiveRecord::Base
- set_table_name 'test_schema.view_things'
+ self.table_name = 'test_schema.view_things'
end
def setup
diff --git a/activerecord/test/cases/adapters/sqlite3/explain_test.rb b/activerecord/test/cases/adapters/sqlite3/explain_test.rb
new file mode 100644
index 0000000000..b227bce680
--- /dev/null
+++ b/activerecord/test/cases/adapters/sqlite3/explain_test.rb
@@ -0,0 +1,26 @@
+require "cases/helper"
+require 'models/developer'
+
+module ActiveRecord
+ module ConnectionAdapters
+ class SQLite3Adapter
+ class ExplainTest < ActiveRecord::TestCase
+ fixtures :developers
+
+ def test_explain_for_one_query
+ explain = Developer.where(:id => 1).explain
+ assert_match %(EXPLAIN for: SELECT "developers".* FROM "developers" WHERE "developers"."id" = 1), explain
+ assert_match(/(SEARCH )?TABLE developers USING (INTEGER )?PRIMARY KEY/, explain)
+ end
+
+ def test_explain_with_eager_loading
+ explain = Developer.where(:id => 1).includes(:audit_logs).explain
+ assert_match %(EXPLAIN for: SELECT "developers".* FROM "developers" WHERE "developers"."id" = 1), explain
+ assert_match(/(SEARCH )?TABLE developers USING (INTEGER )?PRIMARY KEY/, explain)
+ assert_match %(EXPLAIN for: SELECT "audit_logs".* FROM "audit_logs" WHERE "audit_logs"."developer_id" IN (1)), explain
+ assert_match(/(SCAN )?TABLE audit_logs/, explain)
+ end
+ end
+ end
+ end
+end
diff --git a/activerecord/test/cases/adapters/sqlite3/quoting_test.rb b/activerecord/test/cases/adapters/sqlite3/quoting_test.rb
index e0152e7ccf..46da1b0a2b 100644
--- a/activerecord/test/cases/adapters/sqlite3/quoting_test.rb
+++ b/activerecord/test/cases/adapters/sqlite3/quoting_test.rb
@@ -70,9 +70,9 @@ module ActiveRecord
assert_equal bd.to_f, @conn.type_cast(bd, nil)
end
- def test_type_cast_unknown
+ def test_type_cast_unknown_should_raise_error
obj = Class.new.new
- assert_equal YAML.dump(obj), @conn.type_cast(obj, nil)
+ assert_raise(TypeError) { @conn.type_cast(obj, nil) }
end
def test_quoted_id
diff --git a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb
index 2b598220ee..17bde6cb62 100644
--- a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb
+++ b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb
@@ -5,6 +5,8 @@ require 'models/owner'
module ActiveRecord
module ConnectionAdapters
class SQLite3AdapterTest < ActiveRecord::TestCase
+ self.use_transactional_fixtures = false
+
class DualEncoding < ActiveRecord::Base
end
@@ -21,8 +23,6 @@ module ActiveRecord
end
def test_column_types
- return skip('only test encoding on 1.9') unless "<3".encoding_aware?
-
owner = Owner.create!(:name => "hello".encode('ascii-8bit'))
owner.reload
select = Owner.columns.map { |c| "typeof(#{c.name})" }.join ', '
@@ -142,8 +142,6 @@ module ActiveRecord
end
def test_quote_binary_column_escapes_it
- return unless "<3".respond_to?(:encode)
-
DualEncoding.connection.execute(<<-eosql)
CREATE TABLE dual_encodings (
id integer PRIMARY KEY AUTOINCREMENT,
@@ -155,6 +153,9 @@ module ActiveRecord
binary = DualEncoding.new :name => 'いただきます!', :data => str
binary.save!
assert_equal str, binary.data
+
+ ensure
+ DualEncoding.connection.drop_table('dual_encodings')
end
def test_execute
diff --git a/activerecord/test/cases/associations/belongs_to_associations_test.rb b/activerecord/test/cases/associations/belongs_to_associations_test.rb
index 866a3cca10..1160d236c9 100644
--- a/activerecord/test/cases/associations/belongs_to_associations_test.rb
+++ b/activerecord/test/cases/associations/belongs_to_associations_test.rb
@@ -13,6 +13,7 @@ require 'models/comment'
require 'models/sponsor'
require 'models/member'
require 'models/essay'
+require 'models/toy'
class BelongsToAssociationsTest < ActiveRecord::TestCase
fixtures :accounts, :companies, :developers, :projects, :topics,
@@ -696,4 +697,11 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
assert_equal nil, comment.reload.parent
assert_equal 0, comments(:greetings).reload.children_count
end
+
+ def test_polymorphic_with_custom_primary_key
+ toy = Toy.create!
+ sponsor = Sponsor.create!(:sponsorable => toy)
+
+ assert_equal toy, sponsor.reload.sponsorable
+ end
end
diff --git a/activerecord/test/cases/associations/cascaded_eager_loading_test.rb b/activerecord/test/cases/associations/cascaded_eager_loading_test.rb
index ff376a68d8..6733f3e889 100644
--- a/activerecord/test/cases/associations/cascaded_eager_loading_test.rb
+++ b/activerecord/test/cases/associations/cascaded_eager_loading_test.rb
@@ -61,7 +61,7 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase
end
def test_cascaded_eager_association_loading_with_duplicated_includes
- categories = Category.includes(:categorizations).includes(:categorizations => :author).where("categorizations.id is not null")
+ categories = Category.includes(:categorizations).includes(:categorizations => :author).where("categorizations.id is not null").references(:categorizations)
assert_nothing_raised do
assert_equal 3, categories.count
assert_equal 3, categories.all.size
@@ -69,7 +69,7 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase
end
def test_cascaded_eager_association_loading_with_twice_includes_edge_cases
- categories = Category.includes(:categorizations => :author).includes(:categorizations => :post).where("posts.id is not null")
+ categories = Category.includes(:categorizations => :author).includes(:categorizations => :post).where("posts.id is not null").references(:posts)
assert_nothing_raised do
assert_equal 3, categories.count
assert_equal 3, categories.all.size
@@ -127,7 +127,7 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase
silly.parent_id = 1
assert silly.save
- topics = Topic.find(:all, :include => :replies, :order => 'topics.id, replies_topics.id')
+ topics = Topic.find(:all, :include => :replies, :order => ['topics.id', 'replies_topics.id'])
assert_no_queries do
assert_equal 2, topics[0].replies.size
assert_equal 0, topics[1].replies.size
diff --git a/activerecord/test/cases/associations/eager_load_includes_full_sti_class_test.rb b/activerecord/test/cases/associations/eager_load_includes_full_sti_class_test.rb
index d75791cab9..7965bb404c 100644
--- a/activerecord/test/cases/associations/eager_load_includes_full_sti_class_test.rb
+++ b/activerecord/test/cases/associations/eager_load_includes_full_sti_class_test.rb
@@ -4,7 +4,7 @@ require 'models/tagging'
module Namespaced
class Post < ActiveRecord::Base
- set_table_name 'posts'
+ self.table_name = 'posts'
has_one :tagging, :as => :taggable, :class_name => 'Tagging'
end
end
diff --git a/activerecord/test/cases/associations/eager_load_nested_include_test.rb b/activerecord/test/cases/associations/eager_load_nested_include_test.rb
index 2cf9f89c3c..1e1958410c 100644
--- a/activerecord/test/cases/associations/eager_load_nested_include_test.rb
+++ b/activerecord/test/cases/associations/eager_load_nested_include_test.rb
@@ -6,7 +6,6 @@ require 'models/comment'
require 'models/category'
require 'models/categorization'
require 'models/tagging'
-require 'active_support/core_ext/array/random_access'
module Remembered
extend ActiveSupport::Concern
diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb
index c6e451fc57..b79c69bbb5 100644
--- a/activerecord/test/cases/associations/eager_test.rb
+++ b/activerecord/test/cases/associations/eager_test.rb
@@ -61,7 +61,10 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
def test_loading_conditions_with_or
- posts = authors(:david).posts.find(:all, :include => :comments, :conditions => "comments.body like 'Normal%' OR comments.#{QUOTED_TYPE} = 'SpecialComment'")
+ posts = authors(:david).posts.find(
+ :all, :include => :comments, :references => :comments,
+ :conditions => "comments.body like 'Normal%' OR comments.#{QUOTED_TYPE} = 'SpecialComment'"
+ )
assert_nil posts.detect { |p| p.author_id != authors(:david).id },
"expected to find only david's posts"
end
@@ -164,7 +167,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
car_post.categories << categories(:technology)
comment = car_post.comments.create!(:body => "hmm")
- categories = Category.find(:all, :conditions => ["posts.id=?", car_post.id],
+ categories = Category.find(:all, :conditions => { 'posts.id' => car_post.id },
:include => {:posts => :comments})
categories.each do |category|
assert_equal [comment], category.posts[0].comments
@@ -252,6 +255,50 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
end
+ def test_nested_loading_through_has_one_association
+ aa = AuthorAddress.find(author_addresses(:david_address).id, :include => {:author => :posts})
+ assert_equal aa.author.posts.count, aa.author.posts.length
+ end
+
+ def test_nested_loading_through_has_one_association_with_order
+ aa = AuthorAddress.find(author_addresses(:david_address).id, :include => {:author => :posts}, :order => 'author_addresses.id')
+ assert_equal aa.author.posts.count, aa.author.posts.length
+ end
+
+ def test_nested_loading_through_has_one_association_with_order_on_association
+ aa = AuthorAddress.find(author_addresses(:david_address).id, :include => {:author => :posts}, :order => 'authors.id')
+ assert_equal aa.author.posts.count, aa.author.posts.length
+ end
+
+ def test_nested_loading_through_has_one_association_with_order_on_nested_association
+ aa = AuthorAddress.find(author_addresses(:david_address).id, :include => {:author => :posts}, :order => 'posts.id')
+ assert_equal aa.author.posts.count, aa.author.posts.length
+ end
+
+ def test_nested_loading_through_has_one_association_with_conditions
+ aa = AuthorAddress.find(
+ author_addresses(:david_address).id, :include => {:author => :posts},
+ :conditions => "author_addresses.id > 0", :references => :author_addresses
+ )
+ assert_equal aa.author.posts.count, aa.author.posts.length
+ end
+
+ def test_nested_loading_through_has_one_association_with_conditions_on_association
+ aa = AuthorAddress.find(
+ author_addresses(:david_address).id, :include => {:author => :posts},
+ :conditions => "authors.id > 0", :references => :authors
+ )
+ assert_equal aa.author.posts.count, aa.author.posts.length
+ end
+
+ def test_nested_loading_through_has_one_association_with_conditions_on_nested_association
+ aa = AuthorAddress.find(
+ author_addresses(:david_address).id, :include => {:author => :posts},
+ :conditions => "posts.id > 0", :references => :posts
+ )
+ assert_equal aa.author.posts.count, aa.author.posts.length
+ end
+
def test_eager_association_loading_with_belongs_to_and_foreign_keys
pets = Pet.find(:all, :include => :owner)
assert_equal 3, pets.length
@@ -297,7 +344,9 @@ class EagerAssociationTest < ActiveRecord::TestCase
def test_eager_association_loading_with_belongs_to_and_conditions_string_with_unquoted_table_name
assert_nothing_raised do
- Comment.find(:all, :include => :post, :conditions => ['posts.id = ?',4])
+ ActiveSupport::Deprecation.silence do
+ Comment.find(:all, :include => :post, :conditions => ['posts.id = ?',4])
+ end
end
end
@@ -316,7 +365,9 @@ class EagerAssociationTest < ActiveRecord::TestCase
def test_eager_association_loading_with_belongs_to_and_conditions_string_with_quoted_table_name
quoted_posts_id= Comment.connection.quote_table_name('posts') + '.' + Comment.connection.quote_column_name('id')
assert_nothing_raised do
- Comment.find(:all, :include => :post, :conditions => ["#{quoted_posts_id} = ?",4])
+ ActiveSupport::Deprecation.silence do
+ Comment.find(:all, :include => :post, :conditions => ["#{quoted_posts_id} = ?",4])
+ end
end
end
@@ -329,7 +380,9 @@ class EagerAssociationTest < ActiveRecord::TestCase
def test_eager_association_loading_with_belongs_to_and_order_string_with_quoted_table_name
quoted_posts_id= Comment.connection.quote_table_name('posts') + '.' + Comment.connection.quote_column_name('id')
assert_nothing_raised do
- Comment.find(:all, :include => :post, :order => quoted_posts_id)
+ ActiveSupport::Deprecation.silence do
+ Comment.find(:all, :include => :post, :order => quoted_posts_id)
+ end
end
end
@@ -493,22 +546,27 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
def test_eager_with_has_many_and_limit_and_conditions_array_on_the_eagers
- posts = Post.find(:all, :include => [ :author, :comments ], :limit => 2, :conditions => [ "authors.name = ?", 'David' ])
+ posts = ActiveSupport::Deprecation.silence do
+ Post.find(:all, :include => [ :author, :comments ], :limit => 2, :conditions => [ "authors.name = ?", 'David' ])
+ end
assert_equal 2, posts.size
- count = Post.count(:include => [ :author, :comments ], :limit => 2, :conditions => [ "authors.name = ?", 'David' ])
+ count = ActiveSupport::Deprecation.silence do
+ Post.count(:include => [ :author, :comments ], :limit => 2, :conditions => [ "authors.name = ?", 'David' ])
+ end
assert_equal count, posts.size
end
def test_eager_with_has_many_and_limit_and_high_offset
- posts = Post.find(:all, :include => [ :author, :comments ], :limit => 2, :offset => 10, :conditions => [ "authors.name = ?", 'David' ])
+ posts = Post.find(:all, :include => [ :author, :comments ], :limit => 2, :offset => 10, :conditions => { 'authors.name' => 'David' })
assert_equal 0, posts.size
end
def test_eager_with_has_many_and_limit_and_high_offset_and_multiple_array_conditions
assert_queries(1) do
posts = Post.find(:all, :include => [ :author, :comments ], :limit => 2, :offset => 10,
- :conditions => [ "authors.name = ? and comments.body = ?", 'David', 'go crazy' ])
+ :conditions => [ "authors.name = ? and comments.body = ?", 'David', 'go crazy' ],
+ :references => [:authors, :comments])
assert_equal 0, posts.size
end
end
@@ -522,7 +580,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
def test_count_eager_with_has_many_and_limit_and_high_offset
- posts = Post.count(:all, :include => [ :author, :comments ], :limit => 2, :offset => 10, :conditions => [ "authors.name = ?", 'David' ])
+ posts = Post.count(:all, :include => [ :author, :comments ], :limit => 2, :offset => 10, :conditions => { 'authors.name' => 'David' })
assert_equal 0, posts
end
@@ -534,7 +592,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
def test_eager_count_performed_on_a_has_many_association_with_multi_table_conditional
author = authors(:david)
author_posts_without_comments = author.posts.select { |post| post.comments.blank? }
- assert_equal author_posts_without_comments.size, author.posts.count(:all, :include => :comments, :conditions => 'comments.id is null')
+ assert_equal author_posts_without_comments.size, author.posts.count(:all, :include => :comments, :conditions => 'comments.id is null', :references => :comments)
end
def test_eager_count_performed_on_a_has_many_through_association_with_multi_table_conditional
@@ -573,6 +631,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
posts = authors(:david).posts.find(:all,
:include => :comments,
:conditions => "comments.body like 'Normal%' OR comments.#{QUOTED_TYPE}= 'SpecialComment'",
+ :references => :comments,
:limit => 2
)
assert_equal 2, posts.size
@@ -580,6 +639,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
count = Post.count(
:include => [ :comments, :author ],
:conditions => "authors.name = 'David' AND (comments.body like 'Normal%' OR comments.#{QUOTED_TYPE}= 'SpecialComment')",
+ :references => [:authors, :comments],
:limit => 2
)
assert_equal count, posts.size
@@ -589,7 +649,8 @@ class EagerAssociationTest < ActiveRecord::TestCase
posts = nil
Post.send(:with_scope, :find => {
:include => :comments,
- :conditions => "comments.body like 'Normal%' OR comments.#{QUOTED_TYPE}= 'SpecialComment'"
+ :conditions => "comments.body like 'Normal%' OR comments.#{QUOTED_TYPE}= 'SpecialComment'",
+ :references => :comments
}) do
posts = authors(:david).posts.find(:all, :limit => 2)
assert_equal 2, posts.size
@@ -597,7 +658,8 @@ class EagerAssociationTest < ActiveRecord::TestCase
Post.send(:with_scope, :find => {
:include => [ :comments, :author ],
- :conditions => "authors.name = 'David' AND (comments.body like 'Normal%' OR comments.#{QUOTED_TYPE}= 'SpecialComment')"
+ :conditions => "authors.name = 'David' AND (comments.body like 'Normal%' OR comments.#{QUOTED_TYPE}= 'SpecialComment')",
+ :references => [:authors, :comments]
}) do
count = Post.count(:limit => 2)
assert_equal count, posts.size
@@ -609,6 +671,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
posts = authors(:david).posts.find(:all,
:include => :comments,
:conditions => "comments.body like 'Normal%' OR comments.#{QUOTED_TYPE}= 'SpecialComment'",
+ :references => :comments,
:limit => 2
)
assert_equal 2, posts.size
@@ -616,6 +679,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
count = Post.count(
:include => [ :comments, :author ],
:conditions => "authors.name = 'David' AND (comments.body like 'Normal%' OR comments.#{QUOTED_TYPE}= 'SpecialComment')",
+ :references => [:authors, :comments],
:limit => 2
)
assert_equal count, posts.size
@@ -623,9 +687,15 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
def test_eager_with_scoped_order_using_association_limiting_without_explicit_scope
- posts_with_explicit_order = Post.find(:all, :conditions => 'comments.id is not null', :include => :comments, :order => 'posts.id DESC', :limit => 2)
+ posts_with_explicit_order = Post.find(
+ :all, :conditions => 'comments.id is not null', :references => :comments,
+ :include => :comments, :order => 'posts.id DESC', :limit => 2
+ )
posts_with_scoped_order = Post.send(:with_scope, :find => {:order => 'posts.id DESC'}) do
- Post.find(:all, :conditions => 'comments.id is not null', :include => :comments, :limit => 2)
+ Post.find(
+ :all, :conditions => 'comments.id is not null',
+ :references => :comments, :include => :comments, :limit => 2
+ )
end
assert_equal posts_with_explicit_order, posts_with_scoped_order
end
@@ -738,17 +808,49 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
def test_limited_eager_with_order
- assert_equal posts(:thinking, :sti_comments), Post.find(:all, :include => [:author, :comments], :conditions => "authors.name = 'David'", :order => 'UPPER(posts.title)', :limit => 2, :offset => 1)
- assert_equal posts(:sti_post_and_comments, :sti_comments), Post.find(:all, :include => [:author, :comments], :conditions => "authors.name = 'David'", :order => 'UPPER(posts.title) DESC', :limit => 2, :offset => 1)
+ assert_equal(
+ posts(:thinking, :sti_comments),
+ Post.find(
+ :all, :include => [:author, :comments], :conditions => { 'authors.name' => 'David' },
+ :order => 'UPPER(posts.title)', :limit => 2, :offset => 1
+ )
+ )
+ assert_equal(
+ posts(:sti_post_and_comments, :sti_comments),
+ Post.find(
+ :all, :include => [:author, :comments], :conditions => { 'authors.name' => 'David' },
+ :order => 'UPPER(posts.title) DESC', :limit => 2, :offset => 1
+ )
+ )
end
def test_limited_eager_with_multiple_order_columns
- assert_equal posts(:thinking, :sti_comments), Post.find(:all, :include => [:author, :comments], :conditions => "authors.name = 'David'", :order => ['UPPER(posts.title)', 'posts.id'], :limit => 2, :offset => 1)
- assert_equal posts(:sti_post_and_comments, :sti_comments), Post.find(:all, :include => [:author, :comments], :conditions => "authors.name = 'David'", :order => ['UPPER(posts.title) DESC', 'posts.id'], :limit => 2, :offset => 1)
+ assert_equal(
+ posts(:thinking, :sti_comments),
+ Post.find(
+ :all, :include => [:author, :comments], :conditions => { 'authors.name' => 'David' },
+ :order => ['UPPER(posts.title)', 'posts.id'], :limit => 2, :offset => 1
+ )
+ )
+ assert_equal(
+ posts(:sti_post_and_comments, :sti_comments),
+ Post.find(
+ :all, :include => [:author, :comments], :conditions => { 'authors.name' => 'David' },
+ :order => ['UPPER(posts.title) DESC', 'posts.id'], :limit => 2, :offset => 1
+ )
+ )
end
def test_limited_eager_with_numeric_in_association
- assert_equal people(:david, :susan), Person.find(:all, :include => [:readers, :primary_contact, :number1_fan], :conditions => "number1_fans_people.first_name like 'M%'", :order => 'people.id', :limit => 2, :offset => 0)
+ assert_equal(
+ people(:david, :susan),
+ Person.find(
+ :all, :include => [:readers, :primary_contact, :number1_fan],
+ :conditions => "number1_fans_people.first_name like 'M%'",
+ :references => :number1_fans_people,
+ :order => 'people.id', :limit => 2, :offset => 0
+ )
+ )
end
def test_preload_with_interpolation
@@ -863,11 +965,11 @@ class EagerAssociationTest < ActiveRecord::TestCase
def test_count_with_include
if current_adapter?(:SybaseAdapter)
- assert_equal 3, authors(:david).posts_with_comments.count(:conditions => "len(comments.body) > 15")
+ assert_equal 3, authors(:david).posts_with_comments.count(:conditions => "len(comments.body) > 15", :references => :comments)
elsif current_adapter?(:OpenBaseAdapter)
- assert_equal 3, authors(:david).posts_with_comments.count(:conditions => "length(FETCHBLOB(comments.body)) > 15")
+ assert_equal 3, authors(:david).posts_with_comments.count(:conditions => "length(FETCHBLOB(comments.body)) > 15", :references => :comments)
else
- assert_equal 3, authors(:david).posts_with_comments.count(:conditions => "length(comments.body) > 15")
+ assert_equal 3, authors(:david).posts_with_comments.count(:conditions => "length(comments.body) > 15", :references => :comments)
end
end
@@ -878,7 +980,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
def test_conditions_on_join_table_with_include_and_limit
- assert_equal 3, Developer.find(:all, :include => 'projects', :conditions => 'developers_projects.access_level = 1', :limit => 5).size
+ assert_equal 3, Developer.find(:all, :include => 'projects', :conditions => { 'developers_projects.access_level' => 1 }, :limit => 5).size
end
def test_order_on_join_table_with_include_and_limit
@@ -1060,4 +1162,11 @@ class EagerAssociationTest < ActiveRecord::TestCase
Post.includes(:comments).order(nil).where(:comments => {:body => "Thank you for the welcome"}).first
end
end
+
+ def test_deep_including_through_habtm
+ posts = Post.find(:all, :include => {:categories => :categorizations}, :order => "posts.id")
+ assert_no_queries { assert_equal 2, posts[0].categories[0].categorizations.length }
+ assert_no_queries { assert_equal 1, posts[0].categories[1].categorizations.length }
+ assert_no_queries { assert_equal 2, posts[1].categories[0].categorizations.length }
+ end
end
diff --git a/activerecord/test/cases/associations/extension_test.rb b/activerecord/test/cases/associations/extension_test.rb
index 8dc1423375..d7c489c2b5 100644
--- a/activerecord/test/cases/associations/extension_test.rb
+++ b/activerecord/test/cases/associations/extension_test.rb
@@ -36,11 +36,6 @@ class AssociationsExtensionsTest < ActiveRecord::TestCase
end
def test_marshalling_extensions
- if ENV['TRAVIS'] && RUBY_VERSION == "1.8.7"
- return skip("Marshalling tests disabled for Ruby 1.8.7 on Travis CI due to what appears " \
- "to be a Ruby bug.")
- end
-
david = developers(:david)
assert_equal projects(:action_controller), david.projects.find_most_recent
@@ -51,11 +46,6 @@ class AssociationsExtensionsTest < ActiveRecord::TestCase
end
def test_marshalling_named_extensions
- if ENV['TRAVIS'] && RUBY_VERSION == "1.8.7"
- return skip("Marshalling tests disabled for Ruby 1.8.7 on Travis CI due to what appears " \
- "to be a Ruby bug.")
- end
-
david = developers(:david)
assert_equal projects(:action_controller), david.projects_extended_by_name.find_most_recent
@@ -71,6 +61,12 @@ class AssociationsExtensionsTest < ActiveRecord::TestCase
assert_equal 'MyApplication::Business::DeveloperAssociationNameAssociationExtension', extension_name(MyApplication::Business::Developer)
end
+ def test_proxy_association_after_scoped
+ post = posts(:welcome)
+ assert_equal post.association(:comments), post.comments.the_association
+ assert_equal post.association(:comments), post.comments.scoped.the_association
+ end
+
private
def extension_name(model)
diff --git a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
index d8d2a113ff..f457dfb9b3 100644
--- a/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb
@@ -23,7 +23,7 @@ require 'models/treaty'
require 'active_support/core_ext/string/conversions'
class ProjectWithAfterCreateHook < ActiveRecord::Base
- set_table_name 'projects'
+ self.table_name = 'projects'
has_and_belongs_to_many :developers,
:class_name => "DeveloperForProjectWithAfterCreateHook",
:join_table => "developers_projects",
@@ -39,7 +39,7 @@ class ProjectWithAfterCreateHook < ActiveRecord::Base
end
class DeveloperForProjectWithAfterCreateHook < ActiveRecord::Base
- set_table_name 'developers'
+ self.table_name = 'developers'
has_and_belongs_to_many :projects,
:class_name => "ProjectWithAfterCreateHook",
:join_table => "developers_projects",
@@ -48,7 +48,7 @@ class DeveloperForProjectWithAfterCreateHook < ActiveRecord::Base
end
class ProjectWithSymbolsForKeys < ActiveRecord::Base
- set_table_name 'projects'
+ self.table_name = 'projects'
has_and_belongs_to_many :developers,
:class_name => "DeveloperWithSymbolsForKeys",
:join_table => :developers_projects,
@@ -57,7 +57,7 @@ class ProjectWithSymbolsForKeys < ActiveRecord::Base
end
class DeveloperWithSymbolsForKeys < ActiveRecord::Base
- set_table_name 'developers'
+ self.table_name = 'developers'
has_and_belongs_to_many :projects,
:class_name => "ProjectWithSymbolsForKeys",
:join_table => :developers_projects,
@@ -66,7 +66,7 @@ class DeveloperWithSymbolsForKeys < ActiveRecord::Base
end
class DeveloperWithCounterSQL < ActiveRecord::Base
- set_table_name 'developers'
+ self.table_name = 'developers'
has_and_belongs_to_many :projects,
:class_name => "DeveloperWithCounterSQL",
:join_table => "developers_projects",
@@ -77,7 +77,7 @@ end
class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
fixtures :accounts, :companies, :categories, :posts, :categories_posts, :developers, :projects, :developers_projects,
- :parrots, :pirates, :treasures, :price_estimates, :tags, :taggings
+ :parrots, :pirates, :parrots_pirates, :treasures, :price_estimates, :tags, :taggings
def setup_data_for_habtm_case
ActiveRecord::Base.connection.execute('delete from countries_treaties')
@@ -445,6 +445,26 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
assert david.projects(true).empty?
end
+ def test_destroy_associations_destroys_multiple_associations
+ george = parrots(:george)
+ assert !george.pirates.empty?
+ assert !george.treasures.empty?
+
+ assert_no_difference "Pirate.count" do
+ assert_no_difference "Treasure.count" do
+ george.destroy_associations
+ end
+ end
+
+ join_records = Parrot.connection.select_all("SELECT * FROM parrots_pirates WHERE parrot_id = #{george.id}")
+ assert join_records.empty?
+ assert george.pirates(true).empty?
+
+ join_records = Parrot.connection.select_all("SELECT * FROM parrots_treasures WHERE parrot_id = #{george.id}")
+ assert join_records.empty?
+ assert george.treasures(true).empty?
+ end
+
def test_deprecated_push_with_attributes_was_removed
jamis = developers(:jamis)
assert_raise(NoMethodError) do
@@ -650,8 +670,22 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
assert_respond_to categories(:technology).select_testing_posts.find(:first), :correctness_marker
end
+ def test_habtm_selects_all_columns_by_default
+ assert_equal Project.column_names.sort, developers(:david).projects.first.attributes.keys.sort
+ end
+
+ def test_habtm_respects_select_query_method
+ assert_equal ['id'], developers(:david).projects.select(:id).first.attributes.keys
+ end
+
def test_join_table_alias
- assert_equal 3, Developer.find(:all, :include => {:projects => :developers}, :conditions => 'developers_projects_join.joined_on IS NOT NULL').size
+ assert_equal(
+ 3,
+ Developer.find(
+ :all, :include => {:projects => :developers}, :references => :developers_projects_join,
+ :conditions => 'developers_projects_join.joined_on IS NOT NULL'
+ ).size
+ )
end
def test_join_with_group
@@ -661,7 +695,13 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
end
Project.columns.each { |c| group << "projects.#{c.name}" }
- assert_equal 3, Developer.find(:all, :include => {:projects => :developers}, :conditions => 'developers_projects_join.joined_on IS NOT NULL', :group => group.join(",")).size
+ assert_equal(
+ 3,
+ Developer.find(
+ :all, :include => {:projects => :developers}, :conditions => 'developers_projects_join.joined_on IS NOT NULL',
+ :references => :developers_projects_join, :group => group.join(",")
+ ).size
+ )
end
def test_find_grouped
@@ -797,12 +837,11 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
# clear cache possibly created by other tests
david.projects.reset_column_information
- # One query for columns, one for primary key
- assert_queries(2) { david.projects.columns; david.projects.columns }
+ assert_queries(1) { david.projects.columns; david.projects.columns }
## and again to verify that reset_column_information clears the cache correctly
david.projects.reset_column_information
- assert_queries(2) { david.projects.columns; david.projects.columns }
+ assert_queries(1) { david.projects.columns; david.projects.columns }
end
def test_attributes_are_being_set_when_initialized_from_habm_association_with_where_clause
diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb
index 1e59931963..3967009c82 100644
--- a/activerecord/test/cases/associations/has_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_associations_test.rb
@@ -8,6 +8,7 @@ require 'models/reply'
require 'models/category'
require 'models/post'
require 'models/author'
+require 'models/essay'
require 'models/comment'
require 'models/person'
require 'models/reader'
@@ -41,12 +42,37 @@ class HasManyAssociationsTestForCountWithCountSql < ActiveRecord::TestCase
end
end
+class HasManyAssociationsTestForCountDistinctWithFinderSql < ActiveRecord::TestCase
+ class Invoice < ActiveRecord::Base
+ has_many :custom_line_items, :class_name => 'LineItem', :finder_sql => "SELECT DISTINCT line_items.amount from line_items"
+ end
+
+ def test_should_count_distinct_results
+ invoice = Invoice.new
+ invoice.custom_line_items << LineItem.new(:amount => 0)
+ invoice.custom_line_items << LineItem.new(:amount => 0)
+ invoice.save!
+
+ assert_equal 1, invoice.custom_line_items.count
+ end
+end
+
+class HasManyAssociationsTestForReorderWithJoinDependency < ActiveRecord::TestCase
+ fixtures :authors, :posts, :comments
+
+ def test_should_generate_valid_sql
+ author = authors(:david)
+ # this can fail on adapters which require ORDER BY expressions to be included in the SELECT expression
+ # if the reorder clauses are not correctly handled
+ assert author.posts_with_comments_sorted_by_comment_id.where('comments.id > 0').reorder('posts.comments_count DESC', 'posts.taggings_count DESC').last
+ end
+end
class HasManyAssociationsTest < ActiveRecord::TestCase
fixtures :accounts, :categories, :companies, :developers, :projects,
:developers_projects, :topics, :authors, :comments,
- :people, :posts, :readers, :taggings, :cars
+ :people, :posts, :readers, :taggings, :cars, :essays
def setup
Client.destroyed_client_ids.clear
@@ -485,6 +511,14 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_equal 0, authors(:mary).popular_grouped_posts.length
end
+ def test_default_select
+ assert_equal Comment.column_names.sort, posts(:welcome).comments.first.attributes.keys.sort
+ end
+
+ def test_select_query_method
+ assert_equal ['id'], posts(:welcome).comments.select(:id).first.attributes.keys
+ end
+
def test_adding
force_signal37_to_load_all_clients_of_firm
natural = Client.new("name" => "Natural Company")
@@ -704,6 +738,18 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_equal number_of_clients + 1, companies(:first_firm).clients_of_firm.size
end
+ def test_find_or_initialize_returns_the_instantiated_object
+ client = companies(:first_firm).clients_of_firm.find_or_initialize_by_name("name" => "Another Client")
+ assert_equal client, companies(:first_firm).clients_of_firm[-1]
+ end
+
+ def test_find_or_initialize_only_instantiates_a_single_object
+ number_of_clients = Client.count
+ companies(:first_firm).clients_of_firm.find_or_initialize_by_name("name" => "Another Client").save!
+ companies(:first_firm).save!
+ assert_equal number_of_clients+1, Client.count
+ end
+
def test_find_or_create_with_hash
post = authors(:david).posts.find_or_create_by_title(:title => 'Yet another post', :body => 'somebody')
assert_equal post, authors(:david).posts.find_or_create_by_title(:title => 'Yet another post', :body => 'somebody')
@@ -1106,16 +1152,42 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_nil companies(:leetsoft).reload.client_of
assert_nil companies(:jadedpixel).reload.client_of
-
assert_equal num_accounts, Account.count
end
def test_restrict
- firm = RestrictedFirm.new(:name => 'restrict')
- firm.save!
+ option_before = ActiveRecord::Base.dependent_restrict_raises
+ ActiveRecord::Base.dependent_restrict_raises = true
+
+ firm = RestrictedFirm.create!(:name => 'restrict')
firm.companies.create(:name => 'child')
+
assert !firm.companies.empty?
assert_raise(ActiveRecord::DeleteRestrictionError) { firm.destroy }
+ assert RestrictedFirm.exists?(:name => 'restrict')
+ assert firm.companies.exists?(:name => 'child')
+ ensure
+ ActiveRecord::Base.dependent_restrict_raises = option_before
+ end
+
+ def test_restrict_when_dependent_restrict_raises_config_set_to_false
+ option_before = ActiveRecord::Base.dependent_restrict_raises
+ ActiveRecord::Base.dependent_restrict_raises = false
+
+ firm = RestrictedFirm.create!(:name => 'restrict')
+ firm.companies.create(:name => 'child')
+
+ assert !firm.companies.empty?
+
+ firm.destroy
+
+ assert !firm.errors.empty?
+
+ assert_equal "Cannot delete record because dependent companies exist", firm.errors[:base].first
+ assert RestrictedFirm.exists?(:name => 'restrict')
+ assert firm.companies.exists?(:name => 'child')
+ ensure
+ ActiveRecord::Base.dependent_restrict_raises = option_before
end
def test_included_in_collection
@@ -1219,6 +1291,10 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert company.clients_using_sql.loaded?
end
+ def test_get_ids_for_ordered_association
+ assert_equal [companies(:second_client).id, companies(:first_client).id], companies(:first_firm).clients_ordered_by_name_ids
+ end
+
def test_assign_ids_ignoring_blanks
firm = Firm.create!(:name => 'Apple')
firm.client_ids = [companies(:first_client).id, nil, companies(:second_client).id, '']
@@ -1368,6 +1444,32 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
end
+ def test_custom_primary_key_on_new_record_should_fetch_with_query
+ author = Author.new(:name => "David")
+ assert !author.essays.loaded?
+
+ assert_queries 1 do
+ assert_equal 1, author.essays.size
+ end
+
+ assert_equal author.essays, Essay.find_all_by_writer_id("David")
+
+ end
+
+ def test_has_many_custom_primary_key
+ david = authors(:david)
+ assert_equal david.essays, Essay.find_all_by_writer_id("David")
+ end
+
+ def test_blank_custom_primary_key_on_new_record_should_not_run_queries
+ author = Author.new
+ assert !author.essays.loaded?
+
+ assert_queries 0 do
+ assert_equal 0, author.essays.size
+ end
+ end
+
def test_calling_first_or_last_with_find_options_on_loaded_association_should_fetch_with_query
firm = companies(:first_firm)
firm.clients.class # force load target
@@ -1578,4 +1680,27 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_equal car.id, bulb.attributes_after_initialize['car_id']
end
+
+ def test_replace
+ car = Car.create(:name => 'honda')
+ bulb1 = car.bulbs.create
+ bulb2 = Bulb.create
+
+ assert_equal [bulb1], car.bulbs
+ car.bulbs.replace([bulb2])
+ assert_equal [bulb2], car.bulbs
+ assert_equal [bulb2], car.reload.bulbs
+ end
+
+ def test_building_has_many_association_with_restrict_dependency
+ option_before = ActiveRecord::Base.dependent_restrict_raises
+ ActiveRecord::Base.dependent_restrict_raises = true
+
+ klass = Class.new(ActiveRecord::Base)
+
+ assert_deprecated { klass.has_many :companies, :dependent => :restrict }
+ assert_not_deprecated { klass.has_many :companies }
+ ensure
+ ActiveRecord::Base.dependent_restrict_raises = option_before
+ end
end
diff --git a/activerecord/test/cases/associations/has_many_through_associations_test.rb b/activerecord/test/cases/associations/has_many_through_associations_test.rb
index b703c96ec1..9cc09194dc 100644
--- a/activerecord/test/cases/associations/has_many_through_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb
@@ -67,6 +67,31 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
end
end
+ def test_associate_existing_record_twice_should_add_records_twice
+ post = posts(:thinking)
+ person = people(:david)
+
+ assert_difference 'post.people.count', 2 do
+ post.people << person
+ post.people << person
+ end
+ end
+
+ def test_add_two_instance_and_then_deleting
+ post = posts(:thinking)
+ person = people(:david)
+
+ post.people << person
+ post.people << person
+
+ counts = ['post.people.count', 'post.people.to_a.count', 'post.readers.count', 'post.readers.to_a.count']
+ assert_difference counts, -2 do
+ post.people.delete(person)
+ end
+
+ assert !post.people.reload.include?(person)
+ end
+
def test_associating_new
assert_queries(1) { posts(:thinking) }
new_person = nil # so block binding catches it
@@ -503,6 +528,12 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
assert_equal [posts(:welcome).id, posts(:authorless).id].sort, people(:michael).post_ids.sort
end
+ def test_get_ids_for_has_many_through_with_conditions_should_not_preload
+ Tagging.create!(:taggable_type => 'Post', :taggable_id => posts(:welcome).id, :tag => tags(:misc))
+ ActiveRecord::Associations::Preloader.expects(:new).never
+ posts(:welcome).misc_tag_ids
+ end
+
def test_get_ids_for_loaded_associations
person = people(:michael)
person.posts(true)
@@ -707,7 +738,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
def test_select_chosen_fields_only
author = authors(:david)
- assert_equal ['body'], author.comments.select('comments.body').first.attributes.keys
+ assert_equal ['body', 'id'].sort, author.comments.select('comments.body').first.attributes.keys.sort
end
def test_get_has_many_through_belongs_to_ids_with_conditions
@@ -816,7 +847,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
def test_preloading_empty_through_association_via_joins
person = Person.create!(:first_name => "Gaga")
- person = Person.where(:id => person.id).where('readers.id = 1 or 1=1').includes(:posts).to_a.first
+ person = Person.where(:id => person.id).where('readers.id = 1 or 1=1').references(:readers).includes(:posts).to_a.first
assert person.posts.loaded?, 'person.posts should be loaded'
assert_equal [], person.posts
@@ -825,4 +856,9 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
def test_explicitly_joining_join_table
assert_equal owners(:blackbeard).toys, owners(:blackbeard).toys.with_pet
end
+
+ def test_has_many_through_with_polymorphic_source
+ post = tags(:general).tagged_posts.create! :title => "foo", :body => "bar"
+ assert_equal [tags(:general)], post.reload.tags
+ end
end
diff --git a/activerecord/test/cases/associations/has_one_associations_test.rb b/activerecord/test/cases/associations/has_one_associations_test.rb
index 26931e3e85..246877bbed 100644
--- a/activerecord/test/cases/associations/has_one_associations_test.rb
+++ b/activerecord/test/cases/associations/has_one_associations_test.rb
@@ -157,11 +157,62 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
end
def test_dependence_with_restrict
- firm = RestrictedFirm.new(:name => 'restrict')
- firm.save!
+ option_before = ActiveRecord::Base.dependent_restrict_raises
+ ActiveRecord::Base.dependent_restrict_raises = true
+
+ firm = RestrictedFirm.create!(:name => 'restrict')
firm.create_account(:credit_limit => 10)
+
assert_not_nil firm.account
+
assert_raise(ActiveRecord::DeleteRestrictionError) { firm.destroy }
+ assert RestrictedFirm.exists?(:name => 'restrict')
+ assert firm.account.present?
+ ensure
+ ActiveRecord::Base.dependent_restrict_raises = option_before
+ end
+
+ def test_dependence_with_restrict_with_dependent_restrict_raises_config_set_to_false
+ option_before = ActiveRecord::Base.dependent_restrict_raises
+ ActiveRecord::Base.dependent_restrict_raises = false
+
+ firm = RestrictedFirm.create!(:name => 'restrict')
+ firm.create_account(:credit_limit => 10)
+
+ assert_not_nil firm.account
+
+ firm.destroy
+
+ assert !firm.errors.empty?
+ assert_equal "Cannot delete record because a dependent account exists", firm.errors[:base].first
+ assert RestrictedFirm.exists?(:name => 'restrict')
+ assert firm.account.present?
+ ensure
+ ActiveRecord::Base.dependent_restrict_raises = option_before
+ end
+
+ def test_dependence_with_restrict_with_dependent_restrict_raises_config_set_to_false_and_attribute_name
+ old_backend = I18n.backend
+ I18n.backend = I18n::Backend::Simple.new
+ I18n.backend.store_translations 'en', :activerecord => {:attributes => {:restricted_firm => {:account => "account model"}}}
+
+ option_before = ActiveRecord::Base.dependent_restrict_raises
+ ActiveRecord::Base.dependent_restrict_raises = false
+
+ firm = RestrictedFirm.create!(:name => 'restrict')
+ firm.create_account(:credit_limit => 10)
+
+ assert_not_nil firm.account
+
+ firm.destroy
+
+ assert !firm.errors.empty?
+ assert_equal "Cannot delete record because a dependent account model exists", firm.errors[:base].first
+ assert RestrictedFirm.exists?(:name => 'restrict')
+ assert firm.account.present?
+ ensure
+ ActiveRecord::Base.dependent_restrict_raises = option_before
+ I18n.backend = old_backend
end
def test_successful_build_association
@@ -456,4 +507,16 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
assert_equal car.id, bulb.attributes_after_initialize['car_id']
end
+
+ def test_building_has_one_association_with_dependent_restrict
+ option_before = ActiveRecord::Base.dependent_restrict_raises
+ ActiveRecord::Base.dependent_restrict_raises = true
+
+ klass = Class.new(ActiveRecord::Base)
+
+ assert_deprecated { klass.has_one :account, :dependent => :restrict }
+ assert_not_deprecated { klass.has_one :account }
+ ensure
+ ActiveRecord::Base.dependent_restrict_raises = option_before
+ end
end
diff --git a/activerecord/test/cases/associations/inner_join_association_test.rb b/activerecord/test/cases/associations/inner_join_association_test.rb
index e5e9ca6131..68a1e62328 100644
--- a/activerecord/test/cases/associations/inner_join_association_test.rb
+++ b/activerecord/test/cases/associations/inner_join_association_test.rb
@@ -67,7 +67,7 @@ class InnerJoinAssociationTest < ActiveRecord::TestCase
def test_find_with_implicit_inner_joins_does_not_set_associations
authors = Author.joins(:posts).select('authors.*')
assert !authors.empty?, "expected authors to be non-empty"
- assert authors.all? {|a| !a.send(:instance_variable_names).include?("@posts")}, "expected no authors to have the @posts association loaded"
+ assert authors.all? { |a| !a.instance_variable_defined?(:@posts) }, "expected no authors to have the @posts association loaded"
end
def test_count_honors_implicit_inner_joins
diff --git a/activerecord/test/cases/associations/join_model_test.rb b/activerecord/test/cases/associations/join_model_test.rb
index 4ce8b85098..301755249c 100644
--- a/activerecord/test/cases/associations/join_model_test.rb
+++ b/activerecord/test/cases/associations/join_model_test.rb
@@ -362,7 +362,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
end
assert_raise ActiveRecord::EagerLoadPolymorphicError do
- tags(:general).taggings.find(:all, :include => :taggable, :conditions => 'bogus_table.column = 1')
+ tags(:general).taggings.find(:all, :include => :taggable, :references => :bogus_table, :conditions => 'bogus_table.column = 1')
end
end
@@ -733,7 +733,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
class_name = "PostWith#{association.to_s.classify}#{dependency.to_s.classify}"
Post.find(post_id).update_column :type, class_name
klass = Object.const_set(class_name, Class.new(ActiveRecord::Base))
- klass.set_table_name 'posts'
+ klass.table_name = 'posts'
klass.send(association, association_name, :as => :taggable, :dependent => dependency)
klass.find(post_id)
end
diff --git a/activerecord/test/cases/associations/nested_through_associations_test.rb b/activerecord/test/cases/associations/nested_through_associations_test.rb
index 530f5212a2..03d99d19f6 100644
--- a/activerecord/test/cases/associations/nested_through_associations_test.rb
+++ b/activerecord/test/cases/associations/nested_through_associations_test.rb
@@ -505,7 +505,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
def test_nested_has_many_through_with_conditions_on_through_associations_preload_via_joins
# Pointless condition to force single-query loading
assert_includes_and_joins_equal(
- Author.where('tags.id = tags.id'),
+ Author.where('tags.id = tags.id').references(:tags),
[authors(:bob)], :misc_post_first_blue_tags
)
end
@@ -526,7 +526,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
def test_nested_has_many_through_with_conditions_on_source_associations_preload_via_joins
# Pointless condition to force single-query loading
assert_includes_and_joins_equal(
- Author.where('tags.id = tags.id'),
+ Author.where('tags.id = tags.id').references(:tags),
[authors(:bob)], :misc_post_first_blue_tags_2
)
end
@@ -545,6 +545,15 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
assert_equal [organizations(:nsa)], organizations
end
+ def test_nested_has_many_through_should_not_be_autosaved
+ c = Categorization.new
+ c.author = authors(:david)
+ c.post_taggings.to_a
+ assert !c.post_taggings.empty?
+ c.save
+ assert !c.post_taggings.empty?
+ end
+
private
def assert_includes_and_joins_equal(query, expected, association)
diff --git a/activerecord/test/cases/associations_test.rb b/activerecord/test/cases/associations_test.rb
index ffe2993e0f..017905e0ac 100644
--- a/activerecord/test/cases/associations_test.rb
+++ b/activerecord/test/cases/associations_test.rb
@@ -1,4 +1,5 @@
require "cases/helper"
+require 'models/computer'
require 'models/developer'
require 'models/project'
require 'models/company'
@@ -28,7 +29,7 @@ class AssociationsTest < ActiveRecord::TestCase
molecule.electrons.create(:name => 'electron_1')
molecule.electrons.create(:name => 'electron_2')
- liquids = Liquid.includes(:molecules => :electrons).where('molecules.id is not null')
+ liquids = Liquid.includes(:molecules => :electrons).references(:molecules).where('molecules.id is not null')
assert_equal 1, liquids[0].molecules.length
end
@@ -128,6 +129,11 @@ class AssociationsTest < ActiveRecord::TestCase
end
end
+ def test_association_with_references
+ firm = companies(:first_firm)
+ assert_equal ['foo'], firm.association_with_references.scoped.references_values
+ end
+
end
class AssociationProxyTest < ActiveRecord::TestCase
@@ -273,3 +279,18 @@ class OverridingAssociationsTest < ActiveRecord::TestCase
)
end
end
+
+class GeneratedMethodsTest < ActiveRecord::TestCase
+ fixtures :developers, :computers, :posts, :comments
+ def test_association_methods_override_attribute_methods_of_same_name
+ assert_equal(developers(:david), computers(:workstation).developer)
+ # this next line will fail if the attribute methods module is generated lazily
+ # after the association methods module is generated
+ assert_equal(developers(:david), computers(:workstation).developer)
+ assert_equal(developers(:david).id, computers(:workstation)[:developer])
+ end
+
+ def test_model_method_overrides_association_method
+ assert_equal(comments(:greetings).body, posts(:welcome).first_comment)
+ end
+end
diff --git a/activerecord/test/cases/attribute_methods/read_test.rb b/activerecord/test/cases/attribute_methods/read_test.rb
index e03ed33591..764305459d 100644
--- a/activerecord/test/cases/attribute_methods/read_test.rb
+++ b/activerecord/test/cases/attribute_methods/read_test.rb
@@ -1,5 +1,6 @@
require "cases/helper"
require 'active_support/core_ext/object/inclusion'
+require 'thread'
module ActiveRecord
module AttributeMethods
@@ -14,8 +15,18 @@ module ActiveRecord
def setup
@klass = Class.new do
+ def self.superclass; Base; end
+ def self.active_record_super; Base; end
+ def self.base_class; self; end
+
include ActiveRecord::AttributeMethods
- include ActiveRecord::AttributeMethods::Read
+
+ def self.define_attribute_methods
+ # Created in the inherited/included hook for "proper" ARs
+ @attribute_methods_mutex ||= Mutex.new
+
+ super
+ end
def self.column_names
%w{ one two three }
@@ -33,9 +44,6 @@ module ActiveRecord
[name, FakeColumn.new(name)]
}]
end
-
- def self.serialized_attributes; {}; end
- def self.base_class; self; end
end
end
diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb
index b1b41fed0d..d6de668a17 100644
--- a/activerecord/test/cases/attribute_methods_test.rb
+++ b/activerecord/test/cases/attribute_methods_test.rb
@@ -35,6 +35,30 @@ class AttributeMethodsTest < ActiveRecord::TestCase
assert !t.attribute_present?("content")
end
+ def test_attribute_present_with_booleans
+ b1 = Boolean.new
+ b1.value = false
+ assert b1.attribute_present?(:value)
+
+ b2 = Boolean.new
+ b2.value = true
+ assert b2.attribute_present?(:value)
+
+ b3 = Boolean.new
+ assert !b3.attribute_present?(:value)
+
+ b4 = Boolean.new
+ b4.value = false
+ b4.save!
+ assert Boolean.find(b4.id).attribute_present?(:value)
+ end
+
+ def test_caching_nil_primary_key
+ klass = Class.new(Minimalistic)
+ klass.expects(:reset_primary_key).returns(nil).once
+ 2.times { klass.primary_key }
+ end
+
def test_attribute_keys_on_new_instance
t = Topic.new
assert_equal nil, t.title, "The topics table has a title column, so it should be nil"
@@ -78,7 +102,6 @@ class AttributeMethodsTest < ActiveRecord::TestCase
def test_respond_to?
topic = Topic.find(1)
assert_respond_to topic, "title"
- assert_respond_to topic, "_title"
assert_respond_to topic, "title?"
assert_respond_to topic, "title="
assert_respond_to topic, :title
@@ -95,9 +118,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
assert_not_nil keyboard.key_number
assert_equal keyboard.key_number, keyboard.id
assert keyboard.respond_to?('key_number')
- assert keyboard.respond_to?('_key_number')
assert keyboard.respond_to?('id')
- assert keyboard.respond_to?('_id')
end
# Syck calls respond_to? before actually calling initialize
@@ -237,8 +258,14 @@ class AttributeMethodsTest < ActiveRecord::TestCase
topic.send(:write_attribute, :title, "Still another topic")
assert_equal "Still another topic", topic.title
- topic.send(:write_attribute, "title", "Still another topic: part 2")
+ topic[:title] = "Still another topic: part 2"
assert_equal "Still another topic: part 2", topic.title
+
+ topic.send(:write_attribute, "title", "Still another topic: part 3")
+ assert_equal "Still another topic: part 3", topic.title
+
+ topic["title"] = "Still another topic: part 4"
+ assert_equal "Still another topic: part 4", topic.title
end
def test_read_attribute
@@ -291,6 +318,39 @@ class AttributeMethodsTest < ActiveRecord::TestCase
# puts ""
end
+ def test_overridden_write_attribute
+ topic = Topic.new
+ def topic.write_attribute(attr_name, value)
+ super(attr_name, value.downcase)
+ end
+
+ topic.send(:write_attribute, :title, "Yet another topic")
+ assert_equal "yet another topic", topic.title
+
+ topic[:title] = "Yet another topic: part 2"
+ assert_equal "yet another topic: part 2", topic.title
+
+ topic.send(:write_attribute, "title", "Yet another topic: part 3")
+ assert_equal "yet another topic: part 3", topic.title
+
+ topic["title"] = "Yet another topic: part 4"
+ assert_equal "yet another topic: part 4", topic.title
+ end
+
+ def test_overridden_read_attribute
+ topic = Topic.new
+ topic.title = "Stop changing the topic"
+ def topic.read_attribute(attr_name)
+ super(attr_name).upcase
+ end
+
+ assert_equal "STOP CHANGING THE TOPIC", topic.send(:read_attribute, "title")
+ assert_equal "STOP CHANGING THE TOPIC", topic["title"]
+
+ assert_equal "STOP CHANGING THE TOPIC", topic.send(:read_attribute, :title)
+ assert_equal "STOP CHANGING THE TOPIC", topic[:title]
+ end
+
def test_read_overridden_attribute
topic = Topic.new(:title => 'a')
def topic.title() 'b' end
@@ -489,6 +549,14 @@ class AttributeMethodsTest < ActiveRecord::TestCase
end
end
+ def test_write_time_to_date_attributes
+ in_time_zone "Pacific Time (US & Canada)" do
+ record = @target.new
+ record.last_read = Time.utc(2010, 1, 1, 10)
+ assert_equal Date.civil(2010, 1, 1), record.last_read
+ end
+ end
+
def test_time_attributes_are_retrieved_in_current_time_zone
in_time_zone "Pacific Time (US & Canada)" do
utc_time = Time.utc(2008, 1, 1)
@@ -524,6 +592,17 @@ class AttributeMethodsTest < ActiveRecord::TestCase
end
end
+ def test_setting_time_zone_aware_read_attribute
+ utc_time = Time.utc(2008, 1, 1)
+ cst_time = utc_time.in_time_zone("Central Time (US & Canada)")
+ in_time_zone "Pacific Time (US & Canada)" do
+ record = @target.create(:written_on => cst_time).reload
+ assert_equal utc_time, record[:written_on]
+ assert_equal ActiveSupport::TimeZone["Pacific Time (US & Canada)"], record[:written_on].time_zone
+ assert_equal Time.utc(2007, 12, 31, 16), record[:written_on].time
+ end
+ end
+
def test_setting_time_zone_aware_attribute_with_string
utc_time = Time.utc(2008, 1, 1)
(-11..13).each do |timezone_offset|
@@ -543,6 +622,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
record = @target.new
record.written_on = ' '
assert_nil record.written_on
+ assert_nil record[:written_on]
end
end
@@ -657,7 +737,7 @@ class AttributeMethodsTest < ActiveRecord::TestCase
topic = Topic.new(:id => 5)
topic.id = 5
- topic.method(:id).owner.send(:remove_method, :id)
+ topic.method(:id).owner.send(:undef_method, :id)
assert_deprecated do
assert_equal 5, topic.id
@@ -666,19 +746,39 @@ class AttributeMethodsTest < ActiveRecord::TestCase
Topic.undefine_attribute_methods
end
+ def test_read_attribute_with_nil_should_not_asplode
+ assert_equal nil, Topic.new.read_attribute(nil)
+ end
+
+ # If B < A, and A defines an accessor for 'foo', we don't want to override
+ # that by defining a 'foo' method in the generated methods module for B.
+ # (That module will be inserted between the two, e.g. [B, <GeneratedAttributes>, A].)
+ def test_inherited_custom_accessors
+ klass = Class.new(ActiveRecord::Base) do
+ self.table_name = "topics"
+ self.abstract_class = true
+ def title; "omg"; end
+ def title=(val); self.author_name = val; end
+ end
+ subklass = Class.new(klass)
+ [klass, subklass].each(&:define_attribute_methods)
+
+ topic = subklass.find(1)
+ assert_equal "omg", topic.title
+
+ topic.title = "lol"
+ assert_equal "lol", topic.author_name
+ end
+
private
def cached_columns
- @cached_columns ||= (time_related_columns_on_topic + serialized_columns_on_topic).map(&:name)
+ @cached_columns ||= time_related_columns_on_topic.map(&:name)
end
def time_related_columns_on_topic
Topic.columns.select { |c| c.type.in?([:time, :date, :datetime, :timestamp]) }
end
- def serialized_columns_on_topic
- Topic.columns.select { |c| Topic.serialized_attributes.include?(c.name) }
- end
-
def in_time_zone(zone)
old_zone = Time.zone
old_tz = ActiveRecord::Base.time_zone_aware_attributes
diff --git a/activerecord/test/cases/autosave_association_test.rb b/activerecord/test/cases/autosave_association_test.rb
index 4ad2cdfc7e..1376810adf 100644
--- a/activerecord/test/cases/autosave_association_test.rb
+++ b/activerecord/test/cases/autosave_association_test.rb
@@ -347,6 +347,17 @@ class TestDefaultAutosaveAssociationOnABelongsToAssociation < ActiveRecord::Test
client.save!
assert_no_queries { assert_equal apple, client.firm }
end
+
+ def test_validation_does_not_validate_stale_association_target
+ valid_developer = Developer.create!(:name => "Dude", :salary => 50_000)
+ invalid_developer = Developer.new()
+
+ auditlog = AuditLog.new(:message => "foo")
+ auditlog.developer = invalid_developer
+ auditlog.developer_id = valid_developer.id
+
+ assert auditlog.valid?
+ end
end
class TestDefaultAutosaveAssociationOnAHasManyAssociation < ActiveRecord::TestCase
@@ -575,6 +586,7 @@ class TestDestroyAsPartOfAutosaveAssociation < ActiveRecord::TestCase
self.use_transactional_fixtures = false unless supports_savepoints?
def setup
+ super
@pirate = Pirate.create(:catchphrase => "Don' botharrr talkin' like one, savvy?")
@ship = @pirate.create_ship(:name => 'Nights Dirty Lightning')
end
@@ -897,6 +909,7 @@ class TestAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCase
self.use_transactional_fixtures = false unless supports_savepoints?
def setup
+ super
@pirate = Pirate.create(:catchphrase => "Don' botharrr talkin' like one, savvy?")
@ship = @pirate.create_ship(:name => 'Nights Dirty Lightning')
end
@@ -1020,6 +1033,7 @@ class TestAutosaveAssociationOnABelongsToAssociation < ActiveRecord::TestCase
self.use_transactional_fixtures = false unless supports_savepoints?
def setup
+ super
@ship = Ship.create(:name => 'Nights Dirty Lightning')
@pirate = @ship.create_pirate(:catchphrase => "Don' botharrr talkin' like one, savvy?")
end
@@ -1268,6 +1282,7 @@ class TestAutosaveAssociationOnAHasManyAssociation < ActiveRecord::TestCase
self.use_transactional_fixtures = false unless supports_savepoints?
def setup
+ super
@association_name = :birds
@pirate = Pirate.create(:catchphrase => "Don' botharrr talkin' like one, savvy?")
@@ -1282,6 +1297,7 @@ class TestAutosaveAssociationOnAHasAndBelongsToManyAssociation < ActiveRecord::T
self.use_transactional_fixtures = false unless supports_savepoints?
def setup
+ super
@association_name = :parrots
@habtm = true
@@ -1297,6 +1313,7 @@ class TestAutosaveAssociationValidationsOnAHasManyAssociation < ActiveRecord::Te
self.use_transactional_fixtures = false unless supports_savepoints?
def setup
+ super
@pirate = Pirate.create(:catchphrase => "Don' botharrr talkin' like one, savvy?")
@pirate.birds.create(:name => 'cookoo')
end
@@ -1313,6 +1330,7 @@ class TestAutosaveAssociationValidationsOnAHasOneAssociation < ActiveRecord::Tes
self.use_transactional_fixtures = false unless supports_savepoints?
def setup
+ super
@pirate = Pirate.create(:catchphrase => "Don' botharrr talkin' like one, savvy?")
@pirate.create_ship(:name => 'titanic')
super
@@ -1335,6 +1353,7 @@ class TestAutosaveAssociationValidationsOnABelongsToAssociation < ActiveRecord::
self.use_transactional_fixtures = false unless supports_savepoints?
def setup
+ super
@pirate = Pirate.create(:catchphrase => "Don' botharrr talkin' like one, savvy?")
end
@@ -1355,6 +1374,7 @@ class TestAutosaveAssociationValidationsOnAHABTMAssociation < ActiveRecord::Test
self.use_transactional_fixtures = false unless supports_savepoints?
def setup
+ super
@pirate = Pirate.create(:catchphrase => "Don' botharrr talkin' like one, savvy?")
end
@@ -1377,6 +1397,7 @@ class TestAutosaveAssociationValidationMethodsGeneration < ActiveRecord::TestCas
self.use_transactional_fixtures = false unless supports_savepoints?
def setup
+ super
@pirate = Pirate.new
end
diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb
index 12c1cfb30e..d70525b57d 100644
--- a/activerecord/test/cases/base_test.rb
+++ b/activerecord/test/cases/base_test.rb
@@ -23,10 +23,18 @@ require 'models/edge'
require 'models/joke'
require 'models/bulb'
require 'models/bird'
+require 'models/teapot'
require 'rexml/document'
require 'active_support/core_ext/exception'
require 'bcrypt'
+class FirstAbstractClass < ActiveRecord::Base
+ self.abstract_class = true
+end
+class SecondAbstractClass < FirstAbstractClass
+ self.abstract_class = true
+end
+class Photo < SecondAbstractClass; end
class Category < ActiveRecord::Base; end
class Categorization < ActiveRecord::Base; end
class Smarts < ActiveRecord::Base; end
@@ -69,6 +77,16 @@ end
class BasicsTest < ActiveRecord::TestCase
fixtures :topics, :companies, :developers, :projects, :computers, :accounts, :minimalistics, 'warehouse-things', :authors, :categorizations, :categories, :posts
+ 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[/[^:]*$/]
@@ -169,6 +187,31 @@ class BasicsTest < ActiveRecord::TestCase
end
end
+ def test_previously_changed
+ topic = Topic.find :first
+ topic.title = '<3<3<3'
+ assert_equal({}, topic.previous_changes)
+
+ topic.save!
+ expected = ["The First Topic", "<3<3<3"]
+ assert_equal(expected, topic.previous_changes['title'])
+ end
+
+ def test_previously_changed_dup
+ topic = Topic.find :first
+ topic.title = '<3<3<3'
+ topic.save!
+
+ t2 = topic.dup
+
+ assert_equal(topic.previous_changes, t2.previous_changes)
+
+ topic.title = "lolwut"
+ topic.save!
+
+ assert_not_equal(topic.previous_changes, t2.previous_changes)
+ end
+
def test_preserving_time_objects
assert_kind_of(
Time, Topic.find(1).bonus_time,
@@ -561,10 +604,12 @@ class BasicsTest < ActiveRecord::TestCase
weird = Weird.create('a$b' => 'value')
weird.reload
assert_equal 'value', weird.send('a$b')
+ assert_equal 'value', weird.read_attribute('a$b')
weird.update_column('a$b', 'value2')
weird.reload
assert_equal 'value2', weird.send('a$b')
+ assert_equal 'value2', weird.read_attribute('a$b')
end
def test_multiparameter_attributes_on_date
@@ -644,7 +689,7 @@ class BasicsTest < ActiveRecord::TestCase
}
topic = Topic.find(1)
topic.attributes = attributes
- assert_equal Time.local(2004, 6, 24, 16, 24, 0), topic.written_on
+ assert_equal Time.utc(2004, 6, 24, 16, 24, 0), topic.written_on
end
def test_multiparameter_attributes_on_time_with_no_date
@@ -893,7 +938,7 @@ class BasicsTest < ActiveRecord::TestCase
}
topic = Topic.find(1)
topic.attributes = attributes
- assert_equal Time.local(2000, 1, 1, 5, 42, 0), topic.bonus_time
+ assert_equal Time.utc(2000, 1, 1, 5, 42, 0), topic.bonus_time
end
def test_boolean
@@ -947,10 +992,9 @@ class BasicsTest < ActiveRecord::TestCase
assert_equal "b", duped_topic.title
# test if the attribute values have been duped
- topic.title = {"a" => "b"}
duped_topic = topic.dup
- duped_topic.title["a"] = "c"
- assert_equal "b", topic.title["a"]
+ duped_topic.title.replace "c"
+ assert_equal "a", topic.title
# test if attributes set as part of after_initialize are duped correctly
assert_equal topic.author_email_address, duped_topic.author_email_address
@@ -961,8 +1005,7 @@ class BasicsTest < ActiveRecord::TestCase
assert_not_equal duped_topic.id, topic.id
duped_topic.reload
- # FIXME: I think this is poor behavior, and will fix it with #5686
- assert_equal({'a' => 'c'}.to_yaml, duped_topic.title)
+ assert_equal("c", duped_topic.title)
end
def test_dup_with_aggregate_of_same_name_as_attribute
@@ -1061,7 +1104,10 @@ 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
@@ -1185,19 +1231,6 @@ class BasicsTest < ActiveRecord::TestCase
assert(auto.id > 0)
end
- def quote_column_name(name)
- "<#{name}>"
- end
-
- def test_quote_keys
- ar = AutoId.new
- source = {"foo" => "bar", "baz" => "quux"}
- actual = ar.send(:quote_columns, self, source)
- inverted = actual.invert
- assert_equal("<foo>", inverted["bar"])
- assert_equal("<baz>", inverted["quux"])
- end
-
def test_sql_injection_via_find
assert_raise(ActiveRecord::RecordNotFound, ActiveRecord::StatementInvalid) do
Topic.find("123456 OR id > 0")
@@ -1235,6 +1268,27 @@ class BasicsTest < ActiveRecord::TestCase
assert_equal(myobj, topic.content)
end
+ def test_serialized_attribute_in_base_class
+ Topic.serialize("content", Hash)
+
+ hash = { 'content1' => 'value1', 'content2' => 'value2' }
+ important_topic = ImportantTopic.create("content" => hash)
+ assert_equal(hash, important_topic.content)
+
+ important_topic.reload
+ assert_equal(hash, important_topic.content)
+ end
+
+ def test_serialized_attribute_declared_in_subclass
+ hash = { 'important1' => 'value1', 'important2' => 'value2' }
+ important_topic = ImportantTopic.create("important" => hash)
+ assert_equal(hash, important_topic.important)
+
+ important_topic.reload
+ assert_equal(hash, important_topic.important)
+ assert_equal(hash, important_topic.read_attribute(:important))
+ end
+
def test_serialized_time_attribute
myobj = Time.local(2008,1,1,1,0)
topic = Topic.create("content" => myobj).reload
@@ -1247,11 +1301,24 @@ class BasicsTest < ActiveRecord::TestCase
assert_equal(myobj, topic.content)
end
- def test_nil_serialized_attribute_with_class_constraint
+ def test_nil_serialized_attribute_without_class_constraint
topic = Topic.new
assert_nil topic.content
end
+ def test_nil_not_serialized_without_class_constraint
+ assert Topic.new(:content => nil).save
+ assert_equal 1, Topic.where(:content => nil).count
+ end
+
+ def test_nil_not_serialized_with_class_constraint
+ Topic.serialize :content, Hash
+ assert Topic.new(:content => nil).save
+ assert_equal 1, Topic.where(:content => nil).count
+ ensure
+ Topic.serialize(:content)
+ end
+
def test_should_raise_exception_on_serialized_attribute_with_type_mismatch
myobj = MyObject.new('value1', 'value2')
topic = Topic.new(:content => myobj)
@@ -1359,22 +1426,6 @@ class BasicsTest < ActiveRecord::TestCase
assert_equal author_name, Topic.find(topic.id).author_name
end
- if RUBY_VERSION < '1.9'
- def test_quote_chars
- with_kcode('UTF8') do
- str = 'The Narrator'
- topic = Topic.create(:author_name => str)
- assert_equal str, topic.author_name
-
- assert_kind_of ActiveSupport::Multibyte.proxy_class, str.mb_chars
- topic = Topic.find_by_author_name(str.mb_chars)
-
- assert_kind_of Topic, topic
- assert_equal str, topic.author_name, "The right topic should have been found by name even with name passed as Chars"
- end
- end
- end
-
def test_toggle_attribute
assert !topics(:first).approved?
topics(:first).toggle!(:approved)
@@ -1401,86 +1452,48 @@ class BasicsTest < ActiveRecord::TestCase
assert_equal dev, dev.reload
end
- def test_define_attr_method_with_value
- k = Class.new( ActiveRecord::Base )
- k.send(:define_attr_method, :table_name, "foo")
- assert_equal "foo", k.table_name
- end
-
- def test_define_attr_method_with_block
- k = Class.new( ActiveRecord::Base ) do
- class << self
- attr_accessor :foo_key
- end
- end
- k.foo_key = "id"
- k.send(:define_attr_method, :foo_key) { "sys_" + original_foo_key }
- assert_equal "sys_id", k.foo_key
- end
-
- def test_set_table_name_with_value
- k = Class.new( ActiveRecord::Base )
- k.table_name = "foo"
- assert_equal "foo", k.table_name
- k.set_table_name "bar"
- assert_equal "bar", k.table_name
- end
-
def test_switching_between_table_name
assert_difference("GoodJoke.count") do
- Joke.set_table_name "cold_jokes"
+ Joke.table_name = "cold_jokes"
Joke.create
- Joke.set_table_name "funny_jokes"
+ Joke.table_name = "funny_jokes"
Joke.create
end
end
+ def test_set_table_name_symbol_converted_to_string
+ Joke.table_name = :cold_jokes
+ assert_equal 'cold_jokes', Joke.table_name
+ end
+
def test_quoted_table_name_after_set_table_name
klass = Class.new(ActiveRecord::Base)
- klass.set_table_name "foo"
+ klass.table_name = "foo"
assert_equal "foo", klass.table_name
assert_equal klass.connection.quote_table_name("foo"), klass.quoted_table_name
- klass.set_table_name "bar"
+ klass.table_name = "bar"
assert_equal "bar", klass.table_name
assert_equal klass.connection.quote_table_name("bar"), klass.quoted_table_name
end
- def test_set_table_name_with_block
- k = Class.new( ActiveRecord::Base )
- k.set_table_name { "ks" }
- assert_equal "ks", k.table_name
- end
-
- def test_set_primary_key_with_value
- k = Class.new( ActiveRecord::Base )
- k.primary_key = "foo"
- assert_equal "foo", k.primary_key
- k.set_primary_key "bar"
- assert_equal "bar", k.primary_key
- end
-
- def test_set_primary_key_with_block
- k = Class.new( ActiveRecord::Base )
- k.primary_key = 'id'
- k.set_primary_key { "sys_" + original_primary_key }
- assert_equal "sys_id", k.primary_key
- end
-
- def test_set_inheritance_column_with_value
+ def test_set_table_name_with_inheritance
k = Class.new( ActiveRecord::Base )
- k.inheritance_column = "foo"
- assert_equal "foo", k.inheritance_column
- k.set_inheritance_column "bar"
- assert_equal "bar", k.inheritance_column
+ def k.name; "Foo"; end
+ def k.table_name; super + "ks"; end
+ assert_equal "foosks", k.table_name
end
- def test_set_inheritance_column_with_block
- k = Class.new( ActiveRecord::Base )
- k.set_inheritance_column { original_inheritance_column + "_id" }
- assert_equal "type_id", k.inheritance_column
+ def test_sequence_name_with_abstract_class
+ ak = Class.new(ActiveRecord::Base)
+ ak.abstract_class = true
+ k = Class.new(ak)
+ k.table_name = "projects"
+ orig_name = k.sequence_name
+ return skip "sequences not supported by db" unless orig_name
+ assert_equal k.reset_sequence_name, orig_name
end
def test_count_with_join
@@ -1582,7 +1595,7 @@ class BasicsTest < ActiveRecord::TestCase
Developer.find(:all)
end
assert developers.size >= 2
- for i in 1...developers.size
+ (1...developers.size).each do |i|
assert developers[i-1].salary >= developers[i].salary
end
end
@@ -1673,10 +1686,7 @@ class BasicsTest < ActiveRecord::TestCase
end
def test_descends_from_active_record
- # Tries to call Object.abstract_class?
- assert_raise(NoMethodError) do
- ActiveRecord::Base.descends_from_active_record?
- end
+ assert !ActiveRecord::Base.descends_from_active_record?
# Abstract subclass of AR::Base.
assert LoosePerson.descends_from_active_record?
@@ -1699,6 +1709,10 @@ class BasicsTest < ActiveRecord::TestCase
# Concrete subclasses an abstract class which has a type column.
assert !SubStiPost.descends_from_active_record?
+
+ assert Teapot.descends_from_active_record?
+ assert !OtherTeapot.descends_from_active_record?
+ assert CoolTeapot.descends_from_active_record?
end
def test_find_on_abstract_base_class_doesnt_use_type_condition
@@ -1732,7 +1746,7 @@ class BasicsTest < ActiveRecord::TestCase
def test_inspect_instance
topic = topics(:first)
- assert_equal %(#<Topic id: 1, title: "The First Topic", author_name: "David", author_email_address: "david@loudthinking.com", written_on: "#{topic.written_on.to_s(:db)}", bonus_time: "#{topic.bonus_time.to_s(:db)}", last_read: "#{topic.last_read.to_s(:db)}", content: "Have a nice day", approved: false, replies_count: 1, parent_id: nil, parent_title: nil, type: nil, group: nil, created_at: "#{topic.created_at.to_s(:db)}", updated_at: "#{topic.updated_at.to_s(:db)}">), topic.inspect
+ assert_equal %(#<Topic id: 1, title: "The First Topic", author_name: "David", author_email_address: "david@loudthinking.com", written_on: "#{topic.written_on.to_s(:db)}", bonus_time: "#{topic.bonus_time.to_s(:db)}", last_read: "#{topic.last_read.to_s(:db)}", content: "Have a nice day", important: nil, approved: false, replies_count: 1, parent_id: nil, parent_title: nil, type: nil, group: nil, created_at: "#{topic.created_at.to_s(:db)}", updated_at: "#{topic.updated_at.to_s(:db)}">), topic.inspect
end
def test_inspect_new_instance
@@ -1761,10 +1775,18 @@ class BasicsTest < ActiveRecord::TestCase
assert_equal "The First Topic", topics(:first).becomes(Reply).title
end
+ def test_becomes_includes_errors
+ company = Company.new(:name => nil)
+ assert !company.valid?
+ original_errors = company.errors
+ client = company.becomes(Client)
+ assert_equal original_errors, client.errors
+ end
+
def test_silence_sets_log_level_to_error_in_block
original_logger = ActiveRecord::Base.logger
log = StringIO.new
- ActiveRecord::Base.logger = Logger.new(log)
+ ActiveRecord::Base.logger = ActiveSupport::Logger.new(log)
ActiveRecord::Base.logger.level = Logger::DEBUG
ActiveRecord::Base.silence do
ActiveRecord::Base.logger.warn "warn"
@@ -1778,7 +1800,7 @@ class BasicsTest < ActiveRecord::TestCase
def test_silence_sets_log_level_back_to_level_before_yield
original_logger = ActiveRecord::Base.logger
log = StringIO.new
- ActiveRecord::Base.logger = Logger.new(log)
+ ActiveRecord::Base.logger = ActiveSupport::Logger.new(log)
ActiveRecord::Base.logger.level = Logger::WARN
ActiveRecord::Base.silence do
end
@@ -1790,7 +1812,7 @@ class BasicsTest < ActiveRecord::TestCase
def test_benchmark_with_log_level
original_logger = ActiveRecord::Base.logger
log = StringIO.new
- ActiveRecord::Base.logger = Logger.new(log)
+ ActiveRecord::Base.logger = ActiveSupport::Logger.new(log)
ActiveRecord::Base.logger.level = Logger::WARN
ActiveRecord::Base.benchmark("Debug Topic Count", :level => :debug) { Topic.count }
ActiveRecord::Base.benchmark("Warn Topic Count", :level => :warn) { Topic.count }
@@ -1805,10 +1827,8 @@ class BasicsTest < ActiveRecord::TestCase
def test_benchmark_with_use_silence
original_logger = ActiveRecord::Base.logger
log = StringIO.new
- ActiveRecord::Base.logger = Logger.new(log)
- ActiveRecord::Base.benchmark("Logging", :level => :debug, :silence => true) { ActiveRecord::Base.logger.debug "Loud" }
+ ActiveRecord::Base.logger = ActiveSupport::Logger.new(log)
ActiveRecord::Base.benchmark("Logging", :level => :debug, :silence => false) { ActiveRecord::Base.logger.debug "Quiet" }
- assert_no_match(/Loud/, log.string)
assert_match(/Quiet/, log.string)
ensure
ActiveRecord::Base.logger = original_logger
@@ -1840,9 +1860,9 @@ class BasicsTest < ActiveRecord::TestCase
def test_clear_cache!
# preheat cache
- c1 = Post.columns
+ c1 = Post.connection.schema_cache.columns['posts']
ActiveRecord::Base.clear_cache!
- c2 = Post.columns
+ c2 = Post.connection.schema_cache.columns['posts']
assert_not_equal c1, c2
end
@@ -1859,11 +1879,6 @@ class BasicsTest < ActiveRecord::TestCase
end
def test_marshal_round_trip
- if ENV['TRAVIS'] && RUBY_VERSION == "1.8.7"
- return skip("Marshalling tests disabled for Ruby 1.8.7 on Travis CI due to what appears " \
- "to be a Ruby bug.")
- end
-
expected = posts(:welcome)
marshalled = Marshal.dump(expected)
actual = Marshal.load(marshalled)
@@ -1872,11 +1887,6 @@ class BasicsTest < ActiveRecord::TestCase
end
def test_marshal_new_record_round_trip
- if ENV['TRAVIS'] && RUBY_VERSION == "1.8.7"
- return skip("Marshalling tests disabled for Ruby 1.8.7 on Travis CI due to what appears " \
- "to be a Ruby bug.")
- end
-
marshalled = Marshal.dump(Post.new)
post = Marshal.load(marshalled)
@@ -1884,11 +1894,6 @@ class BasicsTest < ActiveRecord::TestCase
end
def test_marshalling_with_associations
- if ENV['TRAVIS'] && RUBY_VERSION == "1.8.7"
- return skip("Marshalling tests disabled for Ruby 1.8.7 on Travis CI due to what appears " \
- "to be a Ruby bug.")
- end
-
post = Post.new
post.comments.build
@@ -1907,7 +1912,7 @@ class BasicsTest < ActiveRecord::TestCase
assert_equal [], NonExistentTable.attribute_names
end
- def test_attribtue_names_on_abstract_class
+ def test_attribute_names_on_abstract_class
assert_equal [], AbstractCompany.attribute_names
end
@@ -1935,4 +1940,23 @@ class BasicsTest < ActiveRecord::TestCase
dev.update_attribute(:updated_at, nil)
assert_match(/\/#{dev.id}$/, dev.cache_key)
end
+
+ def test_uniq_delegates_to_scoped
+ scope = stub
+ Bird.stubs(:scoped).returns(mock(:uniq => scope))
+ assert_equal scope, Bird.uniq
+ end
+
+ def test_active_record_super
+ assert_equal ActiveRecord::Model, ActiveRecord::Base.active_record_super
+ assert_equal ActiveRecord::Base, Topic.active_record_super
+ assert_equal Topic, ImportantTopic.active_record_super
+ assert_equal ActiveRecord::Model, Teapot.active_record_super
+ assert_equal Teapot, OtherTeapot.active_record_super
+ assert_equal ActiveRecord::Model, CoolTeapot.active_record_super
+ end
+
+ def test_table_name_with_2_abstract_subclasses
+ assert_equal "photos", Photo.table_name
+ end
end
diff --git a/activerecord/test/cases/binary_test.rb b/activerecord/test/cases/binary_test.rb
index 06c14cb108..25d2896ab0 100644
--- a/activerecord/test/cases/binary_test.rb
+++ b/activerecord/test/cases/binary_test.rb
@@ -8,11 +8,11 @@ unless current_adapter?(:SybaseAdapter, :DB2Adapter, :FirebirdAdapter)
require 'models/binary'
class BinaryTest < ActiveRecord::TestCase
- FIXTURES = %w(flowers.jpg example.log)
+ FIXTURES = %w(flowers.jpg example.log test.txt)
def test_mixed_encoding
str = "\x80"
- str.force_encoding('ASCII-8BIT') if str.respond_to?(:force_encoding)
+ str.force_encoding('ASCII-8BIT')
binary = Binary.new :name => 'いただきます!', :data => str
binary.save!
@@ -23,7 +23,7 @@ unless current_adapter?(:SybaseAdapter, :DB2Adapter, :FirebirdAdapter)
# Mysql adapter doesn't properly encode things, so we have to do it
if current_adapter?(:MysqlAdapter)
- name.force_encoding('UTF-8') if name.respond_to?(:force_encoding)
+ name.force_encoding('UTF-8')
end
assert_equal 'いただきます!', name
end
@@ -33,7 +33,7 @@ unless current_adapter?(:SybaseAdapter, :DB2Adapter, :FirebirdAdapter)
FIXTURES.each do |filename|
data = File.read(ASSETS_ROOT + "/#{filename}")
- data.force_encoding('ASCII-8BIT') if data.respond_to?(:force_encoding)
+ data.force_encoding('ASCII-8BIT')
data.freeze
bin = Binary.new(:data => data)
diff --git a/activerecord/test/cases/calculations_test.rb b/activerecord/test/cases/calculations_test.rb
index c38814713a..7c9ebf528e 100644
--- a/activerecord/test/cases/calculations_test.rb
+++ b/activerecord/test/cases/calculations_test.rb
@@ -1,5 +1,6 @@
require "cases/helper"
require 'models/company'
+require "models/contract"
require 'models/topic'
require 'models/edge'
require 'models/club'
@@ -48,11 +49,11 @@ class CalculationsTest < ActiveRecord::TestCase
end
def test_should_get_maximum_of_field_with_include
- assert_equal 55, Account.maximum(:credit_limit, :include => :firm, :conditions => "companies.name != 'Summit'")
+ assert_equal 55, Account.maximum(:credit_limit, :include => :firm, :references => :companies, :conditions => "companies.name != 'Summit'")
end
def test_should_get_maximum_of_field_with_scoped_include
- Account.send :with_scope, :find => { :include => :firm, :conditions => "companies.name != 'Summit'" } do
+ Account.send :with_scope, :find => { :include => :firm, :references => :companies, :conditions => "companies.name != 'Summit'" } do
assert_equal 55, Account.maximum(:credit_limit)
end
end
@@ -269,7 +270,7 @@ class CalculationsTest < ActiveRecord::TestCase
end
def test_should_not_modify_options_when_using_includes
- options = {:conditions => 'companies.id > 1', :include => :firm}
+ options = {:conditions => 'companies.id > 1', :include => :firm, :references => :companies}
options_copy = options.dup
Account.count(:all, options)
@@ -446,4 +447,35 @@ class CalculationsTest < ActiveRecord::TestCase
distinct_authors_for_approved_count = Topic.group(:approved).count(:author_name, :distinct => true)[true]
assert_equal distinct_authors_for_approved_count, 2
end
+
+ def test_pluck
+ assert_equal [1,2,3,4], Topic.order(:id).pluck(:id)
+ end
+
+ def test_pluck_type_cast
+ topic = topics(:first)
+ relation = Topic.where(:id => topic.id)
+ assert_equal [ topic.approved ], relation.pluck(:approved)
+ assert_equal [ topic.last_read ], relation.pluck(:last_read)
+ assert_equal [ topic.written_on ], relation.pluck(:written_on)
+ end
+
+ def test_pluck_and_uniq
+ assert_equal [50, 53, 55, 60], Account.order(:credit_limit).uniq.pluck(:credit_limit)
+ end
+
+ def test_pluck_in_relation
+ company = Company.first
+ contract = company.contracts.create!
+ assert_equal [contract.id], company.contracts.pluck(:id)
+ end
+
+ def test_pluck_with_serialization
+ t = Topic.create!(:content => { :foo => :bar })
+ assert_equal [{:foo => :bar}], Topic.where(:id => t.id).pluck(:content)
+ end
+
+ def test_pluck_with_qualified_column_name
+ assert_equal [1,2,3,4], Topic.order(:id).pluck("topics.id")
+ end
end
diff --git a/activerecord/test/cases/callbacks_test.rb b/activerecord/test/cases/callbacks_test.rb
index 7f4d25790b..7690769226 100644
--- a/activerecord/test/cases/callbacks_test.rb
+++ b/activerecord/test/cases/callbacks_test.rb
@@ -1,7 +1,7 @@
require "cases/helper"
class CallbackDeveloper < ActiveRecord::Base
- set_table_name 'developers'
+ self.table_name = 'developers'
class << self
def callback_string(callback_method)
@@ -48,7 +48,7 @@ class CallbackDeveloperWithFalseValidation < CallbackDeveloper
end
class ParentDeveloper < ActiveRecord::Base
- set_table_name 'developers'
+ self.table_name = 'developers'
attr_accessor :after_save_called
before_validation {|record| record.after_save_called = true}
end
@@ -58,7 +58,7 @@ class ChildDeveloper < ParentDeveloper
end
class RecursiveCallbackDeveloper < ActiveRecord::Base
- set_table_name 'developers'
+ self.table_name = 'developers'
before_save :on_before_save
after_save :on_after_save
@@ -79,7 +79,7 @@ class RecursiveCallbackDeveloper < ActiveRecord::Base
end
class ImmutableDeveloper < ActiveRecord::Base
- set_table_name 'developers'
+ self.table_name = 'developers'
validates_inclusion_of :salary, :in => 50000..200000
@@ -98,7 +98,7 @@ class ImmutableDeveloper < ActiveRecord::Base
end
class ImmutableMethodDeveloper < ActiveRecord::Base
- set_table_name 'developers'
+ self.table_name = 'developers'
validates_inclusion_of :salary, :in => 50000..200000
@@ -118,7 +118,7 @@ class ImmutableMethodDeveloper < ActiveRecord::Base
end
class OnCallbacksDeveloper < ActiveRecord::Base
- set_table_name 'developers'
+ self.table_name = 'developers'
before_validation { history << :before_validation }
before_validation(:on => :create){ history << :before_validation_on_create }
@@ -138,7 +138,7 @@ class OnCallbacksDeveloper < ActiveRecord::Base
end
class CallbackCancellationDeveloper < ActiveRecord::Base
- set_table_name 'developers'
+ self.table_name = 'developers'
attr_reader :after_save_called, :after_create_called, :after_update_called, :after_destroy_called
attr_accessor :cancel_before_save, :cancel_before_create, :cancel_before_update, :cancel_before_destroy
diff --git a/activerecord/test/cases/column_test.rb b/activerecord/test/cases/column_test.rb
new file mode 100644
index 0000000000..ccc57cb876
--- /dev/null
+++ b/activerecord/test/cases/column_test.rb
@@ -0,0 +1,29 @@
+require "cases/helper"
+
+module ActiveRecord
+ module ConnectionAdapters
+ class ColumnTest < ActiveRecord::TestCase
+ def test_type_cast_boolean
+ column = Column.new("field", nil, "boolean")
+ assert column.type_cast(true)
+ assert column.type_cast(1)
+ assert column.type_cast('1')
+ assert column.type_cast('t')
+ assert column.type_cast('T')
+ assert column.type_cast('true')
+ assert column.type_cast('TRUE')
+ assert column.type_cast('on')
+ assert column.type_cast('ON')
+ assert !column.type_cast(false)
+ assert !column.type_cast(0)
+ assert !column.type_cast('0')
+ assert !column.type_cast('f')
+ assert !column.type_cast('F')
+ assert !column.type_cast('false')
+ assert !column.type_cast('FALSE')
+ assert !column.type_cast('off')
+ assert !column.type_cast('OFF')
+ end
+ end
+ end
+end
diff --git a/activerecord/test/cases/connection_adapters/abstract_adapter_test.rb b/activerecord/test/cases/connection_adapters/abstract_adapter_test.rb
new file mode 100644
index 0000000000..7dc6e8afcb
--- /dev/null
+++ b/activerecord/test/cases/connection_adapters/abstract_adapter_test.rb
@@ -0,0 +1,54 @@
+require "cases/helper"
+
+module ActiveRecord
+ module ConnectionAdapters
+ class AbstractAdapterTest < ActiveRecord::TestCase
+ attr_reader :adapter
+
+ def setup
+ @adapter = AbstractAdapter.new nil, nil
+ end
+
+ def test_in_use?
+ # FIXME: change to refute in Rails 4.0 / mt
+ assert !adapter.in_use?, 'adapter is not in use'
+ assert adapter.lease, 'lease adapter'
+ assert adapter.in_use?, 'adapter is in use'
+ end
+
+ def test_lease_twice
+ assert adapter.lease, 'should lease adapter'
+ assert !adapter.lease, 'should not lease adapter'
+ end
+
+ def test_last_use
+ assert !adapter.last_use
+ adapter.lease
+ assert adapter.last_use
+ end
+
+ def test_expire_mutates_in_use
+ assert adapter.lease, 'lease adapter'
+ assert adapter.in_use?, 'adapter is in use'
+ adapter.expire
+ assert !adapter.in_use?, 'adapter is in use'
+ end
+
+ def test_close
+ pool = ConnectionPool.new(ConnectionSpecification.new({}, nil))
+ pool.connections << adapter
+ adapter.pool = pool
+
+ # Make sure the pool marks the connection in use
+ assert_equal adapter, pool.connection
+ assert adapter.in_use?
+
+ # Close should put the adapter back in the pool
+ adapter.close
+ assert !adapter.in_use?
+
+ assert_equal adapter, pool.connection
+ end
+ end
+ end
+end
diff --git a/activerecord/test/cases/connection_adapters/connection_handler_test.rb b/activerecord/test/cases/connection_adapters/connection_handler_test.rb
index bd0d161838..dc99ac665c 100644
--- a/activerecord/test/cases/connection_adapters/connection_handler_test.rb
+++ b/activerecord/test/cases/connection_adapters/connection_handler_test.rb
@@ -8,6 +8,9 @@ module ActiveRecord
@handler.establish_connection 'america', Base.connection_pool.spec
@klass = Class.new do
def self.name; 'america'; end
+ class << self
+ alias active_record_super superclass
+ end
end
@subklass = Class.new(@klass) do
def self.name; 'north america'; end
@@ -40,8 +43,6 @@ module ActiveRecord
def test_retrieve_connection_pool_uses_superclass_pool_after_subclass_establish_and_remove
@handler.establish_connection 'north america', Base.connection_pool.spec
- assert_not_same @handler.retrieve_connection_pool(@klass),
- @handler.retrieve_connection_pool(@subklass)
@handler.remove_connection @subklass
assert_same @handler.retrieve_connection_pool(@klass),
diff --git a/activerecord/test/cases/connection_adapters/connection_specification_test.rb b/activerecord/test/cases/connection_adapters/connection_specification_test.rb
new file mode 100644
index 0000000000..ea2196cda2
--- /dev/null
+++ b/activerecord/test/cases/connection_adapters/connection_specification_test.rb
@@ -0,0 +1,12 @@
+require "cases/helper"
+
+module ActiveRecord
+ module ConnectionAdapters
+ class ConnectionSpecificationTest < ActiveRecord::TestCase
+ def test_dup_deep_copy_config
+ spec = ConnectionSpecification.new({ :a => :b }, "bar")
+ assert_not_equal(spec.config.object_id, spec.dup.config.object_id)
+ end
+ end
+ end
+end
diff --git a/activerecord/test/cases/connection_adapters/schema_cache_test.rb b/activerecord/test/cases/connection_adapters/schema_cache_test.rb
new file mode 100644
index 0000000000..42e39d534c
--- /dev/null
+++ b/activerecord/test/cases/connection_adapters/schema_cache_test.rb
@@ -0,0 +1,44 @@
+require "cases/helper"
+
+module ActiveRecord
+ module ConnectionAdapters
+ class SchemaCacheTest < ActiveRecord::TestCase
+ def setup
+ connection = ActiveRecord::Base.connection
+ @cache = SchemaCache.new connection
+ end
+
+ def test_primary_key
+ assert_equal 'id', @cache.primary_keys['posts']
+ end
+
+ def test_primary_key_for_non_existent_table
+ assert_nil @cache.primary_keys['omgponies']
+ end
+
+ def test_caches_columns
+ columns = @cache.columns['posts']
+ assert_equal columns, @cache.columns['posts']
+ end
+
+ def test_caches_columns_hash
+ columns_hash = @cache.columns_hash['posts']
+ assert_equal columns_hash, @cache.columns_hash['posts']
+ end
+
+ def test_clearing
+ @cache.columns['posts']
+ @cache.columns_hash['posts']
+ @cache.tables['posts']
+ @cache.primary_keys['posts']
+
+ @cache.clear!
+
+ assert_equal 0, @cache.columns.size
+ assert_equal 0, @cache.columns_hash.size
+ assert_equal 0, @cache.tables.size
+ assert_equal 0, @cache.primary_keys.size
+ end
+ end
+ end
+end
diff --git a/activerecord/test/cases/connection_management_test.rb b/activerecord/test/cases/connection_management_test.rb
index f554ceef35..496cd78136 100644
--- a/activerecord/test/cases/connection_management_test.rb
+++ b/activerecord/test/cases/connection_management_test.rb
@@ -1,4 +1,5 @@
require "cases/helper"
+require "rack"
module ActiveRecord
module ConnectionAdapters
@@ -25,40 +26,6 @@ module ActiveRecord
assert ActiveRecord::Base.connection_handler.active_connections?
end
- class FakeBase < ActiveRecord::Base
- def self.establish_connection spec
- String === spec ? super : spec
- end
- end
-
- def test_url_host_no_db
- spec = FakeBase.establish_connection 'postgres://foo?encoding=utf8'
- assert_equal({
- :adapter => "postgresql",
- :database => "",
- :host => "foo",
- :encoding => "utf8" }, spec)
- end
-
- def test_url_host_db
- spec = FakeBase.establish_connection 'postgres://foo/bar?encoding=utf8'
- assert_equal({
- :adapter => "postgresql",
- :database => "bar",
- :host => "foo",
- :encoding => "utf8" }, spec)
- end
-
- def test_url_port
- spec = FakeBase.establish_connection 'postgres://foo:123?encoding=utf8'
- assert_equal({
- :adapter => "postgresql",
- :database => "",
- :port => 123,
- :host => "foo",
- :encoding => "utf8" }, spec)
- end
-
def test_app_delegation
manager = ConnectionManagement.new(@app)
@@ -114,9 +81,10 @@ module ActiveRecord
test "proxy is polite to it's body and responds to it" do
body = Class.new(String) { def to_path; "/path"; end }.new
- proxy = ConnectionManagement::Proxy.new(body)
- assert proxy.respond_to?(:to_path)
- assert_equal proxy.to_path, "/path"
+ app = lambda { |_| [200, {}, body] }
+ response_body = ConnectionManagement.new(app).call(@env)[2]
+ assert response_body.respond_to?(:to_path)
+ assert_equal response_body.to_path, "/path"
end
end
end
diff --git a/activerecord/test/cases/connection_pool_test.rb b/activerecord/test/cases/connection_pool_test.rb
index 8a0f453127..2c69bfde5b 100644
--- a/activerecord/test/cases/connection_pool_test.rb
+++ b/activerecord/test/cases/connection_pool_test.rb
@@ -4,6 +4,8 @@ module ActiveRecord
module ConnectionAdapters
class ConnectionPoolTest < ActiveRecord::TestCase
def setup
+ super
+
# Keep a duplicate pool so we do not bother others
@pool = ConnectionPool.new ActiveRecord::Base.connection_pool.spec
@@ -18,73 +20,76 @@ module ActiveRecord
end
end
- def test_active_connection?
- assert !@pool.active_connection?
- assert @pool.connection
- assert @pool.active_connection?
- @pool.release_connection
- assert !@pool.active_connection?
+ def teardown
+ super
+ @pool.disconnect!
end
- def test_pool_caches_columns
- columns = @pool.columns['posts']
- assert_equal columns, @pool.columns['posts']
+ def test_full_pool_exception
+ assert_raises(PoolFullError) do
+ (@pool.size + 1).times do
+ @pool.checkout
+ end
+ end
end
- def test_pool_caches_columns_hash
- columns_hash = @pool.columns_hash['posts']
- assert_equal columns_hash, @pool.columns_hash['posts']
- end
+ def test_reap_and_active
+ @pool.checkout
+ @pool.checkout
+ @pool.checkout
+ @pool.timeout = 0
- def test_clearing_column_cache
- @pool.columns['posts']
- @pool.columns_hash['posts']
+ connections = @pool.connections.dup
- @pool.clear_cache!
+ @pool.reap
- assert_equal 0, @pool.columns.size
- assert_equal 0, @pool.columns_hash.size
+ assert_equal connections.length, @pool.connections.length
end
- def test_primary_key
- assert_equal 'id', @pool.primary_keys['posts']
- end
+ def test_reap_inactive
+ @pool.checkout
+ @pool.checkout
+ @pool.checkout
+ @pool.timeout = 0
- def test_primary_key_for_non_existent_table
- assert_equal 'id', @pool.primary_keys['omgponies']
- end
+ connections = @pool.connections.dup
+ connections.each do |conn|
+ conn.extend(Module.new { def active?; false; end; })
+ end
- def test_primary_key_is_set_on_columns
- posts_columns = @pool.columns_hash['posts']
- assert posts_columns['id'].primary
+ @pool.reap
- (posts_columns.keys - ['id']).each do |key|
- assert !posts_columns[key].primary
- end
+ assert_equal 0, @pool.connections.length
+ ensure
+ connections.each(&:close)
end
- def test_clear_stale_cached_connections!
- pool = ConnectionPool.new ActiveRecord::Base.connection_pool.spec
-
- threads = [
- Thread.new { pool.connection },
- Thread.new { pool.connection }]
+ def test_remove_connection
+ conn = @pool.checkout
+ assert conn.in_use?
- threads.map { |t| t.join }
+ length = @pool.connections.length
+ @pool.remove conn
+ assert conn.in_use?
+ assert_equal(length - 1, @pool.connections.length)
+ ensure
+ conn.close
+ end
- pool.extend Module.new {
- attr_accessor :checkins
- def checkin conn
- @checkins << conn
- conn.object_id
- end
- }
- pool.checkins = []
+ def test_remove_connection_for_thread
+ conn = @pool.connection
+ @pool.remove conn
+ assert_not_equal(conn, @pool.connection)
+ ensure
+ conn.close if conn
+ end
- cleared_threads = pool.clear_stale_cached_connections!
- assert((cleared_threads - threads.map { |x| x.object_id }).empty?,
- "threads should have been removed")
- assert_equal pool.checkins.length, threads.length
+ def test_active_connection?
+ assert !@pool.active_connection?
+ assert @pool.connection
+ assert @pool.active_connection?
+ @pool.release_connection
+ assert !@pool.active_connection?
end
def test_checkout_behaviour
@@ -96,24 +101,16 @@ module ActiveRecord
threads << Thread.new(i) do |pool_count|
connection = pool.connection
assert_not_nil connection
+ connection.close
end
end
- threads.each {|t| t.join}
+ threads.each(&:join)
Thread.new do
- threads.each do |t|
- thread_ids = pool.instance_variable_get(:@reserved_connections).keys
- assert thread_ids.include?(t.object_id)
- end
-
- pool.connection
- threads.each do |t|
- thread_ids = pool.instance_variable_get(:@reserved_connections).keys
- assert !thread_ids.include?(t.object_id)
- end
- end.join()
-
+ assert pool.connection
+ pool.connection.close
+ end.join
end
def test_automatic_reconnect=
diff --git a/activerecord/test/cases/connection_specification/resolver_test.rb b/activerecord/test/cases/connection_specification/resolver_test.rb
new file mode 100644
index 0000000000..e6cb1b9521
--- /dev/null
+++ b/activerecord/test/cases/connection_specification/resolver_test.rb
@@ -0,0 +1,41 @@
+require "cases/helper"
+
+module ActiveRecord
+ module ConnectionAdapters
+ class ConnectionSpecification
+ class ResolverTest < ActiveRecord::TestCase
+ def resolve(spec)
+ Resolver.new(spec, {}).spec.config
+ end
+
+ def test_url_host_no_db
+ spec = resolve 'mysql://foo?encoding=utf8'
+ assert_equal({
+ :adapter => "mysql",
+ :database => "",
+ :host => "foo",
+ :encoding => "utf8" }, spec)
+ end
+
+ def test_url_host_db
+ spec = resolve 'mysql://foo/bar?encoding=utf8'
+ assert_equal({
+ :adapter => "mysql",
+ :database => "bar",
+ :host => "foo",
+ :encoding => "utf8" }, spec)
+ end
+
+ def test_url_port
+ spec = resolve 'mysql://foo:123?encoding=utf8'
+ assert_equal({
+ :adapter => "mysql",
+ :database => "",
+ :port => 123,
+ :host => "foo",
+ :encoding => "utf8" }, spec)
+ end
+ end
+ end
+ end
+end
diff --git a/activerecord/test/cases/counter_cache_test.rb b/activerecord/test/cases/counter_cache_test.rb
index 3ed96a3ec8..cd3d19e783 100644
--- a/activerecord/test/cases/counter_cache_test.rb
+++ b/activerecord/test/cases/counter_cache_test.rb
@@ -6,9 +6,11 @@ require 'models/engine'
require 'models/reply'
require 'models/category'
require 'models/categorization'
+require 'models/dog'
+require 'models/dog_lover'
class CounterCacheTest < ActiveRecord::TestCase
- fixtures :topics, :categories, :categorizations, :cars
+ fixtures :topics, :categories, :categorizations, :cars, :dogs, :dog_lovers
class ::SpecialTopic < ::Topic
has_many :special_replies, :foreign_key => 'parent_id'
@@ -61,7 +63,7 @@ class CounterCacheTest < ActiveRecord::TestCase
end
end
- test "reset counter should with belongs_to which has class_name" do
+ test "reset counter with belongs_to which has class_name" do
car = cars(:honda)
assert_nothing_raised do
Car.reset_counters(car.id, :engines)
@@ -71,6 +73,20 @@ class CounterCacheTest < ActiveRecord::TestCase
end
end
+ test "reset the right counter if two have the same class_name" do
+ david = dog_lovers(:david)
+
+ DogLover.increment_counter(:bred_dogs_count, david.id)
+ DogLover.increment_counter(:trained_dogs_count, david.id)
+
+ assert_difference 'david.reload.bred_dogs_count', -1 do
+ DogLover.reset_counters(david.id, :bred_dogs)
+ end
+ assert_difference 'david.reload.trained_dogs_count', -1 do
+ DogLover.reset_counters(david.id, :trained_dogs)
+ end
+ end
+
test "update counter with initial null value" do
category = categories(:general)
assert_equal 2, category.categorizations.count
diff --git a/activerecord/test/cases/dup_test.rb b/activerecord/test/cases/dup_test.rb
index 0236f9b0a1..9705a11387 100644
--- a/activerecord/test/cases/dup_test.rb
+++ b/activerecord/test/cases/dup_test.rb
@@ -99,5 +99,13 @@ module ActiveRecord
assert_not_nil new_topic.created_at
end
+ def test_dup_after_initialize_callbacks
+ topic = Topic.new
+ assert Topic.after_initialize_called
+ Topic.after_initialize_called = false
+ topic.dup
+ assert Topic.after_initialize_called
+ end
+
end
end
diff --git a/activerecord/test/cases/explain_test.rb b/activerecord/test/cases/explain_test.rb
new file mode 100644
index 0000000000..83c9b6e107
--- /dev/null
+++ b/activerecord/test/cases/explain_test.rb
@@ -0,0 +1,115 @@
+require 'cases/helper'
+require 'models/car'
+require 'active_support/core_ext/string/strip'
+
+if ActiveRecord::Base.connection.supports_explain?
+ class ExplainTest < ActiveRecord::TestCase
+ fixtures :cars
+
+ def base
+ ActiveRecord::Base
+ end
+
+ def connection
+ base.connection
+ end
+
+ def test_logging_query_plan_with_logger
+ base.logger.expects(:warn).with do |message|
+ message.starts_with?('EXPLAIN for:')
+ end
+
+ with_threshold(0) do
+ Car.where(:name => 'honda').all
+ end
+ end
+
+ def test_logging_query_plan_without_logger
+ original = base.logger
+ base.logger = nil
+
+ base.logger.expects(:warn).never
+
+ with_threshold(0) do
+ car = Car.where(:name => 'honda').first
+ assert_equal 'honda', car.name
+ end
+ ensure
+ base.logger = original
+ end
+
+ def test_collect_queries_for_explain
+ base.auto_explain_threshold_in_seconds = nil
+ queries = Thread.current[:available_queries_for_explain] = []
+
+ with_threshold(0) do
+ Car.where(:name => 'honda').all
+ end
+
+ sql, binds = queries[0]
+ assert_match "SELECT", sql
+ assert_match "honda", sql
+ assert_equal [], binds
+ ensure
+ Thread.current[:available_queries_for_explain] = nil
+ end
+
+ def test_collecting_queries_for_explain
+ result, queries = ActiveRecord::Base.collecting_queries_for_explain do
+ Car.where(:name => 'honda').all
+ end
+
+ sql, binds = queries[0]
+ assert_match "SELECT", sql
+ assert_match "honda", sql
+ assert_equal [], binds
+ assert_equal [cars(:honda)], result
+ end
+
+ def test_exec_explain_with_no_binds
+ sqls = %w(foo bar)
+ binds = [[], []]
+ queries = sqls.zip(binds)
+
+ connection.stubs(:explain).returns('query plan foo', 'query plan bar')
+ expected = sqls.map {|sql| "EXPLAIN for: #{sql}\nquery plan #{sql}"}.join("\n")
+ assert_equal expected, base.exec_explain(queries)
+ end
+
+ def test_exec_explain_with_binds
+ cols = [Object.new, Object.new]
+ cols[0].expects(:name).returns('wadus')
+ cols[1].expects(:name).returns('chaflan')
+
+ sqls = %w(foo bar)
+ binds = [[[cols[0], 1]], [[cols[1], 2]]]
+ queries = sqls.zip(binds)
+
+ connection.stubs(:explain).returns("query plan foo\n", "query plan bar\n")
+ expected = <<-SQL.strip_heredoc
+ EXPLAIN for: #{sqls[0]} [["wadus", 1]]
+ query plan foo
+
+ EXPLAIN for: #{sqls[1]} [["chaflan", 2]]
+ query plan bar
+ SQL
+ assert_equal expected, base.exec_explain(queries)
+ end
+
+ def test_silence_auto_explain
+ base.expects(:collecting_sqls_for_explain).never
+ base.logger.expects(:warn).never
+ base.silence_auto_explain do
+ with_threshold(0) { Car.all }
+ end
+ end
+
+ def with_threshold(threshold)
+ current_threshold = base.auto_explain_threshold_in_seconds
+ base.auto_explain_threshold_in_seconds = threshold
+ yield
+ ensure
+ base.auto_explain_threshold_in_seconds = current_threshold
+ end
+ end
+end
diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb
index 3088ab012f..76c041397a 100644
--- a/activerecord/test/cases/finder_test.rb
+++ b/activerecord/test/cases/finder_test.rb
@@ -57,6 +57,11 @@ class FinderTest < ActiveRecord::TestCase
assert Topic.first.replies.exists?
end
+ # ensures +exists?+ runs valid SQL by excluding order value
+ def test_exists_with_order
+ assert Topic.order(:id).uniq.exists?
+ end
+
def test_does_not_exist_with_empty_table_and_no_args_given
Topic.delete_all
assert !Topic.exists?
@@ -369,6 +374,10 @@ class FinderTest < ActiveRecord::TestCase
assert_equal [1], Comment.find(:all, :conditions => { :id => 1..1, :post_id => 1..10 }).map(&:id).sort
end
+ def test_find_on_hash_conditions_with_array_of_integers_and_ranges
+ assert_equal [1,2,3,5,6,7,8,9], Comment.find(:all, :conditions => {:id => [1..2, 3, 5, 6..8, 9]}).map(&:id).sort
+ end
+
def test_find_on_multiple_hash_conditions
assert Topic.find(1, :conditions => { :author_name => "David", :title => "The First Topic", :replies_count => 1, :approved => false })
assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1, :conditions => { :author_name => "David", :title => "The First Topic", :replies_count => 1, :approved => true }) }
@@ -872,6 +881,17 @@ class FinderTest < ActiveRecord::TestCase
assert_equal 23, sig38.client_of
end
+ def test_find_or_create_from_two_attributes_and_hash
+ number_of_companies = Company.count
+ sig38 = Company.find_or_create_by_name_and_firm_id({:name => "38signals", :firm_id => 17, :client_of => 23})
+ assert_equal number_of_companies + 1, Company.count
+ assert_equal sig38, Company.find_or_create_by_name_and_firm_id({:name => "38signals", :firm_id => 17, :client_of => 23})
+ assert sig38.persisted?
+ assert_equal "38signals", sig38.name
+ assert_equal 17, sig38.firm_id
+ assert_equal 23, sig38.client_of
+ end
+
def test_find_or_create_from_one_aggregate_attribute
number_of_customers = Customer.count
created_customer = Customer.find_or_create_by_balance(Money.new(123))
@@ -1110,10 +1130,10 @@ class FinderTest < ActiveRecord::TestCase
end
def test_find_with_order_on_included_associations_with_construct_finder_sql_for_association_limiting_and_is_distinct
- assert_equal 2, Post.find(:all, :include => { :authors => :author_address }, :order => ' author_addresses.id DESC ', :limit => 2).size
+ assert_equal 2, Post.find(:all, :include => { :authors => :author_address }, :order => 'author_addresses.id DESC ', :limit => 2).size
assert_equal 3, Post.find(:all, :include => { :author => :author_address, :authors => :author_address},
- :order => ' author_addresses_authors.id DESC ', :limit => 3).size
+ :order => 'author_addresses_authors.id DESC ', :limit => 3).size
end
def test_find_with_nil_inside_set_passed_for_one_attribute
@@ -1140,7 +1160,10 @@ class FinderTest < ActiveRecord::TestCase
end
def test_with_limiting_with_custom_select
- posts = Post.find(:all, :include => :author, :select => ' posts.*, authors.id as "author_id"', :limit => 3, :order => 'posts.id')
+ posts = Post.find(
+ :all, :include => :author, :select => ' posts.*, authors.id as "author_id"',
+ :references => :authors, :limit => 3, :order => 'posts.id'
+ )
assert_equal 3, posts.size
assert_equal [0, 1, 1], posts.map(&:author_id).sort
end
@@ -1154,7 +1177,7 @@ class FinderTest < ActiveRecord::TestCase
end
def test_find_one_message_with_custom_primary_key
- Toy.set_primary_key :name
+ Toy.primary_key = :name
begin
Toy.find 'Hello World!'
rescue ActiveRecord::RecordNotFound => e
diff --git a/activerecord/test/cases/fixtures_test.rb b/activerecord/test/cases/fixtures_test.rb
index 866dcefbab..562b370c97 100644
--- a/activerecord/test/cases/fixtures_test.rb
+++ b/activerecord/test/cases/fixtures_test.rb
@@ -1,31 +1,34 @@
-require "cases/helper"
-require 'models/post'
+require 'cases/helper'
+require 'models/admin'
+require 'models/admin/account'
+require 'models/admin/randomly_named_c1'
+require 'models/admin/user'
require 'models/binary'
-require 'models/topic'
+require 'models/book'
+require 'models/category'
+require 'models/company'
require 'models/computer'
+require 'models/course'
require 'models/developer'
-require 'models/company'
-require 'models/task'
-require 'models/reply'
require 'models/joke'
-require 'models/course'
-require 'models/category'
+require 'models/matey'
require 'models/parrot'
require 'models/pirate'
-require 'models/treasure'
-require 'models/traffic_light'
-require 'models/matey'
+require 'models/post'
+require 'models/randomly_named_c1'
+require 'models/reply'
require 'models/ship'
-require 'models/book'
-require 'models/admin'
-require 'models/admin/account'
-require 'models/admin/user'
+require 'models/task'
+require 'models/topic'
+require 'models/traffic_light'
+require 'models/treasure'
require 'tempfile'
class FixturesTest < ActiveRecord::TestCase
self.use_instantiated_fixtures = true
self.use_transactional_fixtures = false
+ # other_topics fixture should not be included here
fixtures :topics, :developers, :accounts, :tasks, :categories, :funny_jokes, :binaries, :traffic_lights
FIXTURES = %w( accounts binaries companies customers
@@ -48,11 +51,11 @@ class FixturesTest < ActiveRecord::TestCase
def test_broken_yaml_exception
badyaml = Tempfile.new ['foo', '.yml']
- badyaml.write 'a: !ruby.yaml.org,2002:str |\nfoo'
+ badyaml.write 'a: : '
badyaml.flush
dir = File.dirname badyaml.path
- name =File.basename badyaml.path, '.yml'
+ name = File.basename badyaml.path, '.yml'
assert_raises(ActiveRecord::Fixture::FormatError) do
ActiveRecord::Fixtures.create_fixtures(dir, name)
end
@@ -62,8 +65,9 @@ class FixturesTest < ActiveRecord::TestCase
end
def test_create_fixtures
- ActiveRecord::Fixtures.create_fixtures(FIXTURES_ROOT, "parrots")
- assert Parrot.find_by_name('Curious George'), 'George is in the database'
+ fixtures = ActiveRecord::Fixtures.create_fixtures(FIXTURES_ROOT, "parrots")
+ assert Parrot.find_by_name('Curious George'), 'George is not in the database'
+ assert fixtures.detect { |f| f.name == 'parrots' }, "no fixtures named 'parrots' in #{fixtures.map(&:name).inspect}"
end
def test_multiple_clean_fixtures
@@ -73,6 +77,13 @@ class FixturesTest < ActiveRecord::TestCase
fixtures_array.each { |fixtures| assert_kind_of(ActiveRecord::Fixtures, fixtures) }
end
+ def test_create_symbol_fixtures
+ fixtures = ActiveRecord::Fixtures.create_fixtures(FIXTURES_ROOT, :collections, :collections => Course) { Course.connection }
+
+ assert Course.find_by_name('Collection'), 'course is not in the database'
+ assert fixtures.detect { |f| f.name == 'collections' }, "no fixtures named 'collections' in #{fixtures.map(&:name).inspect}"
+ end
+
def test_attributes
topics = create_fixtures("topics").first
assert_equal("The First Topic", topics["first"]["title"])
@@ -93,7 +104,7 @@ class FixturesTest < ActiveRecord::TestCase
# Reset cache to make finds on the new table work
ActiveRecord::Fixtures.reset_cache
- ActiveRecord::Base.connection.create_table :prefix_topics_suffix do |t|
+ ActiveRecord::Base.connection.create_table :prefix_other_topics_suffix do |t|
t.column :title, :string
t.column :author_name, :string
t.column :author_email_address, :string
@@ -115,23 +126,36 @@ class FixturesTest < ActiveRecord::TestCase
ActiveRecord::Base.table_name_prefix = 'prefix_'
ActiveRecord::Base.table_name_suffix = '_suffix'
- topics = create_fixtures("topics")
-
- first_row = ActiveRecord::Base.connection.select_one("SELECT * FROM prefix_topics_suffix WHERE author_name = 'David'")
- assert_equal("The First Topic", first_row["title"])
+ other_topic_klass = Class.new(ActiveRecord::Base) do
+ def self.name
+ "OtherTopic"
+ end
+ end
- second_row = ActiveRecord::Base.connection.select_one("SELECT * FROM prefix_topics_suffix WHERE author_name = 'Mary'")
- assert_nil(second_row["author_email_address"])
+ topics = [create_fixtures("other_topics")].flatten.first
# This checks for a caching problem which causes a bug in the fixtures
# class-level configuration helper.
assert_not_nil topics, "Fixture data inserted, but fixture objects not returned from create"
+
+ first_row = ActiveRecord::Base.connection.select_one("SELECT * FROM prefix_other_topics_suffix WHERE author_name = 'David'")
+ assert_not_nil first_row, "The prefix_other_topics_suffix table appears to be empty despite create_fixtures: the row with author_name = 'David' was not found"
+ assert_equal("The First Topic", first_row["title"])
+
+ second_row = ActiveRecord::Base.connection.select_one("SELECT * FROM prefix_other_topics_suffix WHERE author_name = 'Mary'")
+ assert_nil(second_row["author_email_address"])
+
+ assert_equal :prefix_other_topics_suffix, topics.table_name.to_sym
+ # This assertion should preferably be the last in the list, because calling
+ # other_topic_klass.table_name sets a class-level instance variable
+ assert_equal :prefix_other_topics_suffix, other_topic_klass.table_name.to_sym
+
ensure
# Restore prefix/suffix to its previous values
ActiveRecord::Base.table_name_prefix = old_prefix
ActiveRecord::Base.table_name_suffix = old_suffix
- ActiveRecord::Base.connection.drop_table :prefix_topics_suffix rescue nil
+ ActiveRecord::Base.connection.drop_table :prefix_other_topics_suffix rescue nil
end
end
@@ -179,7 +203,7 @@ class FixturesTest < ActiveRecord::TestCase
#sanity check to make sure that this file never exists
assert Dir[nonexistent_fixture_path+"*"].empty?
- assert_raise(FixturesFileNotFound) do
+ assert_raise(Errno::ENOENT) do
ActiveRecord::Fixtures.new( Account.connection, "companies", 'Company', nonexistent_fixture_path)
end
end
@@ -213,7 +237,7 @@ class FixturesTest < ActiveRecord::TestCase
def test_binary_in_fixtures
data = File.open(ASSETS_ROOT + "/flowers.jpg", 'rb') { |f| f.read }
- data.force_encoding('ASCII-8BIT') if data.respond_to?(:force_encoding)
+ data.force_encoding('ASCII-8BIT')
data.freeze
assert_equal data, @flowers.data
end
@@ -451,14 +475,36 @@ end
class CustomConnectionFixturesTest < ActiveRecord::TestCase
set_fixture_class :courses => Course
fixtures :courses
- # Set to false to blow away fixtures cache and ensure our fixtures are loaded
- # and thus takes into account our set_fixture_class
self.use_transactional_fixtures = false
def test_connection
assert_kind_of Course, courses(:ruby)
assert_equal Course.connection, courses(:ruby).connection
end
+
+ def test_leaky_destroy
+ assert_nothing_raised { courses(:ruby) }
+ courses(:ruby).destroy
+ end
+
+ def test_it_twice_in_whatever_order_to_check_for_fixture_leakage
+ test_leaky_destroy
+ end
+end
+
+class TransactionalFixturesOnCustomConnectionTest < ActiveRecord::TestCase
+ set_fixture_class :courses => Course
+ fixtures :courses
+ self.use_transactional_fixtures = true
+
+ def test_leaky_destroy
+ assert_nothing_raised { courses(:ruby) }
+ courses(:ruby).destroy
+ end
+
+ def test_it_twice_in_whatever_order_to_check_for_fixture_leakage
+ test_leaky_destroy
+ end
end
class InvalidTableNameFixturesTest < ActiveRecord::TestCase
@@ -496,7 +542,9 @@ class ManyToManyFixturesWithClassDefined < ActiveRecord::TestCase
end
class FixturesBrokenRollbackTest < ActiveRecord::TestCase
- def blank_setup; end
+ def blank_setup
+ @fixture_connections = [ActiveRecord::Base.connection]
+ end
alias_method :ar_setup_fixtures, :setup_fixtures
alias_method :setup_fixtures, :blank_setup
alias_method :setup, :blank_setup
@@ -549,7 +597,7 @@ class FasterFixturesTest < ActiveRecord::TestCase
load_extra_fixture('posts')
assert ActiveRecord::Fixtures.fixture_is_cached?(ActiveRecord::Base.connection, 'posts')
- self.class.setup_fixture_accessors('posts')
+ self.class.setup_fixture_accessors :posts
assert_equal 'Welcome to the weblog', posts(:welcome).title
end
end
@@ -707,3 +755,34 @@ class FixtureLoadingTest < ActiveRecord::TestCase
ActiveRecord::TestCase.try_to_load_dependency(:works_out_fine)
end
end
+
+class CustomNameForFixtureOrModelTest < ActiveRecord::TestCase
+ ActiveRecord::Fixtures.reset_cache
+
+ set_fixture_class :randomly_named_a9 =>
+ ClassNameThatDoesNotFollowCONVENTIONS,
+ :'admin/randomly_named_a9' =>
+ Admin::ClassNameThatDoesNotFollowCONVENTIONS,
+ 'admin/randomly_named_b0' =>
+ Admin::ClassNameThatDoesNotFollowCONVENTIONS
+
+ fixtures :randomly_named_a9, 'admin/randomly_named_a9',
+ :'admin/randomly_named_b0'
+
+ def test_named_accessor_for_randomly_named_fixture_and_class
+ assert_kind_of ClassNameThatDoesNotFollowCONVENTIONS,
+ randomly_named_a9(:first_instance)
+ end
+
+ def test_named_accessor_for_randomly_named_namespaced_fixture_and_class
+ assert_kind_of Admin::ClassNameThatDoesNotFollowCONVENTIONS,
+ admin_randomly_named_a9(:first_instance)
+ assert_kind_of Admin::ClassNameThatDoesNotFollowCONVENTIONS,
+ admin_randomly_named_b0(:second_instance)
+ end
+
+ def test_table_name_is_defined_in_the_model
+ assert_equal 'randomly_named_table', ActiveRecord::Fixtures::all_loaded_fixtures["admin/randomly_named_a9"].table_name
+ assert_equal 'randomly_named_table', Admin::ClassNameThatDoesNotFollowCONVENTIONS.table_name
+ end
+end
diff --git a/activerecord/test/cases/helper.rb b/activerecord/test/cases/helper.rb
index 6735bc521b..734d017b6e 100644
--- a/activerecord/test/cases/helper.rb
+++ b/activerecord/test/cases/helper.rb
@@ -2,12 +2,14 @@ require File.expand_path('../../../../load_paths', __FILE__)
require 'config'
-require 'test/unit'
+require 'minitest/autorun'
require 'stringio'
require 'mocha'
+require 'cases/test_case'
require 'active_record'
require 'active_support/dependencies'
+require 'active_support/logger'
require 'support/config'
require 'support/connection'
@@ -20,6 +22,9 @@ ActiveSupport::Deprecation.debug = true
# Enable Identity Map only when ENV['IM'] is set to "true"
ActiveRecord::IdentityMap.enabled = (ENV['IM'] == "true")
+# Avoid deprecation warning setting dependent_restrict_raises to false. The default is true
+ActiveRecord::Base.dependent_restrict_raises = false
+
# Connect to the database
ARTest.connect
@@ -68,14 +73,19 @@ module ActiveRecord
cattr_accessor :log
self.log = []
+ attr_reader :ignore
+
+ def initialize(ignore = Regexp.union(self.class.ignored_sql))
+ @ignore = ignore
+ end
+
def call(name, start, finish, message_id, values)
sql = values[:sql]
# FIXME: this seems bad. we should probably have a better way to indicate
# the query was cached
- unless 'CACHE' == values[:name]
- self.class.log << sql unless self.class.ignored_sql.any? { |r| sql =~ r }
- end
+ return if 'CACHE' == values[:name] || ignore =~ sql
+ self.class.log << sql
end
end
@@ -85,7 +95,10 @@ end
unless ENV['FIXTURE_DEBUG']
module ActiveRecord::TestFixtures::ClassMethods
def try_to_load_dependency_with_silence(*args)
- ActiveRecord::Base.logger.silence { try_to_load_dependency_without_silence(*args)}
+ old = ActiveRecord::Base.logger.level
+ ActiveRecord::Base.logger.level = ActiveSupport::Logger::ERROR
+ try_to_load_dependency_without_silence(*args)
+ ActiveRecord::Base.logger.level = old
end
alias_method_chain :try_to_load_dependency, :silence
diff --git a/activerecord/test/cases/identity_map/middleware_test.rb b/activerecord/test/cases/identity_map/middleware_test.rb
index 60dcad4586..5b32a1c6d2 100644
--- a/activerecord/test/cases/identity_map/middleware_test.rb
+++ b/activerecord/test/cases/identity_map/middleware_test.rb
@@ -1,4 +1,5 @@
require "cases/helper"
+require "rack"
module ActiveRecord
module IdentityMap
@@ -19,6 +20,7 @@ module ActiveRecord
called = false
mw = Middleware.new lambda { |env|
called = true
+ [200, {}, nil]
}
mw.call({})
assert called, 'middleware delegated'
@@ -27,6 +29,7 @@ module ActiveRecord
def test_im_enabled_during_delegation
mw = Middleware.new lambda { |env|
assert IdentityMap.enabled?, 'identity map should be enabled'
+ [200, {}, nil]
}
mw.call({})
end
diff --git a/activerecord/test/cases/inclusion_test.rb b/activerecord/test/cases/inclusion_test.rb
new file mode 100644
index 0000000000..8726ba5e51
--- /dev/null
+++ b/activerecord/test/cases/inclusion_test.rb
@@ -0,0 +1,113 @@
+require 'cases/helper'
+require 'models/teapot'
+
+class BasicInclusionModelTest < ActiveRecord::TestCase
+ def test_basic_model
+ Teapot.create!(:name => "Ronnie Kemper")
+ assert_equal "Ronnie Kemper", Teapot.find(1).name
+ end
+
+ def test_initialization
+ t = Teapot.new(:name => "Bob")
+ assert_equal "Bob", t.name
+ end
+
+ def test_inherited_model
+ teapot = CoolTeapot.create!(:name => "Bob")
+ teapot.reload
+
+ assert_equal "Bob", teapot.name
+ assert_equal "mmm", teapot.aaahhh
+ end
+
+ def test_generated_feature_methods
+ assert Teapot < Teapot::GeneratedFeatureMethods
+ end
+
+ def test_exists
+ t = Teapot.create!(:name => "Ronnie Kemper")
+ assert Teapot.exists?(t)
+ end
+
+ def test_predicate_builder
+ t = Teapot.create!(:name => "Bob")
+ assert_equal "Bob", Teapot.where(:id => [t]).first.name
+ assert_equal "Bob", Teapot.where(:id => t).first.name
+ end
+
+ def test_nested_model
+ assert_equal "ceiling_teapots", Ceiling::Teapot.table_name
+ end
+end
+
+class InclusionUnitTest < ActiveRecord::TestCase
+ def setup
+ @klass = Class.new { include ActiveRecord::Model }
+ end
+
+ def test_non_abstract_class
+ assert !@klass.abstract_class?
+ end
+
+ def test_abstract_class
+ @klass.abstract_class = true
+ assert @klass.abstract_class?
+ end
+
+ def test_establish_connection
+ assert @klass.respond_to?(:establish_connection)
+ assert ActiveRecord::Model.respond_to?(:establish_connection)
+ end
+
+ def test_adapter_connection
+ name = "#{ActiveRecord::Base.connection_config[:adapter]}_connection"
+ assert @klass.respond_to?(name)
+ assert ActiveRecord::Model.respond_to?(name)
+ end
+
+ def test_connection_handler
+ assert_equal ActiveRecord::Base.connection_handler, @klass.connection_handler
+ end
+
+ def test_mirrored_configuration
+ ActiveRecord::Base.time_zone_aware_attributes = true
+ assert @klass.time_zone_aware_attributes
+ ActiveRecord::Base.time_zone_aware_attributes = false
+ assert !@klass.time_zone_aware_attributes
+ ensure
+ ActiveRecord::Base.time_zone_aware_attributes = false
+ end
+
+ # Doesn't really test anything, but this is here to ensure warnings don't occur
+ def test_included_twice
+ @klass.send :include, ActiveRecord::Model
+ end
+
+ def test_deprecation_proxy
+ assert_equal ActiveRecord::Model.name, ActiveRecord::Model::DeprecationProxy.name
+ assert_equal ActiveRecord::Base.superclass, assert_deprecated { ActiveRecord::Model::DeprecationProxy.superclass }
+
+ sup, sup2 = nil, nil
+ ActiveSupport.on_load(:__test_active_record_model_deprecation) do
+ sup = superclass
+ sup2 = send(:superclass)
+ end
+ assert_deprecated do
+ ActiveSupport.run_load_hooks(:__test_active_record_model_deprecation, ActiveRecord::Model::DeprecationProxy)
+ end
+ assert_equal ActiveRecord::Base.superclass, sup
+ assert_equal ActiveRecord::Base.superclass, sup2
+ end
+end
+
+class InclusionFixturesTest < ActiveRecord::TestCase
+ fixtures :teapots
+
+ def test_fixtured_record
+ assert_equal "Bob", teapots(:bob).name
+ end
+
+ def test_timestamped_fixture
+ assert_not_nil teapots(:bob).created_at
+ end
+end
diff --git a/activerecord/test/cases/inheritance_test.rb b/activerecord/test/cases/inheritance_test.rb
index b5d8314541..02df464469 100644
--- a/activerecord/test/cases/inheritance_test.rb
+++ b/activerecord/test/cases/inheritance_test.rb
@@ -65,7 +65,6 @@ class InheritanceTest < ActiveRecord::TestCase
end
def test_company_descends_from_active_record
- assert_raise(NoMethodError) { ActiveRecord::Base.descends_from_active_record? }
assert AbstractCompany.descends_from_active_record?, 'AbstractCompany should descend from ActiveRecord::Base'
assert Company.descends_from_active_record?, 'Company should descend from ActiveRecord::Base'
assert !Class.new(Company).descends_from_active_record?, 'Company subclass should not descend from ActiveRecord::Base'
@@ -236,11 +235,11 @@ class InheritanceTest < ActiveRecord::TestCase
c.save
end
[ Company, Firm, Client].each { |klass| klass.reset_column_information }
- Company.set_inheritance_column('ruby_type')
+ Company.inheritance_column = 'ruby_type'
end
def switch_to_default_inheritance_column
[ Company, Firm, Client].each { |klass| klass.reset_column_information }
- Company.set_inheritance_column('type')
+ Company.inheritance_column = 'type'
end
end
diff --git a/activerecord/test/cases/invertible_migration_test.rb b/activerecord/test/cases/invertible_migration_test.rb
index 3ae7b63dff..8f1cdd47ea 100644
--- a/activerecord/test/cases/invertible_migration_test.rb
+++ b/activerecord/test/cases/invertible_migration_test.rb
@@ -88,5 +88,17 @@ module ActiveRecord
LegacyMigration.down
assert !ActiveRecord::Base.connection.table_exists?("horses"), "horses should not exist"
end
+
+ def test_migrate_down_with_table_name_prefix
+ ActiveRecord::Base.table_name_prefix = 'p_'
+ ActiveRecord::Base.table_name_suffix = '_s'
+ migration = InvertibleMigration.new
+ migration.migrate(:up)
+ assert_nothing_raised { migration.migrate(:down) }
+ assert !ActiveRecord::Base.connection.table_exists?("p_horses_s"), "p_horses_s should not exist"
+ ensure
+ ActiveRecord::Base.table_name_prefix = ActiveRecord::Base.table_name_suffix = ''
+ end
+
end
end
diff --git a/activerecord/test/cases/locking_test.rb b/activerecord/test/cases/locking_test.rb
index 61baa55027..807274ca67 100644
--- a/activerecord/test/cases/locking_test.rb
+++ b/activerecord/test/cases/locking_test.rb
@@ -6,12 +6,15 @@ require 'models/reader'
require 'models/legacy_thing'
require 'models/reference'
require 'models/string_key_object'
+require 'models/car'
+require 'models/engine'
+require 'models/wheel'
class LockWithoutDefault < ActiveRecord::Base; end
class LockWithCustomColumnWithoutDefault < ActiveRecord::Base
- set_table_name :lock_without_defaults_cust
- set_locking_column :custom_lock_version
+ self.table_name = :lock_without_defaults_cust
+ self.locking_column = :custom_lock_version
end
class ReadonlyFirstNamePerson < Person
@@ -125,6 +128,24 @@ class OptimisticLockingTest < ActiveRecord::TestCase
assert_raise(ActiveRecord::StaleObjectError) { p2.save! }
end
+ def test_lock_exception_record
+ p1 = Person.new(:first_name => 'mira')
+ assert_equal 0, p1.lock_version
+
+ p1.first_name = 'mira2'
+ p1.save!
+ p2 = Person.find(p1.id)
+ assert_equal 0, p1.lock_version
+ assert_equal 0, p2.lock_version
+
+ p1.first_name = 'mira3'
+ p1.save!
+
+ p2.first_name = 'sue'
+ error = assert_raise(ActiveRecord::StaleObjectError) { p2.save! }
+ assert_equal(error.record.object_id, p2.object_id)
+ end
+
def test_lock_new_with_nil
p1 = Person.new(:first_name => 'anika')
p1.save!
@@ -141,7 +162,6 @@ class OptimisticLockingTest < ActiveRecord::TestCase
assert_equal 1, p1.lock_version
end
-
def test_lock_column_name_existing
t1 = LegacyThing.find(1)
t2 = LegacyThing.find(1)
@@ -207,6 +227,18 @@ class OptimisticLockingTest < ActiveRecord::TestCase
assert_equal lock_version, p1.lock_version
end
end
+
+ def test_polymorphic_destroy_with_dependencies_and_lock_version
+ car = Car.create!
+
+ assert_difference 'car.wheels.count' do
+ car.wheels << Wheel.create!
+ end
+ assert_difference 'car.wheels.count', -1 do
+ car.destroy
+ end
+ assert car.destroyed?
+ end
end
class OptimisticLockingWithSchemaChangeTest < ActiveRecord::TestCase
@@ -261,6 +293,8 @@ class OptimisticLockingWithSchemaChangeTest < ActiveRecord::TestCase
assert_equal true, p1.frozen?
assert_raises(ActiveRecord::RecordNotFound) { Person.find(p1.id) }
assert_raises(ActiveRecord::RecordNotFound) { LegacyThing.find(t.id) }
+ ensure
+ remove_counter_column_from(Person, 'legacy_things_count')
end
private
@@ -272,8 +306,8 @@ class OptimisticLockingWithSchemaChangeTest < ActiveRecord::TestCase
model.update_all(col => 0) if current_adapter?(:OpenBaseAdapter)
end
- def remove_counter_column_from(model)
- model.connection.remove_column model.table_name, :test_count
+ def remove_counter_column_from(model, col = :test_count)
+ model.connection.remove_column model.table_name, col
model.reset_column_information
end
@@ -354,22 +388,32 @@ unless current_adapter?(:SybaseAdapter, :OpenBaseAdapter) || in_memory_db?
end
end
+ def test_with_lock_commits_transaction
+ person = Person.find 1
+ person.with_lock do
+ person.first_name = 'fooman'
+ person.save!
+ end
+ assert_equal 'fooman', person.reload.first_name
+ end
+
+ def test_with_lock_rolls_back_transaction
+ person = Person.find 1
+ old = person.first_name
+ person.with_lock do
+ person.first_name = 'fooman'
+ person.save!
+ raise 'oops'
+ end rescue nil
+ assert_equal old, person.reload.first_name
+ end
+
if current_adapter?(:PostgreSQLAdapter, :OracleAdapter)
def test_no_locks_no_wait
first, second = duel { Person.find 1 }
assert first.end > second.end
end
- # Hit by ruby deadlock detection since connection checkout is mutexed.
- if RUBY_VERSION < '1.9.0'
- def test_second_lock_waits
- assert [0.2, 1, 5].any? { |zzz|
- first, second = duel(zzz) { Person.find 1, :lock => true }
- second.end > first.end
- }
- end
- end
-
protected
def duel(zzz = 5)
t0, t1, t2, t3 = nil, nil, nil, nil
diff --git a/activerecord/test/cases/log_subscriber_test.rb b/activerecord/test/cases/log_subscriber_test.rb
index 9e8475465e..d1f0ace184 100644
--- a/activerecord/test/cases/log_subscriber_test.rb
+++ b/activerecord/test/cases/log_subscriber_test.rb
@@ -5,7 +5,7 @@ require "active_support/log_subscriber/test_helper"
class LogSubscriberTest < ActiveRecord::TestCase
include ActiveSupport::LogSubscriber::TestHelper
- include ActiveSupport::BufferedLogger::Severity
+ include ActiveSupport::Logger::Severity
fixtures :posts
@@ -13,6 +13,7 @@ class LogSubscriberTest < ActiveRecord::TestCase
@old_logger = ActiveRecord::Base.logger
@using_identity_map = ActiveRecord::IdentityMap.enabled?
ActiveRecord::IdentityMap.enabled = false
+ Developer.primary_key
super
ActiveRecord::LogSubscriber.attach_to(:active_record)
end
diff --git a/activerecord/test/cases/mass_assignment_security_test.rb b/activerecord/test/cases/mass_assignment_security_test.rb
index ef35f3341e..8122857f52 100644
--- a/activerecord/test/cases/mass_assignment_security_test.rb
+++ b/activerecord/test/cases/mass_assignment_security_test.rb
@@ -50,6 +50,13 @@ module MassAssignmentTestHelpers
assert_equal 'm', person.gender
assert_equal 'rides a sweet bike', person.comments
end
+
+ def with_strict_sanitizer
+ ActiveRecord::Base.mass_assignment_sanitizer = :strict
+ yield
+ ensure
+ ActiveRecord::Base.mass_assignment_sanitizer = :logger
+ end
end
module MassAssignmentRelationTestHelpers
@@ -231,7 +238,9 @@ class MassAssignmentSecurityTest < ActiveRecord::TestCase
def test_protection_against_class_attribute_writers
[:logger, :configurations, :primary_key_prefix_type, :table_name_prefix, :table_name_suffix, :pluralize_table_names,
- :default_timezone, :schema_format, :lock_optimistically, :record_timestamps].each do |method|
+ :default_timezone, :schema_format, :lock_optimistically, :timestamped_migrations, :default_scopes,
+ :connection_handler, :nested_attributes_options, :_attr_readonly, :attribute_types_cached_by_default,
+ :attribute_method_matchers, :time_zone_aware_attributes, :skip_time_zone_conversion_for_attributes].each do |method|
assert_respond_to Task, method
assert_respond_to Task, "#{method}="
assert_respond_to Task.new, method
@@ -321,6 +330,13 @@ class MassAssignmentSecurityHasOneRelationsTest < ActiveRecord::TestCase
assert_all_attributes(best_friend)
end
+ def test_has_one_build_with_strict_sanitizer
+ with_strict_sanitizer do
+ best_friend = @person.build_best_friend(attributes_hash.except(:id, :comments))
+ assert_equal @person.id, best_friend.best_friend_id
+ end
+ end
+
# create
def test_has_one_create_with_attr_protected_attributes
@@ -348,6 +364,13 @@ class MassAssignmentSecurityHasOneRelationsTest < ActiveRecord::TestCase
assert_all_attributes(best_friend)
end
+ def test_has_one_create_with_strict_sanitizer
+ with_strict_sanitizer do
+ best_friend = @person.create_best_friend(attributes_hash.except(:id, :comments))
+ assert_equal @person.id, best_friend.best_friend_id
+ end
+ end
+
# create!
def test_has_one_create_with_bang_with_attr_protected_attributes
@@ -375,6 +398,13 @@ class MassAssignmentSecurityHasOneRelationsTest < ActiveRecord::TestCase
assert_all_attributes(best_friend)
end
+ def test_has_one_create_with_bang_with_strict_sanitizer
+ with_strict_sanitizer do
+ best_friend = @person.create_best_friend!(attributes_hash.except(:id, :comments))
+ assert_equal @person.id, best_friend.best_friend_id
+ end
+ end
+
end
@@ -436,6 +466,13 @@ class MassAssignmentSecurityBelongsToRelationsTest < ActiveRecord::TestCase
assert_all_attributes(best_friend)
end
+ def test_belongs_to_create_with_strict_sanitizer
+ with_strict_sanitizer do
+ best_friend = @person.create_best_friend_of(attributes_hash.except(:id, :comments))
+ assert_equal best_friend.id, @person.best_friend_of_id
+ end
+ end
+
# create!
def test_belongs_to_create_with_bang_with_attr_protected_attributes
@@ -463,6 +500,13 @@ class MassAssignmentSecurityBelongsToRelationsTest < ActiveRecord::TestCase
assert_all_attributes(best_friend)
end
+ def test_belongs_to_create_with_bang_with_strict_sanitizer
+ with_strict_sanitizer do
+ best_friend = @person.create_best_friend_of!(attributes_hash.except(:id, :comments))
+ assert_equal best_friend.id, @person.best_friend_of_id
+ end
+ end
+
end
@@ -497,6 +541,13 @@ class MassAssignmentSecurityHasManyRelationsTest < ActiveRecord::TestCase
assert_all_attributes(best_friend)
end
+ def test_has_many_build_with_strict_sanitizer
+ with_strict_sanitizer do
+ best_friend = @person.best_friends.build(attributes_hash.except(:id, :comments))
+ assert_equal @person.id, best_friend.best_friend_id
+ end
+ end
+
# create
def test_has_many_create_with_attr_protected_attributes
@@ -524,6 +575,13 @@ class MassAssignmentSecurityHasManyRelationsTest < ActiveRecord::TestCase
assert_all_attributes(best_friend)
end
+ def test_has_many_create_with_strict_sanitizer
+ with_strict_sanitizer do
+ best_friend = @person.best_friends.create(attributes_hash.except(:id, :comments))
+ assert_equal @person.id, best_friend.best_friend_id
+ end
+ end
+
# create!
def test_has_many_create_with_bang_with_attr_protected_attributes
@@ -551,6 +609,13 @@ class MassAssignmentSecurityHasManyRelationsTest < ActiveRecord::TestCase
assert_all_attributes(best_friend)
end
+ def test_has_many_create_with_bang_with_strict_sanitizer
+ with_strict_sanitizer do
+ best_friend = @person.best_friends.create!(attributes_hash.except(:id, :comments))
+ assert_equal @person.id, best_friend.best_friend_id
+ end
+ end
+
end
diff --git a/activerecord/test/cases/method_scoping_test.rb b/activerecord/test/cases/method_scoping_test.rb
index 0ab4f30363..ebf6e26385 100644
--- a/activerecord/test/cases/method_scoping_test.rb
+++ b/activerecord/test/cases/method_scoping_test.rb
@@ -104,7 +104,7 @@ class MethodScopingTest < ActiveRecord::TestCase
def test_scoped_find_include
# with the include, will retrieve only developers for the given project
scoped_developers = Developer.send(:with_scope, :find => { :include => :projects }) do
- Developer.find(:all, :conditions => 'projects.id = 2')
+ Developer.find(:all, :conditions => { 'projects.id' => 2 })
end
assert scoped_developers.include?(developers(:david))
assert !scoped_developers.include?(developers(:jamis))
@@ -204,7 +204,7 @@ class MethodScopingTest < ActiveRecord::TestCase
def test_scoped_count_include
# with the include, will retrieve only developers for the given project
Developer.send(:with_scope, :find => { :include => :projects }) do
- assert_equal 1, Developer.count(:conditions => 'projects.id = 2')
+ assert_equal 1, Developer.count(:conditions => { 'projects.id' => 2 })
end
end
@@ -339,7 +339,7 @@ class NestedScopingTest < ActiveRecord::TestCase
def test_nested_scoped_find_include
Developer.send(:with_scope, :find => { :include => :projects }) do
- Developer.send(:with_scope, :find => { :conditions => "projects.id = 2" }) do
+ Developer.send(:with_scope, :find => { :conditions => { 'projects.id' => 2 } }) do
assert_nothing_raised { Developer.find(1) }
assert_equal('David', Developer.find(:first).name)
end
@@ -348,7 +348,7 @@ class NestedScopingTest < ActiveRecord::TestCase
def test_nested_scoped_find_merged_include
# :include's remain unique and don't "double up" when merging
- Developer.send(:with_scope, :find => { :include => :projects, :conditions => "projects.id = 2" }) do
+ Developer.send(:with_scope, :find => { :include => :projects, :conditions => { 'projects.id' => 2 } }) do
Developer.send(:with_scope, :find => { :include => :projects }) do
assert_equal 1, Developer.scoped.includes_values.uniq.length
assert_equal 'David', Developer.find(:first).name
@@ -356,7 +356,7 @@ class NestedScopingTest < ActiveRecord::TestCase
end
# the nested scope doesn't remove the first :include
- Developer.send(:with_scope, :find => { :include => :projects, :conditions => "projects.id = 2" }) do
+ Developer.send(:with_scope, :find => { :include => :projects, :conditions => { 'projects.id' => 2 } }) do
Developer.send(:with_scope, :find => { :include => [] }) do
assert_equal 1, Developer.scoped.includes_values.uniq.length
assert_equal('David', Developer.find(:first).name)
@@ -364,7 +364,7 @@ class NestedScopingTest < ActiveRecord::TestCase
end
# mixing array and symbol include's will merge correctly
- Developer.send(:with_scope, :find => { :include => [:projects], :conditions => "projects.id = 2" }) do
+ Developer.send(:with_scope, :find => { :include => [:projects], :conditions => { 'projects.id' => 2 } }) do
Developer.send(:with_scope, :find => { :include => :projects }) do
assert_equal 1, Developer.scoped.includes_values.uniq.length
assert_equal('David', Developer.find(:first).name)
diff --git a/activerecord/test/cases/migration/change_schema_test.rb b/activerecord/test/cases/migration/change_schema_test.rb
new file mode 100644
index 0000000000..f0b1f74bd3
--- /dev/null
+++ b/activerecord/test/cases/migration/change_schema_test.rb
@@ -0,0 +1,328 @@
+require 'cases/helper'
+
+module ActiveRecord
+ class Migration
+ class ChangeSchemaTest < ActiveRecord::TestCase
+ attr_reader :connection, :table_name
+
+ def setup
+ super
+ @connection = ActiveRecord::Base.connection
+ @table_name = :testings
+ end
+
+ def teardown
+ super
+ connection.drop_table :testings rescue nil
+ ActiveRecord::Base.primary_key_prefix_type = nil
+ end
+
+ def test_create_table_without_id
+ testing_table_with_only_foo_attribute do
+ assert_equal connection.columns(:testings).size, 1
+ end
+ end
+
+ def test_add_column_with_primary_key_attribute
+ testing_table_with_only_foo_attribute do
+ connection.add_column :testings, :id, :primary_key
+ assert_equal connection.columns(:testings).size, 2
+ end
+ end
+
+ def test_create_table_adds_id
+ connection.create_table :testings do |t|
+ t.column :foo, :string
+ end
+
+ assert_equal %w(foo id), connection.columns(:testings).map(&:name).sort
+ end
+
+ def test_create_table_with_not_null_column
+ connection.create_table :testings do |t|
+ t.column :foo, :string, :null => false
+ end
+
+ assert_raises(ActiveRecord::StatementInvalid) do
+ connection.execute "insert into testings (foo) values (NULL)"
+ end
+ end
+
+ def test_create_table_with_defaults
+ # MySQL doesn't allow defaults on TEXT or BLOB columns.
+ mysql = current_adapter?(:MysqlAdapter) || current_adapter?(:Mysql2Adapter)
+
+ connection.create_table :testings do |t|
+ t.column :one, :string, :default => "hello"
+ t.column :two, :boolean, :default => true
+ t.column :three, :boolean, :default => false
+ t.column :four, :integer, :default => 1
+ t.column :five, :text, :default => "hello" unless mysql
+ end
+
+ columns = connection.columns(:testings)
+ one = columns.detect { |c| c.name == "one" }
+ two = columns.detect { |c| c.name == "two" }
+ three = columns.detect { |c| c.name == "three" }
+ four = columns.detect { |c| c.name == "four" }
+ five = columns.detect { |c| c.name == "five" } unless mysql
+
+ assert_equal "hello", one.default
+ assert_equal true, two.default
+ assert_equal false, three.default
+ assert_equal 1, four.default
+ assert_equal "hello", five.default unless mysql
+ end
+
+ def test_create_table_with_limits
+ connection.create_table :testings do |t|
+ t.column :foo, :string, :limit => 255
+
+ t.column :default_int, :integer
+
+ t.column :one_int, :integer, :limit => 1
+ t.column :four_int, :integer, :limit => 4
+ t.column :eight_int, :integer, :limit => 8
+ t.column :eleven_int, :integer, :limit => 11
+ end
+
+ columns = connection.columns(:testings)
+ foo = columns.detect { |c| c.name == "foo" }
+ assert_equal 255, foo.limit
+
+ default = columns.detect { |c| c.name == "default_int" }
+ one = columns.detect { |c| c.name == "one_int" }
+ four = columns.detect { |c| c.name == "four_int" }
+ eight = columns.detect { |c| c.name == "eight_int" }
+ eleven = columns.detect { |c| c.name == "eleven_int" }
+
+ if current_adapter?(:PostgreSQLAdapter)
+ assert_equal 'integer', default.sql_type
+ assert_equal 'smallint', one.sql_type
+ assert_equal 'integer', four.sql_type
+ assert_equal 'bigint', eight.sql_type
+ assert_equal 'integer', eleven.sql_type
+ elsif current_adapter?(:MysqlAdapter) or current_adapter?(:Mysql2Adapter)
+ assert_match 'int(11)', default.sql_type
+ assert_match 'tinyint', one.sql_type
+ assert_match 'int', four.sql_type
+ assert_match 'bigint', eight.sql_type
+ assert_match 'int(11)', eleven.sql_type
+ elsif current_adapter?(:OracleAdapter)
+ assert_equal 'NUMBER(38)', default.sql_type
+ assert_equal 'NUMBER(1)', one.sql_type
+ assert_equal 'NUMBER(4)', four.sql_type
+ assert_equal 'NUMBER(8)', eight.sql_type
+ end
+ end
+
+ def test_create_table_with_primary_key_prefix_as_table_name_with_underscore
+ ActiveRecord::Base.primary_key_prefix_type = :table_name_with_underscore
+
+ connection.create_table :testings do |t|
+ t.column :foo, :string
+ end
+
+ assert_equal %w(foo testing_id), connection.columns(:testings).map(&:name).sort
+ end
+
+ def test_create_table_with_primary_key_prefix_as_table_name
+ ActiveRecord::Base.primary_key_prefix_type = :table_name
+
+ connection.create_table :testings do |t|
+ t.column :foo, :string
+ end
+
+ assert_equal %w(foo testingid), connection.columns(:testings).map(&:name).sort
+ end
+
+ def test_create_table_with_timestamps_should_create_datetime_columns
+ connection.create_table table_name do |t|
+ t.timestamps
+ end
+ created_columns = connection.columns(table_name)
+
+ created_at_column = created_columns.detect {|c| c.name == 'created_at' }
+ updated_at_column = created_columns.detect {|c| c.name == 'updated_at' }
+
+ assert !created_at_column.null
+ assert !updated_at_column.null
+ end
+
+ def test_create_table_with_timestamps_should_create_datetime_columns_with_options
+ connection.create_table table_name do |t|
+ t.timestamps :null => false
+ end
+ created_columns = connection.columns(table_name)
+
+ created_at_column = created_columns.detect {|c| c.name == 'created_at' }
+ updated_at_column = created_columns.detect {|c| c.name == 'updated_at' }
+
+ assert !created_at_column.null
+ assert !updated_at_column.null
+ end
+
+ def test_create_table_without_a_block
+ connection.create_table table_name
+ end
+
+ def test_add_column_not_null_without_default
+ # Sybase, and SQLite3 will not allow you to add a NOT NULL
+ # column to a table without a default value.
+ if current_adapter?(:SybaseAdapter, :SQLite3Adapter)
+ skip "not supported on #{connection.class}"
+ end
+
+ connection.create_table :testings do |t|
+ t.column :foo, :string
+ end
+ connection.add_column :testings, :bar, :string, :null => false
+
+ assert_raise(ActiveRecord::StatementInvalid) do
+ connection.execute "insert into testings (foo, bar) values ('hello', NULL)"
+ end
+ end
+
+ def test_add_column_not_null_with_default
+ connection.create_table :testings do |t|
+ t.column :foo, :string
+ end
+
+ con = connection
+ connection.enable_identity_insert("testings", true) if current_adapter?(:SybaseAdapter)
+ connection.execute "insert into testings (#{con.quote_column_name('id')}, #{con.quote_column_name('foo')}) values (1, 'hello')"
+ connection.enable_identity_insert("testings", false) if current_adapter?(:SybaseAdapter)
+ assert_nothing_raised {connection.add_column :testings, :bar, :string, :null => false, :default => "default" }
+
+ assert_raises(ActiveRecord::StatementInvalid) do
+ unless current_adapter?(:OpenBaseAdapter)
+ connection.execute "insert into testings (#{con.quote_column_name('id')}, #{con.quote_column_name('foo')}, #{con.quote_column_name('bar')}) values (2, 'hello', NULL)"
+ else
+ connection.insert("INSERT INTO testings (#{con.quote_column_name('id')}, #{con.quote_column_name('foo')}, #{con.quote_column_name('bar')}) VALUES (2, 'hello', NULL)",
+ "Testing Insert","id",2)
+ end
+ end
+ end
+
+ def test_change_column_quotes_column_names
+ connection.create_table :testings do |t|
+ t.column :select, :string
+ end
+
+ connection.change_column :testings, :select, :string, :limit => 10
+
+ # Oracle needs primary key value from sequence
+ if current_adapter?(:OracleAdapter)
+ connection.execute "insert into testings (id, #{connection.quote_column_name('select')}) values (testings_seq.nextval, '7 chars')"
+ else
+ connection.execute "insert into testings (#{connection.quote_column_name('select')}) values ('7 chars')"
+ end
+ end
+
+ def test_keeping_default_and_notnull_constaint_on_change
+ connection.create_table :testings do |t|
+ t.column :title, :string
+ end
+ person_klass = Class.new(ActiveRecord::Base)
+ person_klass.table_name = 'testings'
+
+ person_klass.connection.add_column "testings", "wealth", :integer, :null => false, :default => 99
+ person_klass.reset_column_information
+ assert_equal 99, person_klass.columns_hash["wealth"].default
+ assert_equal false, person_klass.columns_hash["wealth"].null
+ # Oracle needs primary key value from sequence
+ if current_adapter?(:OracleAdapter)
+ assert_nothing_raised {person_klass.connection.execute("insert into testings (id, title) values (testings_seq.nextval, 'tester')")}
+ else
+ assert_nothing_raised {person_klass.connection.execute("insert into testings (title) values ('tester')")}
+ end
+
+ # change column default to see that column doesn't lose its not null definition
+ person_klass.connection.change_column_default "testings", "wealth", 100
+ person_klass.reset_column_information
+ assert_equal 100, person_klass.columns_hash["wealth"].default
+ assert_equal false, person_klass.columns_hash["wealth"].null
+
+ # rename column to see that column doesn't lose its not null and/or default definition
+ person_klass.connection.rename_column "testings", "wealth", "money"
+ person_klass.reset_column_information
+ assert_nil person_klass.columns_hash["wealth"]
+ assert_equal 100, person_klass.columns_hash["money"].default
+ assert_equal false, person_klass.columns_hash["money"].null
+
+ # change column
+ person_klass.connection.change_column "testings", "money", :integer, :null => false, :default => 1000
+ person_klass.reset_column_information
+ assert_equal 1000, person_klass.columns_hash["money"].default
+ assert_equal false, person_klass.columns_hash["money"].null
+
+ # change column, make it nullable and clear default
+ person_klass.connection.change_column "testings", "money", :integer, :null => true, :default => nil
+ person_klass.reset_column_information
+ assert_nil person_klass.columns_hash["money"].default
+ assert_equal true, person_klass.columns_hash["money"].null
+
+ # change_column_null, make it not nullable and set null values to a default value
+ person_klass.connection.execute('UPDATE testings SET money = NULL')
+ person_klass.connection.change_column_null "testings", "money", false, 2000
+ person_klass.reset_column_information
+ assert_nil person_klass.columns_hash["money"].default
+ assert_equal false, person_klass.columns_hash["money"].null
+ assert_equal 2000, connection.select_values("SELECT money FROM testings").first.to_i
+ end
+
+ def test_column_exists
+ connection.create_table :testings do |t|
+ t.column :foo, :string
+ end
+
+ assert connection.column_exists?(:testings, :foo)
+ refute connection.column_exists?(:testings, :bar)
+ end
+
+ def test_column_exists_with_type
+ connection.create_table :testings do |t|
+ t.column :foo, :string
+ t.column :bar, :decimal, :precision => 8, :scale => 2
+ end
+
+ assert connection.column_exists?(:testings, :foo, :string)
+ refute connection.column_exists?(:testings, :foo, :integer)
+
+ assert connection.column_exists?(:testings, :bar, :decimal)
+ refute connection.column_exists?(:testings, :bar, :integer)
+ end
+
+ def test_column_exists_with_definition
+ connection.create_table :testings do |t|
+ t.column :foo, :string, :limit => 100
+ t.column :bar, :decimal, :precision => 8, :scale => 2
+ end
+
+ assert connection.column_exists?(:testings, :foo, :string, :limit => 100)
+ refute connection.column_exists?(:testings, :foo, :string, :limit => 50)
+ assert connection.column_exists?(:testings, :bar, :decimal, :precision => 8, :scale => 2)
+ refute connection.column_exists?(:testings, :bar, :decimal, :precision => 10, :scale => 2)
+ end
+
+ def test_column_exists_on_table_with_no_options_parameter_supplied
+ connection.create_table :testings do |t|
+ t.string :foo
+ end
+ connection.change_table :testings do |t|
+ assert t.column_exists?(:foo)
+ assert !(t.column_exists?(:bar))
+ end
+ end
+
+ private
+ def testing_table_with_only_foo_attribute
+ connection.create_table :testings, :id => false do |t|
+ t.column :foo, :string
+ end
+
+ yield
+ end
+ end
+ end
+end
diff --git a/activerecord/test/cases/migration/column_attributes_test.rb b/activerecord/test/cases/migration/column_attributes_test.rb
new file mode 100644
index 0000000000..040445ef12
--- /dev/null
+++ b/activerecord/test/cases/migration/column_attributes_test.rb
@@ -0,0 +1,188 @@
+require "cases/migration/helper"
+
+module ActiveRecord
+ class Migration
+ class ColumnAttributesTest < ActiveRecord::TestCase
+ include ActiveRecord::Migration::TestHelper
+
+ self.use_transactional_fixtures = false
+
+ def test_add_remove_single_field_using_string_arguments
+ refute TestModel.column_methods_hash.key?(:last_name)
+
+ add_column 'test_models', 'last_name', :string
+
+ TestModel.reset_column_information
+
+ assert TestModel.column_methods_hash.key?(:last_name)
+
+ remove_column 'test_models', 'last_name'
+
+ TestModel.reset_column_information
+ refute TestModel.column_methods_hash.key?(:last_name)
+ end
+
+ def test_add_remove_single_field_using_symbol_arguments
+ refute TestModel.column_methods_hash.key?(:last_name)
+
+ add_column :test_models, :last_name, :string
+
+ TestModel.reset_column_information
+ assert TestModel.column_methods_hash.key?(:last_name)
+
+ remove_column :test_models, :last_name
+
+ TestModel.reset_column_information
+ refute TestModel.column_methods_hash.key?(:last_name)
+ end
+
+ def test_unabstracted_database_dependent_types
+ skip "not supported" unless current_adapter?(:MysqlAdapter, :Mysql2Adapter)
+
+ add_column :test_models, :intelligence_quotient, :tinyint
+ TestModel.reset_column_information
+ assert_match(/tinyint/, TestModel.columns_hash['intelligence_quotient'].sql_type)
+ end
+
+ # We specifically do a manual INSERT here, and then test only the SELECT
+ # functionality. This allows us to more easily catch INSERT being broken,
+ # but SELECT actually working fine.
+ def test_native_decimal_insert_manual_vs_automatic
+ correct_value = '0012345678901234567890.0123456789'.to_d
+
+ connection.add_column "test_models", "wealth", :decimal, :precision => '30', :scale => '10'
+
+ # Do a manual insertion
+ if current_adapter?(:OracleAdapter)
+ connection.execute "insert into test_models (id, wealth, created_at, updated_at) values (people_seq.nextval, 12345678901234567890.0123456789, sysdate, sysdate)"
+ elsif current_adapter?(:OpenBaseAdapter) || (current_adapter?(:MysqlAdapter) && Mysql.client_version < 50003) #before mysql 5.0.3 decimals stored as strings
+ connection.execute "insert into test_models (wealth, created_at, updated_at) values ('12345678901234567890.0123456789', 0, 0)"
+ elsif current_adapter?(:PostgreSQLAdapter)
+ connection.execute "insert into test_models (wealth, created_at, updated_at) values (12345678901234567890.0123456789, now(), now())"
+ else
+ connection.execute "insert into test_models (wealth, created_at, updated_at) values (12345678901234567890.0123456789, 0, 0)"
+ end
+
+ # SELECT
+ row = TestModel.find(:first)
+ assert_kind_of BigDecimal, row.wealth
+
+ # If this assert fails, that means the SELECT is broken!
+ unless current_adapter?(:SQLite3Adapter)
+ assert_equal correct_value, row.wealth
+ end
+
+ # Reset to old state
+ TestModel.delete_all
+
+ # Now use the Rails insertion
+ TestModel.create :wealth => BigDecimal.new("12345678901234567890.0123456789")
+
+ # SELECT
+ row = TestModel.find(:first)
+ assert_kind_of BigDecimal, row.wealth
+
+ # If these asserts fail, that means the INSERT (create function, or cast to SQL) is broken!
+ unless current_adapter?(:SQLite3Adapter)
+ assert_equal correct_value, row.wealth
+ end
+ end
+
+ def test_add_column_with_precision_and_scale
+ connection.add_column 'test_models', 'wealth', :decimal, :precision => 9, :scale => 7
+
+ wealth_column = TestModel.columns_hash['wealth']
+ assert_equal 9, wealth_column.precision
+ assert_equal 7, wealth_column.scale
+ end
+
+ def test_change_column_preserve_other_column_precision_and_scale
+ skip "only on sqlite3" unless current_adapter?(:SQLite3Adapter)
+
+ connection.add_column 'test_models', 'last_name', :string
+ connection.add_column 'test_models', 'wealth', :decimal, :precision => 9, :scale => 7
+
+ wealth_column = TestModel.columns_hash['wealth']
+ assert_equal 9, wealth_column.precision
+ assert_equal 7, wealth_column.scale
+
+ connection.change_column 'test_models', 'last_name', :string, :null => false
+ TestModel.reset_column_information
+
+ wealth_column = TestModel.columns_hash['wealth']
+ assert_equal 9, wealth_column.precision
+ assert_equal 7, wealth_column.scale
+ end
+
+ def test_native_types
+ add_column "test_models", "first_name", :string
+ add_column "test_models", "last_name", :string
+ add_column "test_models", "bio", :text
+ add_column "test_models", "age", :integer
+ add_column "test_models", "height", :float
+ add_column "test_models", "wealth", :decimal, :precision => '30', :scale => '10'
+ add_column "test_models", "birthday", :datetime
+ add_column "test_models", "favorite_day", :date
+ add_column "test_models", "moment_of_truth", :datetime
+ add_column "test_models", "male", :boolean
+
+ TestModel.create :first_name => 'bob', :last_name => 'bobsen',
+ :bio => "I was born ....", :age => 18, :height => 1.78,
+ :wealth => BigDecimal.new("12345678901234567890.0123456789"),
+ :birthday => 18.years.ago, :favorite_day => 10.days.ago,
+ :moment_of_truth => "1782-10-10 21:40:18", :male => true
+
+ bob = TestModel.find(:first)
+ assert_equal 'bob', bob.first_name
+ assert_equal 'bobsen', bob.last_name
+ assert_equal "I was born ....", bob.bio
+ assert_equal 18, bob.age
+
+ # Test for 30 significant digits (beyond the 16 of float), 10 of them
+ # after the decimal place.
+
+ unless current_adapter?(:SQLite3Adapter)
+ assert_equal BigDecimal.new("0012345678901234567890.0123456789"), bob.wealth
+ end
+
+ assert_equal true, bob.male?
+
+ assert_equal String, bob.first_name.class
+ assert_equal String, bob.last_name.class
+ assert_equal String, bob.bio.class
+ assert_equal Fixnum, bob.age.class
+ assert_equal Time, bob.birthday.class
+
+ if current_adapter?(:OracleAdapter, :SybaseAdapter)
+ # Sybase, and Oracle don't differentiate between date/time
+ assert_equal Time, bob.favorite_day.class
+ else
+ assert_equal Date, bob.favorite_day.class
+ end
+
+ # Oracle adapter stores Time or DateTime with timezone value already in _before_type_cast column
+ # therefore no timezone change is done afterwards when default timezone is changed
+ unless current_adapter?(:OracleAdapter)
+ # Test DateTime column and defaults, including timezone.
+ # FIXME: moment of truth may be Time on 64-bit platforms.
+ if bob.moment_of_truth.is_a?(DateTime)
+
+ with_env_tz 'US/Eastern' do
+ bob.reload
+ assert_equal DateTime.local_offset, bob.moment_of_truth.offset
+ assert_not_equal 0, bob.moment_of_truth.offset
+ assert_not_equal "Z", bob.moment_of_truth.zone
+ # US/Eastern is -5 hours from GMT
+ assert_equal Rational(-5, 24), bob.moment_of_truth.offset
+ assert_match(/\A-05:?00\Z/, bob.moment_of_truth.zone) #ruby 1.8.6 uses HH:MM, prior versions use HHMM
+ assert_equal DateTime::ITALY, bob.moment_of_truth.start
+ end
+ end
+ end
+
+ assert_instance_of TrueClass, bob.male?
+ assert_kind_of BigDecimal, bob.wealth
+ end
+ end
+ end
+end
diff --git a/activerecord/test/cases/migration/column_positioning_test.rb b/activerecord/test/cases/migration/column_positioning_test.rb
new file mode 100644
index 0000000000..913d935f7c
--- /dev/null
+++ b/activerecord/test/cases/migration/column_positioning_test.rb
@@ -0,0 +1,60 @@
+require 'cases/helper'
+
+module ActiveRecord
+ class Migration
+ class ColumnPositioningTest < ActiveRecord::TestCase
+ attr_reader :connection, :table_name
+ alias :conn :connection
+
+ def setup
+ super
+
+ unless current_adapter?(:MysqlAdapter, :Mysql2Adapter)
+ skip "not supported on #{connection.class}"
+ end
+
+ @connection = ActiveRecord::Base.connection
+
+ connection.create_table :testings, :id => false do |t|
+ t.column :first, :integer
+ t.column :second, :integer
+ t.column :third, :integer
+ end
+ end
+
+ def teardown
+ super
+ connection.drop_table :testings rescue nil
+ ActiveRecord::Base.primary_key_prefix_type = nil
+ end
+
+ def test_column_positioning
+ assert_equal %w(first second third), conn.columns(:testings).map {|c| c.name }
+ end
+
+ def test_add_column_with_positioning
+ conn.add_column :testings, :new_col, :integer
+ assert_equal %w(first second third new_col), conn.columns(:testings).map {|c| c.name }
+ end
+
+ def test_add_column_with_positioning_first
+ conn.add_column :testings, :new_col, :integer, :first => true
+ assert_equal %w(new_col first second third), conn.columns(:testings).map {|c| c.name }
+ end
+
+ def test_add_column_with_positioning_after
+ conn.add_column :testings, :new_col, :integer, :after => :first
+ assert_equal %w(first new_col second third), conn.columns(:testings).map {|c| c.name }
+ end
+
+ def test_change_column_with_positioning
+ conn.change_column :testings, :second, :integer, :first => true
+ assert_equal %w(second first third), conn.columns(:testings).map {|c| c.name }
+
+ conn.change_column :testings, :second, :integer, :after => :third
+ assert_equal %w(first third second), conn.columns(:testings).map {|c| c.name }
+ end
+
+ end
+ end
+end
diff --git a/activerecord/test/cases/migration/command_recorder_test.rb b/activerecord/test/cases/migration/command_recorder_test.rb
index d108b456f0..7d026961be 100644
--- a/activerecord/test/cases/migration/command_recorder_test.rb
+++ b/activerecord/test/cases/migration/command_recorder_test.rb
@@ -67,6 +67,24 @@ module ActiveRecord
assert_equal [:drop_table, [:system_settings]], drop_table
end
+ def test_invert_create_table_with_options
+ @recorder.record :create_table, [:people_reminders, {:id => false}]
+ drop_table = @recorder.inverse.first
+ assert_equal [:drop_table, [:people_reminders]], drop_table
+ end
+
+ def test_invert_create_join_table
+ @recorder.record :create_join_table, [:musics, :artists]
+ drop_table = @recorder.inverse.first
+ assert_equal [:drop_table, [:artists_musics]], drop_table
+ end
+
+ def test_invert_create_join_table_with_table_name
+ @recorder.record :create_join_table, [:musics, :artists, {:table_name => :catalog}]
+ drop_table = @recorder.inverse.first
+ assert_equal [:drop_table, [:catalog]], drop_table
+ end
+
def test_invert_rename_table
@recorder.record :rename_table, [:old, :new]
rename = @recorder.inverse.first
diff --git a/activerecord/test/cases/migration/create_join_table_test.rb b/activerecord/test/cases/migration/create_join_table_test.rb
new file mode 100644
index 0000000000..0428d9ba76
--- /dev/null
+++ b/activerecord/test/cases/migration/create_join_table_test.rb
@@ -0,0 +1,70 @@
+require 'cases/helper'
+
+module ActiveRecord
+ class Migration
+ class CreateJoinTableTest < ActiveRecord::TestCase
+ attr_reader :connection
+
+ def setup
+ super
+ @connection = ActiveRecord::Base.connection
+ end
+
+ def test_create_join_table
+ connection.create_join_table :artists, :musics
+
+ assert_equal %w(artist_id music_id), connection.columns(:artists_musics).map(&:name).sort
+ ensure
+ connection.drop_table :artists_musics
+ end
+
+ def test_create_join_table_set_not_null_by_default
+ connection.create_join_table :artists, :musics
+
+ assert_equal [false, false], connection.columns(:artists_musics).map(&:null)
+ ensure
+ connection.drop_table :artists_musics
+ end
+
+ def test_create_join_table_with_strings
+ connection.create_join_table 'artists', 'musics'
+
+ assert_equal %w(artist_id music_id), connection.columns(:artists_musics).map(&:name).sort
+ ensure
+ connection.drop_table :artists_musics
+ end
+
+ def test_create_join_table_with_the_proper_order
+ connection.create_join_table :videos, :musics
+
+ assert_equal %w(music_id video_id), connection.columns(:musics_videos).map(&:name).sort
+ ensure
+ connection.drop_table :musics_videos
+ end
+
+ def test_create_join_table_with_the_table_name
+ connection.create_join_table :artists, :musics, :table_name => :catalog
+
+ assert_equal %w(artist_id music_id), connection.columns(:catalog).map(&:name).sort
+ ensure
+ connection.drop_table :catalog
+ end
+
+ def test_create_join_table_with_the_table_name_as_string
+ connection.create_join_table :artists, :musics, :table_name => 'catalog'
+
+ assert_equal %w(artist_id music_id), connection.columns(:catalog).map(&:name).sort
+ ensure
+ connection.drop_table :catalog
+ end
+
+ def test_create_join_table_with_column_options
+ connection.create_join_table :artists, :musics, :column_options => {:null => true}
+
+ assert_equal [true, true], connection.columns(:artists_musics).map(&:null)
+ ensure
+ connection.drop_table :artists_musics
+ end
+ end
+ end
+end
diff --git a/activerecord/test/cases/migration/helper.rb b/activerecord/test/cases/migration/helper.rb
new file mode 100644
index 0000000000..fe53510ba2
--- /dev/null
+++ b/activerecord/test/cases/migration/helper.rb
@@ -0,0 +1,64 @@
+require "cases/helper"
+
+module ActiveRecord
+ class Migration
+ class << self
+ attr_accessor :message_count
+ end
+
+ def puts(text="")
+ ActiveRecord::Migration.message_count ||= 0
+ ActiveRecord::Migration.message_count += 1
+ end
+
+ module TestHelper
+ attr_reader :connection, :table_name
+
+ class TestModel < ActiveRecord::Base
+ self.table_name = 'test_models'
+ end
+
+ def setup
+ super
+ @connection = ActiveRecord::Base.connection
+ connection.create_table :test_models do |t|
+ t.timestamps
+ end
+
+ TestModel.reset_column_information
+ end
+
+ def teardown
+ super
+ TestModel.reset_table_name
+ TestModel.reset_sequence_name
+ connection.drop_table :test_models rescue nil
+ end
+
+ private
+ def add_column(*args)
+ connection.add_column(*args)
+ end
+
+ def remove_column(*args)
+ connection.remove_column(*args)
+ end
+
+ def rename_column(*args)
+ connection.rename_column(*args)
+ end
+
+ def add_index(*args)
+ connection.add_index(*args)
+ end
+
+ def change_column(*args)
+ connection.change_column(*args)
+ end
+
+ def rename_table(*args)
+ connection.rename_table(*args)
+ end
+ end
+ end
+end
diff --git a/activerecord/test/cases/migration/index_test.rb b/activerecord/test/cases/migration/index_test.rb
new file mode 100644
index 0000000000..89cf0f5e93
--- /dev/null
+++ b/activerecord/test/cases/migration/index_test.rb
@@ -0,0 +1,176 @@
+require 'cases/helper'
+
+module ActiveRecord
+ class Migration
+ class IndexTest < ActiveRecord::TestCase
+ attr_reader :connection, :table_name
+
+ def setup
+ super
+ @connection = ActiveRecord::Base.connection
+ @table_name = :testings
+
+ connection.create_table table_name do |t|
+ t.column :foo, :string, :limit => 100
+ t.column :bar, :string, :limit => 100
+
+ t.string :first_name
+ t.string :last_name, :limit => 100
+ t.string :key, :limit => 100
+ t.boolean :administrator
+ end
+ end
+
+ def teardown
+ super
+ connection.drop_table :testings rescue nil
+ ActiveRecord::Base.primary_key_prefix_type = nil
+ end
+
+ def test_rename_index
+ skip "not supported on openbase" if current_adapter?(:OpenBaseAdapter)
+
+ # keep the names short to make Oracle and similar behave
+ connection.add_index(table_name, [:foo], :name => 'old_idx')
+ connection.rename_index(table_name, 'old_idx', 'new_idx')
+
+ # if the adapter doesn't support the indexes call, pick defaults that let the test pass
+ refute connection.index_name_exists?(table_name, 'old_idx', false)
+ assert connection.index_name_exists?(table_name, 'new_idx', true)
+ end
+
+ def test_double_add_index
+ skip "not supported on openbase" if current_adapter?(:OpenBaseAdapter)
+
+ connection.add_index(table_name, [:foo], :name => 'some_idx')
+ assert_raises(ArgumentError) {
+ connection.add_index(table_name, [:foo], :name => 'some_idx')
+ }
+ end
+
+ def test_remove_nonexistent_index
+ skip "not supported on openbase" if current_adapter?(:OpenBaseAdapter)
+
+ # we do this by name, so OpenBase is a wash as noted above
+ assert_raise(ArgumentError) { connection.remove_index(table_name, "no_such_index") }
+ end
+
+ def test_add_index_name_length_limit
+ good_index_name = 'x' * connection.index_name_length
+ too_long_index_name = good_index_name + 'x'
+
+ assert_raises(ArgumentError) {
+ connection.add_index(table_name, "foo", :name => too_long_index_name)
+ }
+
+ refute connection.index_name_exists?(table_name, too_long_index_name, false)
+ connection.add_index(table_name, "foo", :name => good_index_name)
+
+ assert connection.index_name_exists?(table_name, good_index_name, false)
+ connection.remove_index(table_name, :name => good_index_name)
+ end
+
+ def test_index_symbol_names
+ connection.add_index table_name, :foo, :name => :symbol_index_name
+ assert connection.index_exists?(table_name, :foo, :name => :symbol_index_name)
+
+ connection.remove_index table_name, :name => :symbol_index_name
+ refute connection.index_exists?(table_name, :foo, :name => :symbol_index_name)
+ end
+
+ def test_index_exists
+ connection.add_index :testings, :foo
+
+ assert connection.index_exists?(:testings, :foo)
+ assert !connection.index_exists?(:testings, :bar)
+ end
+
+ def test_index_exists_on_multiple_columns
+ connection.add_index :testings, [:foo, :bar]
+
+ assert connection.index_exists?(:testings, [:foo, :bar])
+ end
+
+ def test_unique_index_exists
+ connection.add_index :testings, :foo, :unique => true
+
+ assert connection.index_exists?(:testings, :foo, :unique => true)
+ end
+
+ def test_named_index_exists
+ connection.add_index :testings, :foo, :name => "custom_index_name"
+
+ assert connection.index_exists?(:testings, :foo, :name => "custom_index_name")
+ end
+
+ def test_add_index_attribute_length_limit
+ connection.add_index :testings, [:foo, :bar], :length => {:foo => 10, :bar => nil}
+
+ assert connection.index_exists?(:testings, [:foo, :bar])
+ end
+
+ def test_add_index
+ connection.add_index("testings", "last_name")
+ connection.remove_index("testings", "last_name")
+
+ # Orcl nds shrt indx nms. Sybs 2.
+ # OpenBase does not have named indexes. You must specify a single column name
+ unless current_adapter?(:SybaseAdapter, :OpenBaseAdapter)
+ connection.add_index("testings", ["last_name", "first_name"])
+ connection.remove_index("testings", :column => ["last_name", "first_name"])
+
+ # Oracle adapter cannot have specified index name larger than 30 characters
+ # Oracle adapter is shortening index name when just column list is given
+ unless current_adapter?(:OracleAdapter)
+ connection.add_index("testings", ["last_name", "first_name"])
+ connection.remove_index("testings", :name => :index_testings_on_last_name_and_first_name)
+ connection.add_index("testings", ["last_name", "first_name"])
+ connection.remove_index("testings", "last_name_and_first_name")
+ end
+ connection.add_index("testings", ["last_name", "first_name"])
+ connection.remove_index("testings", ["last_name", "first_name"])
+
+ connection.add_index("testings", ["last_name"], :length => 10)
+ connection.remove_index("testings", "last_name")
+
+ connection.add_index("testings", ["last_name"], :length => {:last_name => 10})
+ connection.remove_index("testings", ["last_name"])
+
+ connection.add_index("testings", ["last_name", "first_name"], :length => 10)
+ connection.remove_index("testings", ["last_name", "first_name"])
+
+ connection.add_index("testings", ["last_name", "first_name"], :length => {:last_name => 10, :first_name => 20})
+ connection.remove_index("testings", ["last_name", "first_name"])
+ end
+
+ # quoting
+ # Note: changed index name from "key" to "key_idx" since "key" is a Firebird reserved word
+ # OpenBase does not have named indexes. You must specify a single column name
+ unless current_adapter?(:OpenBaseAdapter)
+ connection.add_index("testings", ["key"], :name => "key_idx", :unique => true)
+ connection.remove_index("testings", :name => "key_idx", :unique => true)
+ end
+
+ # Sybase adapter does not support indexes on :boolean columns
+ # OpenBase does not have named indexes. You must specify a single column
+ unless current_adapter?(:SybaseAdapter, :OpenBaseAdapter)
+ connection.add_index("testings", %w(last_name first_name administrator), :name => "named_admin")
+ connection.remove_index("testings", :name => "named_admin")
+ end
+
+ # Selected adapters support index sort order
+ if current_adapter?(:SQLite3Adapter, :MysqlAdapter, :Mysql2Adapter, :PostgreSQLAdapter)
+ connection.add_index("testings", ["last_name"], :order => {:last_name => :desc})
+ connection.remove_index("testings", ["last_name"])
+ connection.add_index("testings", ["last_name", "first_name"], :order => {:last_name => :desc})
+ connection.remove_index("testings", ["last_name", "first_name"])
+ connection.add_index("testings", ["last_name", "first_name"], :order => {:last_name => :desc, :first_name => :asc})
+ connection.remove_index("testings", ["last_name", "first_name"])
+ connection.add_index("testings", ["last_name", "first_name"], :order => :desc)
+ connection.remove_index("testings", ["last_name", "first_name"])
+ end
+ end
+
+ end
+ end
+end
diff --git a/activerecord/test/cases/migration/logger_test.rb b/activerecord/test/cases/migration/logger_test.rb
new file mode 100644
index 0000000000..ee0c20747e
--- /dev/null
+++ b/activerecord/test/cases/migration/logger_test.rb
@@ -0,0 +1,37 @@
+require "cases/helper"
+
+module ActiveRecord
+ class Migration
+ class LoggerTest < ActiveRecord::TestCase
+ # mysql can't roll back ddl changes
+ self.use_transactional_fixtures = false
+
+ Migration = Struct.new(:name, :version) do
+ def migrate direction
+ # do nothing
+ end
+ end
+
+ def setup
+ super
+ ActiveRecord::SchemaMigration.create_table
+ ActiveRecord::SchemaMigration.delete_all
+ end
+
+ def teardown
+ super
+ ActiveRecord::SchemaMigration.drop_table
+ end
+
+ def test_migration_should_be_run_without_logger
+ previous_logger = ActiveRecord::Base.logger
+ ActiveRecord::Base.logger = nil
+ migrations = [Migration.new('a', 1), Migration.new('b', 2), Migration.new('c', 3)]
+ ActiveRecord::Migrator.new(:up, migrations).migrate
+ ensure
+ ActiveRecord::Base.logger = previous_logger
+ end
+ end
+ end
+end
+
diff --git a/activerecord/test/cases/migration/rename_column_test.rb b/activerecord/test/cases/migration/rename_column_test.rb
new file mode 100644
index 0000000000..16e09fd80e
--- /dev/null
+++ b/activerecord/test/cases/migration/rename_column_test.rb
@@ -0,0 +1,194 @@
+require "cases/migration/helper"
+
+module ActiveRecord
+ class Migration
+ class RenameColumnTest < ActiveRecord::TestCase
+ include ActiveRecord::Migration::TestHelper
+
+ self.use_transactional_fixtures = false
+
+ # FIXME: this is more of an integration test with AR::Base and the
+ # schema modifications. Maybe we should move this?
+ def test_add_rename
+ add_column "test_models", "girlfriend", :string
+ TestModel.reset_column_information
+
+ TestModel.create :girlfriend => 'bobette'
+
+ rename_column "test_models", "girlfriend", "exgirlfriend"
+
+ TestModel.reset_column_information
+ bob = TestModel.find(:first)
+
+ assert_equal "bobette", bob.exgirlfriend
+ end
+
+ # FIXME: another integration test. We should decouple this from the
+ # AR::Base implementation.
+ def test_rename_column_using_symbol_arguments
+ add_column :test_models, :first_name, :string
+
+ TestModel.create :first_name => 'foo'
+
+ rename_column :test_models, :first_name, :nick_name
+ TestModel.reset_column_information
+ assert TestModel.column_names.include?("nick_name")
+ assert_equal ['foo'], TestModel.find(:all).map(&:nick_name)
+ end
+
+ # FIXME: another integration test. We should decouple this from the
+ # AR::Base implementation.
+ def test_rename_column
+ add_column "test_models", "first_name", "string"
+
+ TestModel.create :first_name => 'foo'
+
+ rename_column "test_models", "first_name", "nick_name"
+ TestModel.reset_column_information
+ assert TestModel.column_names.include?("nick_name")
+ assert_equal ['foo'], TestModel.find(:all).map(&:nick_name)
+ end
+
+ def test_rename_column_preserves_default_value_not_null
+ add_column 'test_models', 'salary', :integer, :default => 70000
+
+ default_before = connection.columns("test_models").find { |c| c.name == "salary" }.default
+ assert_equal 70000, default_before
+
+ rename_column "test_models", "salary", "anual_salary"
+
+ assert TestModel.column_names.include?("anual_salary")
+ default_after = connection.columns("test_models").find { |c| c.name == "anual_salary" }.default
+ assert_equal 70000, default_after
+ end
+
+ def test_rename_nonexistent_column
+ exception = if current_adapter?(:PostgreSQLAdapter, :OracleAdapter)
+ ActiveRecord::StatementInvalid
+ else
+ ActiveRecord::ActiveRecordError
+ end
+ assert_raise(exception) do
+ rename_column "test_models", "nonexistent", "should_fail"
+ end
+ end
+
+ def test_rename_column_with_sql_reserved_word
+ add_column 'test_models', 'first_name', :string
+ rename_column "test_models", "first_name", "group"
+
+ assert TestModel.column_names.include?("group")
+ end
+
+ def test_rename_column_with_an_index
+ add_column "test_models", :hat_name, :string
+ add_index :test_models, :hat_name
+
+ # FIXME: we should test that the index goes away
+ rename_column "test_models", "hat_name", "name"
+ end
+
+ def test_remove_column_with_index
+ add_column "test_models", :hat_name, :string
+ add_index :test_models, :hat_name
+
+ # FIXME: we should test that the index goes away
+ remove_column("test_models", "hat_name")
+ end
+
+ def test_remove_column_with_multi_column_index
+ add_column "test_models", :hat_size, :integer
+ add_column "test_models", :hat_style, :string, :limit => 100
+ add_index "test_models", ["hat_style", "hat_size"], :unique => true
+
+ # FIXME: we should test that the index goes away
+ remove_column("test_models", "hat_size")
+ end
+
+ # FIXME: we need to test that these calls do something
+ def test_change_type_of_not_null_column
+ change_column "test_models", "updated_at", :datetime, :null => false
+ change_column "test_models", "updated_at", :datetime, :null => false
+ change_column "test_models", "updated_at", :datetime, :null => true
+ end
+
+ def test_change_column_nullability
+ add_column "test_models", "funny", :boolean
+ assert TestModel.columns_hash["funny"].null, "Column 'funny' must initially allow nulls"
+
+ change_column "test_models", "funny", :boolean, :null => false, :default => true
+
+ TestModel.reset_column_information
+ refute TestModel.columns_hash["funny"].null, "Column 'funny' must *not* allow nulls at this point"
+
+ change_column "test_models", "funny", :boolean, :null => true
+ TestModel.reset_column_information
+ assert TestModel.columns_hash["funny"].null, "Column 'funny' must allow nulls again at this point"
+ end
+
+ def test_change_column
+ add_column 'test_models', 'age', :integer
+ add_column 'test_models', 'approved', :boolean, :default => true
+
+ old_columns = connection.columns(TestModel.table_name)
+
+ assert old_columns.find { |c| c.name == 'age' && c.type == :integer }
+
+ change_column "test_models", "age", :string
+
+ new_columns = connection.columns(TestModel.table_name)
+
+ refute new_columns.find { |c| c.name == 'age' and c.type == :integer }
+ assert new_columns.find { |c| c.name == 'age' and c.type == :string }
+
+ old_columns = connection.columns(TestModel.table_name)
+ assert old_columns.find { |c|
+ c.name == 'approved' && c.type == :boolean && c.default == true
+ }
+
+ change_column :test_models, :approved, :boolean, :default => false
+ new_columns = connection.columns(TestModel.table_name)
+
+ refute new_columns.find { |c| c.name == 'approved' and c.type == :boolean and c.default == true }
+ assert new_columns.find { |c| c.name == 'approved' and c.type == :boolean and c.default == false }
+ change_column :test_models, :approved, :boolean, :default => true
+ end
+
+ def test_change_column_with_nil_default
+ add_column "test_models", "contributor", :boolean, :default => true
+ assert TestModel.new.contributor?
+
+ change_column "test_models", "contributor", :boolean, :default => nil
+ TestModel.reset_column_information
+ refute TestModel.new.contributor?
+ assert_nil TestModel.new.contributor
+ end
+
+ def test_change_column_with_new_default
+ add_column "test_models", "administrator", :boolean, :default => true
+ assert TestModel.new.administrator?
+
+ change_column "test_models", "administrator", :boolean, :default => false
+ TestModel.reset_column_information
+ refute TestModel.new.administrator?
+ end
+
+ def test_change_column_default
+ add_column "test_models", "first_name", :string
+ connection.change_column_default "test_models", "first_name", "Tester"
+
+ assert_equal "Tester", TestModel.new.first_name
+ end
+
+ def test_change_column_default_to_null
+ add_column "test_models", "first_name", :string
+ connection.change_column_default "test_models", "first_name", nil
+ assert_nil TestModel.new.first_name
+ end
+
+ def test_remove_column_no_second_parameter_raises_exception
+ assert_raise(ArgumentError) { connection.remove_column("funny") }
+ end
+ end
+ end
+end
diff --git a/activerecord/test/cases/migration/rename_table_test.rb b/activerecord/test/cases/migration/rename_table_test.rb
new file mode 100644
index 0000000000..d5ff2c607f
--- /dev/null
+++ b/activerecord/test/cases/migration/rename_table_test.rb
@@ -0,0 +1,72 @@
+require "cases/migration/helper"
+
+module ActiveRecord
+ class Migration
+ class RenameTableTest < ActiveRecord::TestCase
+ include ActiveRecord::Migration::TestHelper
+
+ self.use_transactional_fixtures = false
+
+ def setup
+ super
+ add_column 'test_models', :url, :string
+ remove_column 'test_models', :created_at
+ remove_column 'test_models', :updated_at
+ end
+
+ def test_rename_table_for_sqlite_should_work_with_reserved_words
+ renamed = false
+
+ skip "not supported" unless current_adapter?(:SQLite3Adapter)
+
+ add_column :test_models, :url, :string
+ connection.rename_table :references, :old_references
+ connection.rename_table :test_models, :references
+
+ renamed = true
+
+ # Using explicit id in insert for compatibility across all databases
+ con = connection
+ con.execute "INSERT INTO 'references' (url, created_at, updated_at) VALUES ('http://rubyonrails.com', 0, 0)"
+ assert_equal 'http://rubyonrails.com', connection.select_value("SELECT url FROM 'references' WHERE id=1")
+ ensure
+ return unless renamed
+ connection.rename_table :references, :test_models
+ connection.rename_table :old_references, :references
+ end
+
+ def test_rename_table
+ rename_table :test_models, :octopi
+
+ # Using explicit id in insert for compatibility across all databases
+ con = connection
+ con.enable_identity_insert("octopi", true) if current_adapter?(:SybaseAdapter)
+
+ con.execute "INSERT INTO octopi (#{con.quote_column_name('id')}, #{con.quote_column_name('url')}) VALUES (1, 'http://www.foreverflying.com/octopus-black7.jpg')"
+
+ con.enable_identity_insert("octopi", false) if current_adapter?(:SybaseAdapter)
+
+ assert_equal 'http://www.foreverflying.com/octopus-black7.jpg', connection.select_value("SELECT url FROM octopi WHERE id=1")
+
+ rename_table :octopi, :test_models
+ end
+
+ def test_rename_table_with_an_index
+ add_index :test_models, :url
+
+ rename_table :test_models, :octopi
+
+ # Using explicit id in insert for compatibility across all databases
+ con = ActiveRecord::Base.connection
+ con.enable_identity_insert("octopi", true) if current_adapter?(:SybaseAdapter)
+ con.execute "INSERT INTO octopi (#{con.quote_column_name('id')}, #{con.quote_column_name('url')}) VALUES (1, 'http://www.foreverflying.com/octopus-black7.jpg')"
+ con.enable_identity_insert("octopi", false) if current_adapter?(:SybaseAdapter)
+
+ assert_equal 'http://www.foreverflying.com/octopus-black7.jpg', connection.select_value("SELECT url FROM octopi WHERE id=1")
+ assert connection.indexes(:octopi).first.columns.include?("url")
+
+ rename_table :octopi, :test_models
+ end
+ end
+ end
+end
diff --git a/activerecord/test/cases/migration/table_and_index_test.rb b/activerecord/test/cases/migration/table_and_index_test.rb
new file mode 100644
index 0000000000..8fd770abd1
--- /dev/null
+++ b/activerecord/test/cases/migration/table_and_index_test.rb
@@ -0,0 +1,24 @@
+require "cases/helper"
+
+module ActiveRecord
+ class Migration
+ class TableAndIndexTest < ActiveRecord::TestCase
+ def test_add_schema_info_respects_prefix_and_suffix
+ conn = ActiveRecord::Base.connection
+
+ conn.drop_table(ActiveRecord::Migrator.schema_migrations_table_name) if conn.table_exists?(ActiveRecord::Migrator.schema_migrations_table_name)
+ # Use shorter prefix and suffix as in Oracle database identifier cannot be larger than 30 characters
+ ActiveRecord::Base.table_name_prefix = 'p_'
+ ActiveRecord::Base.table_name_suffix = '_s'
+ conn.drop_table(ActiveRecord::Migrator.schema_migrations_table_name) if conn.table_exists?(ActiveRecord::Migrator.schema_migrations_table_name)
+
+ conn.initialize_schema_migrations_table
+
+ assert_equal "p_unique_schema_migrations_s", conn.indexes(ActiveRecord::Migrator.schema_migrations_table_name)[0][:name]
+ ensure
+ ActiveRecord::Base.table_name_prefix = ""
+ ActiveRecord::Base.table_name_suffix = ""
+ end
+ end
+ end
+end
diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb
index 93a1249e43..575df2f84b 100644
--- a/activerecord/test/cases/migration_test.rb
+++ b/activerecord/test/cases/migration_test.rb
@@ -1,4 +1,5 @@
require "cases/helper"
+require "cases/migration/helper"
require 'bigdecimal/util'
require 'models/person'
@@ -6,2235 +7,941 @@ require 'models/topic'
require 'models/developer'
require MIGRATIONS_ROOT + "/valid/2_we_need_reminders"
+require MIGRATIONS_ROOT + "/rename/1_we_need_things"
+require MIGRATIONS_ROOT + "/rename/2_rename_things"
require MIGRATIONS_ROOT + "/decimal/1_give_me_big_numbers"
-if ActiveRecord::Base.connection.supports_migrations?
- class BigNumber < ActiveRecord::Base; end
+class BigNumber < ActiveRecord::Base; end
- class Reminder < ActiveRecord::Base; end
+class Reminder < ActiveRecord::Base; end
- class ActiveRecord::Migration
- class << self
- attr_accessor :message_count
- end
-
- def puts(text="")
- ActiveRecord::Migration.message_count ||= 0
- ActiveRecord::Migration.message_count += 1
- end
- end
-
- class MigrationTableAndIndexTest < ActiveRecord::TestCase
- def test_add_schema_info_respects_prefix_and_suffix
- conn = ActiveRecord::Base.connection
+class Thing < ActiveRecord::Base; end
- conn.drop_table(ActiveRecord::Migrator.schema_migrations_table_name) if conn.table_exists?(ActiveRecord::Migrator.schema_migrations_table_name)
- # Use shorter prefix and suffix as in Oracle database identifier cannot be larger than 30 characters
- ActiveRecord::Base.table_name_prefix = 'p_'
- ActiveRecord::Base.table_name_suffix = '_s'
- conn.drop_table(ActiveRecord::Migrator.schema_migrations_table_name) if conn.table_exists?(ActiveRecord::Migrator.schema_migrations_table_name)
+class MigrationTest < ActiveRecord::TestCase
+ self.use_transactional_fixtures = false
- conn.initialize_schema_migrations_table
+ fixtures :people
- assert_equal "p_unique_schema_migrations_s", conn.indexes(ActiveRecord::Migrator.schema_migrations_table_name)[0][:name]
- ensure
- ActiveRecord::Base.table_name_prefix = ""
- ActiveRecord::Base.table_name_suffix = ""
+ def setup
+ super
+ %w(reminders people_reminders prefix_reminders_suffix).each do |table|
+ Reminder.connection.drop_table(table) rescue nil
end
+ Reminder.reset_column_information
+ ActiveRecord::Migration.verbose = true
+ ActiveRecord::Migration.message_count = 0
end
- class MigrationTest < ActiveRecord::TestCase
- self.use_transactional_fixtures = false
-
- fixtures :people
-
- def setup
- ActiveRecord::Migration.verbose = true
- ActiveRecord::Migration.message_count = 0
- end
-
- def teardown
- ActiveRecord::Base.connection.initialize_schema_migrations_table
- ActiveRecord::Base.connection.execute "DELETE FROM #{ActiveRecord::Migrator.schema_migrations_table_name}"
-
- %w(reminders people_reminders prefix_reminders_suffix).each do |table|
- Reminder.connection.drop_table(table) rescue nil
- end
- Reminder.reset_column_information
-
- %w(last_name key bio age height wealth birthday favorite_day
- moment_of_truth male administrator funny).each do |column|
- Person.connection.remove_column('people', column) rescue nil
- end
- Person.connection.remove_column("people", "first_name") rescue nil
- Person.connection.remove_column("people", "middle_name") rescue nil
- Person.connection.add_column("people", "first_name", :string, :limit => 40)
- Person.reset_column_information
- end
-
- def test_add_index
- # Limit size of last_name and key columns to support Firebird index limitations
- Person.connection.add_column "people", "last_name", :string, :limit => 100
- Person.connection.add_column "people", "key", :string, :limit => 100
- Person.connection.add_column "people", "administrator", :boolean
-
- assert_nothing_raised { Person.connection.add_index("people", "last_name") }
- assert_nothing_raised { Person.connection.remove_index("people", "last_name") }
-
- # Orcl nds shrt indx nms. Sybs 2.
- # OpenBase does not have named indexes. You must specify a single column name
- unless current_adapter?(:SybaseAdapter, :OpenBaseAdapter)
- assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"]) }
- assert_nothing_raised { Person.connection.remove_index("people", :column => ["last_name", "first_name"]) }
- # Oracle adapter cannot have specified index name larger than 30 characters
- # Oracle adapter is shortening index name when just column list is given
- unless current_adapter?(:OracleAdapter)
- assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"]) }
- assert_nothing_raised { Person.connection.remove_index("people", :name => :index_people_on_last_name_and_first_name) }
- assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"]) }
- assert_nothing_raised { Person.connection.remove_index("people", "last_name_and_first_name") }
- end
- assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"]) }
- assert_nothing_raised { Person.connection.remove_index("people", ["last_name", "first_name"]) }
- assert_nothing_raised { Person.connection.add_index("people", ["last_name"], :length => 10) }
- assert_nothing_raised { Person.connection.remove_index("people", "last_name") }
- assert_nothing_raised { Person.connection.add_index("people", ["last_name"], :length => {:last_name => 10}) }
- assert_nothing_raised { Person.connection.remove_index("people", ["last_name"]) }
- assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"], :length => 10) }
- assert_nothing_raised { Person.connection.remove_index("people", ["last_name", "first_name"]) }
- assert_nothing_raised { Person.connection.add_index("people", ["last_name", "first_name"], :length => {:last_name => 10, :first_name => 20}) }
- assert_nothing_raised { Person.connection.remove_index("people", ["last_name", "first_name"]) }
- end
-
- # quoting
- # Note: changed index name from "key" to "key_idx" since "key" is a Firebird reserved word
- # OpenBase does not have named indexes. You must specify a single column name
- unless current_adapter?(:OpenBaseAdapter)
- Person.update_all "#{Person.connection.quote_column_name 'key'}=#{Person.connection.quote_column_name 'id'}" #some databases (including sqlite2 won't add a unique index if existing data non unique)
- assert_nothing_raised { Person.connection.add_index("people", ["key"], :name => "key_idx", :unique => true) }
- assert_nothing_raised { Person.connection.remove_index("people", :name => "key_idx", :unique => true) }
- end
-
- # Sybase adapter does not support indexes on :boolean columns
- # OpenBase does not have named indexes. You must specify a single column
- unless current_adapter?(:SybaseAdapter, :OpenBaseAdapter)
- assert_nothing_raised { Person.connection.add_index("people", %w(last_name first_name administrator), :name => "named_admin") }
- assert_nothing_raised { Person.connection.remove_index("people", :name => "named_admin") }
- end
- end
-
- def test_index_symbol_names
- assert_nothing_raised { Person.connection.add_index :people, :primary_contact_id, :name => :symbol_index_name }
- assert Person.connection.index_exists?(:people, :primary_contact_id, :name => :symbol_index_name)
- assert_nothing_raised { Person.connection.remove_index :people, :name => :symbol_index_name }
- assert !Person.connection.index_exists?(:people, :primary_contact_id, :name => :symbol_index_name)
- end
-
- def test_add_index_length_limit
- good_index_name = 'x' * Person.connection.index_name_length
- too_long_index_name = good_index_name + 'x'
- assert_raise(ArgumentError) { Person.connection.add_index("people", "first_name", :name => too_long_index_name) }
- assert !Person.connection.index_name_exists?("people", too_long_index_name, false)
- assert_nothing_raised { Person.connection.add_index("people", "first_name", :name => good_index_name) }
- assert Person.connection.index_name_exists?("people", good_index_name, false)
- Person.connection.remove_index("people", :name => good_index_name)
- end
-
- def test_remove_nonexistent_index
- # we do this by name, so OpenBase is a wash as noted above
- unless current_adapter?(:OpenBaseAdapter)
- assert_raise(ArgumentError) { Person.connection.remove_index("people", "no_such_index") }
- end
- end
-
- def test_rename_index
- unless current_adapter?(:OpenBaseAdapter)
- # keep the names short to make Oracle and similar behave
- Person.connection.add_index('people', [:first_name], :name => 'old_idx')
- assert_nothing_raised { Person.connection.rename_index('people', 'old_idx', 'new_idx') }
- # if the adapter doesn't support the indexes call, pick defaults that let the test pass
- assert !Person.connection.index_name_exists?('people', 'old_idx', false)
- assert Person.connection.index_name_exists?('people', 'new_idx', true)
- end
- end
-
- def test_double_add_index
- unless current_adapter?(:OpenBaseAdapter)
- Person.connection.add_index('people', [:first_name], :name => 'some_idx')
- assert_raise(ArgumentError) { Person.connection.add_index('people', [:first_name], :name => 'some_idx') }
- end
- end
-
- def test_index_exists
- Person.connection.create_table :testings do |t|
- t.column :foo, :string, :limit => 100
- t.column :bar, :string, :limit => 100
- end
- Person.connection.add_index :testings, :foo
+ def teardown
+ ActiveRecord::Base.connection.initialize_schema_migrations_table
+ ActiveRecord::Base.connection.execute "DELETE FROM #{ActiveRecord::Migrator.schema_migrations_table_name}"
- assert Person.connection.index_exists?(:testings, :foo)
- assert !Person.connection.index_exists?(:testings, :bar)
- ensure
- Person.connection.drop_table :testings rescue nil
+ %w(things awesome_things prefix_things_suffix prefix_awesome_things_suffix).each do |table|
+ Thing.connection.drop_table(table) rescue nil
end
+ Thing.reset_column_information
- def test_index_exists_on_multiple_columns
- Person.connection.create_table :testings do |t|
- t.column :foo, :string, :limit => 100
- t.column :bar, :string, :limit => 100
- end
- Person.connection.add_index :testings, [:foo, :bar]
-
- assert Person.connection.index_exists?(:testings, [:foo, :bar])
- ensure
- Person.connection.drop_table :testings rescue nil
+ %w(reminders people_reminders prefix_reminders_suffix).each do |table|
+ Reminder.connection.drop_table(table) rescue nil
end
+ Reminder.reset_column_information
- def test_unique_index_exists
- Person.connection.create_table :testings do |t|
- t.column :foo, :string, :limit => 100
- end
- Person.connection.add_index :testings, :foo, :unique => true
-
- assert Person.connection.index_exists?(:testings, :foo, :unique => true)
- ensure
- Person.connection.drop_table :testings rescue nil
+ %w(last_name key bio age height wealth birthday favorite_day
+ moment_of_truth male administrator funny).each do |column|
+ Person.connection.remove_column('people', column) rescue nil
end
+ Person.connection.remove_column("people", "first_name") rescue nil
+ Person.connection.remove_column("people", "middle_name") rescue nil
+ Person.connection.add_column("people", "first_name", :string, :limit => 40)
+ Person.reset_column_information
+ end
- def test_named_index_exists
- Person.connection.create_table :testings do |t|
- t.column :foo, :string, :limit => 100
- end
- Person.connection.add_index :testings, :foo, :name => "custom_index_name"
-
- assert Person.connection.index_exists?(:testings, :foo, :name => "custom_index_name")
- ensure
- Person.connection.drop_table :testings rescue nil
+ def test_create_table_with_force_true_does_not_drop_nonexisting_table
+ if Person.connection.table_exists?(:testings2)
+ Person.connection.drop_table :testings2
end
- def testing_table_with_only_foo_attribute
- Person.connection.create_table :testings, :id => false do |t|
- t.column :foo, :string
- end
+ # using a copy as we need the drop_table method to
+ # continue to work for the ensure block of the test
+ temp_conn = Person.connection.dup
- yield Person.connection
- ensure
- Person.connection.drop_table :testings rescue nil
- end
- protected :testing_table_with_only_foo_attribute
+ assert_not_equal temp_conn, Person.connection
- def test_create_table_without_id
- testing_table_with_only_foo_attribute do |connection|
- assert_equal connection.columns(:testings).size, 1
- end
- end
-
- def test_add_column_with_primary_key_attribute
- testing_table_with_only_foo_attribute do |connection|
- assert_nothing_raised { connection.add_column :testings, :id, :primary_key }
- assert_equal connection.columns(:testings).size, 2
- end
+ temp_conn.create_table :testings2, :force => true do |t|
+ t.column :foo, :string
end
+ ensure
+ Person.connection.drop_table :testings2 rescue nil
+ end
- def test_create_table_adds_id
- Person.connection.create_table :testings do |t|
- t.column :foo, :string
- end
-
- assert_equal %w(foo id),
- Person.connection.columns(:testings).map { |c| c.name }.sort
- ensure
- Person.connection.drop_table :testings rescue nil
- end
-
- def test_create_table_with_not_null_column
- assert_nothing_raised do
- Person.connection.create_table :testings do |t|
- t.column :foo, :string, :null => false
- end
- end
-
- assert_raise(ActiveRecord::StatementInvalid) do
- Person.connection.execute "insert into testings (foo) values (NULL)"
- end
- ensure
- Person.connection.drop_table :testings rescue nil
- end
+ def connection
+ ActiveRecord::Base.connection
+ end
- def test_create_table_with_defaults
- # MySQL doesn't allow defaults on TEXT or BLOB columns.
- mysql = current_adapter?(:MysqlAdapter) || current_adapter?(:Mysql2Adapter)
+ def test_migration_instance_has_connection
+ migration = Class.new(ActiveRecord::Migration).new
+ assert_equal connection, migration.connection
+ end
- Person.connection.create_table :testings do |t|
- t.column :one, :string, :default => "hello"
- t.column :two, :boolean, :default => true
- t.column :three, :boolean, :default => false
- t.column :four, :integer, :default => 1
- t.column :five, :text, :default => "hello" unless mysql
+ def test_method_missing_delegates_to_connection
+ migration = Class.new(ActiveRecord::Migration) {
+ def connection
+ Class.new {
+ def create_table; "hi mom!"; end
+ }.new
end
+ }.new
- columns = Person.connection.columns(:testings)
- one = columns.detect { |c| c.name == "one" }
- two = columns.detect { |c| c.name == "two" }
- three = columns.detect { |c| c.name == "three" }
- four = columns.detect { |c| c.name == "four" }
- five = columns.detect { |c| c.name == "five" } unless mysql
-
- assert_equal "hello", one.default
- assert_equal true, two.default
- assert_equal false, three.default
- assert_equal 1, four.default
- assert_equal "hello", five.default unless mysql
+ assert_equal "hi mom!", migration.method_missing(:create_table)
+ end
- ensure
- Person.connection.drop_table :testings rescue nil
+ def test_add_table_with_decimals
+ Person.connection.drop_table :big_numbers rescue nil
+
+ assert !BigNumber.table_exists?
+ GiveMeBigNumbers.up
+
+ assert BigNumber.create(
+ :bank_balance => 1586.43,
+ :big_bank_balance => BigDecimal("1000234000567.95"),
+ :world_population => 6000000000,
+ :my_house_population => 3,
+ :value_of_e => BigDecimal("2.7182818284590452353602875")
+ )
+
+ b = BigNumber.find(:first)
+ assert_not_nil b
+
+ assert_not_nil b.bank_balance
+ assert_not_nil b.big_bank_balance
+ assert_not_nil b.world_population
+ assert_not_nil b.my_house_population
+ assert_not_nil b.value_of_e
+
+ # TODO: set world_population >= 2**62 to cover 64-bit platforms and test
+ # is_a?(Bignum)
+ assert_kind_of Integer, b.world_population
+ assert_equal 6000000000, b.world_population
+ assert_kind_of Fixnum, b.my_house_population
+ assert_equal 3, b.my_house_population
+ assert_kind_of BigDecimal, b.bank_balance
+ assert_equal BigDecimal("1586.43"), b.bank_balance
+ assert_kind_of BigDecimal, b.big_bank_balance
+ assert_equal BigDecimal("1000234000567.95"), b.big_bank_balance
+
+ # This one is fun. The 'value_of_e' field is defined as 'DECIMAL' with
+ # precision/scale explicitly left out. By the SQL standard, numbers
+ # assigned to this field should be truncated but that's seldom respected.
+ if current_adapter?(:PostgreSQLAdapter)
+ # - PostgreSQL changes the SQL spec on columns declared simply as
+ # "decimal" to something more useful: instead of being given a scale
+ # of 0, they take on the compile-time limit for precision and scale,
+ # so the following should succeed unless you have used really wacky
+ # compilation options
+ # - SQLite2 has the default behavior of preserving all data sent in,
+ # so this happens there too
+ assert_kind_of BigDecimal, b.value_of_e
+ assert_equal BigDecimal("2.7182818284590452353602875"), b.value_of_e
+ elsif current_adapter?(:SQLite3Adapter)
+ # - SQLite3 stores a float, in violation of SQL
+ assert_kind_of BigDecimal, b.value_of_e
+ assert_in_delta BigDecimal("2.71828182845905"), b.value_of_e, 0.00000000000001
+ else
+ # - SQL standard is an integer
+ assert_kind_of Fixnum, b.value_of_e
+ assert_equal 2, b.value_of_e
end
- def test_create_table_with_limits
- assert_nothing_raised do
- Person.connection.create_table :testings do |t|
- t.column :foo, :string, :limit => 255
+ GiveMeBigNumbers.down
+ assert_raise(ActiveRecord::StatementInvalid) { BigNumber.find(:first) }
+ end
- t.column :default_int, :integer
+ def test_filtering_migrations
+ assert !Person.column_methods_hash.include?(:last_name)
+ assert !Reminder.table_exists?
- t.column :one_int, :integer, :limit => 1
- t.column :four_int, :integer, :limit => 4
- t.column :eight_int, :integer, :limit => 8
- t.column :eleven_int, :integer, :limit => 11
- end
- end
+ name_filter = lambda { |migration| migration.name == "ValidPeopleHaveLastNames" }
+ ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", &name_filter)
- columns = Person.connection.columns(:testings)
- foo = columns.detect { |c| c.name == "foo" }
- assert_equal 255, foo.limit
-
- default = columns.detect { |c| c.name == "default_int" }
- one = columns.detect { |c| c.name == "one_int" }
- four = columns.detect { |c| c.name == "four_int" }
- eight = columns.detect { |c| c.name == "eight_int" }
- eleven = columns.detect { |c| c.name == "eleven_int" }
-
- if current_adapter?(:PostgreSQLAdapter)
- assert_equal 'integer', default.sql_type
- assert_equal 'smallint', one.sql_type
- assert_equal 'integer', four.sql_type
- assert_equal 'bigint', eight.sql_type
- assert_equal 'integer', eleven.sql_type
- elsif current_adapter?(:MysqlAdapter) or current_adapter?(:Mysql2Adapter)
- assert_match 'int(11)', default.sql_type
- assert_match 'tinyint', one.sql_type
- assert_match 'int', four.sql_type
- assert_match 'bigint', eight.sql_type
- assert_match 'int(11)', eleven.sql_type
- elsif current_adapter?(:OracleAdapter)
- assert_equal 'NUMBER(38)', default.sql_type
- assert_equal 'NUMBER(1)', one.sql_type
- assert_equal 'NUMBER(4)', four.sql_type
- assert_equal 'NUMBER(8)', eight.sql_type
- end
- ensure
- Person.connection.drop_table :testings rescue nil
- end
+ Person.reset_column_information
+ assert Person.column_methods_hash.include?(:last_name)
+ assert_raise(ActiveRecord::StatementInvalid) { Reminder.find(:first) }
- def test_create_table_with_primary_key_prefix_as_table_name_with_underscore
- ActiveRecord::Base.primary_key_prefix_type = :table_name_with_underscore
+ ActiveRecord::Migrator.down(MIGRATIONS_ROOT + "/valid", &name_filter)
- Person.connection.create_table :testings do |t|
- t.column :foo, :string
- end
+ Person.reset_column_information
+ assert !Person.column_methods_hash.include?(:last_name)
+ assert_raise(ActiveRecord::StatementInvalid) { Reminder.find(:first) }
+ end
- assert_equal %w(foo testing_id), Person.connection.columns(:testings).map { |c| c.name }.sort
- ensure
- Person.connection.drop_table :testings rescue nil
- ActiveRecord::Base.primary_key_prefix_type = nil
+ class MockMigration < ActiveRecord::Migration
+ attr_reader :went_up, :went_down
+ def initialize
+ @went_up = false
+ @went_down = false
end
- def test_create_table_with_primary_key_prefix_as_table_name
- ActiveRecord::Base.primary_key_prefix_type = :table_name
-
- Person.connection.create_table :testings do |t|
- t.column :foo, :string
- end
-
- assert_equal %w(foo testingid), Person.connection.columns(:testings).map { |c| c.name }.sort
- ensure
- Person.connection.drop_table :testings rescue nil
- ActiveRecord::Base.primary_key_prefix_type = nil
+ def up
+ @went_up = true
+ super
end
- def test_create_table_with_force_true_does_not_drop_nonexisting_table
- if Person.connection.table_exists?(:testings2)
- Person.connection.drop_table :testings2
- end
-
- # using a copy as we need the drop_table method to
- # continue to work for the ensure block of the test
- temp_conn = Person.connection.dup
- temp_conn.expects(:drop_table).never
- temp_conn.create_table :testings2, :force => true do |t|
- t.column :foo, :string
- end
- ensure
- Person.connection.drop_table :testings2 rescue nil
+ def down
+ @went_down = true
+ super
end
+ end
- def test_create_table_with_timestamps_should_create_datetime_columns
- table_name = :testings
-
- Person.connection.create_table table_name do |t|
- t.timestamps
- end
- created_columns = Person.connection.columns(table_name)
-
- created_at_column = created_columns.detect {|c| c.name == 'created_at' }
- updated_at_column = created_columns.detect {|c| c.name == 'updated_at' }
-
- assert created_at_column.null
- assert updated_at_column.null
- ensure
- Person.connection.drop_table table_name rescue nil
- end
-
- def test_create_table_with_timestamps_should_create_datetime_columns_with_options
- table_name = :testings
-
- Person.connection.create_table table_name do |t|
- t.timestamps :null => false
- end
- created_columns = Person.connection.columns(table_name)
-
- created_at_column = created_columns.detect {|c| c.name == 'created_at' }
- updated_at_column = created_columns.detect {|c| c.name == 'updated_at' }
+ def test_instance_based_migration_up
+ migration = MockMigration.new
+ assert !migration.went_up, 'have not gone up'
+ assert !migration.went_down, 'have not gone down'
- assert !created_at_column.null
- assert !updated_at_column.null
- ensure
- Person.connection.drop_table table_name rescue nil
- end
+ migration.migrate :up
+ assert migration.went_up, 'have gone up'
+ assert !migration.went_down, 'have not gone down'
+ end
- def test_create_table_without_a_block
- table_name = :testings
- Person.connection.create_table table_name
- ensure
- Person.connection.drop_table table_name rescue nil
- end
+ def test_instance_based_migration_down
+ migration = MockMigration.new
+ assert !migration.went_up, 'have not gone up'
+ assert !migration.went_down, 'have not gone down'
- # Sybase, and SQLite3 will not allow you to add a NOT NULL
- # column to a table without a default value.
- unless current_adapter?(:SybaseAdapter, :SQLite3Adapter)
- def test_add_column_not_null_without_default
- Person.connection.create_table :testings do |t|
- t.column :foo, :string
- end
- Person.connection.add_column :testings, :bar, :string, :null => false
+ migration.migrate :down
+ assert !migration.went_up, 'have gone up'
+ assert migration.went_down, 'have not gone down'
+ end
- assert_raise(ActiveRecord::StatementInvalid) do
- Person.connection.execute "insert into testings (foo, bar) values ('hello', NULL)"
- end
- ensure
- Person.connection.drop_table :testings rescue nil
- end
+ def test_migrator_one_up_with_exception_and_rollback
+ unless ActiveRecord::Base.connection.supports_ddl_transactions?
+ skip "not supported on #{ActiveRecord::Base.connection.class}"
end
- def test_add_column_not_null_with_default
- Person.connection.create_table :testings do |t|
- t.column :foo, :string
- end
-
- con = Person.connection
- Person.connection.enable_identity_insert("testings", true) if current_adapter?(:SybaseAdapter)
- Person.connection.execute "insert into testings (#{con.quote_column_name('id')}, #{con.quote_column_name('foo')}) values (1, 'hello')"
- Person.connection.enable_identity_insert("testings", false) if current_adapter?(:SybaseAdapter)
- assert_nothing_raised {Person.connection.add_column :testings, :bar, :string, :null => false, :default => "default" }
-
- assert_raise(ActiveRecord::StatementInvalid) do
- unless current_adapter?(:OpenBaseAdapter)
- Person.connection.execute "insert into testings (#{con.quote_column_name('id')}, #{con.quote_column_name('foo')}, #{con.quote_column_name('bar')}) values (2, 'hello', NULL)"
- else
- Person.connection.insert("INSERT INTO testings (#{con.quote_column_name('id')}, #{con.quote_column_name('foo')}, #{con.quote_column_name('bar')}) VALUES (2, 'hello', NULL)",
- "Testing Insert","id",2)
- end
- end
- ensure
- Person.connection.drop_table :testings rescue nil
- end
-
- # We specifically do a manual INSERT here, and then test only the SELECT
- # functionality. This allows us to more easily catch INSERT being broken,
- # but SELECT actually working fine.
- def test_native_decimal_insert_manual_vs_automatic
- correct_value = '0012345678901234567890.0123456789'.to_d
-
- Person.delete_all
- Person.connection.add_column "people", "wealth", :decimal, :precision => '30', :scale => '10'
- Person.reset_column_information
-
- # Do a manual insertion
- if current_adapter?(:OracleAdapter)
- Person.connection.execute "insert into people (id, wealth) values (people_seq.nextval, 12345678901234567890.0123456789)"
- elsif current_adapter?(:OpenBaseAdapter) || (current_adapter?(:MysqlAdapter) && Mysql.client_version < 50003) #before mysql 5.0.3 decimals stored as strings
- Person.connection.execute "insert into people (wealth) values ('12345678901234567890.0123456789')"
- else
- Person.connection.execute "insert into people (wealth) values (12345678901234567890.0123456789)"
- end
-
- # SELECT
- row = Person.find(:first)
- assert_kind_of BigDecimal, row.wealth
+ refute Person.column_methods_hash.include?(:last_name)
- # If this assert fails, that means the SELECT is broken!
- unless current_adapter?(:SQLite3Adapter)
- assert_equal correct_value, row.wealth
- end
+ migration = Struct.new(:name, :version) {
+ def migrate(x); raise 'Something broke'; end
+ }.new('zomg', 100)
- # Reset to old state
- Person.delete_all
+ migrator = ActiveRecord::Migrator.new(:up, [migration], 100)
- # Now use the Rails insertion
- assert_nothing_raised { Person.create :wealth => BigDecimal.new("12345678901234567890.0123456789") }
+ e = assert_raise(StandardError) { migrator.migrate }
- # SELECT
- row = Person.find(:first)
- assert_kind_of BigDecimal, row.wealth
+ assert_equal "An error has occurred, this and all later migrations canceled:\n\nSomething broke", e.message
- # If these asserts fail, that means the INSERT (create function, or cast to SQL) is broken!
- unless current_adapter?(:SQLite3Adapter)
- assert_equal correct_value, row.wealth
- end
-
- # Reset to old state
- Person.connection.del_column "people", "wealth" rescue nil
- Person.reset_column_information
- end
-
- def test_add_column_with_precision_and_scale
- Person.connection.add_column 'people', 'wealth', :decimal, :precision => 9, :scale => 7
- Person.reset_column_information
-
- wealth_column = Person.columns_hash['wealth']
- assert_equal 9, wealth_column.precision
- assert_equal 7, wealth_column.scale
- end
-
- def test_native_types
- Person.delete_all
- Person.connection.add_column "people", "last_name", :string
- Person.connection.add_column "people", "bio", :text
- Person.connection.add_column "people", "age", :integer
- Person.connection.add_column "people", "height", :float
- Person.connection.add_column "people", "wealth", :decimal, :precision => '30', :scale => '10'
- Person.connection.add_column "people", "birthday", :datetime
- Person.connection.add_column "people", "favorite_day", :date
- Person.connection.add_column "people", "moment_of_truth", :datetime
- Person.connection.add_column "people", "male", :boolean
- Person.reset_column_information
-
- assert_nothing_raised do
- Person.create :first_name => 'bob', :last_name => 'bobsen',
- :bio => "I was born ....", :age => 18, :height => 1.78,
- :wealth => BigDecimal.new("12345678901234567890.0123456789"),
- :birthday => 18.years.ago, :favorite_day => 10.days.ago,
- :moment_of_truth => "1782-10-10 21:40:18", :male => true
- end
-
- bob = Person.find(:first)
- assert_equal 'bob', bob.first_name
- assert_equal 'bobsen', bob.last_name
- assert_equal "I was born ....", bob.bio
- assert_equal 18, bob.age
+ Person.reset_column_information
+ refute Person.column_methods_hash.include?(:last_name)
+ end
- # Test for 30 significant digits (beyond the 16 of float), 10 of them
- # after the decimal place.
+ def test_schema_migrations_table_name
+ ActiveRecord::Base.table_name_prefix = "prefix_"
+ ActiveRecord::Base.table_name_suffix = "_suffix"
+ Reminder.reset_table_name
+ assert_equal "prefix_schema_migrations_suffix", ActiveRecord::Migrator.schema_migrations_table_name
+ ActiveRecord::Base.table_name_prefix = ""
+ ActiveRecord::Base.table_name_suffix = ""
+ Reminder.reset_table_name
+ assert_equal "schema_migrations", ActiveRecord::Migrator.schema_migrations_table_name
+ ensure
+ ActiveRecord::Base.table_name_prefix = ""
+ ActiveRecord::Base.table_name_suffix = ""
+ end
- unless current_adapter?(:SQLite3Adapter)
- assert_equal BigDecimal.new("0012345678901234567890.0123456789"), bob.wealth
- end
+ def test_proper_table_name
+ assert_equal "table", ActiveRecord::Migrator.proper_table_name('table')
+ assert_equal "table", ActiveRecord::Migrator.proper_table_name(:table)
+ assert_equal "reminders", ActiveRecord::Migrator.proper_table_name(Reminder)
+ Reminder.reset_table_name
+ assert_equal Reminder.table_name, ActiveRecord::Migrator.proper_table_name(Reminder)
+
+ # Use the model's own prefix/suffix if a model is given
+ ActiveRecord::Base.table_name_prefix = "ARprefix_"
+ ActiveRecord::Base.table_name_suffix = "_ARsuffix"
+ Reminder.table_name_prefix = 'prefix_'
+ Reminder.table_name_suffix = '_suffix'
+ Reminder.reset_table_name
+ assert_equal "prefix_reminders_suffix", ActiveRecord::Migrator.proper_table_name(Reminder)
+ Reminder.table_name_prefix = ''
+ Reminder.table_name_suffix = ''
+ Reminder.reset_table_name
+
+ # Use AR::Base's prefix/suffix if string or symbol is given
+ ActiveRecord::Base.table_name_prefix = "prefix_"
+ ActiveRecord::Base.table_name_suffix = "_suffix"
+ Reminder.reset_table_name
+ assert_equal "prefix_table_suffix", ActiveRecord::Migrator.proper_table_name('table')
+ assert_equal "prefix_table_suffix", ActiveRecord::Migrator.proper_table_name(:table)
+ ActiveRecord::Base.table_name_prefix = ""
+ ActiveRecord::Base.table_name_suffix = ""
+ Reminder.reset_table_name
+ end
- assert_equal true, bob.male?
+ def test_rename_table_with_prefix_and_suffix
+ assert !Thing.table_exists?
+ ActiveRecord::Base.table_name_prefix = 'prefix_'
+ ActiveRecord::Base.table_name_suffix = '_suffix'
+ Thing.reset_table_name
+ Thing.reset_sequence_name
+ WeNeedThings.up
+
+ assert Thing.create("content" => "hello world")
+ assert_equal "hello world", Thing.find(:first).content
+
+ RenameThings.up
+ Thing.table_name = "prefix_awesome_things_suffix"
+
+ assert_equal "hello world", Thing.find(:first).content
+ ensure
+ ActiveRecord::Base.table_name_prefix = ''
+ ActiveRecord::Base.table_name_suffix = ''
+ Thing.reset_table_name
+ Thing.reset_sequence_name
+ end
- assert_equal String, bob.first_name.class
- assert_equal String, bob.last_name.class
- assert_equal String, bob.bio.class
- assert_equal Fixnum, bob.age.class
- assert_equal Time, bob.birthday.class
+ def test_add_drop_table_with_prefix_and_suffix
+ assert !Reminder.table_exists?
+ ActiveRecord::Base.table_name_prefix = 'prefix_'
+ ActiveRecord::Base.table_name_suffix = '_suffix'
+ Reminder.reset_table_name
+ Reminder.reset_sequence_name
+ WeNeedReminders.up
+ assert Reminder.create("content" => "hello world", "remind_at" => Time.now)
+ assert_equal "hello world", Reminder.find(:first).content
+
+ WeNeedReminders.down
+ assert_raise(ActiveRecord::StatementInvalid) { Reminder.find(:first) }
+ ensure
+ ActiveRecord::Base.table_name_prefix = ''
+ ActiveRecord::Base.table_name_suffix = ''
+ Reminder.reset_table_name
+ Reminder.reset_sequence_name
+ end
- if current_adapter?(:OracleAdapter, :SybaseAdapter)
- # Sybase, and Oracle don't differentiate between date/time
- assert_equal Time, bob.favorite_day.class
- else
- assert_equal Date, bob.favorite_day.class
- end
+ def test_create_table_with_binary_column
+ Person.connection.drop_table :binary_testings rescue nil
- # Oracle adapter stores Time or DateTime with timezone value already in _before_type_cast column
- # therefore no timezone change is done afterwards when default timezone is changed
- unless current_adapter?(:OracleAdapter)
- # Test DateTime column and defaults, including timezone.
- # FIXME: moment of truth may be Time on 64-bit platforms.
- if bob.moment_of_truth.is_a?(DateTime)
-
- with_env_tz 'US/Eastern' do
- bob.reload
- assert_equal DateTime.local_offset, bob.moment_of_truth.offset
- assert_not_equal 0, bob.moment_of_truth.offset
- assert_not_equal "Z", bob.moment_of_truth.zone
- # US/Eastern is -5 hours from GMT
- assert_equal Rational(-5, 24), bob.moment_of_truth.offset
- assert_match(/\A-05:?00\Z/, bob.moment_of_truth.zone) #ruby 1.8.6 uses HH:MM, prior versions use HHMM
- assert_equal DateTime::ITALY, bob.moment_of_truth.start
- end
- end
+ assert_nothing_raised {
+ Person.connection.create_table :binary_testings do |t|
+ t.column "data", :binary, :null => false
end
+ }
- assert_instance_of TrueClass, bob.male?
- assert_kind_of BigDecimal, bob.wealth
- end
+ columns = Person.connection.columns(:binary_testings)
+ data_column = columns.detect { |c| c.name == "data" }
if current_adapter?(:MysqlAdapter) or current_adapter?(:Mysql2Adapter)
- def test_unabstracted_database_dependent_types
- Person.delete_all
-
- ActiveRecord::Migration.add_column :people, :intelligence_quotient, :tinyint
- Person.reset_column_information
- assert_match(/tinyint/, Person.columns_hash['intelligence_quotient'].sql_type)
- ensure
- ActiveRecord::Migration.remove_column :people, :intelligence_quotient rescue nil
- end
- end
-
- def test_add_remove_single_field_using_string_arguments
- assert !Person.column_methods_hash.include?(:last_name)
-
- ActiveRecord::Migration.add_column 'people', 'last_name', :string
-
- Person.reset_column_information
- assert Person.column_methods_hash.include?(:last_name)
-
- ActiveRecord::Migration.remove_column 'people', 'last_name'
-
- Person.reset_column_information
- assert !Person.column_methods_hash.include?(:last_name)
- end
-
- def test_add_remove_single_field_using_symbol_arguments
- assert !Person.column_methods_hash.include?(:last_name)
-
- ActiveRecord::Migration.add_column :people, :last_name, :string
-
- Person.reset_column_information
- assert Person.column_methods_hash.include?(:last_name)
-
- ActiveRecord::Migration.remove_column :people, :last_name
-
- Person.reset_column_information
- assert !Person.column_methods_hash.include?(:last_name)
- end
-
- if current_adapter?(:MysqlAdapter) or current_adapter?(:Mysql2Adapter)
- def testing_table_for_positioning
- Person.connection.create_table :testings, :id => false do |t|
- t.column :first, :integer
- t.column :second, :integer
- t.column :third, :integer
- end
-
- yield Person.connection
- ensure
- Person.connection.drop_table :testings rescue nil
- end
- protected :testing_table_for_positioning
-
- def test_column_positioning
- testing_table_for_positioning do |conn|
- assert_equal %w(first second third), conn.columns(:testings).map {|c| c.name }
- end
- end
-
- def test_add_column_with_positioning
- testing_table_for_positioning do |conn|
- conn.add_column :testings, :new_col, :integer
- assert_equal %w(first second third new_col), conn.columns(:testings).map {|c| c.name }
- end
- testing_table_for_positioning do |conn|
- conn.add_column :testings, :new_col, :integer, :first => true
- assert_equal %w(new_col first second third), conn.columns(:testings).map {|c| c.name }
- end
- testing_table_for_positioning do |conn|
- conn.add_column :testings, :new_col, :integer, :after => :first
- assert_equal %w(first new_col second third), conn.columns(:testings).map {|c| c.name }
- end
- end
-
- def test_change_column_with_positioning
- testing_table_for_positioning do |conn|
- conn.change_column :testings, :second, :integer, :first => true
- assert_equal %w(second first third), conn.columns(:testings).map {|c| c.name }
- end
- testing_table_for_positioning do |conn|
- conn.change_column :testings, :second, :integer, :after => :third
- assert_equal %w(first third second), conn.columns(:testings).map {|c| c.name }
- end
- end
- end
-
- def test_add_rename
- Person.delete_all
-
- begin
- Person.connection.add_column "people", "girlfriend", :string
- Person.reset_column_information
- Person.create :girlfriend => 'bobette'
-
- Person.connection.rename_column "people", "girlfriend", "exgirlfriend"
-
- Person.reset_column_information
- bob = Person.find(:first)
-
- assert_equal "bobette", bob.exgirlfriend
- ensure
- Person.connection.remove_column("people", "girlfriend") rescue nil
- Person.connection.remove_column("people", "exgirlfriend") rescue nil
- end
-
- end
-
- def test_rename_column_using_symbol_arguments
- begin
- names_before = Person.find(:all).map(&:first_name)
- Person.connection.rename_column :people, :first_name, :nick_name
- Person.reset_column_information
- assert Person.column_names.include?("nick_name")
- assert_equal names_before, Person.find(:all).map(&:nick_name)
- ensure
- Person.connection.remove_column("people","nick_name")
- Person.connection.add_column("people","first_name", :string)
- end
- end
-
- def test_rename_column
- begin
- names_before = Person.find(:all).map(&:first_name)
- Person.connection.rename_column "people", "first_name", "nick_name"
- Person.reset_column_information
- assert Person.column_names.include?("nick_name")
- assert_equal names_before, Person.find(:all).map(&:nick_name)
- ensure
- Person.connection.remove_column("people","nick_name")
- Person.connection.add_column("people","first_name", :string)
- end
- end
-
- def test_rename_column_preserves_default_value_not_null
- begin
- default_before = Developer.connection.columns("developers").find { |c| c.name == "salary" }.default
- assert_equal 70000, default_before
- Developer.connection.rename_column "developers", "salary", "anual_salary"
- Developer.reset_column_information
- assert Developer.column_names.include?("anual_salary")
- default_after = Developer.connection.columns("developers").find { |c| c.name == "anual_salary" }.default
- assert_equal 70000, default_after
- ensure
- Developer.connection.rename_column "developers", "anual_salary", "salary"
- Developer.reset_column_information
- end
- end
-
- def test_rename_nonexistent_column
- ActiveRecord::Base.connection.create_table(:hats) do |table|
- table.column :hat_name, :string, :default => nil
- end
- exception = if current_adapter?(:PostgreSQLAdapter, :OracleAdapter)
- ActiveRecord::StatementInvalid
- else
- ActiveRecord::ActiveRecordError
- end
- assert_raise(exception) do
- Person.connection.rename_column "hats", "nonexistent", "should_fail"
- end
- ensure
- ActiveRecord::Base.connection.drop_table(:hats)
- end
-
- def test_rename_column_with_sql_reserved_word
- begin
- assert_nothing_raised { Person.connection.rename_column "people", "first_name", "group" }
- Person.reset_column_information
- assert Person.column_names.include?("group")
- ensure
- Person.connection.remove_column("people", "group") rescue nil
- Person.connection.add_column("people", "first_name", :string) rescue nil
- end
- end
-
- def test_rename_column_with_an_index
- ActiveRecord::Base.connection.create_table(:hats) do |table|
- table.column :hat_name, :string, :limit => 100
- table.column :hat_size, :integer
- end
- Person.connection.add_index :hats, :hat_name
- assert_nothing_raised do
- Person.connection.rename_column "hats", "hat_name", "name"
- end
- ensure
- ActiveRecord::Base.connection.drop_table(:hats)
- end
-
- def test_remove_column_with_index
- ActiveRecord::Base.connection.create_table(:hats) do |table|
- table.column :hat_name, :string, :limit => 100
- table.column :hat_size, :integer
- end
- ActiveRecord::Base.connection.add_index "hats", "hat_size"
-
- assert_nothing_raised { Person.connection.remove_column("hats", "hat_size") }
- ensure
- ActiveRecord::Base.connection.drop_table(:hats)
- end
-
- def test_remove_column_with_multi_column_index
- ActiveRecord::Base.connection.create_table(:hats) do |table|
- table.column :hat_name, :string, :limit => 100
- table.column :hat_size, :integer
- table.column :hat_style, :string, :limit => 100
- end
- ActiveRecord::Base.connection.add_index "hats", ["hat_style", "hat_size"], :unique => true
-
- assert_nothing_raised { Person.connection.remove_column("hats", "hat_size") }
- ensure
- ActiveRecord::Base.connection.drop_table(:hats)
- end
-
- def test_remove_column_no_second_parameter_raises_exception
- assert_raise(ArgumentError) { Person.connection.remove_column("funny") }
+ assert_equal '', data_column.default
+ else
+ assert_nil data_column.default
end
- def test_change_type_of_not_null_column
- assert_nothing_raised do
- Topic.connection.change_column "topics", "written_on", :datetime, :null => false
- Topic.reset_column_information
-
- Topic.connection.change_column "topics", "written_on", :datetime, :null => false
- Topic.reset_column_information
-
- Topic.connection.change_column "topics", "written_on", :datetime, :null => true
- Topic.reset_column_information
- end
- end
+ Person.connection.drop_table :binary_testings rescue nil
+ end
- if current_adapter?(:SQLite3Adapter)
- def test_rename_table_for_sqlite_should_work_with_reserved_words
- begin
- assert_nothing_raised do
- ActiveRecord::Base.connection.rename_table :references, :old_references
- ActiveRecord::Base.connection.create_table :octopuses do |t|
- t.column :url, :string
- end
- end
-
- assert_nothing_raised { ActiveRecord::Base.connection.rename_table :octopuses, :references }
-
- # Using explicit id in insert for compatibility across all databases
- con = ActiveRecord::Base.connection
- assert_nothing_raised do
- con.execute "INSERT INTO 'references' (#{con.quote_column_name('id')}, #{con.quote_column_name('url')}) VALUES (1, 'http://rubyonrails.com')"
- end
- assert_equal 'http://rubyonrails.com', ActiveRecord::Base.connection.select_value("SELECT url FROM 'references' WHERE id=1")
-
- ensure
- ActiveRecord::Base.connection.drop_table :references
- ActiveRecord::Base.connection.rename_table :old_references, :references
- end
- end
- end
+ def test_create_table_with_custom_sequence_name
+ skip "not supported" unless current_adapter? :OracleAdapter
- def test_rename_table
+ # table name is 29 chars, the standard sequence name will
+ # be 33 chars and should be shortened
+ assert_nothing_raised do
begin
- ActiveRecord::Base.connection.create_table :octopuses do |t|
- t.column :url, :string
+ Person.connection.create_table :table_with_name_thats_just_ok do |t|
+ t.column :foo, :string, :null => false
end
- ActiveRecord::Base.connection.rename_table :octopuses, :octopi
-
- # Using explicit id in insert for compatibility across all databases
- con = ActiveRecord::Base.connection
- con.enable_identity_insert("octopi", true) if current_adapter?(:SybaseAdapter)
- assert_nothing_raised { con.execute "INSERT INTO octopi (#{con.quote_column_name('id')}, #{con.quote_column_name('url')}) VALUES (1, 'http://www.foreverflying.com/octopus-black7.jpg')" }
- con.enable_identity_insert("octopi", false) if current_adapter?(:SybaseAdapter)
-
- assert_equal 'http://www.foreverflying.com/octopus-black7.jpg', ActiveRecord::Base.connection.select_value("SELECT url FROM octopi WHERE id=1")
-
ensure
- ActiveRecord::Base.connection.drop_table :octopuses rescue nil
- ActiveRecord::Base.connection.drop_table :octopi rescue nil
+ Person.connection.drop_table :table_with_name_thats_just_ok rescue nil
end
end
- def test_change_column_nullability
- Person.delete_all
- Person.connection.add_column "people", "funny", :boolean
- Person.reset_column_information
- assert Person.columns_hash["funny"].null, "Column 'funny' must initially allow nulls"
- Person.connection.change_column "people", "funny", :boolean, :null => false, :default => true
- Person.reset_column_information
- assert !Person.columns_hash["funny"].null, "Column 'funny' must *not* allow nulls at this point"
- Person.connection.change_column "people", "funny", :boolean, :null => true
- Person.reset_column_information
- assert Person.columns_hash["funny"].null, "Column 'funny' must allow nulls again at this point"
- end
-
- def test_rename_table_with_an_index
+ # should be all good w/ a custom sequence name
+ assert_nothing_raised do
begin
- ActiveRecord::Base.connection.create_table :octopuses do |t|
- t.column :url, :string
+ Person.connection.create_table :table_with_name_thats_just_ok,
+ :sequence_name => 'suitably_short_seq' do |t|
+ t.column :foo, :string, :null => false
end
- ActiveRecord::Base.connection.add_index :octopuses, :url
- ActiveRecord::Base.connection.rename_table :octopuses, :octopi
-
- # Using explicit id in insert for compatibility across all databases
- con = ActiveRecord::Base.connection
- con.enable_identity_insert("octopi", true) if current_adapter?(:SybaseAdapter)
- assert_nothing_raised { con.execute "INSERT INTO octopi (#{con.quote_column_name('id')}, #{con.quote_column_name('url')}) VALUES (1, 'http://www.foreverflying.com/octopus-black7.jpg')" }
- con.enable_identity_insert("octopi", false) if current_adapter?(:SybaseAdapter)
+ Person.connection.execute("select suitably_short_seq.nextval from dual")
- assert_equal 'http://www.foreverflying.com/octopus-black7.jpg', ActiveRecord::Base.connection.select_value("SELECT url FROM octopi WHERE id=1")
- assert ActiveRecord::Base.connection.indexes(:octopi).first.columns.include?("url")
ensure
- ActiveRecord::Base.connection.drop_table :octopuses rescue nil
- ActiveRecord::Base.connection.drop_table :octopi rescue nil
+ Person.connection.drop_table :table_with_name_thats_just_ok,
+ :sequence_name => 'suitably_short_seq' rescue nil
end
end
- def test_change_column
- Person.connection.add_column 'people', 'age', :integer
- label = "test_change_column Columns"
- old_columns = Person.connection.columns(Person.table_name, label)
- assert old_columns.find { |c| c.name == 'age' and c.type == :integer }
-
- assert_nothing_raised { Person.connection.change_column "people", "age", :string }
-
- new_columns = Person.connection.columns(Person.table_name, label)
- assert_nil new_columns.find { |c| c.name == 'age' and c.type == :integer }
- assert new_columns.find { |c| c.name == 'age' and c.type == :string }
-
- old_columns = Topic.connection.columns(Topic.table_name, label)
- assert old_columns.find { |c| c.name == 'approved' and c.type == :boolean and c.default == true }
- assert_nothing_raised { Topic.connection.change_column :topics, :approved, :boolean, :default => false }
- new_columns = Topic.connection.columns(Topic.table_name, label)
- assert_nil new_columns.find { |c| c.name == 'approved' and c.type == :boolean and c.default == true }
- assert new_columns.find { |c| c.name == 'approved' and c.type == :boolean and c.default == false }
- assert_nothing_raised { Topic.connection.change_column :topics, :approved, :boolean, :default => true }
- end
-
- def test_change_column_with_nil_default
- Person.connection.add_column "people", "contributor", :boolean, :default => true
- Person.reset_column_information
- assert Person.new.contributor?
-
- assert_nothing_raised { Person.connection.change_column "people", "contributor", :boolean, :default => nil }
- Person.reset_column_information
- assert !Person.new.contributor?
- assert_nil Person.new.contributor
- ensure
- Person.connection.remove_column("people", "contributor") rescue nil
- end
-
- def test_change_column_with_new_default
- Person.connection.add_column "people", "administrator", :boolean, :default => true
- Person.reset_column_information
- assert Person.new.administrator?
-
- assert_nothing_raised { Person.connection.change_column "people", "administrator", :boolean, :default => false }
- Person.reset_column_information
- assert !Person.new.administrator?
- ensure
- Person.connection.remove_column("people", "administrator") rescue nil
- end
-
- def test_change_column_default
- Person.connection.change_column_default "people", "first_name", "Tester"
- Person.reset_column_information
- assert_equal "Tester", Person.new.first_name
- end
-
- def test_change_column_quotes_column_names
- Person.connection.create_table :testings do |t|
- t.column :select, :string
- end
-
- assert_nothing_raised { Person.connection.change_column :testings, :select, :string, :limit => 10 }
-
- # Oracle needs primary key value from sequence
- if current_adapter?(:OracleAdapter)
- assert_nothing_raised { Person.connection.execute "insert into testings (id, #{Person.connection.quote_column_name('select')}) values (testings_seq.nextval, '7 chars')" }
- else
- assert_nothing_raised { Person.connection.execute "insert into testings (#{Person.connection.quote_column_name('select')}) values ('7 chars')" }
- end
- ensure
- Person.connection.drop_table :testings rescue nil
- end
-
- def test_keeping_default_and_notnull_constaint_on_change
- Person.connection.create_table :testings do |t|
- t.column :title, :string
- end
- person_klass = Class.new(Person)
- person_klass.set_table_name 'testings'
-
- person_klass.connection.add_column "testings", "wealth", :integer, :null => false, :default => 99
- person_klass.reset_column_information
- assert_equal 99, person_klass.columns_hash["wealth"].default
- assert_equal false, person_klass.columns_hash["wealth"].null
- # Oracle needs primary key value from sequence
- if current_adapter?(:OracleAdapter)
- assert_nothing_raised {person_klass.connection.execute("insert into testings (id, title) values (testings_seq.nextval, 'tester')")}
- else
- assert_nothing_raised {person_klass.connection.execute("insert into testings (title) values ('tester')")}
- end
-
- # change column default to see that column doesn't lose its not null definition
- person_klass.connection.change_column_default "testings", "wealth", 100
- person_klass.reset_column_information
- assert_equal 100, person_klass.columns_hash["wealth"].default
- assert_equal false, person_klass.columns_hash["wealth"].null
-
- # rename column to see that column doesn't lose its not null and/or default definition
- person_klass.connection.rename_column "testings", "wealth", "money"
- person_klass.reset_column_information
- assert_nil person_klass.columns_hash["wealth"]
- assert_equal 100, person_klass.columns_hash["money"].default
- assert_equal false, person_klass.columns_hash["money"].null
-
- # change column
- person_klass.connection.change_column "testings", "money", :integer, :null => false, :default => 1000
- person_klass.reset_column_information
- assert_equal 1000, person_klass.columns_hash["money"].default
- assert_equal false, person_klass.columns_hash["money"].null
-
- # change column, make it nullable and clear default
- person_klass.connection.change_column "testings", "money", :integer, :null => true, :default => nil
- person_klass.reset_column_information
- assert_nil person_klass.columns_hash["money"].default
- assert_equal true, person_klass.columns_hash["money"].null
-
- # change_column_null, make it not nullable and set null values to a default value
- person_klass.connection.execute('UPDATE testings SET money = NULL')
- person_klass.connection.change_column_null "testings", "money", false, 2000
- person_klass.reset_column_information
- assert_nil person_klass.columns_hash["money"].default
- assert_equal false, person_klass.columns_hash["money"].null
- assert_equal [2000], Person.connection.select_values("SELECT money FROM testings").map { |s| s.to_i }.sort
- ensure
- Person.connection.drop_table :testings rescue nil
- end
-
- def test_change_column_default_to_null
- Person.connection.change_column_default "people", "first_name", nil
- Person.reset_column_information
- assert_nil Person.new.first_name
- end
-
- def test_column_exists
- Person.connection.create_table :testings do |t|
- t.column :foo, :string
- end
-
- assert Person.connection.column_exists?(:testings, :foo)
- assert !Person.connection.column_exists?(:testings, :bar)
- ensure
- Person.connection.drop_table :testings rescue nil
- end
-
- def test_column_exists_with_type
- Person.connection.create_table :testings do |t|
- t.column :foo, :string
- t.column :bar, :decimal, :precision => 8, :scale => 2
- end
-
- assert Person.connection.column_exists?(:testings, :foo, :string)
- assert !Person.connection.column_exists?(:testings, :foo, :integer)
- assert Person.connection.column_exists?(:testings, :bar, :decimal)
- assert !Person.connection.column_exists?(:testings, :bar, :integer)
- ensure
- Person.connection.drop_table :testings rescue nil
- end
-
- def test_column_exists_with_definition
- Person.connection.create_table :testings do |t|
- t.column :foo, :string, :limit => 100
- t.column :bar, :decimal, :precision => 8, :scale => 2
- end
-
- assert Person.connection.column_exists?(:testings, :foo, :string, :limit => 100)
- assert !Person.connection.column_exists?(:testings, :foo, :string, :limit => 50)
- assert Person.connection.column_exists?(:testings, :bar, :decimal, :precision => 8, :scale => 2)
- assert !Person.connection.column_exists?(:testings, :bar, :decimal, :precision => 10, :scale => 2)
- ensure
- Person.connection.drop_table :testings rescue nil
+ # confirm the custom sequence got dropped
+ assert_raise(ActiveRecord::StatementInvalid) do
+ Person.connection.execute("select suitably_short_seq.nextval from dual")
end
+ end
- def test_column_exists_on_table_with_no_options_parameter_supplied
- Person.connection.create_table :testings do |t|
- t.string :foo
- end
- Person.connection.change_table :testings do |t|
- assert t.column_exists?(:foo)
- assert !(t.column_exists?(:bar))
- end
+ protected
+ def with_env_tz(new_tz = 'US/Eastern')
+ old_tz, ENV['TZ'] = ENV['TZ'], new_tz
+ yield
ensure
- Person.connection.drop_table :testings rescue nil
- end
-
- def test_add_table
- assert !Reminder.table_exists?
-
- WeNeedReminders.up
-
- assert Reminder.create("content" => "hello world", "remind_at" => Time.now)
- assert_equal "hello world", Reminder.find(:first).content
-
- WeNeedReminders.down
- assert_raise(ActiveRecord::StatementInvalid) { Reminder.find(:first) }
- end
-
- def test_add_table_with_decimals
- Person.connection.drop_table :big_numbers rescue nil
-
- assert !BigNumber.table_exists?
- GiveMeBigNumbers.up
-
- assert BigNumber.create(
- :bank_balance => 1586.43,
- :big_bank_balance => BigDecimal("1000234000567.95"),
- :world_population => 6000000000,
- :my_house_population => 3,
- :value_of_e => BigDecimal("2.7182818284590452353602875")
- )
-
- b = BigNumber.find(:first)
- assert_not_nil b
-
- assert_not_nil b.bank_balance
- assert_not_nil b.big_bank_balance
- assert_not_nil b.world_population
- assert_not_nil b.my_house_population
- assert_not_nil b.value_of_e
-
- # TODO: set world_population >= 2**62 to cover 64-bit platforms and test
- # is_a?(Bignum)
- assert_kind_of Integer, b.world_population
- assert_equal 6000000000, b.world_population
- assert_kind_of Fixnum, b.my_house_population
- assert_equal 3, b.my_house_population
- assert_kind_of BigDecimal, b.bank_balance
- assert_equal BigDecimal("1586.43"), b.bank_balance
- assert_kind_of BigDecimal, b.big_bank_balance
- assert_equal BigDecimal("1000234000567.95"), b.big_bank_balance
-
- # This one is fun. The 'value_of_e' field is defined as 'DECIMAL' with
- # precision/scale explicitly left out. By the SQL standard, numbers
- # assigned to this field should be truncated but that's seldom respected.
- if current_adapter?(:PostgreSQLAdapter)
- # - PostgreSQL changes the SQL spec on columns declared simply as
- # "decimal" to something more useful: instead of being given a scale
- # of 0, they take on the compile-time limit for precision and scale,
- # so the following should succeed unless you have used really wacky
- # compilation options
- # - SQLite2 has the default behavior of preserving all data sent in,
- # so this happens there too
- assert_kind_of BigDecimal, b.value_of_e
- assert_equal BigDecimal("2.7182818284590452353602875"), b.value_of_e
- elsif current_adapter?(:SQLite3Adapter)
- # - SQLite3 stores a float, in violation of SQL
- assert_kind_of BigDecimal, b.value_of_e
- assert_in_delta BigDecimal("2.71828182845905"), b.value_of_e, 0.00000000000001
- else
- # - SQL standard is an integer
- assert_kind_of Fixnum, b.value_of_e
- assert_equal 2, b.value_of_e
- end
-
- GiveMeBigNumbers.down
- assert_raise(ActiveRecord::StatementInvalid) { BigNumber.find(:first) }
- end
-
- def test_migrator
- assert !Person.column_methods_hash.include?(:last_name)
- assert !Reminder.table_exists?
-
- ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid")
-
- assert_equal 3, ActiveRecord::Migrator.current_version
- Person.reset_column_information
- assert Person.column_methods_hash.include?(:last_name)
- assert Reminder.create("content" => "hello world", "remind_at" => Time.now)
- assert_equal "hello world", Reminder.find(:first).content
-
- ActiveRecord::Migrator.down(MIGRATIONS_ROOT + "/valid")
-
- assert_equal 0, ActiveRecord::Migrator.current_version
- Person.reset_column_information
- assert !Person.column_methods_hash.include?(:last_name)
- assert_raise(ActiveRecord::StatementInvalid) { Reminder.find(:first) }
- end
-
- class MockMigration < ActiveRecord::Migration
- attr_reader :went_up, :went_down
- def initialize
- @went_up = false
- @went_down = false
- end
-
- def up
- @went_up = true
- super
- end
-
- def down
- @went_down = true
- super
- end
+ old_tz ? ENV['TZ'] = old_tz : ENV.delete('TZ')
end
+end
- def test_instance_based_migration_up
- migration = MockMigration.new
- assert !migration.went_up, 'have not gone up'
- assert !migration.went_down, 'have not gone down'
-
- migration.migrate :up
- assert migration.went_up, 'have gone up'
- assert !migration.went_down, 'have not gone down'
- end
-
- def test_instance_based_migration_down
- migration = MockMigration.new
- assert !migration.went_up, 'have not gone up'
- assert !migration.went_down, 'have not gone down'
-
- migration.migrate :down
- assert !migration.went_up, 'have gone up'
- assert migration.went_down, 'have not gone down'
- end
-
- def test_migrator_one_up
- assert !Person.column_methods_hash.include?(:last_name)
- assert !Reminder.table_exists?
-
- ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", 1)
-
- Person.reset_column_information
- assert Person.column_methods_hash.include?(:last_name)
- assert !Reminder.table_exists?
-
- ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", 2)
-
- assert Reminder.create("content" => "hello world", "remind_at" => Time.now)
- assert_equal "hello world", Reminder.find(:first).content
- end
-
- def test_migrator_one_down
- ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid")
-
- ActiveRecord::Migrator.down(MIGRATIONS_ROOT + "/valid", 1)
-
- Person.reset_column_information
- assert Person.column_methods_hash.include?(:last_name)
- assert !Reminder.table_exists?
- end
-
- def test_migrator_one_up_one_down
- ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", 1)
- ActiveRecord::Migrator.down(MIGRATIONS_ROOT + "/valid", 0)
-
- assert !Person.column_methods_hash.include?(:last_name)
- assert !Reminder.table_exists?
- end
-
- def test_migrator_double_up
- assert_equal(0, ActiveRecord::Migrator.current_version)
- ActiveRecord::Migrator.run(:up, MIGRATIONS_ROOT + "/valid", 1)
- assert_nothing_raised { ActiveRecord::Migrator.run(:up, MIGRATIONS_ROOT + "/valid", 1) }
- assert_equal(1, ActiveRecord::Migrator.current_version)
- end
-
- def test_migrator_double_down
- assert_equal(0, ActiveRecord::Migrator.current_version)
- ActiveRecord::Migrator.run(:up, MIGRATIONS_ROOT + "/valid", 1)
- ActiveRecord::Migrator.run(:down, MIGRATIONS_ROOT + "/valid", 1)
- assert_nothing_raised { ActiveRecord::Migrator.run(:down, MIGRATIONS_ROOT + "/valid", 1) }
- assert_equal(0, ActiveRecord::Migrator.current_version)
- end
-
- if ActiveRecord::Base.connection.supports_ddl_transactions?
- def test_migrator_one_up_with_exception_and_rollback
- assert !Person.column_methods_hash.include?(:last_name)
-
- e = assert_raise(StandardError) do
- ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/broken", 100)
- end
-
- assert_equal "An error has occurred, this and all later migrations canceled:\n\nSomething broke", e.message
-
- Person.reset_column_information
- assert !Person.column_methods_hash.include?(:last_name)
- end
+class ReservedWordsMigrationTest < ActiveRecord::TestCase
+ def test_drop_index_from_table_named_values
+ connection = Person.connection
+ connection.create_table :values, :force => true do |t|
+ t.integer :value
end
- def test_finds_migrations
- migrations = ActiveRecord::Migrator.new(:up, MIGRATIONS_ROOT + "/valid").migrations
-
- [[1, 'ValidPeopleHaveLastNames'], [2, 'WeNeedReminders'], [3, 'InnocentJointable']].each_with_index do |pair, i|
- assert_equal migrations[i].version, pair.first
- assert_equal migrations[i].name, pair.last
- end
+ assert_nothing_raised do
+ connection.add_index :values, :value
+ connection.remove_index :values, :column => :value
end
- def test_finds_migrations_from_two_directories
- directories = [MIGRATIONS_ROOT + '/valid_with_timestamps', MIGRATIONS_ROOT + '/to_copy_with_timestamps']
- migrations = ActiveRecord::Migrator.new(:up, directories).migrations
-
- [[20090101010101, "PeopleHaveHobbies"],
- [20090101010202, "PeopleHaveDescriptions"],
- [20100101010101, "ValidWithTimestampsPeopleHaveLastNames"],
- [20100201010101, "ValidWithTimestampsWeNeedReminders"],
- [20100301010101, "ValidWithTimestampsInnocentJointable"]].each_with_index do |pair, i|
- assert_equal pair.first, migrations[i].version
- assert_equal pair.last, migrations[i].name
- end
- end
+ connection.drop_table :values rescue nil
+ end
+end
- def test_finds_pending_migrations
- ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/interleaved/pass_2", 1)
- migrations = ActiveRecord::Migrator.new(:up, MIGRATIONS_ROOT + "/interleaved/pass_2").pending_migrations
- assert_equal 1, migrations.size
- assert_equal migrations[0].version, 3
- assert_equal migrations[0].name, 'InterleavedInnocentJointable'
+class ChangeTableMigrationsTest < ActiveRecord::TestCase
+ def setup
+ @connection = Person.connection
+ @connection.create_table :delete_me, :force => true do |t|
end
+ end
- def test_relative_migrations
- list = Dir.chdir(MIGRATIONS_ROOT) do
- ActiveRecord::Migrator.up("valid/", 1)
- end
+ def teardown
+ Person.connection.drop_table :delete_me rescue nil
+ end
- migration_proxy = list.find { |item|
- item.name == 'ValidPeopleHaveLastNames'
- }
- assert migration_proxy, 'should find pending migration'
+ def test_references_column_type_adds_id
+ with_change_table do |t|
+ @connection.expects(:add_column).with(:delete_me, 'customer_id', :integer, {})
+ t.references :customer
end
+ end
- def test_only_loads_pending_migrations
- # migrate up to 1
- ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", 1)
-
- proxies = ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid", nil)
-
- names = proxies.map(&:name)
- assert !names.include?('ValidPeopleHaveLastNames')
- assert names.include?('WeNeedReminders')
- assert names.include?('InnocentJointable')
+ def test_remove_references_column_type_removes_id
+ with_change_table do |t|
+ @connection.expects(:remove_column).with(:delete_me, 'customer_id')
+ t.remove_references :customer
end
+ end
- def test_target_version_zero_should_run_only_once
- # migrate up to 1
- ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid", 1)
-
- # migrate down to 0
- ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid", 0)
-
- # migrate down to 0 again
- proxies = ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid", 0)
- assert_equal [], proxies
+ def test_add_belongs_to_works_like_add_references
+ with_change_table do |t|
+ @connection.expects(:add_column).with(:delete_me, 'customer_id', :integer, {})
+ t.belongs_to :customer
end
+ end
- def test_migrator_db_has_no_schema_migrations_table
- # Oracle adapter raises error if semicolon is present as last character
- if current_adapter?(:OracleAdapter)
- ActiveRecord::Base.connection.execute("DROP TABLE schema_migrations")
- else
- ActiveRecord::Base.connection.execute("DROP TABLE schema_migrations;")
- end
- assert_nothing_raised do
- ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid", 1)
- end
+ def test_remove_belongs_to_works_like_remove_references
+ with_change_table do |t|
+ @connection.expects(:remove_column).with(:delete_me, 'customer_id')
+ t.remove_belongs_to :customer
end
+ end
- def test_migrator_verbosity
- ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", 1)
- assert_not_equal 0, ActiveRecord::Migration.message_count
- ActiveRecord::Migration.message_count = 0
-
- ActiveRecord::Migrator.down(MIGRATIONS_ROOT + "/valid", 0)
- assert_not_equal 0, ActiveRecord::Migration.message_count
- ActiveRecord::Migration.message_count = 0
+ def test_references_column_type_with_polymorphic_adds_type
+ with_change_table do |t|
+ @connection.expects(:add_column).with(:delete_me, 'taggable_type', :string, {})
+ @connection.expects(:add_column).with(:delete_me, 'taggable_id', :integer, {})
+ t.references :taggable, :polymorphic => true
end
+ end
- def test_migrator_verbosity_off
- ActiveRecord::Migration.verbose = false
- ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", 1)
- assert_equal 0, ActiveRecord::Migration.message_count
- ActiveRecord::Migrator.down(MIGRATIONS_ROOT + "/valid", 0)
- assert_equal 0, ActiveRecord::Migration.message_count
+ def test_remove_references_column_type_with_polymorphic_removes_type
+ with_change_table do |t|
+ @connection.expects(:remove_column).with(:delete_me, 'taggable_type')
+ @connection.expects(:remove_column).with(:delete_me, 'taggable_id')
+ t.remove_references :taggable, :polymorphic => true
end
+ end
- def test_migrator_going_down_due_to_version_target
- ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/valid", 1)
- ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid", 0)
-
- assert !Person.column_methods_hash.include?(:last_name)
- assert !Reminder.table_exists?
-
- ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid")
-
- Person.reset_column_information
- assert Person.column_methods_hash.include?(:last_name)
- assert Reminder.create("content" => "hello world", "remind_at" => Time.now)
- assert_equal "hello world", Reminder.find(:first).content
+ def test_references_column_type_with_polymorphic_and_options_null_is_false_adds_table_flag
+ with_change_table do |t|
+ @connection.expects(:add_column).with(:delete_me, 'taggable_type', :string, {:null => false})
+ @connection.expects(:add_column).with(:delete_me, 'taggable_id', :integer, {:null => false})
+ t.references :taggable, :polymorphic => true, :null => false
end
+ end
- def test_migrator_rollback
- ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid")
- assert_equal(3, ActiveRecord::Migrator.current_version)
-
- ActiveRecord::Migrator.rollback(MIGRATIONS_ROOT + "/valid")
- assert_equal(2, ActiveRecord::Migrator.current_version)
-
- ActiveRecord::Migrator.rollback(MIGRATIONS_ROOT + "/valid")
- assert_equal(1, ActiveRecord::Migrator.current_version)
-
- ActiveRecord::Migrator.rollback(MIGRATIONS_ROOT + "/valid")
- assert_equal(0, ActiveRecord::Migrator.current_version)
-
- ActiveRecord::Migrator.rollback(MIGRATIONS_ROOT + "/valid")
- assert_equal(0, ActiveRecord::Migrator.current_version)
+ def test_remove_references_column_type_with_polymorphic_and_options_null_is_false_removes_table_flag
+ with_change_table do |t|
+ @connection.expects(:remove_column).with(:delete_me, 'taggable_type')
+ @connection.expects(:remove_column).with(:delete_me, 'taggable_id')
+ t.remove_references :taggable, :polymorphic => true, :null => false
end
+ end
- def test_migrator_forward
- ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid", 1)
- assert_equal(1, ActiveRecord::Migrator.current_version)
-
- ActiveRecord::Migrator.forward(MIGRATIONS_ROOT + "/valid", 2)
- assert_equal(3, ActiveRecord::Migrator.current_version)
-
- ActiveRecord::Migrator.forward(MIGRATIONS_ROOT + "/valid")
- assert_equal(3, ActiveRecord::Migrator.current_version)
+ def test_timestamps_creates_updated_at_and_created_at
+ with_change_table do |t|
+ @connection.expects(:add_timestamps).with(:delete_me)
+ t.timestamps
end
+ end
- def test_get_all_versions
- ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid")
- assert_equal([1,2,3], ActiveRecord::Migrator.get_all_versions)
-
- ActiveRecord::Migrator.rollback(MIGRATIONS_ROOT + "/valid")
- assert_equal([1,2], ActiveRecord::Migrator.get_all_versions)
-
- ActiveRecord::Migrator.rollback(MIGRATIONS_ROOT + "/valid")
- assert_equal([1], ActiveRecord::Migrator.get_all_versions)
-
- ActiveRecord::Migrator.rollback(MIGRATIONS_ROOT + "/valid")
- assert_equal([], ActiveRecord::Migrator.get_all_versions)
+ def test_remove_timestamps_creates_updated_at_and_created_at
+ with_change_table do |t|
+ @connection.expects(:remove_timestamps).with(:delete_me)
+ t.remove_timestamps
end
+ end
- def test_schema_migrations_table_name
- ActiveRecord::Base.table_name_prefix = "prefix_"
- ActiveRecord::Base.table_name_suffix = "_suffix"
- Reminder.reset_table_name
- assert_equal "prefix_schema_migrations_suffix", ActiveRecord::Migrator.schema_migrations_table_name
- ActiveRecord::Base.table_name_prefix = ""
- ActiveRecord::Base.table_name_suffix = ""
- Reminder.reset_table_name
- assert_equal "schema_migrations", ActiveRecord::Migrator.schema_migrations_table_name
- ensure
- ActiveRecord::Base.table_name_prefix = ""
- ActiveRecord::Base.table_name_suffix = ""
- end
-
- def test_proper_table_name
- assert_equal "table", ActiveRecord::Migrator.proper_table_name('table')
- assert_equal "table", ActiveRecord::Migrator.proper_table_name(:table)
- assert_equal "reminders", ActiveRecord::Migrator.proper_table_name(Reminder)
- Reminder.reset_table_name
- assert_equal Reminder.table_name, ActiveRecord::Migrator.proper_table_name(Reminder)
-
- # Use the model's own prefix/suffix if a model is given
- ActiveRecord::Base.table_name_prefix = "ARprefix_"
- ActiveRecord::Base.table_name_suffix = "_ARsuffix"
- Reminder.table_name_prefix = 'prefix_'
- Reminder.table_name_suffix = '_suffix'
- Reminder.reset_table_name
- assert_equal "prefix_reminders_suffix", ActiveRecord::Migrator.proper_table_name(Reminder)
- Reminder.table_name_prefix = ''
- Reminder.table_name_suffix = ''
- Reminder.reset_table_name
-
- # Use AR::Base's prefix/suffix if string or symbol is given
- ActiveRecord::Base.table_name_prefix = "prefix_"
- ActiveRecord::Base.table_name_suffix = "_suffix"
- Reminder.reset_table_name
- assert_equal "prefix_table_suffix", ActiveRecord::Migrator.proper_table_name('table')
- assert_equal "prefix_table_suffix", ActiveRecord::Migrator.proper_table_name(:table)
- ActiveRecord::Base.table_name_prefix = ""
- ActiveRecord::Base.table_name_suffix = ""
- Reminder.reset_table_name
- end
-
- def test_add_drop_table_with_prefix_and_suffix
- assert !Reminder.table_exists?
- ActiveRecord::Base.table_name_prefix = 'prefix_'
- ActiveRecord::Base.table_name_suffix = '_suffix'
- Reminder.reset_table_name
- Reminder.reset_sequence_name
- WeNeedReminders.up
- assert Reminder.create("content" => "hello world", "remind_at" => Time.now)
- assert_equal "hello world", Reminder.find(:first).content
-
- WeNeedReminders.down
- assert_raise(ActiveRecord::StatementInvalid) { Reminder.find(:first) }
- ensure
- ActiveRecord::Base.table_name_prefix = ''
- ActiveRecord::Base.table_name_suffix = ''
- Reminder.reset_table_name
- Reminder.reset_sequence_name
+ def string_column
+ if current_adapter?(:PostgreSQLAdapter)
+ "character varying(255)"
+ elsif current_adapter?(:OracleAdapter)
+ 'VARCHAR2(255)'
+ else
+ 'varchar(255)'
end
+ end
- def test_create_table_with_binary_column
- Person.connection.drop_table :binary_testings rescue nil
-
- assert_nothing_raised {
- Person.connection.create_table :binary_testings do |t|
- t.column "data", :binary, :null => false
- end
- }
-
- columns = Person.connection.columns(:binary_testings)
- data_column = columns.detect { |c| c.name == "data" }
-
- if current_adapter?(:MysqlAdapter) or current_adapter?(:Mysql2Adapter)
- assert_equal '', data_column.default
- else
- assert_nil data_column.default
- end
-
- Person.connection.drop_table :binary_testings rescue nil
+ def integer_column
+ if current_adapter?(:MysqlAdapter) or current_adapter?(:Mysql2Adapter)
+ 'int(11)'
+ elsif current_adapter?(:OracleAdapter)
+ 'NUMBER(38)'
+ else
+ 'integer'
end
+ end
- def test_migrator_with_duplicates
- assert_raise(ActiveRecord::DuplicateMigrationVersionError) do
- ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/duplicate", nil)
- end
+ def test_integer_creates_integer_column
+ with_change_table do |t|
+ @connection.expects(:add_column).with(:delete_me, :foo, integer_column, {})
+ @connection.expects(:add_column).with(:delete_me, :bar, integer_column, {})
+ t.integer :foo, :bar
end
+ end
- def test_migrator_with_duplicate_names
- assert_raise(ActiveRecord::DuplicateMigrationNameError, "Multiple migrations have the name Chunky") do
- ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/duplicate_names", nil)
- end
+ def test_string_creates_string_column
+ with_change_table do |t|
+ @connection.expects(:add_column).with(:delete_me, :foo, string_column, {})
+ @connection.expects(:add_column).with(:delete_me, :bar, string_column, {})
+ t.string :foo, :bar
end
+ end
- def test_migrator_with_missing_version_numbers
- assert_raise(ActiveRecord::UnknownMigrationVersionError) do
- ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/missing", 500)
- end
+ def test_column_creates_column
+ with_change_table do |t|
+ @connection.expects(:add_column).with(:delete_me, :bar, :integer, {})
+ t.column :bar, :integer
end
+ end
- def test_create_table_with_custom_sequence_name
- return unless current_adapter? :OracleAdapter
-
- # table name is 29 chars, the standard sequence name will
- # be 33 chars and should be shortened
- assert_nothing_raised do
- begin
- Person.connection.create_table :table_with_name_thats_just_ok do |t|
- t.column :foo, :string, :null => false
- end
- ensure
- Person.connection.drop_table :table_with_name_thats_just_ok rescue nil
- end
- end
-
- # should be all good w/ a custom sequence name
- assert_nothing_raised do
- begin
- Person.connection.create_table :table_with_name_thats_just_ok,
- :sequence_name => 'suitably_short_seq' do |t|
- t.column :foo, :string, :null => false
- end
-
- Person.connection.execute("select suitably_short_seq.nextval from dual")
-
- ensure
- Person.connection.drop_table :table_with_name_thats_just_ok,
- :sequence_name => 'suitably_short_seq' rescue nil
- end
- end
-
- # confirm the custom sequence got dropped
- assert_raise(ActiveRecord::StatementInvalid) do
- Person.connection.execute("select suitably_short_seq.nextval from dual")
- end
+ def test_column_creates_column_with_options
+ with_change_table do |t|
+ @connection.expects(:add_column).with(:delete_me, :bar, :integer, {:null => false})
+ t.column :bar, :integer, :null => false
end
-
- protected
- def with_env_tz(new_tz = 'US/Eastern')
- old_tz, ENV['TZ'] = ENV['TZ'], new_tz
- yield
- ensure
- old_tz ? ENV['TZ'] = old_tz : ENV.delete('TZ')
- end
-
end
- class SexyMigrationsTest < ActiveRecord::TestCase
- def test_references_column_type_adds_id
- with_new_table do |t|
- t.expects(:column).with('customer_id', :integer, {})
- t.references :customer
- end
+ def test_index_creates_index
+ with_change_table do |t|
+ @connection.expects(:add_index).with(:delete_me, :bar, {})
+ t.index :bar
end
+ end
- def test_references_column_type_with_polymorphic_adds_type
- with_new_table do |t|
- t.expects(:column).with('taggable_type', :string, {})
- t.expects(:column).with('taggable_id', :integer, {})
- t.references :taggable, :polymorphic => true
- end
+ def test_index_creates_index_with_options
+ with_change_table do |t|
+ @connection.expects(:add_index).with(:delete_me, :bar, {:unique => true})
+ t.index :bar, :unique => true
end
+ end
- def test_references_column_type_with_polymorphic_and_options_null_is_false_adds_table_flag
- with_new_table do |t|
- t.expects(:column).with('taggable_type', :string, {:null => false})
- t.expects(:column).with('taggable_id', :integer, {:null => false})
- t.references :taggable, :polymorphic => true, :null => false
- end
+ def test_index_exists
+ with_change_table do |t|
+ @connection.expects(:index_exists?).with(:delete_me, :bar, {})
+ t.index_exists?(:bar)
end
+ end
- def test_belongs_to_works_like_references
- with_new_table do |t|
- t.expects(:column).with('customer_id', :integer, {})
- t.belongs_to :customer
- end
+ def test_index_exists_with_options
+ with_change_table do |t|
+ @connection.expects(:index_exists?).with(:delete_me, :bar, {:unique => true})
+ t.index_exists?(:bar, :unique => true)
end
+ end
- def test_timestamps_creates_updated_at_and_created_at
- with_new_table do |t|
- t.expects(:column).with(:created_at, :datetime, kind_of(Hash))
- t.expects(:column).with(:updated_at, :datetime, kind_of(Hash))
- t.timestamps
- end
+ def test_change_changes_column
+ with_change_table do |t|
+ @connection.expects(:change_column).with(:delete_me, :bar, :string, {})
+ t.change :bar, :string
end
+ end
- def test_integer_creates_integer_column
- with_new_table do |t|
- t.expects(:column).with(:foo, 'integer', {})
- t.expects(:column).with(:bar, 'integer', {})
- t.integer :foo, :bar
- end
+ def test_change_changes_column_with_options
+ with_change_table do |t|
+ @connection.expects(:change_column).with(:delete_me, :bar, :string, {:null => true})
+ t.change :bar, :string, :null => true
end
+ end
- def test_string_creates_string_column
- with_new_table do |t|
- t.expects(:column).with(:foo, 'string', {})
- t.expects(:column).with(:bar, 'string', {})
- t.string :foo, :bar
- end
+ def test_change_default_changes_column
+ with_change_table do |t|
+ @connection.expects(:change_column_default).with(:delete_me, :bar, :string)
+ t.change_default :bar, :string
end
+ end
- if current_adapter?(:PostgreSQLAdapter) || current_adapter?(:SQLite3Adapter) || current_adapter?(:MysqlAdapter) || current_adapter?(:Mysql2Adapter)
- def test_xml_creates_xml_column
- type = current_adapter?(:PostgreSQLAdapter) ? 'xml' : :text
-
- with_new_table do |t|
- t.expects(:column).with(:data, type, {})
- t.xml :data
- end
- end
- else
- def test_xml_creates_xml_column
- with_new_table do |t|
- assert_raises(NotImplementedError) do
- t.xml :data
- end
- end
- end
+ def test_remove_drops_single_column
+ with_change_table do |t|
+ @connection.expects(:remove_column).with(:delete_me, [:bar])
+ t.remove :bar
end
+ end
- protected
- def with_new_table
- Person.connection.create_table :delete_me, :force => true do |t|
- yield t
- end
- ensure
- Person.connection.drop_table :delete_me rescue nil
+ def test_remove_drops_multiple_columns
+ with_change_table do |t|
+ @connection.expects(:remove_column).with(:delete_me, [:bar, :baz])
+ t.remove :bar, :baz
end
+ end
- end # SexyMigrationsTest
-
- class MigrationLoggerTest < ActiveRecord::TestCase
- def test_migration_should_be_run_without_logger
- previous_logger = ActiveRecord::Base.logger
- ActiveRecord::Base.logger = nil
- assert_nothing_raised do
- ActiveRecord::Migrator.migrate(MIGRATIONS_ROOT + "/valid")
- end
- ensure
- ActiveRecord::Base.logger = previous_logger
+ def test_remove_index_removes_index_with_options
+ with_change_table do |t|
+ @connection.expects(:remove_index).with(:delete_me, {:unique => true})
+ t.remove_index :unique => true
end
end
- class InterleavedMigrationsTest < ActiveRecord::TestCase
- def test_migrator_interleaved_migrations
- ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/interleaved/pass_1")
-
- assert_nothing_raised do
- ActiveRecord::Migrator.up(MIGRATIONS_ROOT + "/interleaved/pass_2")
- end
-
- Person.reset_column_information
- assert Person.column_methods_hash.include?(:last_name)
-
- assert_nothing_raised do
- proxies = ActiveRecord::Migrator.down(
- MIGRATIONS_ROOT + "/interleaved/pass_3")
- names = proxies.map(&:name)
- assert names.include?('InterleavedPeopleHaveLastNames')
- assert names.include?('InterleavedInnocentJointable')
- end
+ def test_rename_renames_column
+ with_change_table do |t|
+ @connection.expects(:rename_column).with(:delete_me, :bar, :baz)
+ t.rename :bar, :baz
end
end
- class ReservedWordsMigrationTest < ActiveRecord::TestCase
- def test_drop_index_from_table_named_values
- connection = Person.connection
- connection.create_table :values, :force => true do |t|
- t.integer :value
- end
-
- assert_nothing_raised do
- connection.add_index :values, :value
- connection.remove_index :values, :column => :value
- end
-
- connection.drop_table :values rescue nil
+ protected
+ def with_change_table
+ Person.connection.change_table :delete_me do |t|
+ yield t
end
end
+end
-
- class ChangeTableMigrationsTest < ActiveRecord::TestCase
+if ActiveRecord::Base.connection.supports_bulk_alter?
+ class BulkAlterTableMigrationsTest < ActiveRecord::TestCase
def setup
@connection = Person.connection
- @connection.create_table :delete_me, :force => true do |t|
- end
+ @connection.create_table(:delete_me, :force => true) {|t| }
end
def teardown
- Person.connection.drop_table :delete_me rescue nil
+ Person.connection.drop_table(:delete_me) rescue nil
end
- def test_references_column_type_adds_id
- with_change_table do |t|
- @connection.expects(:add_column).with(:delete_me, 'customer_id', :integer, {})
- t.references :customer
+ def test_adding_multiple_columns
+ assert_queries(1) do
+ with_bulk_change_table do |t|
+ t.column :name, :string
+ t.string :qualification, :experience
+ t.integer :age, :default => 0
+ t.date :birthdate
+ t.timestamps
+ end
end
- end
- def test_remove_references_column_type_removes_id
- with_change_table do |t|
- @connection.expects(:remove_column).with(:delete_me, 'customer_id')
- t.remove_references :customer
- end
+ assert_equal 8, columns.size
+ [:name, :qualification, :experience].each {|s| assert_equal :string, column(s).type }
+ assert_equal 0, column(:age).default
end
- def test_add_belongs_to_works_like_add_references
- with_change_table do |t|
- @connection.expects(:add_column).with(:delete_me, 'customer_id', :integer, {})
- t.belongs_to :customer
+ def test_removing_columns
+ with_bulk_change_table do |t|
+ t.string :qualification, :experience
end
- end
- def test_remove_belongs_to_works_like_remove_references
- with_change_table do |t|
- @connection.expects(:remove_column).with(:delete_me, 'customer_id')
- t.remove_belongs_to :customer
- end
- end
+ [:qualification, :experience].each {|c| assert column(c) }
- def test_references_column_type_with_polymorphic_adds_type
- with_change_table do |t|
- @connection.expects(:add_column).with(:delete_me, 'taggable_type', :string, {})
- @connection.expects(:add_column).with(:delete_me, 'taggable_id', :integer, {})
- t.references :taggable, :polymorphic => true
+ assert_queries(1) do
+ with_bulk_change_table do |t|
+ t.remove :qualification, :experience
+ t.string :qualification_experience
+ end
end
- end
- def test_remove_references_column_type_with_polymorphic_removes_type
- with_change_table do |t|
- @connection.expects(:remove_column).with(:delete_me, 'taggable_type')
- @connection.expects(:remove_column).with(:delete_me, 'taggable_id')
- t.remove_references :taggable, :polymorphic => true
- end
+ [:qualification, :experience].each {|c| assert ! column(c) }
+ assert column(:qualification_experience)
end
- def test_references_column_type_with_polymorphic_and_options_null_is_false_adds_table_flag
- with_change_table do |t|
- @connection.expects(:add_column).with(:delete_me, 'taggable_type', :string, {:null => false})
- @connection.expects(:add_column).with(:delete_me, 'taggable_id', :integer, {:null => false})
- t.references :taggable, :polymorphic => true, :null => false
+ def test_adding_indexes
+ with_bulk_change_table do |t|
+ t.string :username
+ t.string :name
+ t.integer :age
end
- end
- def test_remove_references_column_type_with_polymorphic_and_options_null_is_false_removes_table_flag
- with_change_table do |t|
- @connection.expects(:remove_column).with(:delete_me, 'taggable_type')
- @connection.expects(:remove_column).with(:delete_me, 'taggable_id')
- t.remove_references :taggable, :polymorphic => true, :null => false
- end
- end
-
- def test_timestamps_creates_updated_at_and_created_at
- with_change_table do |t|
- @connection.expects(:add_timestamps).with(:delete_me)
- t.timestamps
+ # Adding an index fires a query every time to check if an index already exists or not
+ assert_queries(3) do
+ with_bulk_change_table do |t|
+ t.index :username, :unique => true, :name => :awesome_username_index
+ t.index [:name, :age]
+ end
end
- end
- def test_remove_timestamps_creates_updated_at_and_created_at
- with_change_table do |t|
- @connection.expects(:remove_timestamps).with(:delete_me)
- t.remove_timestamps
- end
- end
+ assert_equal 2, indexes.size
- def string_column
- if current_adapter?(:PostgreSQLAdapter)
- "character varying(255)"
- elsif current_adapter?(:OracleAdapter)
- 'VARCHAR2(255)'
- else
- 'varchar(255)'
- end
- end
+ name_age_index = index(:index_delete_me_on_name_and_age)
+ assert_equal ['name', 'age'].sort, name_age_index.columns.sort
+ assert ! name_age_index.unique
- def integer_column
- if current_adapter?(:MysqlAdapter) or current_adapter?(:Mysql2Adapter)
- 'int(11)'
- elsif current_adapter?(:OracleAdapter)
- 'NUMBER(38)'
- else
- 'integer'
- end
+ assert index(:awesome_username_index).unique
end
- def test_integer_creates_integer_column
- with_change_table do |t|
- @connection.expects(:add_column).with(:delete_me, :foo, integer_column, {})
- @connection.expects(:add_column).with(:delete_me, :bar, integer_column, {})
- t.integer :foo, :bar
+ def test_removing_index
+ with_bulk_change_table do |t|
+ t.string :name
+ t.index :name
end
- end
- def test_string_creates_string_column
- with_change_table do |t|
- @connection.expects(:add_column).with(:delete_me, :foo, string_column, {})
- @connection.expects(:add_column).with(:delete_me, :bar, string_column, {})
- t.string :foo, :bar
- end
- end
+ assert index(:index_delete_me_on_name)
- def test_column_creates_column
- with_change_table do |t|
- @connection.expects(:add_column).with(:delete_me, :bar, :integer, {})
- t.column :bar, :integer
+ assert_queries(3) do
+ with_bulk_change_table do |t|
+ t.remove_index :name
+ t.index :name, :name => :new_name_index, :unique => true
+ end
end
- end
- def test_column_creates_column_with_options
- with_change_table do |t|
- @connection.expects(:add_column).with(:delete_me, :bar, :integer, {:null => false})
- t.column :bar, :integer, :null => false
- end
- end
+ assert ! index(:index_delete_me_on_name)
- def test_index_creates_index
- with_change_table do |t|
- @connection.expects(:add_index).with(:delete_me, :bar, {})
- t.index :bar
- end
+ new_name_index = index(:new_name_index)
+ assert new_name_index.unique
end
- def test_index_creates_index_with_options
- with_change_table do |t|
- @connection.expects(:add_index).with(:delete_me, :bar, {:unique => true})
- t.index :bar, :unique => true
+ def test_changing_columns
+ with_bulk_change_table do |t|
+ t.string :name
+ t.date :birthdate
end
- end
- def test_index_exists
- with_change_table do |t|
- @connection.expects(:index_exists?).with(:delete_me, :bar, {})
- t.index_exists?(:bar)
- end
- end
+ assert ! column(:name).default
+ assert_equal :date, column(:birthdate).type
- def test_index_exists_with_options
- with_change_table do |t|
- @connection.expects(:index_exists?).with(:delete_me, :bar, {:unique => true})
- t.index_exists?(:bar, :unique => true)
+ # One query for columns (delete_me table)
+ # One query for primary key (delete_me table)
+ # One query to do the bulk change
+ assert_queries(3) do
+ with_bulk_change_table do |t|
+ t.change :name, :string, :default => 'NONAME'
+ t.change :birthdate, :datetime
+ end
end
- end
- def test_change_changes_column
- with_change_table do |t|
- @connection.expects(:change_column).with(:delete_me, :bar, :string, {})
- t.change :bar, :string
- end
+ assert_equal 'NONAME', column(:name).default
+ assert_equal :datetime, column(:birthdate).type
end
- def test_change_changes_column_with_options
- with_change_table do |t|
- @connection.expects(:change_column).with(:delete_me, :bar, :string, {:null => true})
- t.change :bar, :string, :null => true
- end
- end
+ protected
- def test_change_default_changes_column
- with_change_table do |t|
- @connection.expects(:change_column_default).with(:delete_me, :bar, :string)
- t.change_default :bar, :string
- end
- end
+ def with_bulk_change_table
+ # Reset columns/indexes cache as we're changing the table
+ @columns = @indexes = nil
- def test_remove_drops_single_column
- with_change_table do |t|
- @connection.expects(:remove_column).with(:delete_me, [:bar])
- t.remove :bar
+ Person.connection.change_table(:delete_me, :bulk => true) do |t|
+ yield t
end
end
- def test_remove_drops_multiple_columns
- with_change_table do |t|
- @connection.expects(:remove_column).with(:delete_me, [:bar, :baz])
- t.remove :bar, :baz
- end
+ def column(name)
+ columns.detect {|c| c.name == name.to_s }
end
- def test_remove_index_removes_index_with_options
- with_change_table do |t|
- @connection.expects(:remove_index).with(:delete_me, {:unique => true})
- t.remove_index :unique => true
- end
+ def columns
+ @columns ||= Person.connection.columns('delete_me')
end
- def test_rename_renames_column
- with_change_table do |t|
- @connection.expects(:rename_column).with(:delete_me, :bar, :baz)
- t.rename :bar, :baz
- end
+ def index(name)
+ indexes.detect {|i| i.name == name.to_s }
end
- protected
- def with_change_table
- Person.connection.change_table :delete_me do |t|
- yield t
- end
+ def indexes
+ @indexes ||= Person.connection.indexes('delete_me')
end
- end
+ end # AlterTableMigrationsTest
- if ActiveRecord::Base.connection.supports_bulk_alter?
- class BulkAlterTableMigrationsTest < ActiveRecord::TestCase
- def setup
- @connection = Person.connection
- @connection.create_table(:delete_me, :force => true) {|t| }
- end
-
- def teardown
- Person.connection.drop_table(:delete_me) rescue nil
- end
-
- def test_adding_multiple_columns
- assert_queries(1) do
- with_bulk_change_table do |t|
- t.column :name, :string
- t.string :qualification, :experience
- t.integer :age, :default => 0
- t.date :birthdate
- t.timestamps
- end
- end
-
- assert_equal 8, columns.size
- [:name, :qualification, :experience].each {|s| assert_equal :string, column(s).type }
- assert_equal 0, column(:age).default
- end
-
- def test_removing_columns
- with_bulk_change_table do |t|
- t.string :qualification, :experience
- end
-
- [:qualification, :experience].each {|c| assert column(c) }
-
- assert_queries(1) do
- with_bulk_change_table do |t|
- t.remove :qualification, :experience
- t.string :qualification_experience
- end
- end
-
- [:qualification, :experience].each {|c| assert ! column(c) }
- assert column(:qualification_experience)
- end
-
- def test_adding_indexes
- with_bulk_change_table do |t|
- t.string :username
- t.string :name
- t.integer :age
- end
-
- # Adding an index fires a query every time to check if an index already exists or not
- assert_queries(3) do
- with_bulk_change_table do |t|
- t.index :username, :unique => true, :name => :awesome_username_index
- t.index [:name, :age]
- end
- end
-
- assert_equal 2, indexes.size
-
- name_age_index = index(:index_delete_me_on_name_and_age)
- assert_equal ['name', 'age'].sort, name_age_index.columns.sort
- assert ! name_age_index.unique
-
- assert index(:awesome_username_index).unique
- end
-
- def test_removing_index
- with_bulk_change_table do |t|
- t.string :name
- t.index :name
- end
-
- assert index(:index_delete_me_on_name)
-
- assert_queries(3) do
- with_bulk_change_table do |t|
- t.remove_index :name
- t.index :name, :name => :new_name_index, :unique => true
- end
- end
-
- assert ! index(:index_delete_me_on_name)
-
- new_name_index = index(:new_name_index)
- assert new_name_index.unique
- end
-
- def test_changing_columns
- with_bulk_change_table do |t|
- t.string :name
- t.date :birthdate
- end
-
- assert ! column(:name).default
- assert_equal :date, column(:birthdate).type
-
- # One query for columns (delete_me table)
- # One query for primary key (delete_me table)
- # One query to do the bulk change
- assert_queries(3) do
- with_bulk_change_table do |t|
- t.change :name, :string, :default => 'NONAME'
- t.change :birthdate, :datetime
- end
- end
-
- assert_equal 'NONAME', column(:name).default
- assert_equal :datetime, column(:birthdate).type
- end
-
- protected
-
- def with_bulk_change_table
- # Reset columns/indexes cache as we're changing the table
- @columns = @indexes = nil
-
- Person.connection.change_table(:delete_me, :bulk => true) do |t|
- yield t
- end
- end
-
- def column(name)
- columns.detect {|c| c.name == name.to_s }
- end
-
- def columns
- @columns ||= Person.connection.columns('delete_me')
- end
-
- def index(name)
- indexes.detect {|i| i.name == name.to_s }
- end
+end
- def indexes
- @indexes ||= Person.connection.indexes('delete_me')
- end
- end # AlterTableMigrationsTest
+class CopyMigrationsTest < ActiveRecord::TestCase
+ def setup
+ end
+ def clear
+ ActiveRecord::Base.timestamped_migrations = true
+ to_delete = Dir[@migrations_path + "/*.rb"] - @existing_migrations
+ File.delete(*to_delete)
end
- class CopyMigrationsTest < ActiveRecord::TestCase
- def setup
- end
+ def test_copying_migrations_without_timestamps
+ ActiveRecord::Base.timestamped_migrations = false
+ @migrations_path = MIGRATIONS_ROOT + "/valid"
+ @existing_migrations = Dir[@migrations_path + "/*.rb"]
+
+ copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy"})
+ assert File.exists?(@migrations_path + "/4_people_have_hobbies.bukkits.rb")
+ assert File.exists?(@migrations_path + "/5_people_have_descriptions.bukkits.rb")
+ assert_equal [@migrations_path + "/4_people_have_hobbies.bukkits.rb", @migrations_path + "/5_people_have_descriptions.bukkits.rb"], copied.map(&:filename)
+
+ expected = "# This migration comes from bukkits (originally 1)"
+ assert_equal expected, IO.readlines(@migrations_path + "/4_people_have_hobbies.bukkits.rb")[0].chomp
+
+ files_count = Dir[@migrations_path + "/*.rb"].length
+ copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy"})
+ assert_equal files_count, Dir[@migrations_path + "/*.rb"].length
+ assert copied.empty?
+ ensure
+ clear
+ end
- def clear
- ActiveRecord::Base.timestamped_migrations = true
- to_delete = Dir[@migrations_path + "/*.rb"] - @existing_migrations
- File.delete(*to_delete)
- end
+ def test_copying_migrations_without_timestamps_from_2_sources
+ ActiveRecord::Base.timestamped_migrations = false
+ @migrations_path = MIGRATIONS_ROOT + "/valid"
+ @existing_migrations = Dir[@migrations_path + "/*.rb"]
+
+ sources = ActiveSupport::OrderedHash.new
+ sources[:bukkits] = MIGRATIONS_ROOT + "/to_copy"
+ sources[:omg] = MIGRATIONS_ROOT + "/to_copy2"
+ ActiveRecord::Migration.copy(@migrations_path, sources)
+ assert File.exists?(@migrations_path + "/4_people_have_hobbies.bukkits.rb")
+ assert File.exists?(@migrations_path + "/5_people_have_descriptions.bukkits.rb")
+ assert File.exists?(@migrations_path + "/6_create_articles.omg.rb")
+ assert File.exists?(@migrations_path + "/7_create_comments.omg.rb")
+
+ files_count = Dir[@migrations_path + "/*.rb"].length
+ ActiveRecord::Migration.copy(@migrations_path, sources)
+ assert_equal files_count, Dir[@migrations_path + "/*.rb"].length
+ ensure
+ clear
+ end
- def test_copying_migrations_without_timestamps
- ActiveRecord::Base.timestamped_migrations = false
- @migrations_path = MIGRATIONS_ROOT + "/valid"
- @existing_migrations = Dir[@migrations_path + "/*.rb"]
+ def test_copying_migrations_with_timestamps
+ @migrations_path = MIGRATIONS_ROOT + "/valid_with_timestamps"
+ @existing_migrations = Dir[@migrations_path + "/*.rb"]
- copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy"})
- assert File.exists?(@migrations_path + "/4_people_have_hobbies.rb")
- assert File.exists?(@migrations_path + "/5_people_have_descriptions.rb")
- assert_equal [@migrations_path + "/4_people_have_hobbies.rb", @migrations_path + "/5_people_have_descriptions.rb"], copied.map(&:filename)
+ Time.travel_to(Time.utc(2010, 7, 26, 10, 10, 10)) do
+ copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy_with_timestamps"})
+ assert File.exists?(@migrations_path + "/20100726101010_people_have_hobbies.bukkits.rb")
+ assert File.exists?(@migrations_path + "/20100726101011_people_have_descriptions.bukkits.rb")
+ expected = [@migrations_path + "/20100726101010_people_have_hobbies.bukkits.rb",
+ @migrations_path + "/20100726101011_people_have_descriptions.bukkits.rb"]
+ assert_equal expected, copied.map(&:filename)
files_count = Dir[@migrations_path + "/*.rb"].length
- copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy"})
+ copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy_with_timestamps"})
assert_equal files_count, Dir[@migrations_path + "/*.rb"].length
assert copied.empty?
- ensure
- clear
end
+ ensure
+ clear
+ end
- def test_copying_migrations_without_timestamps_from_2_sources
- ActiveRecord::Base.timestamped_migrations = false
- @migrations_path = MIGRATIONS_ROOT + "/valid"
- @existing_migrations = Dir[@migrations_path + "/*.rb"]
+ def test_copying_migrations_with_timestamps_from_2_sources
+ @migrations_path = MIGRATIONS_ROOT + "/valid_with_timestamps"
+ @existing_migrations = Dir[@migrations_path + "/*.rb"]
- sources = ActiveSupport::OrderedHash.new
- sources[:bukkits] = MIGRATIONS_ROOT + "/to_copy"
- sources[:omg] = MIGRATIONS_ROOT + "/to_copy2"
- ActiveRecord::Migration.copy(@migrations_path, sources)
- assert File.exists?(@migrations_path + "/4_people_have_hobbies.rb")
- assert File.exists?(@migrations_path + "/5_people_have_descriptions.rb")
- assert File.exists?(@migrations_path + "/6_create_articles.rb")
- assert File.exists?(@migrations_path + "/7_create_comments.rb")
+ sources = ActiveSupport::OrderedHash.new
+ sources[:bukkits] = MIGRATIONS_ROOT + "/to_copy_with_timestamps"
+ sources[:omg] = MIGRATIONS_ROOT + "/to_copy_with_timestamps2"
+
+ Time.travel_to(Time.utc(2010, 7, 26, 10, 10, 10)) do
+ copied = ActiveRecord::Migration.copy(@migrations_path, sources)
+ assert File.exists?(@migrations_path + "/20100726101010_people_have_hobbies.bukkits.rb")
+ assert File.exists?(@migrations_path + "/20100726101011_people_have_descriptions.bukkits.rb")
+ assert File.exists?(@migrations_path + "/20100726101012_create_articles.omg.rb")
+ assert File.exists?(@migrations_path + "/20100726101013_create_comments.omg.rb")
+ assert_equal 4, copied.length
files_count = Dir[@migrations_path + "/*.rb"].length
ActiveRecord::Migration.copy(@migrations_path, sources)
assert_equal files_count, Dir[@migrations_path + "/*.rb"].length
- ensure
- clear
end
+ ensure
+ clear
+ end
- def test_copying_migrations_with_timestamps
- @migrations_path = MIGRATIONS_ROOT + "/valid_with_timestamps"
- @existing_migrations = Dir[@migrations_path + "/*.rb"]
+ def test_copying_migrations_with_timestamps_to_destination_with_timestamps_in_future
+ @migrations_path = MIGRATIONS_ROOT + "/valid_with_timestamps"
+ @existing_migrations = Dir[@migrations_path + "/*.rb"]
- Time.travel_to(Time.utc(2010, 7, 26, 10, 10, 10)) do
- copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy_with_timestamps"})
- assert File.exists?(@migrations_path + "/20100726101010_people_have_hobbies.rb")
- assert File.exists?(@migrations_path + "/20100726101011_people_have_descriptions.rb")
- expected = [@migrations_path + "/20100726101010_people_have_hobbies.rb",
- @migrations_path + "/20100726101011_people_have_descriptions.rb"]
- assert_equal expected, copied.map(&:filename)
+ Time.travel_to(Time.utc(2010, 2, 20, 10, 10, 10)) do
+ ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy_with_timestamps"})
+ assert File.exists?(@migrations_path + "/20100301010102_people_have_hobbies.bukkits.rb")
+ assert File.exists?(@migrations_path + "/20100301010103_people_have_descriptions.bukkits.rb")
- files_count = Dir[@migrations_path + "/*.rb"].length
- copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy_with_timestamps"})
- assert_equal files_count, Dir[@migrations_path + "/*.rb"].length
- assert copied.empty?
- end
- ensure
- clear
+ files_count = Dir[@migrations_path + "/*.rb"].length
+ copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy_with_timestamps"})
+ assert_equal files_count, Dir[@migrations_path + "/*.rb"].length
+ assert copied.empty?
end
+ ensure
+ clear
+ end
- def test_copying_migrations_with_timestamps_from_2_sources
- @migrations_path = MIGRATIONS_ROOT + "/valid_with_timestamps"
- @existing_migrations = Dir[@migrations_path + "/*.rb"]
+ def test_skipping_migrations
+ @migrations_path = MIGRATIONS_ROOT + "/valid_with_timestamps"
+ @existing_migrations = Dir[@migrations_path + "/*.rb"]
- sources = ActiveSupport::OrderedHash.new
- sources[:bukkits] = MIGRATIONS_ROOT + "/to_copy_with_timestamps"
- sources[:omg] = MIGRATIONS_ROOT + "/to_copy_with_timestamps2"
+ sources = ActiveSupport::OrderedHash.new
+ sources[:bukkits] = MIGRATIONS_ROOT + "/to_copy_with_timestamps"
+ sources[:omg] = MIGRATIONS_ROOT + "/to_copy_with_name_collision"
- Time.travel_to(Time.utc(2010, 7, 26, 10, 10, 10)) do
- copied = ActiveRecord::Migration.copy(@migrations_path, sources)
- assert File.exists?(@migrations_path + "/20100726101010_people_have_hobbies.rb")
- assert File.exists?(@migrations_path + "/20100726101011_people_have_descriptions.rb")
- assert File.exists?(@migrations_path + "/20100726101012_create_articles.rb")
- assert File.exists?(@migrations_path + "/20100726101013_create_comments.rb")
- assert_equal 4, copied.length
+ skipped = []
+ on_skip = Proc.new { |name, migration| skipped << "#{name} #{migration.name}" }
+ copied = ActiveRecord::Migration.copy(@migrations_path, sources, :on_skip => on_skip)
+ assert_equal 2, copied.length
- files_count = Dir[@migrations_path + "/*.rb"].length
- ActiveRecord::Migration.copy(@migrations_path, sources)
- assert_equal files_count, Dir[@migrations_path + "/*.rb"].length
- end
- ensure
- clear
- end
+ assert_equal 1, skipped.length
+ assert_equal ["omg PeopleHaveHobbies"], skipped
+ ensure
+ clear
+ end
- def test_copying_migrations_with_timestamps_to_destination_with_timestamps_in_future
- @migrations_path = MIGRATIONS_ROOT + "/valid_with_timestamps"
- @existing_migrations = Dir[@migrations_path + "/*.rb"]
+ def test_skip_is_not_called_if_migrations_are_from_the_same_plugin
+ @migrations_path = MIGRATIONS_ROOT + "/valid_with_timestamps"
+ @existing_migrations = Dir[@migrations_path + "/*.rb"]
- Time.travel_to(Time.utc(2010, 2, 20, 10, 10, 10)) do
- ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy_with_timestamps"})
- assert File.exists?(@migrations_path + "/20100301010102_people_have_hobbies.rb")
- assert File.exists?(@migrations_path + "/20100301010103_people_have_descriptions.rb")
+ sources = ActiveSupport::OrderedHash.new
+ sources[:bukkits] = MIGRATIONS_ROOT + "/to_copy_with_timestamps"
- files_count = Dir[@migrations_path + "/*.rb"].length
- copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy_with_timestamps"})
- assert_equal files_count, Dir[@migrations_path + "/*.rb"].length
- assert copied.empty?
- end
- ensure
- clear
- end
+ skipped = []
+ on_skip = Proc.new { |name, migration| skipped << "#{name} #{migration.name}" }
+ copied = ActiveRecord::Migration.copy(@migrations_path, sources, :on_skip => on_skip)
+ ActiveRecord::Migration.copy(@migrations_path, sources, :on_skip => on_skip)
- def test_skipping_migrations
- @migrations_path = MIGRATIONS_ROOT + "/valid_with_timestamps"
- @existing_migrations = Dir[@migrations_path + "/*.rb"]
+ assert_equal 2, copied.length
+ assert_equal 0, skipped.length
+ ensure
+ clear
+ end
- sources = ActiveSupport::OrderedHash.new
- sources[:bukkits] = sources[:omg] = MIGRATIONS_ROOT + "/to_copy_with_timestamps"
+ def test_copying_migrations_to_non_existing_directory
+ @migrations_path = MIGRATIONS_ROOT + "/non_existing"
+ @existing_migrations = []
- skipped = []
- on_skip = Proc.new { |name, migration| skipped << "#{name} #{migration.name}" }
- copied = ActiveRecord::Migration.copy(@migrations_path, sources, :on_skip => on_skip)
+ Time.travel_to(Time.utc(2010, 7, 26, 10, 10, 10)) do
+ copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy_with_timestamps"})
+ assert File.exists?(@migrations_path + "/20100726101010_people_have_hobbies.bukkits.rb")
+ assert File.exists?(@migrations_path + "/20100726101011_people_have_descriptions.bukkits.rb")
assert_equal 2, copied.length
-
- assert_equal 2, skipped.length
- assert_equal ["bukkits PeopleHaveHobbies", "bukkits PeopleHaveDescriptions"], skipped
- ensure
- clear
- end
-
- def test_copying_migrations_to_non_existing_directory
- @migrations_path = MIGRATIONS_ROOT + "/non_existing"
- @existing_migrations = []
-
- Time.travel_to(Time.utc(2010, 7, 26, 10, 10, 10)) do
- copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy_with_timestamps"})
- assert File.exists?(@migrations_path + "/20100726101010_people_have_hobbies.rb")
- assert File.exists?(@migrations_path + "/20100726101011_people_have_descriptions.rb")
- assert_equal 2, copied.length
- end
- ensure
- clear
- Dir.delete(@migrations_path)
end
+ ensure
+ clear
+ Dir.delete(@migrations_path)
+ end
- def test_copying_migrations_to_empty_directory
- @migrations_path = MIGRATIONS_ROOT + "/empty"
- @existing_migrations = []
+ def test_copying_migrations_to_empty_directory
+ @migrations_path = MIGRATIONS_ROOT + "/empty"
+ @existing_migrations = []
- Time.travel_to(Time.utc(2010, 7, 26, 10, 10, 10)) do
- copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy_with_timestamps"})
- assert File.exists?(@migrations_path + "/20100726101010_people_have_hobbies.rb")
- assert File.exists?(@migrations_path + "/20100726101011_people_have_descriptions.rb")
- assert_equal 2, copied.length
- end
- ensure
- clear
+ Time.travel_to(Time.utc(2010, 7, 26, 10, 10, 10)) do
+ copied = ActiveRecord::Migration.copy(@migrations_path, {:bukkits => MIGRATIONS_ROOT + "/to_copy_with_timestamps"})
+ assert File.exists?(@migrations_path + "/20100726101010_people_have_hobbies.bukkits.rb")
+ assert File.exists?(@migrations_path + "/20100726101011_people_have_descriptions.bukkits.rb")
+ assert_equal 2, copied.length
end
+ ensure
+ clear
end
end
diff --git a/activerecord/test/cases/migrator_test.rb b/activerecord/test/cases/migrator_test.rb
new file mode 100644
index 0000000000..1e16addcf3
--- /dev/null
+++ b/activerecord/test/cases/migrator_test.rb
@@ -0,0 +1,377 @@
+require "cases/helper"
+require "cases/migration/helper"
+
+module ActiveRecord
+ class MigratorTest < ActiveRecord::TestCase
+ self.use_transactional_fixtures = false
+
+ # Use this class to sense if migrations have gone
+ # up or down.
+ class Sensor < ActiveRecord::Migration
+ attr_reader :went_up, :went_down
+
+ def initialize name = self.class.name, version = nil
+ super
+ @went_up = false
+ @went_down = false
+ end
+
+ def up; @went_up = true; end
+ def down; @went_down = true; end
+ end
+
+ def setup
+ super
+ ActiveRecord::SchemaMigration.create_table
+ ActiveRecord::SchemaMigration.delete_all rescue nil
+ end
+
+ def teardown
+ super
+ ActiveRecord::SchemaMigration.delete_all rescue nil
+ end
+
+ def test_migrator_with_duplicate_names
+ assert_raises(ActiveRecord::DuplicateMigrationNameError, "Multiple migrations have the name Chunky") do
+ list = [Migration.new('Chunky'), Migration.new('Chunky')]
+ ActiveRecord::Migrator.new(:up, list)
+ end
+ end
+
+ def test_migrator_with_duplicate_versions
+ assert_raises(ActiveRecord::DuplicateMigrationVersionError) do
+ list = [Migration.new('Foo', 1), Migration.new('Bar', 1)]
+ ActiveRecord::Migrator.new(:up, list)
+ end
+ end
+
+ def test_migrator_with_missing_version_numbers
+ assert_raises(ActiveRecord::UnknownMigrationVersionError) do
+ list = [Migration.new('Foo', 1), Migration.new('Bar', 2)]
+ ActiveRecord::Migrator.new(:up, list, 3).run
+ end
+ end
+
+ def test_finds_migrations
+ migrations = ActiveRecord::Migrator.migrations(MIGRATIONS_ROOT + "/valid")
+
+ [[1, 'ValidPeopleHaveLastNames'], [2, 'WeNeedReminders'], [3, 'InnocentJointable']].each_with_index do |pair, i|
+ assert_equal migrations[i].version, pair.first
+ assert_equal migrations[i].name, pair.last
+ end
+ end
+
+ def test_finds_migrations_in_subdirectories
+ migrations = ActiveRecord::Migrator.migrations(MIGRATIONS_ROOT + "/valid_with_subdirectories")
+
+ [[1, 'ValidPeopleHaveLastNames'], [2, 'WeNeedReminders'], [3, 'InnocentJointable']].each_with_index do |pair, i|
+ assert_equal migrations[i].version, pair.first
+ assert_equal migrations[i].name, pair.last
+ end
+ end
+
+ def test_finds_migrations_from_two_directories
+ directories = [MIGRATIONS_ROOT + '/valid_with_timestamps', MIGRATIONS_ROOT + '/to_copy_with_timestamps']
+ migrations = ActiveRecord::Migrator.migrations directories
+
+ [[20090101010101, "PeopleHaveHobbies"],
+ [20090101010202, "PeopleHaveDescriptions"],
+ [20100101010101, "ValidWithTimestampsPeopleHaveLastNames"],
+ [20100201010101, "ValidWithTimestampsWeNeedReminders"],
+ [20100301010101, "ValidWithTimestampsInnocentJointable"]].each_with_index do |pair, i|
+ assert_equal pair.first, migrations[i].version
+ assert_equal pair.last, migrations[i].name
+ end
+ end
+
+ def test_deprecated_constructor
+ assert_deprecated do
+ ActiveRecord::Migrator.new(:up, MIGRATIONS_ROOT + "/valid")
+ end
+ end
+
+ def test_relative_migrations
+ list = Dir.chdir(MIGRATIONS_ROOT) do
+ ActiveRecord::Migrator.migrations("valid/")
+ end
+
+ migration_proxy = list.find { |item|
+ item.name == 'ValidPeopleHaveLastNames'
+ }
+ assert migration_proxy, 'should find pending migration'
+ end
+
+ def test_finds_pending_migrations
+ ActiveRecord::SchemaMigration.create!(:version => '1')
+ migration_list = [ Migration.new('foo', 1), Migration.new('bar', 3) ]
+ migrations = ActiveRecord::Migrator.new(:up, migration_list).pending_migrations
+
+ assert_equal 1, migrations.size
+ assert_equal migration_list.last, migrations.first
+ end
+
+ def test_migrator_interleaved_migrations
+ pass_one = [Sensor.new('One', 1)]
+
+ ActiveRecord::Migrator.new(:up, pass_one).migrate
+ assert pass_one.first.went_up
+ refute pass_one.first.went_down
+
+ pass_two = [Sensor.new('One', 1), Sensor.new('Three', 3)]
+ ActiveRecord::Migrator.new(:up, pass_two).migrate
+ refute pass_two[0].went_up
+ assert pass_two[1].went_up
+ assert pass_two.all? { |x| !x.went_down }
+
+ pass_three = [Sensor.new('One', 1),
+ Sensor.new('Two', 2),
+ Sensor.new('Three', 3)]
+
+ ActiveRecord::Migrator.new(:down, pass_three).migrate
+ assert pass_three[0].went_down
+ refute pass_three[1].went_down
+ assert pass_three[2].went_down
+ end
+
+ def test_up_calls_up
+ migrations = [Sensor.new(nil, 0), Sensor.new(nil, 1), Sensor.new(nil, 2)]
+ ActiveRecord::Migrator.new(:up, migrations).migrate
+ assert migrations.all? { |m| m.went_up }
+ assert migrations.all? { |m| !m.went_down }
+ assert_equal 2, ActiveRecord::Migrator.current_version
+ end
+
+ def test_down_calls_down
+ test_up_calls_up
+
+ migrations = [Sensor.new(nil, 0), Sensor.new(nil, 1), Sensor.new(nil, 2)]
+ ActiveRecord::Migrator.new(:down, migrations).migrate
+ assert migrations.all? { |m| !m.went_up }
+ assert migrations.all? { |m| m.went_down }
+ assert_equal 0, ActiveRecord::Migrator.current_version
+ end
+
+ def test_current_version
+ ActiveRecord::SchemaMigration.create!(:version => '1000')
+ assert_equal 1000, ActiveRecord::Migrator.current_version
+ end
+
+ def test_migrator_one_up
+ calls, migrations = sensors(3)
+
+ ActiveRecord::Migrator.new(:up, migrations, 1).migrate
+ assert_equal [[:up, 1]], calls
+ calls.clear
+
+ ActiveRecord::Migrator.new(:up, migrations, 2).migrate
+ assert_equal [[:up, 2]], calls
+ end
+
+ def test_migrator_one_down
+ calls, migrations = sensors(3)
+
+ ActiveRecord::Migrator.new(:up, migrations).migrate
+ assert_equal [[:up, 1], [:up, 2], [:up, 3]], calls
+ calls.clear
+
+ ActiveRecord::Migrator.new(:down, migrations, 1).migrate
+
+ assert_equal [[:down, 3], [:down, 2]], calls
+ end
+
+ def test_migrator_one_up_one_down
+ calls, migrations = sensors(3)
+
+ ActiveRecord::Migrator.new(:up, migrations, 1).migrate
+ assert_equal [[:up, 1]], calls
+ calls.clear
+
+ ActiveRecord::Migrator.new(:down, migrations, 0).migrate
+ assert_equal [[:down, 1]], calls
+ end
+
+ def test_migrator_double_up
+ calls, migrations = sensors(3)
+ assert_equal(0, ActiveRecord::Migrator.current_version)
+
+ ActiveRecord::Migrator.new(:up, migrations, 1).migrate
+ assert_equal [[:up, 1]], calls
+ calls.clear
+
+ ActiveRecord::Migrator.new(:up, migrations, 1).migrate
+ assert_equal [], calls
+ end
+
+ def test_migrator_double_down
+ calls, migrations = sensors(3)
+
+ assert_equal(0, ActiveRecord::Migrator.current_version)
+
+ ActiveRecord::Migrator.new(:up, migrations, 1).run
+ assert_equal [[:up, 1]], calls
+ calls.clear
+
+ ActiveRecord::Migrator.new(:down, migrations, 1).run
+ assert_equal [[:down, 1]], calls
+ calls.clear
+
+ ActiveRecord::Migrator.new(:down, migrations, 1).run
+ assert_equal [], calls
+
+ assert_equal(0, ActiveRecord::Migrator.current_version)
+ end
+
+ def test_migrator_verbosity
+ _, migrations = sensors(3)
+
+ ActiveRecord::Migrator.new(:up, migrations, 1).migrate
+ assert_not_equal 0, ActiveRecord::Migration.message_count
+
+ ActiveRecord::Migration.message_count = 0
+
+ ActiveRecord::Migrator.new(:down, migrations, 0).migrate
+ assert_not_equal 0, ActiveRecord::Migration.message_count
+ ActiveRecord::Migration.message_count = 0
+ end
+
+ def test_migrator_verbosity_off
+ _, migrations = sensors(3)
+
+ ActiveRecord::Migration.message_count = 0
+ ActiveRecord::Migration.verbose = false
+ ActiveRecord::Migrator.new(:up, migrations, 1).migrate
+ assert_equal 0, ActiveRecord::Migration.message_count
+ ActiveRecord::Migrator.new(:down, migrations, 0).migrate
+ assert_equal 0, ActiveRecord::Migration.message_count
+ end
+
+ def test_target_version_zero_should_run_only_once
+ calls, migrations = sensors(3)
+
+ # migrate up to 1
+ ActiveRecord::Migrator.new(:up, migrations, 1).migrate
+ assert_equal [[:up, 1]], calls
+ calls.clear
+
+ # migrate down to 0
+ ActiveRecord::Migrator.new(:down, migrations, 0).migrate
+ assert_equal [[:down, 1]], calls
+ calls.clear
+
+ # migrate down to 0 again
+ ActiveRecord::Migrator.new(:down, migrations, 0).migrate
+ assert_equal [], calls
+ end
+
+ def test_migrator_going_down_due_to_version_target
+ calls, migrator = migrator_class(3)
+
+ migrator.up("valid", 1)
+ assert_equal [[:up, 1]], calls
+ calls.clear
+
+ migrator.migrate("valid", 0)
+ assert_equal [[:down, 1]], calls
+ calls.clear
+
+ migrator.migrate("valid")
+ assert_equal [[:up, 1], [:up, 2], [:up, 3]], calls
+ end
+
+ def test_migrator_rollback
+ _, migrator = migrator_class(3)
+
+ migrator.migrate("valid")
+ assert_equal(3, ActiveRecord::Migrator.current_version)
+
+ migrator.rollback("valid")
+ assert_equal(2, ActiveRecord::Migrator.current_version)
+
+ migrator.rollback("valid")
+ assert_equal(1, ActiveRecord::Migrator.current_version)
+
+ migrator.rollback("valid")
+ assert_equal(0, ActiveRecord::Migrator.current_version)
+
+ migrator.rollback("valid")
+ assert_equal(0, ActiveRecord::Migrator.current_version)
+ end
+
+ def test_migrator_db_has_no_schema_migrations_table
+ _, migrator = migrator_class(3)
+
+ ActiveRecord::Base.connection.execute("DROP TABLE schema_migrations")
+ refute ActiveRecord::Base.connection.table_exists?('schema_migrations')
+ migrator.migrate("valid", 1)
+ assert ActiveRecord::Base.connection.table_exists?('schema_migrations')
+ end
+
+ def test_migrator_forward
+ _, migrator = migrator_class(3)
+ migrator.migrate("/valid", 1)
+ assert_equal(1, ActiveRecord::Migrator.current_version)
+
+ migrator.forward("/valid", 2)
+ assert_equal(3, ActiveRecord::Migrator.current_version)
+
+ migrator.forward("/valid")
+ assert_equal(3, ActiveRecord::Migrator.current_version)
+ end
+
+ def test_only_loads_pending_migrations
+ # migrate up to 1
+ ActiveRecord::SchemaMigration.create!(:version => '1')
+
+ calls, migrator = migrator_class(3)
+ migrator.migrate("valid", nil)
+
+ assert_equal [[:up, 2], [:up, 3]], calls
+ end
+
+ def test_get_all_versions
+ _, migrator = migrator_class(3)
+
+ migrator.migrate("valid")
+ assert_equal([1,2,3], ActiveRecord::Migrator.get_all_versions)
+
+ migrator.rollback("valid")
+ assert_equal([1,2], ActiveRecord::Migrator.get_all_versions)
+
+ migrator.rollback("valid")
+ assert_equal([1], ActiveRecord::Migrator.get_all_versions)
+
+ migrator.rollback("valid")
+ assert_equal([], ActiveRecord::Migrator.get_all_versions)
+ end
+
+ private
+ def m(name, version, &block)
+ x = Sensor.new name, version
+ x.extend(Module.new {
+ define_method(:up) { block.call(:up, x); super() }
+ define_method(:down) { block.call(:down, x); super() }
+ }) if block_given?
+ end
+
+ def sensors(count)
+ calls = []
+ migrations = count.times.map { |i|
+ m(nil, i + 1) { |c,migration|
+ calls << [c, migration.version]
+ }
+ }
+ [calls, migrations]
+ end
+
+ def migrator_class(count)
+ calls, migrations = sensors(count)
+
+ migrator = Class.new(Migrator).extend(Module.new {
+ define_method(:migrations) { |paths|
+ migrations
+ }
+ })
+ [calls, migrator]
+ end
+ end
+end
diff --git a/activerecord/test/cases/modules_test.rb b/activerecord/test/cases/modules_test.rb
index a2041af16a..f7a5d05582 100644
--- a/activerecord/test/cases/modules_test.rb
+++ b/activerecord/test/cases/modules_test.rb
@@ -72,7 +72,7 @@ class ModulesTest < ActiveRecord::TestCase
clients = []
assert_nothing_raised NameError, "Should be able to resolve all class constants via reflection" do
- clients << MyApplication::Business::Client.find(3, :include => {:firm => :account}, :conditions => 'accounts.id IS NOT NULL')
+ clients << MyApplication::Business::Client.find(3, :include => {:firm => :account}, :conditions => 'accounts.id IS NOT NULL', :references => :accounts)
clients << MyApplication::Business::Client.find(3, :include => {:firm => :account})
end
diff --git a/activerecord/test/cases/multiple_db_test.rb b/activerecord/test/cases/multiple_db_test.rb
index bd51388e05..e704322b5d 100644
--- a/activerecord/test/cases/multiple_db_test.rb
+++ b/activerecord/test/cases/multiple_db_test.rb
@@ -85,7 +85,6 @@ class MultipleDbTest < ActiveRecord::TestCase
end
def test_arel_table_engines
- assert_not_equal Entrant.arel_engine, Course.arel_engine
assert_equal Entrant.arel_engine, Bird.arel_engine
end
end
diff --git a/activerecord/test/cases/named_scope_test.rb b/activerecord/test/cases/named_scope_test.rb
index 4a09a87322..e17ba76437 100644
--- a/activerecord/test/cases/named_scope_test.rb
+++ b/activerecord/test/cases/named_scope_test.rb
@@ -1,5 +1,4 @@
require "cases/helper"
-require 'active_support/core_ext/array/random_access'
require 'models/post'
require 'models/topic'
require 'models/comment'
@@ -337,6 +336,11 @@ class NamedScopeTest < ActiveRecord::TestCase
end
end
+ def test_should_not_duplicates_where_values
+ where_values = Topic.where("1=1").scope_with_lambda.where_values
+ assert_equal ["1=1"], where_values
+ end
+
def test_chaining_with_duplicate_joins
join = "INNER JOIN comments ON comments.post_id = posts.id"
post = Post.find(1)
diff --git a/activerecord/test/cases/nested_attributes_test.rb b/activerecord/test/cases/nested_attributes_test.rb
index 67a9ed6cd8..2ae9cb4888 100644
--- a/activerecord/test/cases/nested_attributes_test.rb
+++ b/activerecord/test/cases/nested_attributes_test.rb
@@ -45,6 +45,14 @@ class TestNestedAttributesInGeneral < ActiveRecord::TestCase
end
end
+ def test_should_not_build_a_new_record_using_reject_all_even_if_destroy_is_given
+ pirate = Pirate.create!(:catchphrase => "Don' botharrr talkin' like one, savvy?")
+ pirate.birds_with_reject_all_blank_attributes = [{:name => '', :color => '', :_destroy => '0'}]
+ pirate.save!
+
+ assert pirate.birds_with_reject_all_blank.empty?
+ end
+
def test_should_not_build_a_new_record_if_reject_all_blank_returns_false
pirate = Pirate.create!(:catchphrase => "Don' botharrr talkin' like one, savvy?")
pirate.birds_with_reject_all_blank_attributes = [{:name => '', :color => ''}]
diff --git a/activerecord/test/cases/pooled_connections_test.rb b/activerecord/test/cases/pooled_connections_test.rb
index 379cf5b44e..fba3006ebe 100644
--- a/activerecord/test/cases/pooled_connections_test.rb
+++ b/activerecord/test/cases/pooled_connections_test.rb
@@ -3,19 +3,21 @@ require "models/project"
require "timeout"
class PooledConnectionsTest < ActiveRecord::TestCase
+ self.use_transactional_fixtures = false
+
def setup
@per_test_teardown = []
- @connection = ActiveRecord::Base.remove_connection
+ @connection = ActiveRecord::Model.remove_connection
end
def teardown
- ActiveRecord::Base.clear_all_connections!
- ActiveRecord::Base.establish_connection(@connection)
+ ActiveRecord::Model.clear_all_connections!
+ ActiveRecord::Model.establish_connection(@connection)
@per_test_teardown.each {|td| td.call }
end
def checkout_connections
- ActiveRecord::Base.establish_connection(@connection.merge({:pool => 2, :wait_timeout => 0.3}))
+ ActiveRecord::Model.establish_connection(@connection.merge({:pool => 2, :wait_timeout => 0.3}))
@connections = []
@timed_out = 0
@@ -31,24 +33,16 @@ class PooledConnectionsTest < ActiveRecord::TestCase
end
# Will deadlock due to lack of Monitor timeouts in 1.9
- if RUBY_VERSION < '1.9'
- def test_pooled_connection_checkout
- checkout_connections
- assert_equal 2, @connections.length
- assert_equal 2, @timed_out
- end
- end
-
def checkout_checkin_connections(pool_size, threads)
- ActiveRecord::Base.establish_connection(@connection.merge({:pool => pool_size, :wait_timeout => 0.5}))
+ ActiveRecord::Model.establish_connection(@connection.merge({:pool => pool_size, :wait_timeout => 0.5}))
@connection_count = 0
@timed_out = 0
threads.times do
Thread.new do
begin
- conn = ActiveRecord::Base.connection_pool.checkout
+ conn = ActiveRecord::Model.connection_pool.checkout
sleep 0.1
- ActiveRecord::Base.connection_pool.checkin conn
+ ActiveRecord::Model.connection_pool.checkin conn
@connection_count += 1
rescue ActiveRecord::ConnectionTimeoutError
@timed_out += 1
@@ -61,85 +55,13 @@ class PooledConnectionsTest < ActiveRecord::TestCase
checkout_checkin_connections 1, 2
assert_equal 2, @connection_count
assert_equal 0, @timed_out
- assert_equal 1, ActiveRecord::Base.connection_pool.connections.size
- end
-
- def test_pooled_connection_checkin_two
- checkout_checkin_connections 2, 3
- assert_equal 3, @connection_count
- assert_equal 0, @timed_out
- assert_equal 1, ActiveRecord::Base.connection_pool.connections.size
- end
-
- def test_pooled_connection_checkout_existing_first
- ActiveRecord::Base.establish_connection(@connection.merge({:pool => 1}))
- conn_pool = ActiveRecord::Base.connection_pool
- conn = conn_pool.checkout
- conn_pool.checkin(conn)
- conn = conn_pool.checkout
- assert ActiveRecord::ConnectionAdapters::AbstractAdapter === conn
- conn_pool.checkin(conn)
- end
-
- def test_not_connected_defined_connection_returns_false
- ActiveRecord::Base.establish_connection(@connection)
- assert ! ActiveRecord::Base.connected?
- end
-
- def test_undefined_connection_returns_false
- old_handler = ActiveRecord::Base.connection_handler
- ActiveRecord::Base.connection_handler = ActiveRecord::ConnectionAdapters::ConnectionHandler.new
- assert ! ActiveRecord::Base.connected?
- ensure
- ActiveRecord::Base.connection_handler = old_handler
+ assert_equal 1, ActiveRecord::Model.connection_pool.connections.size
end
- def test_connection_config
- ActiveRecord::Base.establish_connection(@connection)
- assert_equal @connection, ActiveRecord::Base.connection_config
- end
-
- def test_with_connection_nesting_safety
- ActiveRecord::Base.establish_connection(@connection.merge({:pool => 1, :wait_timeout => 0.1}))
-
- before_count = Project.count
-
- add_record('one')
-
- ActiveRecord::Base.connection.transaction do
- add_record('two')
- # Have another thread try to screw up the transaction
- Thread.new do
- ActiveRecord::Base.connection.rollback_db_transaction
- ActiveRecord::Base.connection_pool.release_connection
- end
- add_record('three')
- end
-
- after_count = Project.count
- assert_equal 3, after_count - before_count
- end
-
- def test_connection_pool_callbacks
- checked_out, checked_in = false, false
- ActiveRecord::ConnectionAdapters::AbstractAdapter.class_eval do
- set_callback(:checkout, :after) { checked_out = true }
- set_callback(:checkin, :before) { checked_in = true }
- end
- @per_test_teardown << proc do
- ActiveRecord::ConnectionAdapters::AbstractAdapter.class_eval do
- reset_callbacks :checkout
- reset_callbacks :checkin
- end
- end
- checkout_checkin_connections 1, 1
- assert checked_out
- assert checked_in
- end
private
def add_record(name)
- ActiveRecord::Base.connection_pool.with_connection { Project.create! :name => name }
+ ActiveRecord::Model.connection_pool.with_connection { Project.create! :name => name }
end
end unless current_adapter?(:FrontBase) || in_memory_db?
diff --git a/activerecord/test/cases/primary_keys_test.rb b/activerecord/test/cases/primary_keys_test.rb
index 05a41d8a0a..e6e50a4cd4 100644
--- a/activerecord/test/cases/primary_keys_test.rb
+++ b/activerecord/test/cases/primary_keys_test.rb
@@ -23,6 +23,11 @@ class PrimaryKeysTest < ActiveRecord::TestCase
assert_equal keyboard.to_key, [keyboard.id]
end
+ def test_read_attribute_with_custom_primary_key
+ keyboard = Keyboard.create!
+ assert_equal keyboard.key_number, keyboard.read_attribute(:id)
+ end
+
def test_to_key_with_primary_key_after_destroy
topic = Topic.find(1)
topic.destroy
@@ -142,7 +147,72 @@ class PrimaryKeysTest < ActiveRecord::TestCase
assert_equal k.connection.quote_column_name("id"), k.quoted_primary_key
k.primary_key = "foo"
assert_equal k.connection.quote_column_name("foo"), k.quoted_primary_key
- k.set_primary_key "bar"
- assert_equal k.connection.quote_column_name("bar"), k.quoted_primary_key
+ end
+
+ def test_two_models_with_same_table_but_different_primary_key
+ k1 = Class.new(ActiveRecord::Base)
+ k1.table_name = 'posts'
+ k1.primary_key = 'id'
+
+ k2 = Class.new(ActiveRecord::Base)
+ k2.table_name = 'posts'
+ k2.primary_key = 'title'
+
+ assert k1.columns.find { |c| c.name == 'id' }.primary
+ assert !k1.columns.find { |c| c.name == 'title' }.primary
+ assert k1.columns_hash['id'].primary
+ assert !k1.columns_hash['title'].primary
+
+ assert !k2.columns.find { |c| c.name == 'id' }.primary
+ assert k2.columns.find { |c| c.name == 'title' }.primary
+ assert !k2.columns_hash['id'].primary
+ assert k2.columns_hash['title'].primary
+ end
+
+ def test_models_with_same_table_have_different_columns
+ k1 = Class.new(ActiveRecord::Base)
+ k1.table_name = 'posts'
+
+ k2 = Class.new(ActiveRecord::Base)
+ k2.table_name = 'posts'
+
+ k1.columns.zip(k2.columns).each do |col1, col2|
+ assert !col1.equal?(col2)
+ end
+ end
+end
+
+class PrimaryKeyWithNoConnectionTest < ActiveRecord::TestCase
+ self.use_transactional_fixtures = false
+
+ def test_set_primary_key_with_no_connection
+ return skip("disconnect wipes in-memory db") if in_memory_db?
+
+ connection = ActiveRecord::Model.remove_connection
+
+ model = Class.new(ActiveRecord::Base)
+ model.primary_key = 'foo'
+
+ assert_equal 'foo', model.primary_key
+
+ ActiveRecord::Model.establish_connection(connection)
+
+ assert_equal 'foo', model.primary_key
+ end
+end
+
+if current_adapter?(:MysqlAdapter) or current_adapter?(:Mysql2Adapter)
+ class PrimaryKeyWithAnsiQuotesTest < ActiveRecord::TestCase
+ self.use_transactional_fixtures = false
+
+ def test_primaery_key_method_with_ansi_quotes
+ con = ActiveRecord::Base.connection
+ con.execute("SET SESSION sql_mode='ANSI_QUOTES'")
+ assert_equal "id", con.primary_key("topics")
+ ensure
+ con.reconnect!
+ end
+
end
end
+
diff --git a/activerecord/test/cases/query_cache_test.rb b/activerecord/test/cases/query_cache_test.rb
index e3ad0cad90..f36f4237b1 100644
--- a/activerecord/test/cases/query_cache_test.rb
+++ b/activerecord/test/cases/query_cache_test.rb
@@ -3,7 +3,7 @@ require 'models/topic'
require 'models/task'
require 'models/category'
require 'models/post'
-
+require 'rack'
class QueryCacheTest < ActiveRecord::TestCase
fixtures :tasks, :topics, :categories, :posts, :categories_posts
@@ -43,6 +43,7 @@ class QueryCacheTest < ActiveRecord::TestCase
called = false
mw = ActiveRecord::QueryCache.new lambda { |env|
called = true
+ [200, {}, nil]
}
mw.call({})
assert called, 'middleware should delegate'
@@ -53,6 +54,7 @@ class QueryCacheTest < ActiveRecord::TestCase
Task.find 1
Task.find 1
assert_equal 1, ActiveRecord::Base.connection.query_cache.length
+ [200, {}, nil]
}
mw.call({})
end
@@ -62,6 +64,7 @@ class QueryCacheTest < ActiveRecord::TestCase
mw = ActiveRecord::QueryCache.new lambda { |env|
assert ActiveRecord::Base.connection.query_cache_enabled, 'cache on'
+ [200, {}, nil]
}
mw.call({})
end
@@ -83,7 +86,7 @@ class QueryCacheTest < ActiveRecord::TestCase
end
def test_cache_off_after_close
- mw = ActiveRecord::QueryCache.new lambda { |env| }
+ mw = ActiveRecord::QueryCache.new lambda { |env| [200, {}, nil] }
body = mw.call({}).last
assert ActiveRecord::Base.connection.query_cache_enabled, 'cache enabled'
@@ -94,6 +97,7 @@ class QueryCacheTest < ActiveRecord::TestCase
def test_cache_clear_after_close
mw = ActiveRecord::QueryCache.new lambda { |env|
Post.find(:first)
+ [200, {}, nil]
}
body = mw.call({}).last
@@ -147,13 +151,16 @@ class QueryCacheTest < ActiveRecord::TestCase
end
def test_cache_does_not_wrap_string_results_in_arrays
- require 'sqlite3/version' if current_adapter?(:SQLite3Adapter)
+ if current_adapter?(:SQLite3Adapter)
+ require 'sqlite3/version'
+ sqlite3_version = RUBY_PLATFORM =~ /java/ ? Jdbc::SQLite3::VERSION : SQLite3::VERSION
+ end
Task.cache do
# Oracle adapter returns count() as Fixnum or Float
if current_adapter?(:OracleAdapter)
assert_kind_of Numeric, Task.connection.select_value("SELECT count(*) AS count_all FROM tasks")
- elsif current_adapter?(:SQLite3Adapter) && SQLite3::VERSION > '1.2.5' || current_adapter?(:Mysql2Adapter) || current_adapter?(:MysqlAdapter)
+ elsif current_adapter?(:SQLite3Adapter) && sqlite3_version > '1.2.5' || current_adapter?(:Mysql2Adapter) || current_adapter?(:MysqlAdapter)
# Future versions of the sqlite3 adapter will return numeric
assert_instance_of Fixnum,
Task.connection.select_value("SELECT count(*) AS count_all FROM tasks")
@@ -167,6 +174,18 @@ end
class QueryCacheExpiryTest < ActiveRecord::TestCase
fixtures :tasks, :posts, :categories, :categories_posts
+ def test_cache_gets_cleared_after_migration
+ # warm the cache
+ Post.find(1)
+
+ # change the column definition
+ Post.connection.change_column :posts, :title, :string, :limit => 80
+ assert_nothing_raised { Post.find(1) }
+
+ # restore the old definition
+ Post.connection.change_column :posts, :title, :string
+ end
+
def test_find
Task.connection.expects(:clear_query_cache).times(1)
@@ -229,14 +248,3 @@ class QueryCacheExpiryTest < ActiveRecord::TestCase
end
end
end
-
-class QueryCacheBodyProxyTest < ActiveRecord::TestCase
-
- test "is polite to it's body and responds to it" do
- body = Class.new(String) { def to_path; "/path"; end }.new
- proxy = ActiveRecord::QueryCache::BodyProxy.new(nil, body)
- assert proxy.respond_to?(:to_path)
- assert_equal proxy.to_path, "/path"
- end
-
-end
diff --git a/activerecord/test/cases/reaper_test.rb b/activerecord/test/cases/reaper_test.rb
new file mode 100644
index 0000000000..576ab60090
--- /dev/null
+++ b/activerecord/test/cases/reaper_test.rb
@@ -0,0 +1,81 @@
+require "cases/helper"
+
+module ActiveRecord
+ module ConnectionAdapters
+ class ReaperTest < ActiveRecord::TestCase
+ attr_reader :pool
+
+ def setup
+ super
+ @pool = ConnectionPool.new ActiveRecord::Base.connection_pool.spec
+ end
+
+ def teardown
+ super
+ @pool.connections.each(&:close)
+ end
+
+ class FakePool
+ attr_reader :reaped
+
+ def initialize
+ @reaped = false
+ end
+
+ def reap
+ @reaped = true
+ end
+ end
+
+ # A reaper with nil time should never reap connections
+ def test_nil_time
+ fp = FakePool.new
+ assert !fp.reaped
+ reaper = ConnectionPool::Reaper.new(fp, nil)
+ reaper.run
+ assert !fp.reaped
+ end
+
+ def test_some_time
+ fp = FakePool.new
+ assert !fp.reaped
+
+ reaper = ConnectionPool::Reaper.new(fp, 0.0001)
+ reaper.run
+ until fp.reaped
+ Thread.pass
+ end
+ assert fp.reaped
+ end
+
+ def test_pool_has_reaper
+ assert pool.reaper
+ end
+
+ def test_reaping_frequency_configuration
+ spec = ActiveRecord::Base.connection_pool.spec.dup
+ spec.config[:reaping_frequency] = 100
+ pool = ConnectionPool.new spec
+ assert_equal 100, pool.reaper.frequency
+ end
+
+ def test_connection_pool_starts_reaper
+ spec = ActiveRecord::Base.connection_pool.spec.dup
+ spec.config[:reaping_frequency] = 0.0001
+
+ pool = ConnectionPool.new spec
+ pool.timeout = 0
+
+ conn = pool.checkout
+ count = pool.connections.length
+
+ conn.extend(Module.new { def active?; false; end; })
+
+ while count == pool.connections.length
+ Thread.pass
+ end
+ assert_equal(count - 1, pool.connections.length)
+ end
+ end
+ end
+end
diff --git a/activerecord/test/cases/reflection_test.rb b/activerecord/test/cases/reflection_test.rb
index 41312e8661..16f05f2198 100644
--- a/activerecord/test/cases/reflection_test.rb
+++ b/activerecord/test/cases/reflection_test.rb
@@ -18,6 +18,7 @@ require 'models/subscriber'
require 'models/subscription'
require 'models/tag'
require 'models/sponsor'
+require 'models/edge'
class ReflectionTest < ActiveRecord::TestCase
include ActiveRecord::Reflection
@@ -35,25 +36,25 @@ class ReflectionTest < ActiveRecord::TestCase
def test_read_attribute_names
assert_equal(
- %w( id title author_name author_email_address bonus_time written_on last_read content group approved replies_count parent_id parent_title type created_at updated_at ).sort,
+ %w( id title author_name author_email_address bonus_time written_on last_read content important group approved replies_count parent_id parent_title type created_at updated_at ).sort,
@first.attribute_names.sort
)
end
def test_columns
- assert_equal 16, Topic.columns.length
+ assert_equal 17, Topic.columns.length
end
def test_columns_are_returned_in_the_order_they_were_declared
column_names = Topic.columns.map { |column| column.name }
- assert_equal %w(id title author_name author_email_address written_on bonus_time last_read content approved replies_count parent_id parent_title type group created_at updated_at), column_names
+ assert_equal %w(id title author_name author_email_address written_on bonus_time last_read content important approved replies_count parent_id parent_title type group created_at updated_at), column_names
end
def test_content_columns
content_columns = Topic.content_columns
content_column_names = content_columns.map {|column| column.name}
- assert_equal 12, content_columns.length
- assert_equal %w(title author_name author_email_address written_on bonus_time last_read content group approved parent_title created_at updated_at).sort, content_column_names.sort
+ assert_equal 13, content_columns.length
+ assert_equal %w(title author_name author_email_address written_on bonus_time last_read content important group approved parent_title created_at updated_at).sort, content_column_names.sort
end
def test_column_string_type_and_limit
@@ -188,8 +189,8 @@ class ReflectionTest < ActiveRecord::TestCase
def test_reflection_of_all_associations
# FIXME these assertions bust a lot
- assert_equal 36, Firm.reflect_on_all_associations.size
- assert_equal 26, Firm.reflect_on_all_associations(:has_many).size
+ assert_equal 39, Firm.reflect_on_all_associations.size
+ assert_equal 29, Firm.reflect_on_all_associations(:has_many).size
assert_equal 10, Firm.reflect_on_all_associations(:has_one).size
assert_equal 0, Firm.reflect_on_all_associations(:belongs_to).size
end
@@ -244,7 +245,7 @@ class ReflectionTest < ActiveRecord::TestCase
# Normal association
assert_equal "id", Author.reflect_on_association(:posts).association_primary_key.to_s
assert_equal "name", Author.reflect_on_association(:essay).association_primary_key.to_s
- assert_equal "id", Tagging.reflect_on_association(:taggable).association_primary_key.to_s
+ assert_equal "name", Essay.reflect_on_association(:writer).association_primary_key.to_s
# Through association (uses the :primary_key option from the source reflection)
assert_equal "nick", Author.reflect_on_association(:subscribers).association_primary_key.to_s
@@ -252,11 +253,25 @@ class ReflectionTest < ActiveRecord::TestCase
assert_equal "custom_primary_key", Author.reflect_on_association(:tags_with_primary_key).association_primary_key.to_s # nested
end
+ def test_association_primary_key_raises_when_missing_primary_key
+ reflection = ActiveRecord::Reflection::AssociationReflection.new(:fuu, :edge, {}, Author)
+ assert_raises(ActiveRecord::UnknownPrimaryKey) { reflection.association_primary_key }
+
+ through = ActiveRecord::Reflection::ThroughReflection.new(:fuu, :edge, {}, Author)
+ through.stubs(:source_reflection).returns(stub_everything(:options => {}, :class_name => 'Edge'))
+ assert_raises(ActiveRecord::UnknownPrimaryKey) { through.association_primary_key }
+ end
+
def test_active_record_primary_key
assert_equal "nick", Subscriber.reflect_on_association(:subscriptions).active_record_primary_key.to_s
assert_equal "name", Author.reflect_on_association(:essay).active_record_primary_key.to_s
end
+ def test_active_record_primary_key_raises_when_missing_primary_key
+ reflection = ActiveRecord::Reflection::AssociationReflection.new(:fuu, :author, {}, Edge)
+ assert_raises(ActiveRecord::UnknownPrimaryKey) { reflection.active_record_primary_key }
+ end
+
def test_foreign_type
assert_equal "sponsorable_type", Sponsor.reflect_on_association(:sponsorable).foreign_type.to_s
assert_equal "sponsorable_type", Sponsor.reflect_on_association(:thing).foreign_type.to_s
@@ -304,6 +319,16 @@ class ReflectionTest < ActiveRecord::TestCase
assert_equal "category_id", Post.reflect_on_association(:categorizations).foreign_key.to_s
end
+ def test_through_reflection_conditions_do_not_modify_other_reflections
+ orig_conds = Post.reflect_on_association(:first_blue_tags_2).conditions.inspect
+ Author.reflect_on_association(:misc_post_first_blue_tags_2).conditions
+ assert_equal orig_conds, Post.reflect_on_association(:first_blue_tags_2).conditions.inspect
+ end
+
+ def test_symbol_for_class_name
+ assert_equal Client, Firm.reflect_on_association(:unsorted_clients_with_symbol).klass
+ end
+
private
def assert_reflection(klass, association, options)
assert reflection = klass.reflect_on_association(association)
diff --git a/activerecord/test/cases/relation_scoping_test.rb b/activerecord/test/cases/relation_scoping_test.rb
index 1e2093273e..edf38cb7a3 100644
--- a/activerecord/test/cases/relation_scoping_test.rb
+++ b/activerecord/test/cases/relation_scoping_test.rb
@@ -106,7 +106,7 @@ class RelationScopingTest < ActiveRecord::TestCase
def test_scoped_find_include
# with the include, will retrieve only developers for the given project
scoped_developers = Developer.includes(:projects).scoping do
- Developer.where('projects.id = 2').all
+ Developer.where('projects.id' => 2).all
end
assert scoped_developers.include?(developers(:david))
assert !scoped_developers.include?(developers(:jamis))
@@ -421,6 +421,12 @@ class DefaultScopingTest < ActiveRecord::TestCase
assert_equal expected, received
end
+ def test_order_after_reorder_combines_orders
+ expected = Developer.order('name DESC, id DESC').collect { |dev| [dev.name, dev.id] }
+ received = Developer.order('name ASC').reorder('name DESC').order('id DESC').collect { |dev| [dev.name, dev.id] }
+ assert_equal expected, received
+ end
+
def test_nested_exclusive_scope
expected = Developer.find(:all, :limit => 100).collect { |dev| dev.salary }
received = DeveloperOrderedBySalary.send(:with_exclusive_scope, :find => { :limit => 100 }) do
diff --git a/activerecord/test/cases/relation_test.rb b/activerecord/test/cases/relation_test.rb
index b23ead6feb..ac6dee3c6a 100644
--- a/activerecord/test/cases/relation_test.rb
+++ b/activerecord/test/cases/relation_test.rb
@@ -20,7 +20,7 @@ module ActiveRecord
end
def test_single_values
- assert_equal [:limit, :offset, :lock, :readonly, :from, :reorder, :reverse_order].map(&:to_s).sort,
+ assert_equal [:limit, :offset, :lock, :readonly, :from, :reordering, :reverse_order, :uniq].map(&:to_s).sort,
Relation::SINGLE_VALUE_METHODS.map(&:to_s).sort
end
@@ -44,7 +44,7 @@ module ActiveRecord
end
def test_multi_value_methods
- assert_equal [:select, :group, :order, :joins, :where, :having, :bind].map(&:to_s).sort,
+ assert_equal [:select, :group, :order, :joins, :where, :having, :bind, :references].map(&:to_s).sort,
Relation::MULTI_VALUE_METHODS.map(&:to_s).sort
end
@@ -135,5 +135,24 @@ module ActiveRecord
relation.eager_load_values << :b
assert relation.eager_loading?
end
+
+ def test_references_values
+ relation = Relation.new :a, :b
+ assert_equal [], relation.references_values
+ relation = relation.references(:foo).references(:omg, :lol)
+ assert_equal ['foo', 'omg', 'lol'], relation.references_values
+ end
+
+ def test_references_values_dont_duplicate
+ relation = Relation.new :a, :b
+ relation = relation.references(:foo).references(:foo)
+ assert_equal ['foo'], relation.references_values
+ end
+
+ def test_apply_finder_options_takes_references
+ relation = Relation.new :a, :b
+ relation = relation.apply_finder_options(:references => :foo)
+ assert_equal ['foo'], relation.references_values
+ end
end
end
diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb
index 95408a5f29..0471d03f3b 100644
--- a/activerecord/test/cases/relations_test.rb
+++ b/activerecord/test/cases/relations_test.rb
@@ -184,12 +184,12 @@ class RelationTest < ActiveRecord::TestCase
end
def test_finding_with_complex_order_and_limit
- tags = Tag.includes(:taggings).order("REPLACE('abc', taggings.taggable_type, taggings.taggable_type)").limit(1).to_a
+ tags = Tag.includes(:taggings).references(:taggings).order("REPLACE('abc', taggings.taggable_type, taggings.taggable_type)").limit(1).to_a
assert_equal 1, tags.length
end
def test_finding_with_complex_order
- tags = Tag.includes(:taggings).order("REPLACE('abc', taggings.taggable_type, taggings.taggable_type)").to_a
+ tags = Tag.includes(:taggings).references(:taggings).order("REPLACE('abc', taggings.taggable_type, taggings.taggable_type)").to_a
assert_equal 3, tags.length
end
@@ -215,6 +215,19 @@ class RelationTest < ActiveRecord::TestCase
assert_equal [2, 4, 6, 8, 10], even_ids.sort
end
+ def test_none
+ assert_no_queries do
+ assert_equal [], Developer.none
+ assert_equal [], Developer.scoped.none
+ end
+ end
+
+ def test_none_chainable
+ assert_no_queries do
+ assert_equal [], Developer.none.where(:name => 'David')
+ end
+ end
+
def test_joins_with_nil_argument
assert_nothing_raised { DependentFirm.joins(nil).first }
end
@@ -1104,7 +1117,9 @@ class RelationTest < ActiveRecord::TestCase
)
)
- assert scope.eager_loading?
+ assert_deprecated do
+ assert scope.eager_loading?
+ end
end
def test_ordering_with_extra_spaces
@@ -1148,4 +1163,65 @@ class RelationTest < ActiveRecord::TestCase
assert_equal posts(:thinking), comments(:more_greetings).post
assert_equal posts(:welcome), comments(:greetings).post
end
+
+ def test_uniq
+ tag1 = Tag.create(:name => 'Foo')
+ tag2 = Tag.create(:name => 'Foo')
+
+ query = Tag.select(:name).where(:id => [tag1.id, tag2.id])
+
+ assert_equal ['Foo', 'Foo'], query.map(&:name)
+ assert_sql(/DISTINCT/) do
+ assert_equal ['Foo'], query.uniq.map(&:name)
+ end
+ assert_sql(/DISTINCT/) do
+ assert_equal ['Foo'], query.uniq(true).map(&:name)
+ end
+ assert_equal ['Foo', 'Foo'], query.uniq(true).uniq(false).map(&:name)
+ end
+
+ def test_references_triggers_eager_loading
+ scope = Post.includes(:comments)
+ assert !scope.eager_loading?
+ assert scope.references(:comments).eager_loading?
+ end
+
+ def test_references_doesnt_trigger_eager_loading_if_reference_not_included
+ scope = Post.references(:comments)
+ assert !scope.eager_loading?
+ end
+
+ def test_automatically_added_where_references
+ scope = Post.where(:comments => { :body => "Bla" })
+ assert_equal ['comments'], scope.references_values
+
+ scope = Post.where('comments.body' => 'Bla')
+ assert_equal ['comments'], scope.references_values
+ end
+
+ def test_automatically_added_having_references
+ scope = Post.having(:comments => { :body => "Bla" })
+ assert_equal ['comments'], scope.references_values
+
+ scope = Post.having('comments.body' => 'Bla')
+ assert_equal ['comments'], scope.references_values
+ end
+
+ def test_automatically_added_order_references
+ scope = Post.order('comments.body')
+ assert_equal ['comments'], scope.references_values
+
+ scope = Post.order('comments.body', 'yaks.body')
+ assert_equal ['comments', 'yaks'], scope.references_values
+
+ # Don't infer yaks, let's not go down that road again...
+ scope = Post.order('comments.body, yaks.body')
+ assert_equal ['comments'], scope.references_values
+
+ scope = Post.order('comments.body asc')
+ assert_equal ['comments'], scope.references_values
+
+ scope = Post.order('foo(comments.body)')
+ assert_equal [], scope.references_values
+ end
end
diff --git a/activerecord/test/cases/schema_dumper_test.rb b/activerecord/test/cases/schema_dumper_test.rb
index 71ff727b7f..abeb56fd3f 100644
--- a/activerecord/test/cases/schema_dumper_test.rb
+++ b/activerecord/test/cases/schema_dumper_test.rb
@@ -2,7 +2,13 @@ require "cases/helper"
class SchemaDumperTest < ActiveRecord::TestCase
+ def initialize(*)
+ super
+ ActiveRecord::SchemaMigration.create_table
+ end
+
def setup
+ super
@stream = StringIO.new
end
@@ -13,10 +19,18 @@ class SchemaDumperTest < ActiveRecord::TestCase
@stream.string
end
- if "string".encoding_aware?
- def test_magic_comment
- assert_match "# encoding: #{@stream.external_encoding.name}", standard_dump
+ def test_dump_schema_information_outputs_lexically_ordered_versions
+ versions = %w{ 20100101010101 20100201010101 20100301010101 }
+ versions.reverse.each do |v|
+ ActiveRecord::SchemaMigration.create!(:version => v)
end
+
+ schema_info = ActiveRecord::Base.connection.dump_schema_information
+ assert_match(/20100201010101.*20100301010101/m, schema_info)
+ end
+
+ def test_magic_comment
+ assert_match "# encoding: #{@stream.external_encoding.name}", standard_dump
end
def test_schema_dump
@@ -238,4 +252,9 @@ class SchemaDumperTest < ActiveRecord::TestCase
assert_match %r(:id => false), match[1], "no table id not preserved"
assert_match %r{t.string[[:space:]]+"id",[[:space:]]+:null => false$}, match[2], "non-primary key id column not preserved"
end
+
+ def test_schema_dump_keeps_id_false_when_id_is_false_and_unique_not_null_column_added
+ output = standard_dump
+ assert_match %r{create_table "subscribers", :id => false}, output
+ end
end
diff --git a/activerecord/test/cases/serialization_test.rb b/activerecord/test/cases/serialization_test.rb
index 61b04b3e37..a4c065e667 100644
--- a/activerecord/test/cases/serialization_test.rb
+++ b/activerecord/test/cases/serialization_test.rb
@@ -13,7 +13,8 @@ class SerializationTest < ActiveRecord::TestCase
:created_at => Time.utc(2006, 8, 1),
:awesome => false,
:preferences => { :gem => '<strong>ruby</strong>' },
- :alternative_id => nil
+ :alternative_id => nil,
+ :id => nil
}
end
@@ -24,7 +25,7 @@ class SerializationTest < ActiveRecord::TestCase
end
def test_serialize_should_be_reversible
- for format in FORMATS
+ FORMATS.each do |format|
@serialized = Contact.new.send("to_#{format}")
contact = Contact.new.send("from_#{format}", @serialized)
@@ -33,7 +34,7 @@ class SerializationTest < ActiveRecord::TestCase
end
def test_serialize_should_allow_attribute_only_filtering
- for format in FORMATS
+ FORMATS.each do |format|
@serialized = Contact.new(@contact_attributes).send("to_#{format}", :only => [ :age, :name ])
contact = Contact.new.send("from_#{format}", @serialized)
assert_equal @contact_attributes[:name], contact.name, "For #{format}"
@@ -42,7 +43,7 @@ class SerializationTest < ActiveRecord::TestCase
end
def test_serialize_should_allow_attribute_except_filtering
- for format in FORMATS
+ FORMATS.each do |format|
@serialized = Contact.new(@contact_attributes).send("to_#{format}", :except => [ :age, :name ])
contact = Contact.new.send("from_#{format}", @serialized)
assert_nil contact.name, "For #{format}"
diff --git a/activerecord/test/cases/session_store/session_test.rb b/activerecord/test/cases/session_store/session_test.rb
index 258cee7aba..a3b8ab74d9 100644
--- a/activerecord/test/cases/session_store/session_test.rb
+++ b/activerecord/test/cases/session_store/session_test.rb
@@ -5,11 +5,15 @@ require 'active_record/session_store'
module ActiveRecord
class SessionStore
class SessionTest < ActiveRecord::TestCase
- self.use_transactional_fixtures = false unless supports_savepoints? && ActiveRecord::Base.connection.supports_ddl_transactions?
+ self.use_transactional_fixtures = false
+
+ attr_reader :session_klass
def setup
super
+ ActiveRecord::Base.connection.schema_cache.clear!
Session.drop_table! if Session.table_exists?
+ @session_klass = Class.new(Session)
end
def test_data_column_name
@@ -60,8 +64,8 @@ module ActiveRecord
def test_find_by_session_id
Session.create_table!
session_id = "10"
- s = Session.create!(:data => 'world', :session_id => session_id)
- t = Session.find_by_session_id(session_id)
+ s = session_klass.create!(:data => 'world', :session_id => session_id)
+ t = session_klass.find_by_session_id(session_id)
assert_equal s, t
assert_equal s.data, t.data
Session.drop_table!
diff --git a/activerecord/test/cases/store_test.rb b/activerecord/test/cases/store_test.rb
new file mode 100644
index 0000000000..40520d6da2
--- /dev/null
+++ b/activerecord/test/cases/store_test.rb
@@ -0,0 +1,43 @@
+require 'cases/helper'
+require 'models/admin'
+require 'models/admin/user'
+
+class StoreTest < ActiveRecord::TestCase
+ setup do
+ @john = Admin::User.create(:name => 'John Doe', :color => 'black', :remember_login => true)
+ end
+
+ test "reading store attributes through accessors" do
+ assert_equal 'black', @john.color
+ assert_nil @john.homepage
+ end
+
+ test "writing store attributes through accessors" do
+ @john.color = 'red'
+ @john.homepage = '37signals.com'
+
+ assert_equal 'red', @john.color
+ assert_equal '37signals.com', @john.homepage
+ end
+
+ test "accessing attributes not exposed by accessors" do
+ @john.settings[:icecream] = 'graeters'
+ @john.save
+
+ assert_equal 'graeters', @john.reload.settings[:icecream]
+ end
+
+ test "updating the store will mark it as changed" do
+ @john.color = 'red'
+ assert @john.settings_changed?
+ end
+
+ test "object initialization with not nullable column" do
+ assert_equal true, @john.remember_login
+ end
+
+ test "writing with not nullable column" do
+ @john.remember_login = false
+ assert_equal false, @john.remember_login
+ end
+end
diff --git a/activerecord/test/cases/test_case.rb b/activerecord/test/cases/test_case.rb
new file mode 100644
index 0000000000..f0fefe6c48
--- /dev/null
+++ b/activerecord/test/cases/test_case.rb
@@ -0,0 +1,63 @@
+require 'active_support/test_case'
+
+module ActiveRecord
+ # = Active Record Test Case
+ #
+ # Defines some test assertions to test against SQL queries.
+ class TestCase < ActiveSupport::TestCase #:nodoc:
+ setup :cleanup_identity_map
+
+ def setup
+ cleanup_identity_map
+ end
+
+ def teardown
+ ActiveRecord::SQLCounter.log.clear
+ end
+
+ def cleanup_identity_map
+ ActiveRecord::IdentityMap.clear
+ end
+
+ def assert_date_from_db(expected, actual, message = nil)
+ # SybaseAdapter doesn't have a separate column type just for dates,
+ # so the time is in the string and incorrectly formatted
+ if current_adapter?(:SybaseAdapter)
+ assert_equal expected.to_s, actual.to_date.to_s, message
+ else
+ assert_equal expected.to_s, actual.to_s, message
+ end
+ end
+
+ def assert_sql(*patterns_to_match)
+ ActiveRecord::SQLCounter.log = []
+ yield
+ ActiveRecord::SQLCounter.log
+ ensure
+ failed_patterns = []
+ patterns_to_match.each do |pattern|
+ failed_patterns << pattern unless ActiveRecord::SQLCounter.log.any?{ |sql| pattern === sql }
+ end
+ assert failed_patterns.empty?, "Query pattern(s) #{failed_patterns.map{ |p| p.inspect }.join(', ')} not found.#{ActiveRecord::SQLCounter.log.size == 0 ? '' : "\nQueries:\n#{ActiveRecord::SQLCounter.log.join("\n")}"}"
+ end
+
+ def assert_queries(num = 1)
+ ActiveRecord::SQLCounter.log = []
+ yield
+ ensure
+ assert_equal num, ActiveRecord::SQLCounter.log.size, "#{ActiveRecord::SQLCounter.log.size} instead of #{num} queries were executed.#{ActiveRecord::SQLCounter.log.size == 0 ? '' : "\nQueries:\n#{ActiveRecord::SQLCounter.log.join("\n")}"}"
+ end
+
+ def assert_no_queries(&block)
+ prev_ignored_sql = ActiveRecord::SQLCounter.ignored_sql
+ ActiveRecord::SQLCounter.ignored_sql = []
+ assert_queries(0, &block)
+ ensure
+ ActiveRecord::SQLCounter.ignored_sql = prev_ignored_sql
+ end
+
+ def sqlite3? connection
+ connection.class.name.split('::').last == "SQLite3Adapter"
+ end
+ end
+end
diff --git a/activerecord/test/cases/timestamp_test.rb b/activerecord/test/cases/timestamp_test.rb
index 4445a12e1d..447aa29ffe 100644
--- a/activerecord/test/cases/timestamp_test.rb
+++ b/activerecord/test/cases/timestamp_test.rb
@@ -60,6 +60,16 @@ class TimestampTest < ActiveRecord::TestCase
Developer.record_timestamps = true
end
+ def test_saving_when_instance_record_timestamps_is_false_doesnt_update_its_timestamp
+ @developer.record_timestamps = false
+ assert Developer.record_timestamps
+
+ @developer.name = "John Smith"
+ @developer.save!
+
+ assert_equal @previously_updated_at, @developer.updated_at
+ end
+
def test_touching_an_attribute_updates_timestamp
previously_created_at = @developer.created_at
@developer.touch(:created_at)
diff --git a/activerecord/test/cases/transaction_callbacks_test.rb b/activerecord/test/cases/transaction_callbacks_test.rb
index 85f222bca2..f8b3e01a49 100644
--- a/activerecord/test/cases/transaction_callbacks_test.rb
+++ b/activerecord/test/cases/transaction_callbacks_test.rb
@@ -6,7 +6,7 @@ class TransactionCallbacksTest < ActiveRecord::TestCase
fixtures :topics
class TopicWithCallbacks < ActiveRecord::Base
- set_table_name :topics
+ self.table_name = :topics
after_commit{|record| record.send(:do_after_commit, nil)}
after_commit(:on => :create){|record| record.send(:do_after_commit, :create)}
@@ -252,7 +252,7 @@ class TransactionObserverCallbacksTest < ActiveRecord::TestCase
fixtures :topics
class TopicWithObserverAttached < ActiveRecord::Base
- set_table_name :topics
+ self.table_name = :topics
def history
@history ||= []
end
diff --git a/activerecord/test/cases/transactions_test.rb b/activerecord/test/cases/transactions_test.rb
index 110a18772f..203dd054f1 100644
--- a/activerecord/test/cases/transactions_test.rb
+++ b/activerecord/test/cases/transactions_test.rb
@@ -563,6 +563,7 @@ if current_adapter?(:PostgreSQLAdapter)
topic.approved = !topic.approved?
topic.save!
end
+ Topic.connection.close
end
end
@@ -598,6 +599,7 @@ if current_adapter?(:PostgreSQLAdapter)
dev = Developer.find(1)
assert_equal original_salary, dev.salary
end
+ Developer.connection.close
end
end
@@ -610,6 +612,7 @@ if current_adapter?(:PostgreSQLAdapter)
assert_equal original_salary, Developer.find(1).salary
end
end
+ Developer.connection.close
end
threads.each { |t| t.join }
diff --git a/activerecord/test/cases/unconnected_test.rb b/activerecord/test/cases/unconnected_test.rb
index f85fb4e5da..5a69054445 100644
--- a/activerecord/test/cases/unconnected_test.rb
+++ b/activerecord/test/cases/unconnected_test.rb
@@ -4,16 +4,16 @@ class TestRecord < ActiveRecord::Base
end
class TestUnconnectedAdapter < ActiveRecord::TestCase
- self.use_transactional_fixtures = false unless supports_savepoints?
+ self.use_transactional_fixtures = false
def setup
- @underlying = ActiveRecord::Base.connection
- @specification = ActiveRecord::Base.remove_connection
+ @underlying = ActiveRecord::Model.connection
+ @specification = ActiveRecord::Model.remove_connection
end
def teardown
@underlying = nil
- ActiveRecord::Base.establish_connection(@specification)
+ ActiveRecord::Model.establish_connection(@specification)
load_schema if in_memory_db?
end
diff --git a/activerecord/test/cases/validations/association_validation_test.rb b/activerecord/test/cases/validations/association_validation_test.rb
index 56e345990f..c72b7b35cd 100644
--- a/activerecord/test/cases/validations/association_validation_test.rb
+++ b/activerecord/test/cases/validations/association_validation_test.rb
@@ -61,6 +61,16 @@ class AssociationValidationTest < ActiveRecord::TestCase
assert r.valid?
end
+ def test_validates_associated_marked_for_destruction
+ Topic.validates_associated(:replies)
+ Reply.validates_presence_of(:content)
+ t = Topic.new
+ t.replies << Reply.new
+ assert t.invalid?
+ t.replies.first.mark_for_destruction
+ assert t.valid?
+ end
+
def test_validates_associated_with_custom_message_using_quotes
Reply.validates_associated :topic, :message=> "This string contains 'single' and \"double\" quotes"
Topic.validates_presence_of :content
@@ -81,14 +91,12 @@ class AssociationValidationTest < ActiveRecord::TestCase
end
def test_validates_size_of_association_utf8
- with_kcode('UTF8') do
- assert_nothing_raised { Owner.validates_size_of :pets, :minimum => 1 }
- o = Owner.new('name' => 'あいうえおかきくけこ')
- assert !o.save
- assert o.errors[:pets].any?
- o.pets.build('name' => 'あいうえおかきくけこ')
- assert o.valid?
- end
+ assert_nothing_raised { Owner.validates_size_of :pets, :minimum => 1 }
+ o = Owner.new('name' => 'あいうえおかきくけこ')
+ assert !o.save
+ assert o.errors[:pets].any?
+ o.pets.build('name' => 'あいうえおかきくけこ')
+ assert o.valid?
end
def test_validates_presence_of_belongs_to_association__parent_is_new_record
@@ -110,4 +118,21 @@ class AssociationValidationTest < ActiveRecord::TestCase
end
end
+ def test_validates_associated_models_in_the_same_context
+ Topic.validates_presence_of :title, :on => :custom_context
+ Topic.validates_associated :replies
+ Reply.validates_presence_of :title, :on => :custom_context
+
+ t = Topic.new('title' => '')
+ r = t.replies.new('title' => '')
+
+ assert t.valid?
+ assert !t.valid?(:custom_context)
+
+ t.title = "Longer"
+ assert !t.valid?(:custom_context), "Should NOT be valid if the associated object is not valid in the same context."
+
+ r.title = "Longer"
+ assert t.valid?(:custom_context), "Should be valid if the associated object is not valid in the same context."
+ end
end
diff --git a/activerecord/test/cases/validations/uniqueness_validation_test.rb b/activerecord/test/cases/validations/uniqueness_validation_test.rb
index 0f1b3667cc..79442d68b0 100644
--- a/activerecord/test/cases/validations/uniqueness_validation_test.rb
+++ b/activerecord/test/cases/validations/uniqueness_validation_test.rb
@@ -45,6 +45,18 @@ class UniquenessValidationTest < ActiveRecord::TestCase
assert t2.save, "Should now save t2 as unique"
end
+ def test_validates_uniqueness_with_nil_value
+ Topic.validates_uniqueness_of(:title)
+
+ t = Topic.new("title" => nil)
+ assert t.save, "Should save t as unique"
+
+ t2 = Topic.new("title" => nil)
+ assert !t2.valid?, "Shouldn't be valid"
+ assert !t2.save, "Shouldn't save t2 as unique"
+ assert_equal ["has already been taken"], t2.errors[:title]
+ end
+
def test_validates_uniqueness_with_validates
Topic.validates :title, :uniqueness => true
Topic.create!('title' => 'abc')
@@ -80,6 +92,30 @@ class UniquenessValidationTest < ActiveRecord::TestCase
assert r3.valid?, "Saving r3"
end
+ def test_validate_uniqueness_with_object_scope
+ Reply.validates_uniqueness_of(:content, :scope => :topic)
+
+ t = Topic.create("title" => "I'm unique!")
+
+ r1 = t.replies.create "title" => "r1", "content" => "hello world"
+ assert r1.valid?, "Saving r1"
+
+ r2 = t.replies.create "title" => "r2", "content" => "hello world"
+ assert !r2.valid?, "Saving r2 first time"
+ end
+
+ def test_validate_uniqueness_with_object_arg
+ Reply.validates_uniqueness_of(:topic)
+
+ t = Topic.create("title" => "I'm unique!")
+
+ r1 = t.replies.create "title" => "r1", "content" => "hello world"
+ assert r1.valid?, "Saving r1"
+
+ r2 = t.replies.create "title" => "r2", "content" => "hello world"
+ assert !r2.valid?, "Saving r2 first time"
+ end
+
def test_validate_uniqueness_scoped_to_defining_class
t = Topic.create("title" => "What, me worry?")
@@ -149,16 +185,14 @@ class UniquenessValidationTest < ActiveRecord::TestCase
assert t2.valid?, "should validate with nil"
assert t2.save, "should save with nil"
- with_kcode('UTF8') do
- t_utf8 = Topic.new("title" => "Я тоже уникальный!")
- assert t_utf8.save, "Should save t_utf8 as unique"
+ t_utf8 = Topic.new("title" => "Я тоже уникальный!")
+ assert t_utf8.save, "Should save t_utf8 as unique"
- # If database hasn't UTF-8 character set, this test fails
- if Topic.find(t_utf8, :select => 'LOWER(title) AS title').title == "я тоже уникальный!"
- t2_utf8 = Topic.new("title" => "я тоже УНИКАЛЬНЫЙ!")
- assert !t2_utf8.valid?, "Shouldn't be valid"
- assert !t2_utf8.save, "Shouldn't save t2_utf8 as unique"
- end
+ # If database hasn't UTF-8 character set, this test fails
+ if Topic.find(t_utf8, :select => 'LOWER(title) AS title').title == "я тоже уникальный!"
+ t2_utf8 = Topic.new("title" => "я тоже УНИКАЛЬНЫЙ!")
+ assert !t2_utf8.valid?, "Shouldn't be valid"
+ assert !t2_utf8.save, "Shouldn't save t2_utf8 as unique"
end
end
@@ -256,13 +290,11 @@ class UniquenessValidationTest < ActiveRecord::TestCase
end
def test_validate_uniqueness_with_limit_and_utf8
- with_kcode('UTF8') do
- # Event.title is limited to 5 characters
- e1 = Event.create(:title => "一二三四五")
- assert e1.valid?, "Could not create an event with a unique, 5 character title"
- e2 = Event.create(:title => "一二三四五六七八")
- assert !e2.valid?, "Created an event whose title, with limit taken into account, is not unique"
- end
+ # Event.title is limited to 5 characters
+ e1 = Event.create(:title => "一二三四五")
+ assert e1.valid?, "Could not create an event with a unique, 5 character title"
+ e2 = Event.create(:title => "一二三四五六七八")
+ assert !e2.valid?, "Created an event whose title, with limit taken into account, is not unique"
end
def test_validate_straight_inheritance_uniqueness
diff --git a/activerecord/test/cases/validations_test.rb b/activerecord/test/cases/validations_test.rb
index c3e494866b..e575a98170 100644
--- a/activerecord/test/cases/validations_test.rb
+++ b/activerecord/test/cases/validations_test.rb
@@ -8,7 +8,7 @@ require 'models/parrot'
require 'models/company'
class ProtectedPerson < ActiveRecord::Base
- set_table_name 'people'
+ self.table_name = 'people'
attr_accessor :addon
attr_protected :first_name
end
diff --git a/activerecord/test/cases/yaml_serialization_test.rb b/activerecord/test/cases/yaml_serialization_test.rb
index 0b54c309d1..302913e095 100644
--- a/activerecord/test/cases/yaml_serialization_test.rb
+++ b/activerecord/test/cases/yaml_serialization_test.rb
@@ -1,14 +1,20 @@
-require "cases/helper"
+require 'cases/helper'
require 'models/topic'
class YamlSerializationTest < ActiveRecord::TestCase
fixtures :topics
def test_to_yaml_with_time_with_zone_should_not_raise_exception
+ tz = Time.zone
Time.zone = ActiveSupport::TimeZone["Pacific Time (US & Canada)"]
ActiveRecord::Base.time_zone_aware_attributes = true
+
topic = Topic.new(:written_on => DateTime.now)
assert_nothing_raised { topic.to_yaml }
+
+ ensure
+ Time.zone = tz
+ ActiveRecord::Base.time_zone_aware_attributes = false
end
def test_roundtrip
@@ -18,6 +24,11 @@ class YamlSerializationTest < ActiveRecord::TestCase
assert_equal topic, t
end
+ def test_roundtrip_serialized_column
+ topic = Topic.new(:content => {:omg=>:lol})
+ assert_equal({:omg=>:lol}, YAML.load(YAML.dump(topic)).content)
+ end
+
def test_encode_with_coder
topic = Topic.first
coder = {}
@@ -25,22 +36,17 @@ class YamlSerializationTest < ActiveRecord::TestCase
assert_equal({'attributes' => topic.attributes}, coder)
end
- begin
- require 'psych'
-
- def test_psych_roundtrip
- topic = Topic.first
- assert topic
- t = Psych.load Psych.dump topic
- assert_equal topic, t
- end
-
- def test_psych_roundtrip_new_object
- topic = Topic.new
- assert topic
- t = Psych.load Psych.dump topic
- assert_equal topic.attributes, t.attributes
- end
- rescue LoadError
+ def test_psych_roundtrip
+ topic = Topic.first
+ assert topic
+ t = Psych.load Psych.dump topic
+ assert_equal topic, t
+ end
+
+ def test_psych_roundtrip_new_object
+ topic = Topic.new
+ assert topic
+ t = Psych.load Psych.dump topic
+ assert_equal topic.attributes, t.attributes
end
end
diff --git a/activerecord/test/config.example.yml b/activerecord/test/config.example.yml
index 8c1a45430e..f450efd839 100644
--- a/activerecord/test/config.example.yml
+++ b/activerecord/test/config.example.yml
@@ -37,11 +37,13 @@ connections:
db2:
arunit:
+ adapter: ibm_db
host: localhost
username: arunit
password: arunit
database: arunit
arunit2:
+ adapter: ibm_db
host: localhost
username: arunit
password: arunit
diff --git a/activerecord/test/fixtures/admin/randomly_named_a9.yml b/activerecord/test/fixtures/admin/randomly_named_a9.yml
new file mode 100644
index 0000000000..bc51c83112
--- /dev/null
+++ b/activerecord/test/fixtures/admin/randomly_named_a9.yml
@@ -0,0 +1,7 @@
+first_instance:
+ some_attribute: AAA
+ another_attribute: 000
+
+second_instance:
+ some_attribute: BBB
+ another_attribute: 999
diff --git a/activerecord/test/fixtures/admin/randomly_named_b0.yml b/activerecord/test/fixtures/admin/randomly_named_b0.yml
new file mode 100644
index 0000000000..bc51c83112
--- /dev/null
+++ b/activerecord/test/fixtures/admin/randomly_named_b0.yml
@@ -0,0 +1,7 @@
+first_instance:
+ some_attribute: AAA
+ another_attribute: 000
+
+second_instance:
+ some_attribute: BBB
+ another_attribute: 999
diff --git a/activerecord/test/fixtures/developers.yml b/activerecord/test/fixtures/developers.yml
index 308bf75de2..3656564f63 100644
--- a/activerecord/test/fixtures/developers.yml
+++ b/activerecord/test/fixtures/developers.yml
@@ -8,7 +8,7 @@ jamis:
name: Jamis
salary: 150000
-<% for digit in 3..10 %>
+<% (3..10).each do |digit| %>
dev_<%= digit %>:
id: <%= digit %>
name: fixture_<%= digit %>
diff --git a/activerecord/test/fixtures/dog_lovers.yml b/activerecord/test/fixtures/dog_lovers.yml
new file mode 100644
index 0000000000..d3e5e4a1aa
--- /dev/null
+++ b/activerecord/test/fixtures/dog_lovers.yml
@@ -0,0 +1,4 @@
+david:
+ id: 1
+ bred_dogs_count: 0
+ trained_dogs_count: 1
diff --git a/activerecord/test/fixtures/dogs.yml b/activerecord/test/fixtures/dogs.yml
new file mode 100644
index 0000000000..16d19be2c5
--- /dev/null
+++ b/activerecord/test/fixtures/dogs.yml
@@ -0,0 +1,3 @@
+sophie:
+ id: 1
+ trainer_id: 1
diff --git a/activerecord/test/fixtures/other_topics.yml b/activerecord/test/fixtures/other_topics.yml
new file mode 100644
index 0000000000..93f48aedc4
--- /dev/null
+++ b/activerecord/test/fixtures/other_topics.yml
@@ -0,0 +1,42 @@
+first:
+ id: 1
+ title: The First Topic
+ author_name: David
+ author_email_address: david@loudthinking.com
+ written_on: 2003-07-16t15:28:11.2233+01:00
+ last_read: 2004-04-15
+ bonus_time: 2005-01-30t15:28:00.00+01:00
+ content: Have a nice day
+ approved: false
+ replies_count: 1
+
+second:
+ id: 2
+ title: The Second Topic of the day
+ author_name: Mary
+ written_on: 2004-07-15t15:28:00.0099+01:00
+ content: Have a nice day
+ approved: true
+ replies_count: 0
+ parent_id: 1
+ type: Reply
+
+third:
+ id: 3
+ title: The Third Topic of the day
+ author_name: Carl
+ written_on: 2005-07-15t15:28:00.0099+01:00
+ content: I'm a troll
+ approved: true
+ replies_count: 1
+
+fourth:
+ id: 4
+ title: The Fourth Topic of the day
+ author_name: Carl
+ written_on: 2006-07-15t15:28:00.0099+01:00
+ content: Why not?
+ approved: true
+ type: Reply
+ parent_id: 3
+
diff --git a/activerecord/test/fixtures/randomly_named_a9.yml b/activerecord/test/fixtures/randomly_named_a9.yml
new file mode 100644
index 0000000000..bc51c83112
--- /dev/null
+++ b/activerecord/test/fixtures/randomly_named_a9.yml
@@ -0,0 +1,7 @@
+first_instance:
+ some_attribute: AAA
+ another_attribute: 000
+
+second_instance:
+ some_attribute: BBB
+ another_attribute: 999
diff --git a/activerecord/test/fixtures/teapots.yml b/activerecord/test/fixtures/teapots.yml
new file mode 100644
index 0000000000..ff515beb45
--- /dev/null
+++ b/activerecord/test/fixtures/teapots.yml
@@ -0,0 +1,3 @@
+bob:
+ id: 1
+ name: Bob
diff --git a/activerecord/test/migrations/broken/100_migration_that_raises_exception.rb b/activerecord/test/migrations/broken/100_migration_that_raises_exception.rb
deleted file mode 100644
index ffb224dad9..0000000000
--- a/activerecord/test/migrations/broken/100_migration_that_raises_exception.rb
+++ /dev/null
@@ -1,10 +0,0 @@
-class MigrationThatRaisesException < ActiveRecord::Migration
- def self.up
- add_column "people", "last_name", :string
- raise 'Something broke'
- end
-
- def self.down
- remove_column "people", "last_name"
- end
-end
diff --git a/activerecord/test/migrations/duplicate/3_foo.rb b/activerecord/test/migrations/duplicate/3_foo.rb
deleted file mode 100644
index 916fe580fa..0000000000
--- a/activerecord/test/migrations/duplicate/3_foo.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-class Foo < ActiveRecord::Migration
- def self.up
- end
-
- def self.down
- end
-end \ No newline at end of file
diff --git a/activerecord/test/migrations/duplicate_names/20080507052938_chunky.rb b/activerecord/test/migrations/duplicate_names/20080507052938_chunky.rb
deleted file mode 100644
index 5fe5089e18..0000000000
--- a/activerecord/test/migrations/duplicate_names/20080507052938_chunky.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-class Chunky < ActiveRecord::Migration
- def self.up
- end
-
- def self.down
- end
-end
diff --git a/activerecord/test/migrations/duplicate_names/20080507053028_chunky.rb b/activerecord/test/migrations/duplicate_names/20080507053028_chunky.rb
deleted file mode 100644
index 5fe5089e18..0000000000
--- a/activerecord/test/migrations/duplicate_names/20080507053028_chunky.rb
+++ /dev/null
@@ -1,7 +0,0 @@
-class Chunky < ActiveRecord::Migration
- def self.up
- end
-
- def self.down
- end
-end
diff --git a/activerecord/test/migrations/interleaved/pass_1/3_interleaved_innocent_jointable.rb b/activerecord/test/migrations/interleaved/pass_1/3_interleaved_innocent_jointable.rb
deleted file mode 100644
index bf912fbfc8..0000000000
--- a/activerecord/test/migrations/interleaved/pass_1/3_interleaved_innocent_jointable.rb
+++ /dev/null
@@ -1,12 +0,0 @@
-class InterleavedInnocentJointable < ActiveRecord::Migration
- def self.up
- create_table("people_reminders", :id => false) do |t|
- t.column :reminder_id, :integer
- t.column :person_id, :integer
- end
- end
-
- def self.down
- drop_table "people_reminders"
- end
-end
diff --git a/activerecord/test/migrations/interleaved/pass_2/1_interleaved_people_have_last_names.rb b/activerecord/test/migrations/interleaved/pass_2/1_interleaved_people_have_last_names.rb
deleted file mode 100644
index c6c94213a0..0000000000
--- a/activerecord/test/migrations/interleaved/pass_2/1_interleaved_people_have_last_names.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-class InterleavedPeopleHaveLastNames < ActiveRecord::Migration
- def self.up
- add_column "people", "last_name", :string
- end
-
- def self.down
- remove_column "people", "last_name"
- end
-end
diff --git a/activerecord/test/migrations/interleaved/pass_2/3_interleaved_innocent_jointable.rb b/activerecord/test/migrations/interleaved/pass_2/3_interleaved_innocent_jointable.rb
deleted file mode 100644
index bf912fbfc8..0000000000
--- a/activerecord/test/migrations/interleaved/pass_2/3_interleaved_innocent_jointable.rb
+++ /dev/null
@@ -1,12 +0,0 @@
-class InterleavedInnocentJointable < ActiveRecord::Migration
- def self.up
- create_table("people_reminders", :id => false) do |t|
- t.column :reminder_id, :integer
- t.column :person_id, :integer
- end
- end
-
- def self.down
- drop_table "people_reminders"
- end
-end
diff --git a/activerecord/test/migrations/interleaved/pass_3/1_interleaved_people_have_last_names.rb b/activerecord/test/migrations/interleaved/pass_3/1_interleaved_people_have_last_names.rb
deleted file mode 100644
index c6c94213a0..0000000000
--- a/activerecord/test/migrations/interleaved/pass_3/1_interleaved_people_have_last_names.rb
+++ /dev/null
@@ -1,9 +0,0 @@
-class InterleavedPeopleHaveLastNames < ActiveRecord::Migration
- def self.up
- add_column "people", "last_name", :string
- end
-
- def self.down
- remove_column "people", "last_name"
- end
-end
diff --git a/activerecord/test/migrations/interleaved/pass_3/2_interleaved_i_raise_on_down.rb b/activerecord/test/migrations/interleaved/pass_3/2_interleaved_i_raise_on_down.rb
deleted file mode 100644
index 6849995f5e..0000000000
--- a/activerecord/test/migrations/interleaved/pass_3/2_interleaved_i_raise_on_down.rb
+++ /dev/null
@@ -1,8 +0,0 @@
-class InterleavedIRaiseOnDown < ActiveRecord::Migration
- def self.up
- end
-
- def self.down
- raise
- end
-end
diff --git a/activerecord/test/migrations/interleaved/pass_3/3_interleaved_innocent_jointable.rb b/activerecord/test/migrations/interleaved/pass_3/3_interleaved_innocent_jointable.rb
deleted file mode 100644
index bf912fbfc8..0000000000
--- a/activerecord/test/migrations/interleaved/pass_3/3_interleaved_innocent_jointable.rb
+++ /dev/null
@@ -1,12 +0,0 @@
-class InterleavedInnocentJointable < ActiveRecord::Migration
- def self.up
- create_table("people_reminders", :id => false) do |t|
- t.column :reminder_id, :integer
- t.column :person_id, :integer
- end
- end
-
- def self.down
- drop_table "people_reminders"
- end
-end
diff --git a/activerecord/test/migrations/rename/1_we_need_things.rb b/activerecord/test/migrations/rename/1_we_need_things.rb
new file mode 100644
index 0000000000..cdbe0b1679
--- /dev/null
+++ b/activerecord/test/migrations/rename/1_we_need_things.rb
@@ -0,0 +1,11 @@
+class WeNeedThings < ActiveRecord::Migration
+ def self.up
+ create_table("things") do |t|
+ t.column :content, :text
+ end
+ end
+
+ def self.down
+ drop_table "things"
+ end
+end \ No newline at end of file
diff --git a/activerecord/test/migrations/rename/2_rename_things.rb b/activerecord/test/migrations/rename/2_rename_things.rb
new file mode 100644
index 0000000000..d441b71fc9
--- /dev/null
+++ b/activerecord/test/migrations/rename/2_rename_things.rb
@@ -0,0 +1,9 @@
+class RenameThings < ActiveRecord::Migration
+ def self.up
+ rename_table "things", "awesome_things"
+ end
+
+ def self.down
+ rename_table "awesome_things", "things"
+ end
+end \ No newline at end of file
diff --git a/activerecord/test/migrations/to_copy_with_name_collision/1_people_have_hobbies.rb b/activerecord/test/migrations/to_copy_with_name_collision/1_people_have_hobbies.rb
new file mode 100644
index 0000000000..e438cf5999
--- /dev/null
+++ b/activerecord/test/migrations/to_copy_with_name_collision/1_people_have_hobbies.rb
@@ -0,0 +1,9 @@
+class PeopleHaveLastNames < ActiveRecord::Migration
+ def self.up
+ add_column "people", "hobbies", :string
+ end
+
+ def self.down
+ remove_column "people", "hobbies"
+ end
+end
diff --git a/activerecord/test/migrations/duplicate/1_people_have_last_names.rb b/activerecord/test/migrations/valid_with_subdirectories/1_valid_people_have_last_names.rb
index 81af5fef5e..06cb911117 100644
--- a/activerecord/test/migrations/duplicate/1_people_have_last_names.rb
+++ b/activerecord/test/migrations/valid_with_subdirectories/1_valid_people_have_last_names.rb
@@ -1,4 +1,4 @@
-class PeopleHaveLastNames < ActiveRecord::Migration
+class ValidPeopleHaveLastNames < ActiveRecord::Migration
def self.up
add_column "people", "last_name", :string
end
@@ -6,4 +6,4 @@ class PeopleHaveLastNames < ActiveRecord::Migration
def self.down
remove_column "people", "last_name"
end
-end \ No newline at end of file
+end
diff --git a/activerecord/test/migrations/duplicate/2_we_need_reminders.rb b/activerecord/test/migrations/valid_with_subdirectories/sub/2_we_need_reminders.rb
index d5e71ce8ef..d5e71ce8ef 100644
--- a/activerecord/test/migrations/duplicate/2_we_need_reminders.rb
+++ b/activerecord/test/migrations/valid_with_subdirectories/sub/2_we_need_reminders.rb
diff --git a/activerecord/test/migrations/duplicate/3_innocent_jointable.rb b/activerecord/test/migrations/valid_with_subdirectories/sub1/3_innocent_jointable.rb
index 21c9ca5328..21c9ca5328 100644
--- a/activerecord/test/migrations/duplicate/3_innocent_jointable.rb
+++ b/activerecord/test/migrations/valid_with_subdirectories/sub1/3_innocent_jointable.rb
diff --git a/activerecord/test/models/admin/randomly_named_c1.rb b/activerecord/test/models/admin/randomly_named_c1.rb
new file mode 100644
index 0000000000..2f81d5b831
--- /dev/null
+++ b/activerecord/test/models/admin/randomly_named_c1.rb
@@ -0,0 +1,3 @@
+class Admin::ClassNameThatDoesNotFollowCONVENTIONS < ActiveRecord::Base
+ self.table_name = :randomly_named_table
+end
diff --git a/activerecord/test/models/admin/user.rb b/activerecord/test/models/admin/user.rb
index 74bb21551e..d0e628bd50 100644
--- a/activerecord/test/models/admin/user.rb
+++ b/activerecord/test/models/admin/user.rb
@@ -1,3 +1,5 @@
class Admin::User < ActiveRecord::Base
belongs_to :account
-end \ No newline at end of file
+ store :settings, :accessors => [ :color, :homepage ]
+ store :preferences, :accessors => [ :remember_login ]
+end
diff --git a/activerecord/test/models/author.rb b/activerecord/test/models/author.rb
index 23db5650d4..d50e11d6c9 100644
--- a/activerecord/test/models/author.rb
+++ b/activerecord/test/models/author.rb
@@ -49,12 +49,12 @@ class Author < ActiveRecord::Base
has_many :sti_post_comments, :through => :sti_posts, :source => :comments
has_many :special_nonexistant_posts, :class_name => "SpecialPost", :conditions => "posts.body = 'nonexistant'"
- has_many :special_nonexistant_post_comments, :through => :special_nonexistant_posts, :source => :comments, :conditions => "comments.post_id = 0"
+ has_many :special_nonexistant_post_comments, :through => :special_nonexistant_posts, :source => :comments, :conditions => { 'comments.post_id' => 0 }
has_many :nonexistant_comments, :through => :posts
has_many :hello_posts, :class_name => "Post", :conditions => "posts.body = 'hello'"
has_many :hello_post_comments, :through => :hello_posts, :source => :comments
- has_many :posts_with_no_comments, :class_name => 'Post', :conditions => 'comments.id is null', :include => :comments
+ has_many :posts_with_no_comments, :class_name => 'Post', :conditions => { 'comments.id' => nil }, :include => :comments
has_many :hello_posts_with_hash_conditions, :class_name => "Post",
:conditions => {:body => 'hello'}
@@ -128,7 +128,6 @@ class Author < ActiveRecord::Base
belongs_to :author_address, :dependent => :destroy
belongs_to :author_address_extra, :dependent => :delete, :class_name => "AuthorAddress"
- has_many :post_categories, :through => :posts, :source => :categories
has_many :category_post_comments, :through => :categories, :source => :post_comments
has_many :misc_posts, :class_name => 'Post',
diff --git a/activerecord/test/models/bird.rb b/activerecord/test/models/bird.rb
index e61d48e6a5..dff099c1fb 100644
--- a/activerecord/test/models/bird.rb
+++ b/activerecord/test/models/bird.rb
@@ -1,9 +1,12 @@
class Bird < ActiveRecord::Base
+ belongs_to :pirate
validates_presence_of :name
+ accepts_nested_attributes_for :pirate
+
attr_accessor :cancel_save_from_callback
before_save :cancel_save_callback_method, :if => :cancel_save_from_callback
def cancel_save_callback_method
false
end
-end \ No newline at end of file
+end
diff --git a/activerecord/test/models/car.rb b/activerecord/test/models/car.rb
index b9c2e8ec9a..6ff1329d8e 100644
--- a/activerecord/test/models/car.rb
+++ b/activerecord/test/models/car.rb
@@ -9,7 +9,7 @@ class Car < ActiveRecord::Base
has_many :tyres
has_many :engines, :dependent => :destroy
- has_many :wheels, :as => :wheelable
+ has_many :wheels, :as => :wheelable, :dependent => :destroy
scope :incl_tyres, includes(:tyres)
scope :incl_engines, includes(:engines)
diff --git a/activerecord/test/models/company.rb b/activerecord/test/models/company.rb
index c1f7a4171a..fbdfaa2c29 100644
--- a/activerecord/test/models/company.rb
+++ b/activerecord/test/models/company.rb
@@ -4,7 +4,7 @@ end
class Company < AbstractCompany
attr_protected :rating
- set_sequence_name :companies_nonstd_seq
+ self.sequence_name = :companies_nonstd_seq
validates_presence_of :name
@@ -42,8 +42,10 @@ class Firm < Company
:before_remove => :log_before_remove,
:after_remove => :log_after_remove
has_many :unsorted_clients, :class_name => "Client"
+ has_many :unsorted_clients_with_symbol, :class_name => :Client
has_many :clients_sorted_desc, :class_name => "Client", :order => "id DESC"
has_many :clients_of_firm, :foreign_key => "client_of", :class_name => "Client", :order => "id"
+ has_many :clients_ordered_by_name, :order => "name", :class_name => "Client"
has_many :unvalidated_clients_of_firm, :foreign_key => "client_of", :class_name => "Client", :validate => false
has_many :dependent_clients_of_firm, :foreign_key => "client_of", :class_name => "Client", :order => "id", :dependent => :destroy
has_many :exclusively_dependent_clients_of_firm, :foreign_key => "client_of", :class_name => "Client", :order => "id", :dependent => :delete_all
@@ -87,6 +89,8 @@ class Firm < Company
has_many :accounts
has_many :unautosaved_accounts, :foreign_key => "firm_id", :class_name => 'Account', :autosave => false
+ has_many :association_with_references, :class_name => 'Client', :references => :foo
+
def log
@log ||= []
end
diff --git a/activerecord/test/models/country.rb b/activerecord/test/models/country.rb
index 15e3a1de0b..7db9a4e731 100644
--- a/activerecord/test/models/country.rb
+++ b/activerecord/test/models/country.rb
@@ -1,6 +1,6 @@
class Country < ActiveRecord::Base
- set_primary_key :country_id
+ self.primary_key = :country_id
has_and_belongs_to_many :treaties
diff --git a/activerecord/test/models/dashboard.rb b/activerecord/test/models/dashboard.rb
index a8a25834b1..1b3b54545f 100644
--- a/activerecord/test/models/dashboard.rb
+++ b/activerecord/test/models/dashboard.rb
@@ -1,3 +1,3 @@
class Dashboard < ActiveRecord::Base
- set_primary_key :dashboard_id
-end \ No newline at end of file
+ self.primary_key = :dashboard_id
+end
diff --git a/activerecord/test/models/dog.rb b/activerecord/test/models/dog.rb
new file mode 100644
index 0000000000..72b7d33a86
--- /dev/null
+++ b/activerecord/test/models/dog.rb
@@ -0,0 +1,4 @@
+class Dog < ActiveRecord::Base
+ belongs_to :breeder, :class_name => "DogLover", :counter_cache => :bred_dogs_count
+ belongs_to :trainer, :class_name => "DogLover", :counter_cache => :trained_dogs_count
+end
diff --git a/activerecord/test/models/dog_lover.rb b/activerecord/test/models/dog_lover.rb
new file mode 100644
index 0000000000..a33dc575c5
--- /dev/null
+++ b/activerecord/test/models/dog_lover.rb
@@ -0,0 +1,4 @@
+class DogLover < ActiveRecord::Base
+ has_many :trained_dogs, :class_name => "Dog", :foreign_key => :trainer_id
+ has_many :bred_dogs, :class_name => "Dog", :foreign_key => :breeder_id
+end
diff --git a/activerecord/test/models/joke.rb b/activerecord/test/models/joke.rb
index d7f01e59e6..edda4655dc 100644
--- a/activerecord/test/models/joke.rb
+++ b/activerecord/test/models/joke.rb
@@ -1,7 +1,7 @@
class Joke < ActiveRecord::Base
- set_table_name 'funny_jokes'
+ self.table_name = 'funny_jokes'
end
class GoodJoke < ActiveRecord::Base
- set_table_name 'funny_jokes'
+ self.table_name = 'funny_jokes'
end
diff --git a/activerecord/test/models/keyboard.rb b/activerecord/test/models/keyboard.rb
index 32a4a7fad0..39347e274e 100644
--- a/activerecord/test/models/keyboard.rb
+++ b/activerecord/test/models/keyboard.rb
@@ -1,3 +1,3 @@
class Keyboard < ActiveRecord::Base
- set_primary_key 'key_number'
+ self.primary_key = 'key_number'
end
diff --git a/activerecord/test/models/legacy_thing.rb b/activerecord/test/models/legacy_thing.rb
index eaeb642d12..eead181a0e 100644
--- a/activerecord/test/models/legacy_thing.rb
+++ b/activerecord/test/models/legacy_thing.rb
@@ -1,3 +1,3 @@
class LegacyThing < ActiveRecord::Base
- set_locking_column :version
+ self.locking_column = :version
end
diff --git a/activerecord/test/models/liquid.rb b/activerecord/test/models/liquid.rb
index b96c054f6c..3fcd5e4b69 100644
--- a/activerecord/test/models/liquid.rb
+++ b/activerecord/test/models/liquid.rb
@@ -1,5 +1,5 @@
class Liquid < ActiveRecord::Base
- set_table_name :liquid
+ self.table_name = :liquid
has_many :molecules, :uniq => true
end
diff --git a/activerecord/test/models/minivan.rb b/activerecord/test/models/minivan.rb
index 830cdb5796..4fe79720ad 100644
--- a/activerecord/test/models/minivan.rb
+++ b/activerecord/test/models/minivan.rb
@@ -1,5 +1,5 @@
class Minivan < ActiveRecord::Base
- set_primary_key :minivan_id
+ self.primary_key = :minivan_id
belongs_to :speedometer
has_one :dashboard, :through => :speedometer
diff --git a/activerecord/test/models/mixed_case_monkey.rb b/activerecord/test/models/mixed_case_monkey.rb
index 853f2682b3..763baefd91 100644
--- a/activerecord/test/models/mixed_case_monkey.rb
+++ b/activerecord/test/models/mixed_case_monkey.rb
@@ -1,3 +1,3 @@
class MixedCaseMonkey < ActiveRecord::Base
- set_primary_key 'monkeyID'
+ self.primary_key = 'monkeyID'
end
diff --git a/activerecord/test/models/owner.rb b/activerecord/test/models/owner.rb
index 5760b991ec..fea55f4535 100644
--- a/activerecord/test/models/owner.rb
+++ b/activerecord/test/models/owner.rb
@@ -1,5 +1,5 @@
class Owner < ActiveRecord::Base
- set_primary_key :owner_id
+ self.primary_key = :owner_id
has_many :pets
has_many :toys, :through => :pets
end
diff --git a/activerecord/test/models/parrot.rb b/activerecord/test/models/parrot.rb
index 737ef9131b..c4ee2bd19d 100644
--- a/activerecord/test/models/parrot.rb
+++ b/activerecord/test/models/parrot.rb
@@ -1,5 +1,6 @@
class Parrot < ActiveRecord::Base
- set_inheritance_column :parrot_sti_class
+ self.inheritance_column = :parrot_sti_class
+
has_and_belongs_to_many :pirates
has_and_belongs_to_many :treasures
has_many :loots, :as => :looter
diff --git a/activerecord/test/models/person.rb b/activerecord/test/models/person.rb
index 967a3625aa..d2a0c6b40c 100644
--- a/activerecord/test/models/person.rb
+++ b/activerecord/test/models/person.rb
@@ -3,7 +3,8 @@ class Person < ActiveRecord::Base
has_one :reader
has_many :posts, :through => :readers
- has_many :posts_with_no_comments, :through => :readers, :source => :post, :include => :comments, :conditions => 'comments.id is null'
+ has_many :posts_with_no_comments, :through => :readers, :source => :post, :include => :comments,
+ :conditions => 'comments.id is null', :references => :comments
has_many :references
has_many :bad_references
@@ -54,7 +55,7 @@ class LoosePerson < ActiveRecord::Base
self.table_name = 'people'
self.abstract_class = true
- attr_protected :comments
+ attr_protected :comments, :best_friend_id, :best_friend_of_id
attr_protected :as => :admin
has_one :best_friend, :class_name => 'LoosePerson', :foreign_key => :best_friend_id
@@ -81,4 +82,4 @@ class TightPerson < ActiveRecord::Base
accepts_nested_attributes_for :best_friend, :best_friend_of, :best_friends
end
-class TightDescendant < TightPerson; end \ No newline at end of file
+class TightDescendant < TightPerson; end
diff --git a/activerecord/test/models/pet.rb b/activerecord/test/models/pet.rb
index 113826756a..3cd5bceed5 100644
--- a/activerecord/test/models/pet.rb
+++ b/activerecord/test/models/pet.rb
@@ -2,7 +2,7 @@ class Pet < ActiveRecord::Base
attr_accessor :current_user
- set_primary_key :pet_id
+ self.primary_key = :pet_id
belongs_to :owner, :touch => true
has_many :toys
diff --git a/activerecord/test/models/post.rb b/activerecord/test/models/post.rb
index 198a963cbc..1cab78d8c7 100644
--- a/activerecord/test/models/post.rb
+++ b/activerecord/test/models/post.rb
@@ -24,6 +24,10 @@ class Post < ActiveRecord::Base
belongs_to :author_with_posts, :class_name => "Author", :foreign_key => :author_id, :include => :posts
belongs_to :author_with_address, :class_name => "Author", :foreign_key => :author_id, :include => :author_address
+ def first_comment
+ super.body
+ end
+ has_one :first_comment, :class_name => 'Comment', :order => 'id ASC'
has_one :last_comment, :class_name => 'Comment', :order => 'id desc'
scope :with_special_comments, :joins => :comments, :conditions => {:comments => {:type => 'SpecialComment'} }
@@ -40,6 +44,10 @@ class Post < ActiveRecord::Base
def newest
created.last
end
+
+ def the_association
+ proxy_association
+ end
end
has_many :author_favorites, :through => :author
@@ -181,4 +189,4 @@ end
class SpecialPostWithDefaultScope < ActiveRecord::Base
self.table_name = 'posts'
default_scope where(:id => [1, 5,6])
-end \ No newline at end of file
+end
diff --git a/activerecord/test/models/randomly_named_c1.rb b/activerecord/test/models/randomly_named_c1.rb
new file mode 100644
index 0000000000..18a86c4989
--- /dev/null
+++ b/activerecord/test/models/randomly_named_c1.rb
@@ -0,0 +1,3 @@
+class ClassNameThatDoesNotFollowCONVENTIONS < ActiveRecord::Base
+ self.table_name = :randomly_named_table
+end
diff --git a/activerecord/test/models/speedometer.rb b/activerecord/test/models/speedometer.rb
index 94743eff8e..0a7d38d8ec 100644
--- a/activerecord/test/models/speedometer.rb
+++ b/activerecord/test/models/speedometer.rb
@@ -1,4 +1,4 @@
class Speedometer < ActiveRecord::Base
- set_primary_key :speedometer_id
+ self.primary_key = :speedometer_id
belongs_to :dashboard
-end \ No newline at end of file
+end
diff --git a/activerecord/test/models/string_key_object.rb b/activerecord/test/models/string_key_object.rb
index f8d4c6e0e4..f084ec1bdc 100644
--- a/activerecord/test/models/string_key_object.rb
+++ b/activerecord/test/models/string_key_object.rb
@@ -1,3 +1,3 @@
class StringKeyObject < ActiveRecord::Base
- set_primary_key :id
+ self.primary_key = :id
end
diff --git a/activerecord/test/models/subscriber.rb b/activerecord/test/models/subscriber.rb
index 5b78014e6f..76e85a0cd3 100644
--- a/activerecord/test/models/subscriber.rb
+++ b/activerecord/test/models/subscriber.rb
@@ -1,5 +1,5 @@
class Subscriber < ActiveRecord::Base
- set_primary_key 'nick'
+ self.primary_key = 'nick'
has_many :subscriptions
has_many :books, :through => :subscriptions
end
diff --git a/activerecord/test/models/teapot.rb b/activerecord/test/models/teapot.rb
new file mode 100644
index 0000000000..b035b18c1b
--- /dev/null
+++ b/activerecord/test/models/teapot.rb
@@ -0,0 +1,35 @@
+class Teapot
+ # I'm a little teapot,
+ # Short and stout,
+ # Here is my handle
+ # Here is my spout
+ # When I get all steamed up,
+ # Hear me shout,
+ # Tip me over and pour me out!
+ #
+ # HELL YEAH TEAPOT SONG
+
+ include ActiveRecord::Model
+end
+
+class OtherTeapot < Teapot
+end
+
+class OMFGIMATEAPOT
+ def aaahhh
+ "mmm"
+ end
+end
+
+class CoolTeapot < OMFGIMATEAPOT
+ include ActiveRecord::Model
+ self.table_name = "teapots"
+end
+
+class Ceiling
+ include ActiveRecord::Model
+
+ class Teapot
+ include ActiveRecord::Model
+ end
+end
diff --git a/activerecord/test/models/topic.rb b/activerecord/test/models/topic.rb
index fe424e61b2..8bcb9df8a8 100644
--- a/activerecord/test/models/topic.rb
+++ b/activerecord/test/models/topic.rb
@@ -8,6 +8,8 @@ class Topic < ActiveRecord::Base
scope :approved, :conditions => {:approved => true}
scope :rejected, :conditions => {:approved => false}
+ scope :scope_with_lambda, lambda { scoped }
+
scope :by_lifo, :conditions => {:author_name => 'lifo'}
scope :approved_as_hash_condition, :conditions => {:topics => {:approved => true}}
@@ -78,6 +80,11 @@ class Topic < ActiveRecord::Base
after_initialize :set_email_address
+ class_attribute :after_initialize_called
+ after_initialize do
+ self.class.after_initialize_called = true
+ end
+
def approved=(val)
@custom_approved = val
write_attribute(:approved, val)
@@ -106,6 +113,10 @@ class Topic < ActiveRecord::Base
def after_create_for_transaction; end
end
+class ImportantTopic < Topic
+ serialize :important, Hash
+end
+
module Web
class Topic < ActiveRecord::Base
has_many :replies, :dependent => :destroy, :foreign_key => "parent_id", :class_name => 'Web::Reply'
diff --git a/activerecord/test/models/toy.rb b/activerecord/test/models/toy.rb
index 6c45e99671..0377e50011 100644
--- a/activerecord/test/models/toy.rb
+++ b/activerecord/test/models/toy.rb
@@ -1,5 +1,5 @@
class Toy < ActiveRecord::Base
- set_primary_key :toy_id
+ self.primary_key = :toy_id
belongs_to :pet
scope :with_pet, joins(:pet)
diff --git a/activerecord/test/models/treaty.rb b/activerecord/test/models/treaty.rb
index b46537f0d2..41fd1350f3 100644
--- a/activerecord/test/models/treaty.rb
+++ b/activerecord/test/models/treaty.rb
@@ -1,6 +1,6 @@
class Treaty < ActiveRecord::Base
- set_primary_key :treaty_id
+ self.primary_key = :treaty_id
has_and_belongs_to_many :countries
diff --git a/activerecord/test/models/warehouse_thing.rb b/activerecord/test/models/warehouse_thing.rb
index 6ace1183cc..f20bd1a245 100644
--- a/activerecord/test/models/warehouse_thing.rb
+++ b/activerecord/test/models/warehouse_thing.rb
@@ -1,5 +1,5 @@
class WarehouseThing < ActiveRecord::Base
- set_table_name "warehouse-things"
+ self.table_name = "warehouse-things"
validates_uniqueness_of :value
-end \ No newline at end of file
+end
diff --git a/activerecord/test/schema/mysql2_specific_schema.rb b/activerecord/test/schema/mysql2_specific_schema.rb
index c78d99f4af..ab2c7ccc10 100644
--- a/activerecord/test/schema/mysql2_specific_schema.rb
+++ b/activerecord/test/schema/mysql2_specific_schema.rb
@@ -21,4 +21,15 @@ BEGIN
END
SQL
-end
+ ActiveRecord::Base.connection.execute <<-SQL
+DROP TABLE IF EXISTS collation_tests;
+SQL
+
+ ActiveRecord::Base.connection.execute <<-SQL
+CREATE TABLE collation_tests (
+ string_cs_column VARCHAR(1) COLLATE utf8_bin,
+ string_ci_column VARCHAR(1) COLLATE utf8_general_ci
+) CHARACTER SET utf8 COLLATE utf8_general_ci
+SQL
+
+end \ No newline at end of file
diff --git a/activerecord/test/schema/mysql_specific_schema.rb b/activerecord/test/schema/mysql_specific_schema.rb
index 30e1c5a167..a0adfe3752 100644
--- a/activerecord/test/schema/mysql_specific_schema.rb
+++ b/activerecord/test/schema/mysql_specific_schema.rb
@@ -32,4 +32,15 @@ BEGIN
END
SQL
+ ActiveRecord::Base.connection.execute <<-SQL
+DROP TABLE IF EXISTS collation_tests;
+SQL
+
+ ActiveRecord::Base.connection.execute <<-SQL
+CREATE TABLE collation_tests (
+ string_cs_column VARCHAR(1) COLLATE utf8_bin,
+ string_ci_column VARCHAR(1) COLLATE utf8_general_ci
+) CHARACTER SET utf8 COLLATE utf8_general_ci
+SQL
+
end
diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb
index 9d5ad16a3c..da0c4cecdd 100644
--- a/activerecord/test/schema/schema.rb
+++ b/activerecord/test/schema/schema.rb
@@ -37,6 +37,8 @@ ActiveRecord::Schema.define do
create_table :admin_users, :force => true do |t|
t.string :name
+ t.text :settings, :null => true
+ t.text :preferences, :null => false, :default => ""
t.references :account
end
@@ -106,6 +108,7 @@ ActiveRecord::Schema.define do
t.string :name
t.integer :engines_count
t.integer :wheels_count
+ t.column :lock_version, :integer, :null => false, :default => 0
end
create_table :categories, :force => true do |t|
@@ -211,6 +214,16 @@ ActiveRecord::Schema.define do
t.integer :access_level, :default => 1
end
+ create_table :dog_lovers, :force => true do |t|
+ t.integer :trained_dogs_count, :default => 0
+ t.integer :bred_dogs_count, :default => 0
+ end
+
+ create_table :dogs, :force => true do |t|
+ t.integer :trainer_id
+ t.integer :breeder_id
+ end
+
create_table :edges, :force => true, :id => false do |t|
t.column :source_id, :integer, :null => false
t.column :sink_id, :integer, :null => false
@@ -504,6 +517,11 @@ ActiveRecord::Schema.define do
t.string :type
end
+ create_table :randomly_named_table, :force => true do |t|
+ t.string :some_attribute
+ t.integer :another_attribute
+ end
+
create_table :ratings, :force => true do |t|
t.integer :comment_id
t.integer :value
@@ -595,6 +613,12 @@ ActiveRecord::Schema.define do
t.datetime :ending
end
+ create_table :teapots, :force => true do |t|
+ t.string :name
+ t.string :type
+ t.timestamps
+ end
+
create_table :topics, :force => true do |t|
t.string :title
t.string :author_name
@@ -606,8 +630,10 @@ ActiveRecord::Schema.define do
# Oracle SELECT WHERE clause which causes many unit test failures
if current_adapter?(:OracleAdapter)
t.string :content, :limit => 4000
+ t.string :important, :limit => 4000
else
t.text :content
+ t.text :important
end
t.boolean :approved, :default => true
t.integer :replies_count, :default => 0
@@ -699,8 +725,6 @@ ActiveRecord::Schema.define do
create_table :countries_treaties, :force => true, :id => false do |t|
t.string :country_id, :null => false
t.string :treaty_id, :null => false
- t.datetime :created_at
- t.datetime :updated_at
end
create_table :liquid, :force => true do |t|
diff --git a/activerecord/test/support/connection.rb b/activerecord/test/support/connection.rb
index a39794fa39..60fea46fd3 100644
--- a/activerecord/test/support/connection.rb
+++ b/activerecord/test/support/connection.rb
@@ -1,4 +1,4 @@
-require 'logger'
+require 'active_support/logger'
require_dependency 'models/course'
module ARTest
@@ -12,9 +12,9 @@ module ARTest
def self.connect
puts "Using #{connection_name} with Identity Map #{ActiveRecord::IdentityMap.enabled? ? 'on' : 'off'}"
- ActiveRecord::Base.logger = Logger.new("debug.log")
- ActiveRecord::Base.configurations = connection_config
- ActiveRecord::Base.establish_connection 'arunit'
+ ActiveRecord::Model.logger = ActiveSupport::Logger.new("debug.log")
+ ActiveRecord::Model.configurations = connection_config
+ ActiveRecord::Model.establish_connection 'arunit'
Course.establish_connection 'arunit2'
end
end