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.rb6
-rw-r--r--activerecord/test/cases/adapter_test.rb17
-rw-r--r--activerecord/test/cases/adapters/mysql/active_schema_test.rb18
-rw-r--r--activerecord/test/cases/adapters/mysql/connection_test.rb13
-rw-r--r--activerecord/test/cases/adapters/mysql/explain_test.rb21
-rw-r--r--activerecord/test/cases/adapters/mysql/mysql_adapter_test.rb37
-rw-r--r--activerecord/test/cases/adapters/mysql/quoting_test.rb14
-rw-r--r--activerecord/test/cases/adapters/mysql/schema_test.rb1
-rw-r--r--activerecord/test/cases/adapters/mysql/sp_test.rb29
-rw-r--r--activerecord/test/cases/adapters/mysql/sql_types_test.rb2
-rw-r--r--activerecord/test/cases/adapters/mysql/statement_pool_test.rb2
-rw-r--r--activerecord/test/cases/adapters/mysql/unsigned_type_test.rb39
-rw-r--r--activerecord/test/cases/adapters/mysql2/active_schema_test.rb18
-rw-r--r--activerecord/test/cases/adapters/mysql2/json_test.rb172
-rw-r--r--activerecord/test/cases/adapters/mysql2/quoting_test.rb21
-rw-r--r--activerecord/test/cases/adapters/mysql2/schema_test.rb9
-rw-r--r--activerecord/test/cases/adapters/mysql2/sp_test.rb30
-rw-r--r--activerecord/test/cases/adapters/mysql2/sql_types_test.rb2
-rw-r--r--activerecord/test/cases/adapters/mysql2/unsigned_type_test.rb39
-rw-r--r--activerecord/test/cases/adapters/postgresql/active_schema_test.rb18
-rw-r--r--activerecord/test/cases/adapters/postgresql/connection_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/json_test.rb1
-rw-r--r--activerecord/test/cases/adapters/postgresql/money_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb14
-rw-r--r--activerecord/test/cases/adapters/postgresql/schema_authorization_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/schema_test.rb98
-rw-r--r--activerecord/test/cases/adapters/postgresql/view_test.rb64
-rw-r--r--activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb22
-rw-r--r--activerecord/test/cases/adapters/sqlite3/statement_pool_test.rb2
-rw-r--r--activerecord/test/cases/associations/belongs_to_associations_test.rb52
-rw-r--r--activerecord/test/cases/associations/bidirectional_destroy_dependencies_test.rb41
-rw-r--r--activerecord/test/cases/associations/eager_test.rb102
-rw-r--r--activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb111
-rw-r--r--activerecord/test/cases/associations/has_many_associations_test.rb174
-rw-r--r--activerecord/test/cases/associations/has_many_through_associations_test.rb97
-rw-r--r--activerecord/test/cases/associations/has_one_associations_test.rb54
-rw-r--r--activerecord/test/cases/associations/has_one_through_associations_test.rb40
-rw-r--r--activerecord/test/cases/associations/inverse_associations_test.rb21
-rw-r--r--activerecord/test/cases/associations/join_model_test.rb32
-rw-r--r--activerecord/test/cases/associations/left_outer_join_association_test.rb74
-rw-r--r--activerecord/test/cases/associations/nested_through_associations_test.rb2
-rw-r--r--activerecord/test/cases/associations_test.rb19
-rw-r--r--activerecord/test/cases/attribute_methods_test.rb13
-rw-r--r--activerecord/test/cases/attribute_set_test.rb44
-rw-r--r--activerecord/test/cases/attribute_test.rb60
-rw-r--r--activerecord/test/cases/attributes_test.rb6
-rw-r--r--activerecord/test/cases/autosave_association_test.rb75
-rw-r--r--activerecord/test/cases/base_test.rb87
-rw-r--r--activerecord/test/cases/batches_test.rb259
-rw-r--r--activerecord/test/cases/cache_key_test.rb25
-rw-r--r--activerecord/test/cases/calculations_test.rb108
-rw-r--r--activerecord/test/cases/collection_cache_key_test.rb70
-rw-r--r--activerecord/test/cases/connection_adapters/mysql_type_lookup_test.rb4
-rw-r--r--activerecord/test/cases/connection_adapters/schema_cache_test.rb11
-rw-r--r--activerecord/test/cases/connection_pool_test.rb28
-rw-r--r--activerecord/test/cases/counter_cache_test.rb13
-rw-r--r--activerecord/test/cases/dirty_test.rb21
-rw-r--r--activerecord/test/cases/enum_test.rb32
-rw-r--r--activerecord/test/cases/errors_test.rb16
-rw-r--r--activerecord/test/cases/explain_test.rb47
-rw-r--r--activerecord/test/cases/finder_test.rb23
-rw-r--r--activerecord/test/cases/fixture_set/file_test.rb12
-rw-r--r--activerecord/test/cases/fixtures_test.rb80
-rw-r--r--activerecord/test/cases/helper.rb7
-rw-r--r--activerecord/test/cases/integration_test.rb23
-rw-r--r--activerecord/test/cases/invertible_migration_test.rb65
-rw-r--r--activerecord/test/cases/locking_test.rb2
-rw-r--r--activerecord/test/cases/log_subscriber_test.rb98
-rw-r--r--activerecord/test/cases/migration/change_table_test.rb1
-rw-r--r--activerecord/test/cases/migration/columns_test.rb7
-rw-r--r--activerecord/test/cases/migration/command_recorder_test.rb13
-rw-r--r--activerecord/test/cases/migration/foreign_key_test.rb31
-rw-r--r--activerecord/test/cases/migration/helper.rb2
-rw-r--r--activerecord/test/cases/migration/pending_migrations_test.rb1
-rw-r--r--activerecord/test/cases/migration/postgresql_geometric_types_test.rb93
-rw-r--r--activerecord/test/cases/migration/references_foreign_key_test.rb17
-rw-r--r--activerecord/test/cases/migration_test.rb12
-rw-r--r--activerecord/test/cases/nested_attributes_test.rb81
-rw-r--r--activerecord/test/cases/persistence_test.rb12
-rw-r--r--activerecord/test/cases/primary_keys_test.rb30
-rw-r--r--activerecord/test/cases/query_cache_test.rb69
-rw-r--r--activerecord/test/cases/readonly_test.rb1
-rw-r--r--activerecord/test/cases/reflection_test.rb40
-rw-r--r--activerecord/test/cases/relation/mutation_test.rb7
-rw-r--r--activerecord/test/cases/relation/where_test.rb30
-rw-r--r--activerecord/test/cases/relation_test.rb40
-rw-r--r--activerecord/test/cases/relations_test.rb43
-rw-r--r--activerecord/test/cases/sanitize_test.rb8
-rw-r--r--activerecord/test/cases/schema_dumper_test.rb6
-rw-r--r--activerecord/test/cases/scoping/default_scoping_test.rb6
-rw-r--r--activerecord/test/cases/scoping/named_scoping_test.rb10
-rw-r--r--activerecord/test/cases/serialization_test.rb2
-rw-r--r--activerecord/test/cases/tasks/database_tasks_test.rb4
-rw-r--r--activerecord/test/cases/tasks/mysql_rake_test.rb14
-rw-r--r--activerecord/test/cases/tasks/postgresql_rake_test.rb14
-rw-r--r--activerecord/test/cases/tasks/sqlite_rake_test.rb2
-rw-r--r--activerecord/test/cases/test_case.rb2
-rw-r--r--activerecord/test/cases/test_fixtures_test.rb2
-rw-r--r--activerecord/test/cases/timestamp_test.rb20
-rw-r--r--activerecord/test/cases/touch_later_test.rb27
-rw-r--r--activerecord/test/cases/transactions_test.rb27
-rw-r--r--activerecord/test/cases/type/date_time_test.rb14
-rw-r--r--activerecord/test/cases/type/decimal_test.rb51
-rw-r--r--activerecord/test/cases/type/integer_test.rb100
-rw-r--r--activerecord/test/cases/type/string_test.rb14
-rw-r--r--activerecord/test/cases/type/unsigned_integer_test.rb17
-rw-r--r--activerecord/test/cases/types_test.rb105
-rw-r--r--activerecord/test/cases/validations/i18n_validation_test.rb10
-rw-r--r--activerecord/test/cases/validations/length_validation_test.rb1
-rw-r--r--activerecord/test/cases/validations/uniqueness_validation_test.rb42
-rw-r--r--activerecord/test/cases/validations_test.rb18
-rw-r--r--activerecord/test/cases/view_test.rb103
-rw-r--r--activerecord/test/cases/xml_serialization_test.rb447
-rw-r--r--activerecord/test/fixtures/bad_posts.yml9
-rw-r--r--activerecord/test/fixtures/books.yml5
-rw-r--r--activerecord/test/fixtures/content.yml3
-rw-r--r--activerecord/test/fixtures/content_positions.yml3
-rw-r--r--activerecord/test/fixtures/naked/yml/parrots.yml2
-rw-r--r--activerecord/test/fixtures/nodes.yml29
-rw-r--r--activerecord/test/fixtures/other_comments.yml6
-rw-r--r--activerecord/test/fixtures/other_posts.yml7
-rw-r--r--activerecord/test/fixtures/trees.yml3
-rw-r--r--activerecord/test/models/aircraft.rb1
-rw-r--r--activerecord/test/models/author.rb3
-rw-r--r--activerecord/test/models/book.rb8
-rw-r--r--activerecord/test/models/carrier.rb2
-rw-r--r--activerecord/test/models/categorization.rb2
-rw-r--r--activerecord/test/models/company.rb4
-rw-r--r--activerecord/test/models/contact.rb2
-rw-r--r--activerecord/test/models/content.rb40
-rw-r--r--activerecord/test/models/customer_carrier.rb14
-rw-r--r--activerecord/test/models/developer.rb11
-rw-r--r--activerecord/test/models/face.rb2
-rw-r--r--activerecord/test/models/guitar.rb4
-rw-r--r--activerecord/test/models/member.rb3
-rw-r--r--activerecord/test/models/member_detail.rb7
-rw-r--r--activerecord/test/models/membership.rb15
-rw-r--r--activerecord/test/models/mentor.rb3
-rw-r--r--activerecord/test/models/node.rb5
-rw-r--r--activerecord/test/models/parrot.rb6
-rw-r--r--activerecord/test/models/person.rb1
-rw-r--r--activerecord/test/models/post.rb31
-rw-r--r--activerecord/test/models/professor.rb5
-rw-r--r--activerecord/test/models/project.rb11
-rw-r--r--activerecord/test/models/ship.rb13
-rw-r--r--activerecord/test/models/shop_account.rb6
-rw-r--r--activerecord/test/models/topic.rb4
-rw-r--r--activerecord/test/models/treasure.rb1
-rw-r--r--activerecord/test/models/tree.rb3
-rw-r--r--activerecord/test/models/tuning_peg.rb4
-rw-r--r--activerecord/test/models/vehicle.rb7
-rw-r--r--activerecord/test/schema/mysql2_specific_schema.rb13
-rw-r--r--activerecord/test/schema/mysql_specific_schema.rb6
-rw-r--r--activerecord/test/schema/schema.rb142
-rw-r--r--activerecord/test/support/connection.rb1
155 files changed, 3400 insertions, 1406 deletions
diff --git a/activerecord/test/active_record/connection_adapters/fake_adapter.rb b/activerecord/test/active_record/connection_adapters/fake_adapter.rb
index 49a68fb94c..43c817e057 100644
--- a/activerecord/test/active_record/connection_adapters/fake_adapter.rb
+++ b/activerecord/test/active_record/connection_adapters/fake_adapter.rb
@@ -7,7 +7,7 @@ module ActiveRecord
module ConnectionAdapters
class FakeAdapter < AbstractAdapter
- attr_accessor :tables, :primary_keys
+ attr_accessor :data_sources, :primary_keys
@columns = Hash.new { |h,k| h[k] = [] }
class << self
@@ -16,7 +16,7 @@ module ActiveRecord
def initialize(connection, logger)
super
- @tables = []
+ @data_sources = []
@primary_keys = {}
@columns = self.class.columns
end
@@ -37,7 +37,7 @@ module ActiveRecord
@columns[table_name]
end
- def table_exists?(*)
+ def data_source_exists?(*)
true
end
diff --git a/activerecord/test/cases/adapter_test.rb b/activerecord/test/cases/adapter_test.rb
index 1712ff0ac6..62579a4a7a 100644
--- a/activerecord/test/cases/adapter_test.rb
+++ b/activerecord/test/cases/adapter_test.rb
@@ -36,6 +36,21 @@ module ActiveRecord
assert !@connection.table_exists?(nil)
end
+ def test_data_sources
+ data_sources = @connection.data_sources
+ assert data_sources.include?("accounts")
+ assert data_sources.include?("authors")
+ assert data_sources.include?("tasks")
+ assert data_sources.include?("topics")
+ end
+
+ def test_data_source_exists?
+ assert @connection.data_source_exists?("accounts")
+ assert @connection.data_source_exists?(:accounts)
+ assert_not @connection.data_source_exists?("nonexistingtable")
+ assert_not @connection.data_source_exists?(nil)
+ end
+
def test_indexes
idx_name = "accounts_idx"
@@ -63,7 +78,7 @@ module ActiveRecord
end
end
- if current_adapter?(:MysqlAdapter)
+ if current_adapter?(:MysqlAdapter, :Mysql2Adapter)
def test_charset
assert_not_nil @connection.charset
assert_not_equal 'character_set_database', @connection.charset
diff --git a/activerecord/test/cases/adapters/mysql/active_schema_test.rb b/activerecord/test/cases/adapters/mysql/active_schema_test.rb
index f0fd95ac16..0b5c9e1798 100644
--- a/activerecord/test/cases/adapters/mysql/active_schema_test.rb
+++ b/activerecord/test/cases/adapters/mysql/active_schema_test.rb
@@ -100,17 +100,15 @@ class MysqlActiveSchemaTest < ActiveRecord::MysqlTestCase
assert_equal "DROP TABLE `people`", drop_table(:people)
end
- if current_adapter?(:MysqlAdapter, :Mysql2Adapter)
- def test_create_mysql_database_with_encoding
- assert_equal "CREATE DATABASE `matt` DEFAULT CHARACTER SET `utf8`", create_database(:matt)
- assert_equal "CREATE DATABASE `aimonetti` DEFAULT CHARACTER SET `latin1`", create_database(:aimonetti, {:charset => 'latin1'})
- assert_equal "CREATE DATABASE `matt_aimonetti` DEFAULT CHARACTER SET `big5` COLLATE `big5_chinese_ci`", create_database(:matt_aimonetti, {:charset => :big5, :collation => :big5_chinese_ci})
- end
+ def test_create_mysql_database_with_encoding
+ assert_equal "CREATE DATABASE `matt` DEFAULT CHARACTER SET `utf8`", create_database(:matt)
+ assert_equal "CREATE DATABASE `aimonetti` DEFAULT CHARACTER SET `latin1`", create_database(:aimonetti, {:charset => 'latin1'})
+ assert_equal "CREATE DATABASE `matt_aimonetti` DEFAULT CHARACTER SET `big5` COLLATE `big5_chinese_ci`", create_database(:matt_aimonetti, {:charset => :big5, :collation => :big5_chinese_ci})
+ end
- def test_recreate_mysql_database_with_encoding
- create_database(:luca, {:charset => 'latin1'})
- assert_equal "CREATE DATABASE `luca` DEFAULT CHARACTER SET `latin1`", recreate_database(:luca, {:charset => 'latin1'})
- end
+ def test_recreate_mysql_database_with_encoding
+ create_database(:luca, {:charset => 'latin1'})
+ assert_equal "CREATE DATABASE `luca` DEFAULT CHARACTER SET `latin1`", recreate_database(:luca, {:charset => 'latin1'})
end
def test_add_column
diff --git a/activerecord/test/cases/adapters/mysql/connection_test.rb b/activerecord/test/cases/adapters/mysql/connection_test.rb
index ddbc007b87..decac9e83b 100644
--- a/activerecord/test/cases/adapters/mysql/connection_test.rb
+++ b/activerecord/test/cases/adapters/mysql/connection_test.rb
@@ -26,7 +26,7 @@ class MysqlConnectionTest < ActiveRecord::MysqlTestCase
run_without_connection do
ar_config = ARTest.connection_config['arunit']
- url = "mysql://#{ar_config["username"]}@localhost/#{ar_config["database"]}"
+ url = "mysql://#{ar_config["username"]}:#{ar_config["password"]}@localhost/#{ar_config["database"]}"
Klass.establish_connection(url)
assert_equal ar_config['database'], Klass.connection.current_database
end
@@ -118,15 +118,6 @@ class MysqlConnectionTest < ActiveRecord::MysqlTestCase
end
end
- # 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();')
- 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
- end
-
def test_mysql_connection_collation_is_configured
assert_equal 'utf8_unicode_ci', @connection.show_variable('collation_connection')
assert_equal 'utf8_general_ci', ARUnit2Model.connection.show_variable('collation_connection')
@@ -183,7 +174,7 @@ class MysqlConnectionTest < ActiveRecord::MysqlTestCase
def with_example_table(&block)
definition ||= <<-SQL
- `id` int(11) auto_increment PRIMARY KEY,
+ `id` int auto_increment PRIMARY KEY,
`data` varchar(255)
SQL
super(@connection, 'ex', definition, &block)
diff --git a/activerecord/test/cases/adapters/mysql/explain_test.rb b/activerecord/test/cases/adapters/mysql/explain_test.rb
new file mode 100644
index 0000000000..c44c1e6648
--- /dev/null
+++ b/activerecord/test/cases/adapters/mysql/explain_test.rb
@@ -0,0 +1,21 @@
+require "cases/helper"
+require 'models/developer'
+require 'models/computer'
+
+class MysqlExplainTest < ActiveRecord::MysqlTestCase
+ 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 %r(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 %r(developers |.* const), explain
+ assert_match %(EXPLAIN for: SELECT `audit_logs`.* FROM `audit_logs` WHERE `audit_logs`.`developer_id` = 1), explain
+ assert_match %r(audit_logs |.* ALL), explain
+ 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 b804cb45b9..d2ce48fc00 100644
--- a/activerecord/test/cases/adapters/mysql/mysql_adapter_test.rb
+++ b/activerecord/test/cases/adapters/mysql/mysql_adapter_test.rb
@@ -1,4 +1,3 @@
-
require "cases/helper"
require 'support/ddl_helper'
@@ -66,40 +65,8 @@ module ActiveRecord
end
end
- def test_tables_quoting
- @conn.tables(nil, "foo-bar", nil)
- flunk
- rescue => e
- # assertion for *quoted* database properly
- assert_match(/database 'foo-bar'/, e.inspect)
- end
-
- def test_pk_and_sequence_for
- with_example_table do
- pk, seq = @conn.pk_and_sequence_for('ex')
- assert_equal 'id', pk
- assert_equal @conn.default_sequence_name('ex', 'id'), seq
- end
- end
-
- def test_pk_and_sequence_for_with_non_standard_primary_key
- with_example_table '`code` INT(11) auto_increment, PRIMARY KEY (`code`)' do
- pk, seq = @conn.pk_and_sequence_for('ex')
- assert_equal 'code', pk
- assert_equal @conn.default_sequence_name('ex', 'code'), seq
- end
- end
-
- def test_pk_and_sequence_for_with_custom_index_type_pk
- with_example_table '`id` INT(11) auto_increment, PRIMARY KEY USING BTREE (`id`)' do
- pk, seq = @conn.pk_and_sequence_for('ex')
- assert_equal 'id', pk
- assert_equal @conn.default_sequence_name('ex', 'id'), seq
- end
- end
-
def test_composite_primary_key
- with_example_table '`id` INT(11), `number` INT(11), foo INT(11), PRIMARY KEY (`id`, `number`)' do
+ with_example_table '`id` INT, `number` INT, foo INT, PRIMARY KEY (`id`, `number`)' do
assert_nil @conn.primary_key('ex')
end
end
@@ -141,7 +108,7 @@ module ActiveRecord
def with_example_table(definition = nil, &block)
definition ||= <<-SQL
- `id` int(11) auto_increment PRIMARY KEY,
+ `id` int auto_increment PRIMARY KEY,
`number` integer,
`data` varchar(255)
SQL
diff --git a/activerecord/test/cases/adapters/mysql/quoting_test.rb b/activerecord/test/cases/adapters/mysql/quoting_test.rb
index a296cf9d31..2024aa36ab 100644
--- a/activerecord/test/cases/adapters/mysql/quoting_test.rb
+++ b/activerecord/test/cases/adapters/mysql/quoting_test.rb
@@ -12,4 +12,18 @@ class MysqlQuotingTest < ActiveRecord::MysqlTestCase
def test_type_cast_false
assert_equal 0, @conn.type_cast(false)
end
+
+ def test_quoted_date_precision_for_gte_564
+ @conn.stubs(:full_version).returns('5.6.4')
+ @conn.remove_instance_variable(:@version) if @conn.instance_variable_defined?(:@version)
+ t = Time.now.change(usec: 1)
+ assert_match(/\.000001\z/, @conn.quoted_date(t))
+ end
+
+ def test_quoted_date_precision_for_lt_564
+ @conn.stubs(:full_version).returns('5.6.3')
+ @conn.remove_instance_variable(:@version) if @conn.instance_variable_defined?(:@version)
+ t = Time.now.change(usec: 1)
+ assert_no_match(/\.000001\z/, @conn.quoted_date(t))
+ end
end
diff --git a/activerecord/test/cases/adapters/mysql/schema_test.rb b/activerecord/test/cases/adapters/mysql/schema_test.rb
index 2e18f609fd..a0f3c31e78 100644
--- a/activerecord/test/cases/adapters/mysql/schema_test.rb
+++ b/activerecord/test/cases/adapters/mysql/schema_test.rb
@@ -14,6 +14,7 @@ module ActiveRecord
@db_name = db
@omgpost = Class.new(ActiveRecord::Base) do
+ self.inheritance_column = :disabled
self.table_name = "#{db}.#{table}"
def self.name; 'Post'; end
end
diff --git a/activerecord/test/cases/adapters/mysql/sp_test.rb b/activerecord/test/cases/adapters/mysql/sp_test.rb
index a3d5110032..7849248dcc 100644
--- a/activerecord/test/cases/adapters/mysql/sp_test.rb
+++ b/activerecord/test/cases/adapters/mysql/sp_test.rb
@@ -1,15 +1,30 @@
require "cases/helper"
require 'models/topic'
+require 'models/reply'
-class StoredProcedureTest < ActiveRecord::MysqlTestCase
+class MysqlStoredProcedureTest < ActiveRecord::MysqlTestCase
fixtures :topics
- # Test that MySQL allows multiple results for stored procedures
- if defined?(Mysql) && Mysql.const_defined?(:CLIENT_MULTI_RESULTS)
- def test_multi_results_from_find_by_sql
- topics = Topic.find_by_sql 'CALL topics();'
- assert_equal 1, topics.size
- assert ActiveRecord::Base.connection.active?, "Bad connection use by 'MysqlAdapter.select'"
+ def setup
+ @connection = ActiveRecord::Base.connection
+ unless ActiveRecord::Base.connection.version >= '5.6.0' || Mysql.const_defined?(:CLIENT_MULTI_RESULTS)
+ skip("no stored procedure support")
end
end
+
+ # Test that MySQL allows multiple results for stored procedures
+ #
+ # In MySQL 5.6, CLIENT_MULTI_RESULTS is enabled by default.
+ # http://dev.mysql.com/doc/refman/5.6/en/call.html
+ def test_multi_results
+ rows = @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
+
+ def test_multi_results_from_find_by_sql
+ topics = Topic.find_by_sql 'CALL topics(3);'
+ assert_equal 3, topics.size
+ assert @connection.active?, "Bad connection use by 'MysqlAdapter.select'"
+ end
end
diff --git a/activerecord/test/cases/adapters/mysql/sql_types_test.rb b/activerecord/test/cases/adapters/mysql/sql_types_test.rb
index 25b28de7f0..d18579f242 100644
--- a/activerecord/test/cases/adapters/mysql/sql_types_test.rb
+++ b/activerecord/test/cases/adapters/mysql/sql_types_test.rb
@@ -4,7 +4,7 @@ class MysqlSqlTypesTest < ActiveRecord::MysqlTestCase
def test_binary_types
assert_equal 'varbinary(64)', type_to_sql(:binary, 64)
assert_equal 'varbinary(4095)', type_to_sql(:binary, 4095)
- assert_equal 'blob(4096)', type_to_sql(:binary, 4096)
+ assert_equal 'blob', type_to_sql(:binary, 4096)
assert_equal 'blob', type_to_sql(:binary)
end
diff --git a/activerecord/test/cases/adapters/mysql/statement_pool_test.rb b/activerecord/test/cases/adapters/mysql/statement_pool_test.rb
index 6be36566de..0d1f968022 100644
--- a/activerecord/test/cases/adapters/mysql/statement_pool_test.rb
+++ b/activerecord/test/cases/adapters/mysql/statement_pool_test.rb
@@ -3,7 +3,7 @@ require 'cases/helper'
class MysqlStatementPoolTest < ActiveRecord::MysqlTestCase
if Process.respond_to?(:fork)
def test_cache_is_per_pid
- cache = ActiveRecord::ConnectionAdapters::MysqlAdapter::StatementPool.new nil, 10
+ cache = ActiveRecord::ConnectionAdapters::MysqlAdapter::StatementPool.new(10)
cache['foo'] = 'bar'
assert_equal 'bar', cache['foo']
diff --git a/activerecord/test/cases/adapters/mysql/unsigned_type_test.rb b/activerecord/test/cases/adapters/mysql/unsigned_type_test.rb
index ed9398a918..84c5394c2e 100644
--- a/activerecord/test/cases/adapters/mysql/unsigned_type_test.rb
+++ b/activerecord/test/cases/adapters/mysql/unsigned_type_test.rb
@@ -1,6 +1,8 @@
require "cases/helper"
+require "support/schema_dumping_helper"
class MysqlUnsignedTypeTest < ActiveRecord::MysqlTestCase
+ include SchemaDumpingHelper
self.use_transactional_tests = false
class UnsignedType < ActiveRecord::Base
@@ -9,12 +11,15 @@ class MysqlUnsignedTypeTest < ActiveRecord::MysqlTestCase
setup do
@connection = ActiveRecord::Base.connection
@connection.create_table("unsigned_types", force: true) do |t|
- t.column :unsigned_integer, "int unsigned"
+ t.integer :unsigned_integer, unsigned: true
+ t.bigint :unsigned_bigint, unsigned: true
+ t.float :unsigned_float, unsigned: true
+ t.decimal :unsigned_decimal, unsigned: true, precision: 10, scale: 2
end
end
teardown do
- @connection.drop_table "unsigned_types"
+ @connection.drop_table "unsigned_types", if_exists: true
end
test "unsigned int max value is in range" do
@@ -26,5 +31,35 @@ class MysqlUnsignedTypeTest < ActiveRecord::MysqlTestCase
assert_raise(RangeError) do
UnsignedType.create(unsigned_integer: -10)
end
+ assert_raise(RangeError) do
+ UnsignedType.create(unsigned_bigint: -10)
+ end
+ assert_raise(ActiveRecord::StatementInvalid) do
+ UnsignedType.create(unsigned_float: -10.0)
+ end
+ assert_raise(ActiveRecord::StatementInvalid) do
+ UnsignedType.create(unsigned_decimal: -10.0)
+ end
+ end
+
+ test "schema definition can use unsigned as the type" do
+ @connection.change_table("unsigned_types") do |t|
+ t.unsigned_integer :unsigned_integer_t
+ t.unsigned_bigint :unsigned_bigint_t
+ t.unsigned_float :unsigned_float_t
+ t.unsigned_decimal :unsigned_decimal_t, precision: 10, scale: 2
+ end
+
+ @connection.columns("unsigned_types").select { |c| /^unsigned_/ === c.name }.each do |column|
+ assert column.unsigned?
+ end
+ end
+
+ test "schema dump includes unsigned option" do
+ schema = dump_table_schema "unsigned_types"
+ assert_match %r{t.integer\s+"unsigned_integer",\s+limit: 4,\s+unsigned: true$}, schema
+ assert_match %r{t.integer\s+"unsigned_bigint",\s+limit: 8,\s+unsigned: true$}, schema
+ assert_match %r{t.float\s+"unsigned_float",\s+limit: 24,\s+unsigned: true$}, schema
+ assert_match %r{t.decimal\s+"unsigned_decimal",\s+precision: 10,\s+scale: 2,\s+unsigned: true$}, schema
end
end
diff --git a/activerecord/test/cases/adapters/mysql2/active_schema_test.rb b/activerecord/test/cases/adapters/mysql2/active_schema_test.rb
index 6558d60aa1..31dc69a45b 100644
--- a/activerecord/test/cases/adapters/mysql2/active_schema_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/active_schema_test.rb
@@ -100,17 +100,15 @@ class Mysql2ActiveSchemaTest < ActiveRecord::Mysql2TestCase
assert_equal "DROP TABLE `people`", drop_table(:people)
end
- if current_adapter?(:Mysql2Adapter)
- def test_create_mysql_database_with_encoding
- assert_equal "CREATE DATABASE `matt` DEFAULT CHARACTER SET `utf8`", create_database(:matt)
- assert_equal "CREATE DATABASE `aimonetti` DEFAULT CHARACTER SET `latin1`", create_database(:aimonetti, {:charset => 'latin1'})
- assert_equal "CREATE DATABASE `matt_aimonetti` DEFAULT CHARACTER SET `big5` COLLATE `big5_chinese_ci`", create_database(:matt_aimonetti, {:charset => :big5, :collation => :big5_chinese_ci})
- end
+ def test_create_mysql_database_with_encoding
+ assert_equal "CREATE DATABASE `matt` DEFAULT CHARACTER SET `utf8`", create_database(:matt)
+ assert_equal "CREATE DATABASE `aimonetti` DEFAULT CHARACTER SET `latin1`", create_database(:aimonetti, {:charset => 'latin1'})
+ assert_equal "CREATE DATABASE `matt_aimonetti` DEFAULT CHARACTER SET `big5` COLLATE `big5_chinese_ci`", create_database(:matt_aimonetti, {:charset => :big5, :collation => :big5_chinese_ci})
+ end
- def test_recreate_mysql_database_with_encoding
- create_database(:luca, {:charset => 'latin1'})
- assert_equal "CREATE DATABASE `luca` DEFAULT CHARACTER SET `latin1`", recreate_database(:luca, {:charset => 'latin1'})
- end
+ def test_recreate_mysql_database_with_encoding
+ create_database(:luca, {:charset => 'latin1'})
+ assert_equal "CREATE DATABASE `luca` DEFAULT CHARACTER SET `latin1`", recreate_database(:luca, {:charset => 'latin1'})
end
def test_add_column
diff --git a/activerecord/test/cases/adapters/mysql2/json_test.rb b/activerecord/test/cases/adapters/mysql2/json_test.rb
new file mode 100644
index 0000000000..c8c933af5e
--- /dev/null
+++ b/activerecord/test/cases/adapters/mysql2/json_test.rb
@@ -0,0 +1,172 @@
+require 'cases/helper'
+require 'support/schema_dumping_helper'
+
+if ActiveRecord::Base.connection.supports_json?
+class Mysql2JSONTest < ActiveRecord::Mysql2TestCase
+ include SchemaDumpingHelper
+ self.use_transactional_tests = false
+
+ class JsonDataType < ActiveRecord::Base
+ self.table_name = 'json_data_type'
+
+ store_accessor :settings, :resolution
+ end
+
+ def setup
+ @connection = ActiveRecord::Base.connection
+ begin
+ @connection.create_table('json_data_type') do |t|
+ t.json 'payload'
+ t.json 'settings'
+ end
+ end
+ end
+
+ def teardown
+ @connection.drop_table :json_data_type, if_exists: true
+ JsonDataType.reset_column_information
+ end
+
+ def test_column
+ column = JsonDataType.columns_hash["payload"]
+ assert_equal :json, column.type
+ assert_equal 'json', column.sql_type
+
+ type = JsonDataType.type_for_attribute("payload")
+ assert_not type.binary?
+ end
+
+ def test_change_table_supports_json
+ @connection.change_table('json_data_type') do |t|
+ t.json 'users'
+ end
+ JsonDataType.reset_column_information
+ column = JsonDataType.columns_hash['users']
+ assert_equal :json, column.type
+ end
+
+ def test_schema_dumping
+ output = dump_table_schema("json_data_type")
+ assert_match(/t\.json\s+"settings"/, output)
+ end
+
+ def test_cast_value_on_write
+ x = JsonDataType.new payload: {"string" => "foo", :symbol => :bar}
+ assert_equal({"string" => "foo", :symbol => :bar}, x.payload_before_type_cast)
+ assert_equal({"string" => "foo", "symbol" => "bar"}, x.payload)
+ x.save
+ assert_equal({"string" => "foo", "symbol" => "bar"}, x.reload.payload)
+ end
+
+ def test_type_cast_json
+ type = JsonDataType.type_for_attribute("payload")
+
+ data = "{\"a_key\":\"a_value\"}"
+ hash = type.deserialize(data)
+ assert_equal({'a_key' => 'a_value'}, hash)
+ assert_equal({'a_key' => 'a_value'}, type.deserialize(data))
+
+ assert_equal({}, type.deserialize("{}"))
+ assert_equal({'key'=>nil}, type.deserialize('{"key": null}'))
+ assert_equal({'c'=>'}','"a"'=>'b "a b'}, type.deserialize(%q({"c":"}", "\"a\"":"b \"a b"})))
+ end
+
+ def test_rewrite
+ @connection.execute "insert into json_data_type (payload) VALUES ('{\"k\":\"v\"}')"
+ x = JsonDataType.first
+ x.payload = { '"a\'' => 'b' }
+ assert x.save!
+ end
+
+ def test_select
+ @connection.execute "insert into json_data_type (payload) VALUES ('{\"k\":\"v\"}')"
+ x = JsonDataType.first
+ assert_equal({'k' => 'v'}, x.payload)
+ end
+
+ def test_select_multikey
+ @connection.execute %q|insert into json_data_type (payload) VALUES ('{"k1":"v1", "k2":"v2", "k3":[1,2,3]}')|
+ x = JsonDataType.first
+ assert_equal({'k1' => 'v1', 'k2' => 'v2', 'k3' => [1,2,3]}, x.payload)
+ end
+
+ def test_null_json
+ @connection.execute %q|insert into json_data_type (payload) VALUES(null)|
+ x = JsonDataType.first
+ assert_equal(nil, x.payload)
+ end
+
+ def test_select_array_json_value
+ @connection.execute %q|insert into json_data_type (payload) VALUES ('["v0",{"k1":"v1"}]')|
+ x = JsonDataType.first
+ assert_equal(['v0', {'k1' => 'v1'}], x.payload)
+ end
+
+ def test_rewrite_array_json_value
+ @connection.execute %q|insert into json_data_type (payload) VALUES ('["v0",{"k1":"v1"}]')|
+ x = JsonDataType.first
+ x.payload = ['v1', {'k2' => 'v2'}, 'v3']
+ assert x.save!
+ end
+
+ def test_with_store_accessors
+ x = JsonDataType.new(resolution: "320×480")
+ assert_equal "320×480", x.resolution
+
+ x.save!
+ x = JsonDataType.first
+ assert_equal "320×480", x.resolution
+
+ x.resolution = "640×1136"
+ x.save!
+
+ x = JsonDataType.first
+ assert_equal "640×1136", x.resolution
+ end
+
+ def test_duplication_with_store_accessors
+ x = JsonDataType.new(resolution: "320×480")
+ assert_equal "320×480", x.resolution
+
+ y = x.dup
+ assert_equal "320×480", y.resolution
+ end
+
+ def test_yaml_round_trip_with_store_accessors
+ x = JsonDataType.new(resolution: "320×480")
+ assert_equal "320×480", x.resolution
+
+ y = YAML.load(YAML.dump(x))
+ assert_equal "320×480", y.resolution
+ end
+
+ def test_changes_in_place
+ json = JsonDataType.new
+ assert_not json.changed?
+
+ json.payload = { 'one' => 'two' }
+ assert json.changed?
+ assert json.payload_changed?
+
+ json.save!
+ assert_not json.changed?
+
+ json.payload['three'] = 'four'
+ assert json.payload_changed?
+
+ json.save!
+ json.reload
+
+ assert_equal({ 'one' => 'two', 'three' => 'four' }, json.payload)
+ assert_not json.changed?
+ end
+
+ def test_assigning_invalid_json
+ json = JsonDataType.new
+
+ json.payload = 'foo'
+
+ assert_nil json.payload
+ end
+end
+end
diff --git a/activerecord/test/cases/adapters/mysql2/quoting_test.rb b/activerecord/test/cases/adapters/mysql2/quoting_test.rb
new file mode 100644
index 0000000000..2de7e1b526
--- /dev/null
+++ b/activerecord/test/cases/adapters/mysql2/quoting_test.rb
@@ -0,0 +1,21 @@
+require "cases/helper"
+
+class Mysql2QuotingTest < ActiveRecord::Mysql2TestCase
+ setup do
+ @connection = ActiveRecord::Base.connection
+ end
+
+ test 'quoted date precision for gte 5.6.4' do
+ @connection.stubs(:full_version).returns('5.6.4')
+ @connection.remove_instance_variable(:@version) if @connection.instance_variable_defined?(:@version)
+ t = Time.now.change(usec: 1)
+ assert_match(/\.000001\z/, @connection.quoted_date(t))
+ end
+
+ test 'quoted date precision for lt 5.6.4' do
+ @connection.stubs(:full_version).returns('5.6.3')
+ @connection.remove_instance_variable(:@version) if @connection.instance_variable_defined?(:@version)
+ t = Time.now.change(usec: 1)
+ assert_no_match(/\.000001\z/, @connection.quoted_date(t))
+ end
+end
diff --git a/activerecord/test/cases/adapters/mysql2/schema_test.rb b/activerecord/test/cases/adapters/mysql2/schema_test.rb
index 880a2123d2..1ebdca661c 100644
--- a/activerecord/test/cases/adapters/mysql2/schema_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/schema_test.rb
@@ -14,6 +14,7 @@ module ActiveRecord
@db_name = db
@omgpost = Class.new(ActiveRecord::Base) do
+ self.inheritance_column = :disabled
self.table_name = "#{db}.#{table}"
def self.name; 'Post'; end
end
@@ -36,14 +37,6 @@ module ActiveRecord
assert(!@connection.table_exists?("#{@db_name}.zomg"), "table should not exist")
end
- def test_tables_quoting
- @connection.tables(nil, "foo-bar", nil)
- flunk
- rescue => e
- # assertion for *quoted* database properly
- assert_match(/database 'foo-bar'/, e.inspect)
- end
-
def test_dump_indexes
index_a_name = 'index_key_tests_on_snack'
index_b_name = 'index_key_tests_on_pizza'
diff --git a/activerecord/test/cases/adapters/mysql2/sp_test.rb b/activerecord/test/cases/adapters/mysql2/sp_test.rb
new file mode 100644
index 0000000000..cdaa2cca44
--- /dev/null
+++ b/activerecord/test/cases/adapters/mysql2/sp_test.rb
@@ -0,0 +1,30 @@
+require "cases/helper"
+require 'models/topic'
+require 'models/reply'
+
+class Mysql2StoredProcedureTest < ActiveRecord::Mysql2TestCase
+ fixtures :topics
+
+ def setup
+ @connection = ActiveRecord::Base.connection
+ unless ActiveRecord::Base.connection.version >= '5.6.0'
+ skip("no stored procedure support")
+ end
+ end
+
+ # Test that MySQL allows multiple results for stored procedures
+ #
+ # In MySQL 5.6, CLIENT_MULTI_RESULTS is enabled by default.
+ # http://dev.mysql.com/doc/refman/5.6/en/call.html
+ def test_multi_results
+ rows = @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 'Mysql2Adapter.select_rows'"
+ end
+
+ def test_multi_results_from_find_by_sql
+ topics = Topic.find_by_sql 'CALL topics(3);'
+ assert_equal 3, topics.size
+ assert @connection.active?, "Bad connection use by 'Mysql2Adapter.select'"
+ end
+end
diff --git a/activerecord/test/cases/adapters/mysql2/sql_types_test.rb b/activerecord/test/cases/adapters/mysql2/sql_types_test.rb
index ae505d29c9..4926bc2267 100644
--- a/activerecord/test/cases/adapters/mysql2/sql_types_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/sql_types_test.rb
@@ -4,7 +4,7 @@ class Mysql2SqlTypesTest < ActiveRecord::Mysql2TestCase
def test_binary_types
assert_equal 'varbinary(64)', type_to_sql(:binary, 64)
assert_equal 'varbinary(4095)', type_to_sql(:binary, 4095)
- assert_equal 'blob(4096)', type_to_sql(:binary, 4096)
+ assert_equal 'blob', type_to_sql(:binary, 4096)
assert_equal 'blob', type_to_sql(:binary)
end
diff --git a/activerecord/test/cases/adapters/mysql2/unsigned_type_test.rb b/activerecord/test/cases/adapters/mysql2/unsigned_type_test.rb
index 9e06db2519..a6f6dd21bb 100644
--- a/activerecord/test/cases/adapters/mysql2/unsigned_type_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/unsigned_type_test.rb
@@ -1,6 +1,8 @@
require "cases/helper"
+require "support/schema_dumping_helper"
class Mysql2UnsignedTypeTest < ActiveRecord::Mysql2TestCase
+ include SchemaDumpingHelper
self.use_transactional_tests = false
class UnsignedType < ActiveRecord::Base
@@ -9,12 +11,15 @@ class Mysql2UnsignedTypeTest < ActiveRecord::Mysql2TestCase
setup do
@connection = ActiveRecord::Base.connection
@connection.create_table("unsigned_types", force: true) do |t|
- t.column :unsigned_integer, "int unsigned"
+ t.integer :unsigned_integer, unsigned: true
+ t.bigint :unsigned_bigint, unsigned: true
+ t.float :unsigned_float, unsigned: true
+ t.decimal :unsigned_decimal, unsigned: true, precision: 10, scale: 2
end
end
teardown do
- @connection.drop_table "unsigned_types"
+ @connection.drop_table "unsigned_types", if_exists: true
end
test "unsigned int max value is in range" do
@@ -26,5 +31,35 @@ class Mysql2UnsignedTypeTest < ActiveRecord::Mysql2TestCase
assert_raise(RangeError) do
UnsignedType.create(unsigned_integer: -10)
end
+ assert_raise(RangeError) do
+ UnsignedType.create(unsigned_bigint: -10)
+ end
+ assert_raise(ActiveRecord::StatementInvalid) do
+ UnsignedType.create(unsigned_float: -10.0)
+ end
+ assert_raise(ActiveRecord::StatementInvalid) do
+ UnsignedType.create(unsigned_decimal: -10.0)
+ end
+ end
+
+ test "schema definition can use unsigned as the type" do
+ @connection.change_table("unsigned_types") do |t|
+ t.unsigned_integer :unsigned_integer_t
+ t.unsigned_bigint :unsigned_bigint_t
+ t.unsigned_float :unsigned_float_t
+ t.unsigned_decimal :unsigned_decimal_t, precision: 10, scale: 2
+ end
+
+ @connection.columns("unsigned_types").select { |c| /^unsigned_/ === c.name }.each do |column|
+ assert column.unsigned?
+ end
+ end
+
+ test "schema dump includes unsigned option" do
+ schema = dump_table_schema "unsigned_types"
+ assert_match %r{t.integer\s+"unsigned_integer",\s+limit: 4,\s+unsigned: true$}, schema
+ assert_match %r{t.integer\s+"unsigned_bigint",\s+limit: 8,\s+unsigned: true$}, schema
+ assert_match %r{t.float\s+"unsigned_float",\s+limit: 24,\s+unsigned: true$}, schema
+ assert_match %r{t.decimal\s+"unsigned_decimal",\s+precision: 10,\s+scale: 2,\s+unsigned: true$}, schema
end
end
diff --git a/activerecord/test/cases/adapters/postgresql/active_schema_test.rb b/activerecord/test/cases/adapters/postgresql/active_schema_test.rb
index dc7ba314c6..24def31e36 100644
--- a/activerecord/test/cases/adapters/postgresql/active_schema_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/active_schema_test.rb
@@ -25,7 +25,7 @@ class PostgresqlActiveSchemaTest < ActiveRecord::PostgreSQLTestCase
def test_add_index
# add_index calls index_name_exists? which can't work since execute is stubbed
- ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.stubs(:index_name_exists?).returns(false)
+ ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.send(:define_method, :index_name_exists?) { |*| false }
expected = %(CREATE UNIQUE INDEX "index_people_on_last_name" ON "people" ("last_name") WHERE state = 'active')
assert_equal expected, add_index(:people, :last_name, :unique => true, :where => "state = 'active'")
@@ -49,6 +49,22 @@ class PostgresqlActiveSchemaTest < ActiveRecord::PostgreSQLTestCase
expected = %(CREATE UNIQUE INDEX "index_people_on_last_name" ON "people" USING gist ("last_name") WHERE state = 'active')
assert_equal expected, add_index(:people, :last_name, :unique => true, :where => "state = 'active'", :using => :gist)
+
+ ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.send :remove_method, :index_name_exists?
+ end
+
+ def test_remove_index
+ # remove_index calls index_name_exists? which can't work since execute is stubbed
+ ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.send(:define_method, :index_name_exists?) { |*| true }
+
+ expected = %(DROP INDEX CONCURRENTLY "index_people_on_last_name")
+ assert_equal expected, remove_index(:people, name: "index_people_on_last_name", algorithm: :concurrently)
+
+ assert_raise ArgumentError do
+ add_index(:people, :last_name, algorithm: :copy)
+ end
+
+ ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.send :remove_method, :index_name_exists?
end
private
diff --git a/activerecord/test/cases/adapters/postgresql/connection_test.rb b/activerecord/test/cases/adapters/postgresql/connection_test.rb
index 820d41e13b..722e2377c1 100644
--- a/activerecord/test/cases/adapters/postgresql/connection_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/connection_test.rb
@@ -127,7 +127,7 @@ module ActiveRecord
def test_statement_key_is_logged
bind = Relation::QueryAttribute.new(nil, 1, Type::Value.new)
- @connection.exec_query('SELECT $1::integer', 'SQL', [bind])
+ @connection.exec_query('SELECT $1::integer', 'SQL', [bind], prepare: true)
name = @subscriber.payloads.last[:statement_name]
assert name
res = @connection.exec_query("EXPLAIN (FORMAT JSON) EXECUTE #{name}(1)")
diff --git a/activerecord/test/cases/adapters/postgresql/json_test.rb b/activerecord/test/cases/adapters/postgresql/json_test.rb
index f242f32496..b3b121b4fb 100644
--- a/activerecord/test/cases/adapters/postgresql/json_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/json_test.rb
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
require "cases/helper"
require 'support/schema_dumping_helper'
diff --git a/activerecord/test/cases/adapters/postgresql/money_test.rb b/activerecord/test/cases/adapters/postgresql/money_test.rb
index e3670c203d..c031178479 100644
--- a/activerecord/test/cases/adapters/postgresql/money_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/money_test.rb
@@ -56,7 +56,7 @@ class PostgresqlMoneyTest < ActiveRecord::PostgreSQLTestCase
def test_schema_dumping
output = dump_table_schema("postgresql_moneys")
assert_match %r{t\.money\s+"wealth",\s+scale: 2$}, output
- assert_match %r{t\.money\s+"depth",\s+scale: 2,\s+default: 150\.55$}, output
+ assert_match %r{t\.money\s+"depth",\s+scale: 2,\s+default: "150\.55"$}, output
end
def test_create_and_update_money
diff --git a/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb b/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb
index 6e6850c4a9..e361521155 100644
--- a/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb
@@ -123,6 +123,20 @@ module ActiveRecord
assert_equal expect.to_i, result.rows.first.first
end
+ def test_exec_insert_default_values_with_returning_disabled_and_no_sequence_name_given
+ connection = connection_without_insert_returning
+ result = connection.exec_insert("insert into postgresql_partitioned_table_parent DEFAULT VALUES", nil, [], 'id')
+ expect = connection.query('select max(id) from postgresql_partitioned_table_parent').first.first
+ assert_equal expect.to_i, result.rows.first.first
+ end
+
+ def test_exec_insert_default_values_quoted_schema_with_returning_disabled_and_no_sequence_name_given
+ connection = connection_without_insert_returning
+ result = connection.exec_insert('insert into "public"."postgresql_partitioned_table_parent" DEFAULT VALUES', nil, [], 'id')
+ expect = connection.query('select max(id) from postgresql_partitioned_table_parent').first.first
+ assert_equal expect.to_i, result.rows.first.first
+ end
+
def test_sql_for_insert_with_returning_disabled
connection = connection_without_insert_returning
result = connection.sql_for_insert('sql', nil, nil, nil, 'binds')
diff --git a/activerecord/test/cases/adapters/postgresql/schema_authorization_test.rb b/activerecord/test/cases/adapters/postgresql/schema_authorization_test.rb
index fa6584eae5..a0afd922b2 100644
--- a/activerecord/test/cases/adapters/postgresql/schema_authorization_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/schema_authorization_test.rb
@@ -31,7 +31,7 @@ class SchemaAuthorizationTest < ActiveRecord::PostgreSQLTestCase
set_session_auth
@connection.execute "RESET search_path"
USERS.each do |u|
- @connection.execute "DROP SCHEMA #{u} CASCADE"
+ @connection.drop_schema u
@connection.execute "DROP USER #{u}"
end
end
diff --git a/activerecord/test/cases/adapters/postgresql/schema_test.rb b/activerecord/test/cases/adapters/postgresql/schema_test.rb
index 9aba2b5976..93e98ec872 100644
--- a/activerecord/test/cases/adapters/postgresql/schema_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/schema_test.rb
@@ -2,7 +2,19 @@ require "cases/helper"
require 'models/default'
require 'support/schema_dumping_helper'
+module PGSchemaHelper
+ def with_schema_search_path(schema_search_path)
+ @connection.schema_search_path = schema_search_path
+ @connection.schema_cache.clear!
+ yield if block_given?
+ ensure
+ @connection.schema_search_path = "'$user', public"
+ @connection.schema_cache.clear!
+ end
+end
+
class SchemaTest < ActiveRecord::PostgreSQLTestCase
+ include PGSchemaHelper
self.use_transactional_tests = false
SCHEMA_NAME = 'test_schema'
@@ -84,8 +96,8 @@ class SchemaTest < ActiveRecord::PostgreSQLTestCase
end
teardown do
- @connection.execute "DROP SCHEMA #{SCHEMA2_NAME} CASCADE"
- @connection.execute "DROP SCHEMA #{SCHEMA_NAME} CASCADE"
+ @connection.drop_schema SCHEMA2_NAME, if_exists: true
+ @connection.drop_schema SCHEMA_NAME, if_exists: true
end
def test_schema_names
@@ -121,10 +133,17 @@ class SchemaTest < ActiveRecord::PostgreSQLTestCase
assert !@connection.schema_names.include?("test_schema3")
end
+ def test_drop_schema_if_exists
+ @connection.create_schema "some_schema"
+ assert_includes @connection.schema_names, "some_schema"
+ @connection.drop_schema "some_schema", if_exists: true
+ assert_not_includes @connection.schema_names, "some_schema"
+ end
+
def test_habtm_table_name_with_schema
+ ActiveRecord::Base.connection.drop_schema "music", if_exists: true
+ ActiveRecord::Base.connection.create_schema "music"
ActiveRecord::Base.connection.execute <<-SQL
- DROP SCHEMA IF EXISTS music CASCADE;
- CREATE SCHEMA music;
CREATE TABLE music.albums (id serial primary key);
CREATE TABLE music.songs (id serial primary key);
CREATE TABLE music.albums_songs (album_id integer, song_id integer);
@@ -134,18 +153,22 @@ class SchemaTest < ActiveRecord::PostgreSQLTestCase
Album.create
assert_equal song, Song.includes(:albums).references(:albums).first
ensure
- ActiveRecord::Base.connection.execute "DROP SCHEMA music CASCADE;"
+ ActiveRecord::Base.connection.drop_schema "music", if_exists: true
end
- def test_raise_drop_schema_with_nonexisting_schema
+ def test_drop_schema_with_nonexisting_schema
assert_raises(ActiveRecord::StatementInvalid) do
- @connection.drop_schema "test_schema3"
+ @connection.drop_schema "idontexist"
+ end
+
+ assert_nothing_raised do
+ @connection.drop_schema "idontexist", if_exists: true
end
end
def test_raise_wraped_exception_on_bad_prepare
assert_raises(ActiveRecord::StatementInvalid) do
- @connection.exec_query "select * from developers where id = ?", 'sql', [[nil, 1]]
+ @connection.exec_query "select * from developers where id = ?", 'sql', [bind_param(1)]
end
end
@@ -300,11 +323,11 @@ class SchemaTest < ActiveRecord::PostgreSQLTestCase
def test_with_uppercase_index_name
@connection.execute "CREATE INDEX \"things_Index\" ON #{SCHEMA_NAME}.things (name)"
- assert_nothing_raised { @connection.remove_index! "things", "#{SCHEMA_NAME}.things_Index"}
+ assert_nothing_raised { @connection.remove_index "things", name: "#{SCHEMA_NAME}.things_Index"}
@connection.execute "CREATE INDEX \"things_Index\" ON #{SCHEMA_NAME}.things (name)"
with_schema_search_path SCHEMA_NAME do
- assert_nothing_raised { @connection.remove_index! "things", "things_Index"}
+ assert_nothing_raised { @connection.remove_index "things", name: "things_Index"}
end
end
@@ -404,13 +427,6 @@ class SchemaTest < ActiveRecord::PostgreSQLTestCase
end
end
- def with_schema_search_path(schema_search_path)
- @connection.schema_search_path = schema_search_path
- yield if block_given?
- ensure
- @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, third_index_column_name, fourth_index_column_name)
with_schema_search_path(this_schema_name) do
indexes = @connection.indexes(TABLE_NAME).sort_by(&:name)
@@ -462,14 +478,14 @@ class SchemaForeignKeyTest < ActiveRecord::PostgreSQLTestCase
ensure
@connection.drop_table "wagons", if_exists: true
@connection.drop_table "my_schema.trains", if_exists: true
- @connection.execute "DROP SCHEMA IF EXISTS my_schema"
+ @connection.drop_schema "my_schema", if_exists: true
end
end
class DefaultsUsingMultipleSchemasAndDomainTest < ActiveRecord::PostgreSQLTestCase
setup do
@connection = ActiveRecord::Base.connection
- @connection.execute "DROP SCHEMA IF EXISTS schema_1 CASCADE"
+ @connection.drop_schema "schema_1", if_exists: true
@connection.execute "CREATE SCHEMA schema_1"
@connection.execute "CREATE DOMAIN schema_1.text AS text"
@connection.execute "CREATE DOMAIN schema_1.varchar AS varchar"
@@ -480,13 +496,14 @@ class DefaultsUsingMultipleSchemasAndDomainTest < ActiveRecord::PostgreSQLTestCa
@connection.create_table "defaults" do |t|
t.text "text_col", default: "some value"
t.string "string_col", default: "some value"
+ t.decimal "decimal_col", default: "3.14159265358979323846"
end
Default.reset_column_information
end
teardown do
@connection.schema_search_path = @old_search_path
- @connection.execute "DROP SCHEMA IF EXISTS schema_1 CASCADE"
+ @connection.drop_schema "schema_1", if_exists: true
Default.reset_column_information
end
@@ -498,6 +515,10 @@ class DefaultsUsingMultipleSchemasAndDomainTest < ActiveRecord::PostgreSQLTestCa
assert_equal "some value", Default.new.string_col, "Default of string column was not correctly parsed"
end
+ def test_decimal_defaults_in_new_schema_when_overriding_domain
+ assert_equal BigDecimal.new("3.14159265358979323846"), Default.new.decimal_col, "Default of decimal column was not correctly parsed"
+ end
+
def test_bpchar_defaults_in_new_schema_when_overriding_domain
@connection.execute "ALTER TABLE defaults ADD bpchar_col bpchar DEFAULT 'some value'"
Default.reset_column_information
@@ -514,3 +535,40 @@ class DefaultsUsingMultipleSchemasAndDomainTest < ActiveRecord::PostgreSQLTestCa
assert_equal "foo'::bar", Default.new.string_col
end
end
+
+class SchemaWithDotsTest < ActiveRecord::PostgreSQLTestCase
+ include PGSchemaHelper
+ self.use_transactional_tests = false
+
+ setup do
+ @connection = ActiveRecord::Base.connection
+ @connection.create_schema "my.schema"
+ end
+
+ teardown do
+ @connection.drop_schema "my.schema", if_exists: true
+ end
+
+ test "rename_table" do
+ with_schema_search_path('"my.schema"') do
+ @connection.create_table :posts
+ @connection.rename_table :posts, :articles
+ assert_equal ["articles"], @connection.tables
+ end
+ end
+
+ test "Active Record basics" do
+ with_schema_search_path('"my.schema"') do
+ @connection.create_table :articles do |t|
+ t.string :title
+ end
+ article_class = Class.new(ActiveRecord::Base) do
+ self.table_name = '"my.schema".articles'
+ end
+
+ article_class.create!(title: "zOMG, welcome to my blorgh!")
+ welcome_article = article_class.last
+ assert_equal "zOMG, welcome to my blorgh!", welcome_article.title
+ end
+ end
+end
diff --git a/activerecord/test/cases/adapters/postgresql/view_test.rb b/activerecord/test/cases/adapters/postgresql/view_test.rb
deleted file mode 100644
index 2dd6ec5fe6..0000000000
--- a/activerecord/test/cases/adapters/postgresql/view_test.rb
+++ /dev/null
@@ -1,64 +0,0 @@
-require "cases/helper"
-require "cases/view_test"
-
-class UpdateableViewTest < ActiveRecord::PostgreSQLTestCase
- fixtures :books
-
- class PrintedBook < ActiveRecord::Base
- self.primary_key = "id"
- end
-
- setup do
- @connection = ActiveRecord::Base.connection
- @connection.execute <<-SQL
- CREATE VIEW printed_books
- AS SELECT id, name, status, format FROM books WHERE format = 'paperback'
- SQL
- end
-
- teardown do
- @connection.execute "DROP VIEW printed_books" if @connection.table_exists? "printed_books"
- end
-
- def test_update_record
- book = PrintedBook.first
- book.name = "AWDwR"
- book.save!
- book.reload
- assert_equal "AWDwR", book.name
- end
-
- def test_insert_record
- PrintedBook.create! name: "Rails in Action", status: 0, format: "paperback"
-
- new_book = PrintedBook.last
- assert_equal "Rails in Action", new_book.name
- end
-
- def test_update_record_to_fail_view_conditions
- book = PrintedBook.first
- book.format = "ebook"
- book.save!
-
- assert_raises ActiveRecord::RecordNotFound do
- book.reload
- end
- end
-end
-
-if ActiveRecord::Base.connection.respond_to?(:supports_materialized_views?) &&
- ActiveRecord::Base.connection.supports_materialized_views?
-class MaterializedViewTest < ActiveRecord::PostgreSQLTestCase
- include ViewBehavior
-
- private
- def create_view(name, query)
- @connection.execute "CREATE MATERIALIZED VIEW #{name} AS #{query}"
- end
-
- def drop_view(name)
- @connection.execute "DROP MATERIALIZED VIEW #{name}" if @connection.table_exists? name
-
- end
-end
-end
diff --git a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb
index 7996e7ad50..640df31e2e 100644
--- a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb
+++ b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb
@@ -294,7 +294,7 @@ module ActiveRecord
def test_tables_logs_name
sql = <<-SQL
SELECT name FROM sqlite_master
- WHERE (type = 'table' OR type = 'view') AND NOT name = 'sqlite_sequence'
+ WHERE type IN ('table','view') AND name <> 'sqlite_sequence'
SQL
assert_logged [[sql.squish, 'SCHEMA', []]] do
@conn.tables('hello')
@@ -313,8 +313,7 @@ module ActiveRecord
with_example_table do
sql = <<-SQL
SELECT name FROM sqlite_master
- WHERE (type = 'table' OR type = 'view')
- AND NOT name = 'sqlite_sequence' AND name = \"ex\"
+ WHERE type IN ('table','view') AND name <> 'sqlite_sequence' AND name = 'ex'
SQL
assert_logged [[sql.squish, 'SCHEMA', []]] do
assert @conn.table_exists?('ex')
@@ -425,13 +424,16 @@ module ActiveRecord
configurations['arunit']['database'])
statement = ::SQLite3::Statement.new(db,
'CREATE TABLE statement_test (number integer not null)')
- statement.stubs(:step).raises(::SQLite3::BusyException, 'busy')
- statement.stubs(:columns).once.returns([])
- statement.expects(:close).once
- ::SQLite3::Statement.stubs(:new).returns(statement)
-
- assert_raises ActiveRecord::StatementInvalid do
- @conn.exec_query 'select * from statement_test'
+ statement.stub(:step, ->{ raise ::SQLite3::BusyException.new('busy') }) do
+ assert_called(statement, :columns, returns: []) do
+ assert_called(statement, :close) do
+ ::SQLite3::Statement.stub(:new, statement) do
+ assert_raises ActiveRecord::StatementInvalid do
+ @conn.exec_query 'select * from statement_test'
+ end
+ end
+ end
+ end
end
end
diff --git a/activerecord/test/cases/adapters/sqlite3/statement_pool_test.rb b/activerecord/test/cases/adapters/sqlite3/statement_pool_test.rb
index ef324183a7..559b951109 100644
--- a/activerecord/test/cases/adapters/sqlite3/statement_pool_test.rb
+++ b/activerecord/test/cases/adapters/sqlite3/statement_pool_test.rb
@@ -6,7 +6,7 @@ module ActiveRecord::ConnectionAdapters
if Process.respond_to?(:fork)
def test_cache_is_per_pid
- cache = StatementPool.new nil, 10
+ cache = StatementPool.new(10)
cache['foo'] = 'bar'
assert_equal 'bar', cache['foo']
diff --git a/activerecord/test/cases/associations/belongs_to_associations_test.rb b/activerecord/test/cases/associations/belongs_to_associations_test.rb
index ebe08c618f..938350627f 100644
--- a/activerecord/test/cases/associations/belongs_to_associations_test.rb
+++ b/activerecord/test/cases/associations/belongs_to_associations_test.rb
@@ -1,6 +1,5 @@
require 'cases/helper'
require 'models/developer'
-require 'models/computer'
require 'models/project'
require 'models/company'
require 'models/topic'
@@ -21,6 +20,9 @@ require 'models/column'
require 'models/record'
require 'models/admin'
require 'models/admin/user'
+require 'models/ship'
+require 'models/treasure'
+require 'models/parrot'
class BelongsToAssociationsTest < ActiveRecord::TestCase
fixtures :accounts, :companies, :developers, :projects, :topics,
@@ -91,7 +93,7 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
end
account = model.new
- refute account.valid?
+ assert_not account.valid?
assert_equal [{error: :blank}], account.errors.details[:company]
ensure
ActiveRecord::Base.belongs_to_required_by_default = original_value
@@ -108,7 +110,7 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
end
account = model.new
- refute account.valid?
+ assert_not account.valid?
assert_equal [{error: :blank}], account.errors.details[:company]
ensure
ActiveRecord::Base.belongs_to_required_by_default = original_value
@@ -293,7 +295,8 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
client = Client.find(3)
client.firm = nil
client.save
- assert_nil client.firm(true)
+ client.association(:firm).reload
+ assert_nil client.firm
assert_nil client.client_of
end
@@ -301,7 +304,8 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
client = Client.create(:name => "Primary key client", :firm_name => companies(:first_firm).name)
client.firm_with_primary_key = nil
client.save
- assert_nil client.firm_with_primary_key(true)
+ client.association(:firm_with_primary_key).reload
+ assert_nil client.firm_with_primary_key
assert_nil client.client_of
end
@@ -318,11 +322,13 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
def test_polymorphic_association_class
sponsor = Sponsor.new
assert_nil sponsor.association(:sponsorable).send(:klass)
- assert_nil sponsor.sponsorable(force_reload: true)
+ sponsor.association(:sponsorable).reload
+ assert_nil sponsor.sponsorable
sponsor.sponsorable_type = '' # the column doesn't have to be declared NOT NULL
assert_nil sponsor.association(:sponsorable).send(:klass)
- assert_nil sponsor.sponsorable(force_reload: true)
+ sponsor.association(:sponsorable).reload
+ assert_nil sponsor.sponsorable
sponsor.sponsorable = Member.new :name => "Bert"
assert_equal Member, sponsor.association(:sponsorable).send(:klass)
@@ -343,6 +349,22 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
assert_equal 1, Company.all.merge!(:includes => :firm_with_select ).find(2).firm_with_select.attributes.size
end
+ def test_belongs_to_without_counter_cache_option
+ # Ship has a conventionally named `treasures_count` column, but the counter_cache
+ # option is not given on the association.
+ ship = Ship.create(name: 'Countless')
+
+ assert_no_difference lambda { ship.reload.treasures_count }, "treasures_count should not be changed unless counter_cache is given on the relation" do
+ treasure = Treasure.new(name: 'Gold', ship: ship)
+ treasure.save
+ end
+
+ assert_no_difference lambda { ship.reload.treasures_count }, "treasures_count should not be changed unless counter_cache is given on the relation" do
+ treasure = ship.treasures.first
+ treasure.destroy
+ end
+ end
+
def test_belongs_to_counter
debate = Topic.create("title" => "debate")
assert_equal 0, debate.read_attribute("replies_count"), "No replies yet"
@@ -478,7 +500,9 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
line_item = LineItem.create!
invoice = Invoice.create!(line_items: [line_item])
initial = invoice.updated_at
- line_item.touch
+ travel(1.second) do
+ line_item.touch
+ end
assert_not_equal initial, invoice.reload.updated_at
end
@@ -557,7 +581,8 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
assert final_cut.persisted?
assert firm.persisted?
assert_equal firm, final_cut.firm
- assert_equal firm, final_cut.firm(true)
+ final_cut.association(:firm).reload
+ assert_equal firm, final_cut.firm
end
def test_assignment_before_child_saved_with_primary_key
@@ -569,7 +594,8 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
assert final_cut.persisted?
assert firm.persisted?
assert_equal firm, final_cut.firm_with_primary_key
- assert_equal firm, final_cut.firm_with_primary_key(true)
+ final_cut.association(:firm_with_primary_key).reload
+ assert_equal firm, final_cut.firm_with_primary_key
end
def test_new_record_with_foreign_key_but_no_object
@@ -1064,6 +1090,12 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
Column.create! record: record
assert_equal 1, Column.count
end
+
+ def test_association_force_reload_with_only_true_is_deprecated
+ client = Client.find(3)
+
+ assert_deprecated { client.firm(true) }
+ end
end
class BelongsToWithForeignKeyTest < ActiveRecord::TestCase
diff --git a/activerecord/test/cases/associations/bidirectional_destroy_dependencies_test.rb b/activerecord/test/cases/associations/bidirectional_destroy_dependencies_test.rb
new file mode 100644
index 0000000000..2b867965ba
--- /dev/null
+++ b/activerecord/test/cases/associations/bidirectional_destroy_dependencies_test.rb
@@ -0,0 +1,41 @@
+require 'cases/helper'
+require 'models/content'
+
+class BidirectionalDestroyDependenciesTest < ActiveRecord::TestCase
+ fixtures :content, :content_positions
+
+ def setup
+ Content.destroyed_ids.clear
+ ContentPosition.destroyed_ids.clear
+ end
+
+ def test_bidirectional_dependence_when_destroying_item_with_belongs_to_association
+ content_position = ContentPosition.find(1)
+ content = content_position.content
+ assert_not_nil content
+
+ content_position.destroy
+
+ assert_equal [content_position.id], ContentPosition.destroyed_ids
+ assert_equal [content.id], Content.destroyed_ids
+ end
+
+ def test_bidirectional_dependence_when_destroying_item_with_has_one_association
+ content = Content.find(1)
+ content_position = content.content_position
+ assert_not_nil content_position
+
+ content.destroy
+
+ assert_equal [content.id], Content.destroyed_ids
+ assert_equal [content_position.id], ContentPosition.destroyed_ids
+ end
+
+ def test_bidirectional_dependence_when_destroying_item_with_has_one_association_fails_first_time
+ content = ContentWhichRequiresTwoDestroyCalls.find(1)
+
+ 2.times { content.destroy }
+
+ assert_equal content.destroyed?, true
+ end
+end
diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb
index ffbf60e390..0c09713971 100644
--- a/activerecord/test/cases/associations/eager_test.rb
+++ b/activerecord/test/cases/associations/eager_test.rb
@@ -24,6 +24,8 @@ require 'models/membership'
require 'models/club'
require 'models/categorization'
require 'models/sponsor'
+require 'models/mentor'
+require 'models/contract'
class EagerAssociationTest < ActiveRecord::TestCase
fixtures :posts, :comments, :authors, :essays, :author_addresses, :categories, :categories_posts,
@@ -108,53 +110,57 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
def test_preloading_has_many_in_multiple_queries_with_more_ids_than_database_can_handle
- Comment.connection.expects(:in_clause_length).at_least_once.returns(5)
- posts = Post.all.merge!(:includes=>:comments).to_a
- assert_equal 11, posts.size
+ assert_called(Comment.connection, :in_clause_length, returns: 5) do
+ posts = Post.all.merge!(:includes=>:comments).to_a
+ assert_equal 11, posts.size
+ end
end
def test_preloading_has_many_in_one_queries_when_database_has_no_limit_on_ids_it_can_handle
- Comment.connection.expects(:in_clause_length).at_least_once.returns(nil)
- posts = Post.all.merge!(:includes=>:comments).to_a
- assert_equal 11, posts.size
+ assert_called(Comment.connection, :in_clause_length, returns: nil) do
+ posts = Post.all.merge!(:includes=>:comments).to_a
+ assert_equal 11, posts.size
+ end
end
def test_preloading_habtm_in_multiple_queries_with_more_ids_than_database_can_handle
- Comment.connection.expects(:in_clause_length).at_least_once.returns(5)
- posts = Post.all.merge!(:includes=>:categories).to_a
- assert_equal 11, posts.size
+ assert_called(Comment.connection, :in_clause_length, times: 2, returns: 5) do
+ posts = Post.all.merge!(:includes=>:categories).to_a
+ assert_equal 11, posts.size
+ end
end
def test_preloading_habtm_in_one_queries_when_database_has_no_limit_on_ids_it_can_handle
- Comment.connection.expects(:in_clause_length).at_least_once.returns(nil)
- posts = Post.all.merge!(:includes=>:categories).to_a
- assert_equal 11, posts.size
+ assert_called(Comment.connection, :in_clause_length, times: 2, returns: nil) do
+ posts = Post.all.merge!(:includes=>:categories).to_a
+ assert_equal 11, posts.size
+ end
end
def test_load_associated_records_in_one_query_when_adapter_has_no_limit
- Comment.connection.expects(:in_clause_length).at_least_once.returns(nil)
-
- post = posts(:welcome)
- assert_queries(2) do
- Post.includes(:comments).where(:id => post.id).to_a
+ assert_called(Comment.connection, :in_clause_length, returns: nil) do
+ post = posts(:welcome)
+ assert_queries(2) do
+ Post.includes(:comments).where(:id => post.id).to_a
+ end
end
end
def test_load_associated_records_in_several_queries_when_many_ids_passed
- Comment.connection.expects(:in_clause_length).at_least_once.returns(1)
-
- post1, post2 = posts(:welcome), posts(:thinking)
- assert_queries(3) do
- Post.includes(:comments).where(:id => [post1.id, post2.id]).to_a
+ assert_called(Comment.connection, :in_clause_length, returns: 1) do
+ post1, post2 = posts(:welcome), posts(:thinking)
+ assert_queries(3) do
+ Post.includes(:comments).where(:id => [post1.id, post2.id]).to_a
+ end
end
end
def test_load_associated_records_in_one_query_when_a_few_ids_passed
- Comment.connection.expects(:in_clause_length).at_least_once.returns(3)
-
- post = posts(:welcome)
- assert_queries(2) do
- Post.includes(:comments).where(:id => post.id).to_a
+ assert_called(Comment.connection, :in_clause_length, returns: 3) do
+ post = posts(:welcome)
+ assert_queries(2) do
+ Post.includes(:comments).where(:id => post.id).to_a
+ end
end
end
@@ -1173,6 +1179,24 @@ class EagerAssociationTest < ActiveRecord::TestCase
assert_equal 1, mary.unique_categorized_post_ids.length
end
+ def test_preloading_has_one_using_reorder
+ klass = Class.new(ActiveRecord::Base) do
+ def self.name; "TempAuthor"; end
+ self.table_name = "authors"
+ has_one :post, class_name: "PostWithDefaultScope", foreign_key: :author_id
+ has_one :reorderd_post, -> { reorder(title: :desc) }, class_name: "PostWithDefaultScope", foreign_key: :author_id
+ end
+
+ author = klass.first
+ # PRECONDITION: make sure ordering results in different results
+ assert_not_equal author.post, author.reorderd_post
+
+ preloaded_reorderd_post = klass.preload(:reorderd_post).first.reorderd_post
+
+ assert_equal author.reorderd_post, preloaded_reorderd_post
+ assert_equal Post.order(title: :desc).first.title, preloaded_reorderd_post.title
+ end
+
def test_preloading_polymorphic_with_custom_foreign_type
sponsor = sponsors(:moustache_club_sponsor_for_groucho)
groucho = members(:groucho)
@@ -1197,12 +1221,6 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
end
- def test_join_eager_with_nil_order_should_generate_valid_sql
- assert_nothing_raised(ActiveRecord::StatementInvalid) do
- Post.includes(:comments).order(nil).where(:comments => {:body => "Thank you for the welcome"}).first
- end
- end
-
def test_deep_including_through_habtm
# warm up habtm cache
posts = Post.all.merge!(:includes => {:categories => :categorizations}, :order => "posts.id").to_a
@@ -1214,6 +1232,16 @@ class EagerAssociationTest < ActiveRecord::TestCase
assert_no_queries { assert_equal 2, posts[1].categories[0].categorizations.length }
end
+ def test_eager_load_multiple_associations_with_references
+ mentor = Mentor.create!(name: "Barış Can DAYLIK")
+ developer = Developer.create!(name: "Mehmet Emin İNAÇ", mentor: mentor)
+ Contract.create!(developer: developer)
+ project = Project.create!(name: "VNGRS", mentor: mentor)
+ project.developers << developer
+ projects = Project.references(:mentors).includes(mentor: { developers: :contracts }, developers: :contracts)
+ assert_equal projects.last.mentor.developers.first.contracts, projects.last.developers.last.contracts
+ end
+
test "scoping with a circular preload" do
assert_equal Comment.find(1), Comment.preload(:post => :comments).scoping { Comment.find(1) }
end
@@ -1325,6 +1353,14 @@ class EagerAssociationTest < ActiveRecord::TestCase
assert_match message, error.message
end
+ test "preload with invalid argument" do
+ exception = assert_raises(ArgumentError) do
+ Author.preload(10).to_a
+ end
+ assert_equal('10 was not recognized for preload', exception.message)
+ end
+
+
test "preloading readonly association" do
# has-one
firm = Firm.where(id: "1").preload(:readonly_account).first!
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 e584c94ad8..ccb062f991 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
@@ -3,6 +3,7 @@ require 'models/developer'
require 'models/computer'
require 'models/project'
require 'models/company'
+require 'models/course'
require 'models/customer'
require 'models/order'
require 'models/categorization'
@@ -14,6 +15,7 @@ require 'models/tagging'
require 'models/parrot'
require 'models/person'
require 'models/pirate'
+require 'models/professor'
require 'models/treasure'
require 'models/price_estimate'
require 'models/club'
@@ -93,6 +95,15 @@ class DeveloperWithExtendOption < Developer
has_and_belongs_to_many :projects, extend: NamedExtension
end
+class ProjectUnscopingDavidDefaultScope < ActiveRecord::Base
+ self.table_name = 'projects'
+ has_and_belongs_to_many :developers, -> { unscope(where: 'name') },
+ class_name: "LazyBlockDeveloperCalledDavid",
+ join_table: "developers_projects",
+ foreign_key: "project_id",
+ association_foreign_key: "developer_id"
+end
+
class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
fixtures :accounts, :companies, :categories, :posts, :categories_posts, :developers, :projects, :developers_projects,
:parrots, :pirates, :parrots_pirates, :treasures, :price_estimates, :tags, :taggings, :computers
@@ -157,8 +168,8 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
jamis.projects << action_controller
assert_equal 2, jamis.projects.size
- assert_equal 2, jamis.projects(true).size
- assert_equal 2, action_controller.developers(true).size
+ assert_equal 2, jamis.projects.reload.size
+ assert_equal 2, action_controller.developers.reload.size
end
def test_adding_type_mismatch
@@ -176,9 +187,9 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
action_controller.developers << jamis
- assert_equal 2, jamis.projects(true).size
+ assert_equal 2, jamis.projects.reload.size
assert_equal 2, action_controller.developers.size
- assert_equal 2, action_controller.developers(true).size
+ assert_equal 2, action_controller.developers.reload.size
end
def test_adding_from_the_project_fixed_timestamp
@@ -192,9 +203,9 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
action_controller.developers << jamis
assert_equal updated_at, jamis.updated_at
- assert_equal 2, jamis.projects(true).size
+ assert_equal 2, jamis.projects.reload.size
assert_equal 2, action_controller.developers.size
- assert_equal 2, action_controller.developers(true).size
+ assert_equal 2, action_controller.developers.reload.size
end
def test_adding_multiple
@@ -203,7 +214,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
aredridel.projects.reload
aredridel.projects.push(Project.find(1), Project.find(2))
assert_equal 2, aredridel.projects.size
- assert_equal 2, aredridel.projects(true).size
+ assert_equal 2, aredridel.projects.reload.size
end
def test_adding_a_collection
@@ -212,7 +223,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
aredridel.projects.reload
aredridel.projects.concat([Project.find(1), Project.find(2)])
assert_equal 2, aredridel.projects.size
- assert_equal 2, aredridel.projects(true).size
+ assert_equal 2, aredridel.projects.reload.size
end
def test_habtm_adding_before_save
@@ -227,7 +238,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
assert_equal no_of_devels+1, Developer.count
assert_equal no_of_projects+1, Project.count
assert_equal 2, aredridel.projects.size
- assert_equal 2, aredridel.projects(true).size
+ assert_equal 2, aredridel.projects.reload.size
end
def test_habtm_saving_multiple_relationships
@@ -391,8 +402,8 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
david.projects.delete(active_record)
assert_equal 1, david.projects.size
- assert_equal 1, david.projects(true).size
- assert_equal 2, active_record.developers(true).size
+ assert_equal 1, david.projects.reload.size
+ assert_equal 2, active_record.developers.reload.size
end
def test_deleting_array
@@ -400,7 +411,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
david.projects.reload
david.projects.delete(Project.all.to_a)
assert_equal 0, david.projects.size
- assert_equal 0, david.projects(true).size
+ assert_equal 0, david.projects.reload.size
end
def test_deleting_all
@@ -408,7 +419,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
david.projects.reload
david.projects.clear
assert_equal 0, david.projects.size
- assert_equal 0, david.projects(true).size
+ assert_equal 0, david.projects.reload.size
end
def test_removing_associations_on_destroy
@@ -434,7 +445,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
assert join_records.empty?
assert_equal 1, david.reload.projects.size
- assert_equal 1, david.projects(true).size
+ assert_equal 1, david.projects.reload.size
end
def test_destroying_many
@@ -450,7 +461,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
assert join_records.empty?
assert_equal 0, david.reload.projects.size
- assert_equal 0, david.projects(true).size
+ assert_equal 0, david.projects.reload.size
end
def test_destroy_all
@@ -466,7 +477,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
assert join_records.empty?
assert david.projects.empty?
- assert david.projects(true).empty?
+ assert david.projects.reload.empty?
end
def test_destroy_associations_destroys_multiple_associations
@@ -482,11 +493,11 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
join_records = Parrot.connection.select_all("SELECT * FROM parrots_pirates WHERE parrot_id = #{george.id}")
assert join_records.empty?
- assert george.pirates(true).empty?
+ assert george.pirates.reload.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?
+ assert george.treasures.reload.empty?
end
def test_associations_with_conditions
@@ -654,7 +665,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
end
def test_habtm_respects_select
- categories(:technology).select_testing_posts(true).each do |o|
+ categories(:technology).select_testing_posts.reload.each do |o|
assert_respond_to o, :correctness_marker
end
assert_respond_to categories(:technology).select_testing_posts.first, :correctness_marker
@@ -726,7 +737,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
def test_get_ids_for_loaded_associations
developer = developers(:david)
- developer.projects(true)
+ developer.projects.reload
assert_queries(0) do
developer.project_ids
developer.project_ids
@@ -794,9 +805,10 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
end
def test_association_proxy_transaction_method_starts_transaction_in_association_class
- Post.expects(:transaction)
- Category.first.posts.transaction do
- # nothing
+ assert_called(Post, :transaction) do
+ Category.first.posts.transaction do
+ # nothing
+ end
end
end
@@ -917,4 +929,57 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
DeveloperWithSymbolClassName.new
end
end
+
+ def test_association_force_reload_with_only_true_is_deprecated
+ developer = Developer.find(1)
+
+ assert_deprecated { developer.projects(true) }
+ end
+
+ def test_alternate_database
+ professor = Professor.create(name: "Plum")
+ course = Course.create(name: "Forensics")
+ assert_equal 0, professor.courses.count
+ assert_nothing_raised do
+ professor.courses << course
+ end
+ assert_equal 1, professor.courses.count
+ end
+
+ def test_habtm_scope_can_unscope
+ project = ProjectUnscopingDavidDefaultScope.new
+ project.save!
+
+ developer = LazyBlockDeveloperCalledDavid.new(name: "Not David")
+ developer.save!
+ project.developers << developer
+
+ projects = ProjectUnscopingDavidDefaultScope.includes(:developers).where(id: project.id)
+ assert_equal 1, projects.first.developers.size
+ end
+
+ def test_preloaded_associations_size
+ assert_equal Project.first.salaried_developers.size,
+ Project.preload(:salaried_developers).first.salaried_developers.size
+
+ assert_equal Project.includes(:salaried_developers).references(:salaried_developers).first.salaried_developers.size,
+ Project.preload(:salaried_developers).first.salaried_developers.size
+
+ # Nested HATBM
+ first_project = Developer.first.projects.first
+ preloaded_first_project =
+ Developer.preload(projects: :salaried_developers).
+ first.
+ projects.
+ detect { |p| p.id == first_project.id }
+
+ assert preloaded_first_project.salaried_developers.loaded?, true
+ assert_equal first_project.salaried_developers.size, preloaded_first_project.salaried_developers.size
+ end
+
+ def test_has_and_belongs_to_many_is_useable_with_belongs_to_required_by_default
+ assert_difference "Project.first.developers_required_by_default.size", 1 do
+ Project.first.developers_required_by_default.create!(name: "Sean", salary: 50000)
+ end
+ end
end
diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb
index 2c4e2a875c..eb94870a35 100644
--- a/activerecord/test/cases/associations/has_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_associations_test.rb
@@ -31,6 +31,8 @@ require 'models/student'
require 'models/pirate'
require 'models/ship'
require 'models/ship_part'
+require 'models/treasure'
+require 'models/parrot'
require 'models/tyre'
require 'models/subscriber'
require 'models/subscription'
@@ -168,7 +170,9 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
part = ShipPart.create(name: 'cockpit')
updated_at = part.updated_at
- ship.parts << part
+ travel(1.second) do
+ ship.parts << part
+ end
assert_equal part.ship, ship
assert_not_equal part.updated_at, updated_at
@@ -704,7 +708,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
natural = Client.new("name" => "Natural Company")
companies(:first_firm).clients_of_firm << natural
assert_equal 3, companies(:first_firm).clients_of_firm.size # checking via the collection
- assert_equal 3, companies(:first_firm).clients_of_firm(true).size # checking using the db
+ assert_equal 3, companies(:first_firm).clients_of_firm.reload.size # checking using the db
assert_equal natural, companies(:first_firm).clients_of_firm.last
end
@@ -759,7 +763,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
force_signal37_to_load_all_clients_of_firm
companies(:first_firm).clients_of_firm.concat([Client.new("name" => "Natural Company"), Client.new("name" => "Apple")])
assert_equal 4, companies(:first_firm).clients_of_firm.size
- assert_equal 4, companies(:first_firm).clients_of_firm(true).size
+ assert_equal 4, companies(:first_firm).clients_of_firm.reload.size
end
def test_transactions_when_adding_to_persisted
@@ -771,7 +775,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
rescue Client::RaisedOnSave
end
- assert !companies(:first_firm).clients_of_firm(true).include?(good)
+ assert !companies(:first_firm).clients_of_firm.reload.include?(good)
end
def test_transactions_when_adding_to_new_record
@@ -903,12 +907,12 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
new_client = companies(:first_firm).clients_of_firm.create("name" => "Another Client")
assert new_client.persisted?
assert_equal new_client, companies(:first_firm).clients_of_firm.last
- assert_equal new_client, companies(:first_firm).clients_of_firm(true).last
+ assert_equal new_client, companies(:first_firm).clients_of_firm.reload.last
end
def test_create_many
companies(:first_firm).clients_of_firm.create([{"name" => "Another Client"}, {"name" => "Another Client II"}])
- assert_equal 4, companies(:first_firm).clients_of_firm(true).size
+ assert_equal 4, companies(:first_firm).clients_of_firm.reload.size
end
def test_create_followed_by_save_does_not_load_target
@@ -921,7 +925,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
force_signal37_to_load_all_clients_of_firm
companies(:first_firm).clients_of_firm.delete(companies(:first_firm).clients_of_firm.first)
assert_equal 1, companies(:first_firm).clients_of_firm.size
- assert_equal 1, companies(:first_firm).clients_of_firm(true).size
+ assert_equal 1, companies(:first_firm).clients_of_firm.reload.size
end
def test_deleting_before_save
@@ -932,6 +936,25 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_equal 0, new_firm.clients_of_firm.size
end
+ def test_has_many_without_counter_cache_option
+ # Ship has a conventionally named `treasures_count` column, but the counter_cache
+ # option is not given on the association.
+ ship = Ship.create(name: 'Countless', treasures_count: 10)
+
+ assert_not Ship.reflect_on_association(:treasures).has_cached_counter?
+
+ # Count should come from sql count() of treasures rather than treasures_count attribute
+ assert_equal ship.treasures.size, 0
+
+ assert_no_difference lambda { ship.reload.treasures_count }, "treasures_count should not be changed" do
+ ship.treasures.create(name: 'Gold')
+ end
+
+ assert_no_difference lambda { ship.reload.treasures_count }, "treasures_count should not be changed" do
+ ship.treasures.destroy_all
+ end
+ end
+
def test_deleting_updates_counter_cache
topic = Topic.order("id ASC").first
assert_equal topic.replies.to_a.size, topic.replies_count
@@ -1058,7 +1081,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_equal 3, companies(:first_firm).clients_of_firm.size
companies(:first_firm).clients_of_firm.delete([companies(:first_firm).clients_of_firm[0], companies(:first_firm).clients_of_firm[1], companies(:first_firm).clients_of_firm[2]])
assert_equal 0, companies(:first_firm).clients_of_firm.size
- assert_equal 0, companies(:first_firm).clients_of_firm(true).size
+ assert_equal 0, companies(:first_firm).clients_of_firm.reload.size
end
def test_delete_all
@@ -1079,7 +1102,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
companies(:first_firm).clients_of_firm.reset
companies(:first_firm).clients_of_firm.delete_all
assert_equal 0, companies(:first_firm).clients_of_firm.size
- assert_equal 0, companies(:first_firm).clients_of_firm(true).size
+ assert_equal 0, companies(:first_firm).clients_of_firm.reload.size
end
def test_transaction_when_deleting_persisted
@@ -1093,7 +1116,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
rescue Client::RaisedOnDestroy
end
- assert_equal [good, bad], companies(:first_firm).clients_of_firm(true)
+ assert_equal [good, bad], companies(:first_firm).clients_of_firm.reload
end
def test_transaction_when_deleting_new_record
@@ -1113,7 +1136,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
firm.clients_of_firm.clear
assert_equal 0, firm.clients_of_firm.size
- assert_equal 0, firm.clients_of_firm(true).size
+ assert_equal 0, firm.clients_of_firm.reload.size
assert_equal [], Client.destroyed_client_ids[firm.id]
# Should not be destroyed since the association is not dependent.
@@ -1149,7 +1172,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
firm.dependent_clients_of_firm.clear
assert_equal 0, firm.dependent_clients_of_firm.size
- assert_equal 0, firm.dependent_clients_of_firm(true).size
+ assert_equal 0, firm.dependent_clients_of_firm.reload.size
assert_equal [], Client.destroyed_client_ids[firm.id]
# Should be destroyed since the association is dependent.
@@ -1182,7 +1205,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
firm.exclusively_dependent_clients_of_firm.clear
assert_equal 0, firm.exclusively_dependent_clients_of_firm.size
- assert_equal 0, firm.exclusively_dependent_clients_of_firm(true).size
+ assert_equal 0, firm.exclusively_dependent_clients_of_firm.reload.size
# no destroy-filters should have been called
assert_equal [], Client.destroyed_client_ids[firm.id]
@@ -1231,7 +1254,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
# break the vanilla firm_id foreign key
assert_equal 3, firm.clients.count
firm.clients.first.update_columns(firm_id: nil)
- assert_equal 2, firm.clients(true).count
+ assert_equal 2, firm.clients.reload.count
assert_equal 2, firm.clients_using_primary_key_with_delete_all.count
old_record = firm.clients_using_primary_key_with_delete_all.first
firm = Firm.first
@@ -1257,7 +1280,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
firm.clients_of_firm.clear
assert_equal 0, firm.clients_of_firm.size
- assert_equal 0, firm.clients_of_firm(true).size
+ assert_equal 0, firm.clients_of_firm.reload.size
end
def test_deleting_a_item_which_is_not_in_the_collection
@@ -1265,7 +1288,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
summit = Client.find_by_name('Summit')
companies(:first_firm).clients_of_firm.delete(summit)
assert_equal 2, companies(:first_firm).clients_of_firm.size
- assert_equal 2, companies(:first_firm).clients_of_firm(true).size
+ assert_equal 2, companies(:first_firm).clients_of_firm.reload.size
assert_equal 2, summit.client_of
end
@@ -1303,7 +1326,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
assert_equal 1, companies(:first_firm).reload.clients_of_firm.size
- assert_equal 1, companies(:first_firm).clients_of_firm(true).size
+ assert_equal 1, companies(:first_firm).clients_of_firm.reload.size
end
def test_destroying_by_fixnum_id
@@ -1314,7 +1337,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
assert_equal 1, companies(:first_firm).reload.clients_of_firm.size
- assert_equal 1, companies(:first_firm).clients_of_firm(true).size
+ assert_equal 1, companies(:first_firm).clients_of_firm.reload.size
end
def test_destroying_by_string_id
@@ -1325,7 +1348,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
assert_equal 1, companies(:first_firm).reload.clients_of_firm.size
- assert_equal 1, companies(:first_firm).clients_of_firm(true).size
+ assert_equal 1, companies(:first_firm).clients_of_firm.reload.size
end
def test_destroying_a_collection
@@ -1338,7 +1361,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
assert_equal 1, companies(:first_firm).reload.clients_of_firm.size
- assert_equal 1, companies(:first_firm).clients_of_firm(true).size
+ assert_equal 1, companies(:first_firm).clients_of_firm.reload.size
end
def test_destroy_all
@@ -1349,7 +1372,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_equal clients.sort_by(&:id), destroyed.sort_by(&:id)
assert destroyed.all?(&:frozen?), "destroyed clients should be frozen"
assert companies(:first_firm).clients_of_firm.empty?, "37signals has no clients after destroy all"
- assert companies(:first_firm).clients_of_firm(true).empty?, "37signals has no clients after destroy all and refresh"
+ assert companies(:first_firm).clients_of_firm.reload.empty?, "37signals has no clients after destroy all and refresh"
end
def test_dependence
@@ -1426,6 +1449,26 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert firm.companies.exists?(:name => 'child')
end
+ def test_restrict_with_error_is_deprecated_using_key_many
+ I18n.backend = I18n::Backend::Simple.new
+ I18n.backend.store_translations :en, activerecord: { errors: { messages: { restrict_dependent_destroy: { many: 'message for deprecated key' } } } }
+
+ firm = RestrictedWithErrorFirm.create!(name: 'restrict')
+ firm.companies.create(name: 'child')
+
+ assert !firm.companies.empty?
+
+ assert_deprecated { firm.destroy }
+
+ assert !firm.errors.empty?
+
+ assert_equal 'message for deprecated key', firm.errors[:base].first
+ assert RestrictedWithErrorFirm.exists?(name: 'restrict')
+ assert firm.companies.exists?(name: 'child')
+ ensure
+ I18n.backend.reload!
+ end
+
def test_restrict_with_error
firm = RestrictedWithErrorFirm.create!(:name => 'restrict')
firm.companies.create(:name => 'child')
@@ -1441,6 +1484,25 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert firm.companies.exists?(:name => 'child')
end
+ def test_restrict_with_error_with_locale
+ I18n.backend = I18n::Backend::Simple.new
+ I18n.backend.store_translations 'en', activerecord: {attributes: {restricted_with_error_firm: {companies: 'client companies'}}}
+ firm = RestrictedWithErrorFirm.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 client companies exist", firm.errors[:base].first
+ assert RestrictedWithErrorFirm.exists?(name: 'restrict')
+ assert firm.companies.exists?(name: 'child')
+ ensure
+ I18n.backend.reload!
+ end
+
def test_included_in_collection
assert_equal true, companies(:first_firm).clients.include?(Client.find(2))
end
@@ -1518,7 +1580,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
rescue Client::RaisedOnSave
end
- assert_equal [good], companies(:first_firm).clients_of_firm(true)
+ assert_equal [good], companies(:first_firm).clients_of_firm.reload
end
def test_transactions_when_replacing_on_new_record
@@ -1534,7 +1596,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
def test_get_ids_for_loaded_associations
company = companies(:first_firm)
- company.clients(true)
+ company.clients.reload
assert_queries(0) do
company.client_ids
company.client_ids
@@ -1588,7 +1650,7 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
firm.client_ids = [companies(:first_client).id, nil, companies(:second_client).id, '']
firm.save!
- assert_equal 2, firm.clients(true).size
+ assert_equal 2, firm.clients.reload.size
assert_equal true, firm.clients.include?(companies(:second_client))
end
@@ -2119,6 +2181,26 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_equal [bulb1, bulb2], car.all_bulbs.sort_by(&:id)
end
+ test "can unscope and where the default scope of the associated model" do
+ Car.has_many :other_bulbs, -> { unscope(where: [:name]).where(name: 'other') }, class_name: "Bulb"
+ car = Car.create!
+ bulb1 = Bulb.create! name: "defaulty", car: car
+ bulb2 = Bulb.create! name: "other", car: car
+
+ assert_equal [bulb1], car.bulbs
+ assert_equal [bulb2], car.other_bulbs
+ end
+
+ test "can rewhere the default scope of the associated model" do
+ Car.has_many :old_bulbs, -> { rewhere(name: 'old') }, class_name: "Bulb"
+ car = Car.create!
+ bulb1 = Bulb.create! name: "defaulty", car: car
+ bulb2 = Bulb.create! name: "old", car: car
+
+ assert_equal [bulb1], car.bulbs
+ assert_equal [bulb2], car.old_bulbs
+ end
+
test 'unscopes the default scope of associated model when used with include' do
car = Car.create!
bulb = Bulb.create! name: "other", car: car
@@ -2252,4 +2334,48 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_equal [first_bulb, second_bulb], car.bulbs
end
+
+ def test_association_force_reload_with_only_true_is_deprecated
+ company = Company.find(1)
+
+ assert_deprecated { company.clients_of_firm(true) }
+ end
+
+ class AuthorWithErrorDestroyingAssociation < ActiveRecord::Base
+ self.table_name = "authors"
+ has_many :posts_with_error_destroying,
+ class_name: "PostWithErrorDestroying",
+ foreign_key: :author_id,
+ dependent: :destroy
+ end
+
+ class PostWithErrorDestroying < ActiveRecord::Base
+ self.table_name = "posts"
+ self.inheritance_column = nil
+ before_destroy -> { throw :abort }
+ end
+
+ def test_destroy_does_not_raise_when_association_errors_on_destroy
+ assert_no_difference "AuthorWithErrorDestroyingAssociation.count" do
+ author = AuthorWithErrorDestroyingAssociation.first
+
+ assert_not author.destroy
+ end
+ end
+
+ def test_destroy_with_bang_bubbles_errors_from_associations
+ error = assert_raises ActiveRecord::RecordNotDestroyed do
+ AuthorWithErrorDestroyingAssociation.first.destroy!
+ end
+
+ assert_instance_of PostWithErrorDestroying, error.record
+ end
+
+ def test_ids_reader_memoization
+ car = Car.create!(name: 'TofaÅŸ')
+ bulb = Bulb.create!(car: car)
+
+ assert_equal [bulb.id], car.bulb_ids
+ assert_no_queries { car.bulb_ids }
+ 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 1d545af5a5..226ecf5447 100644
--- a/activerecord/test/cases/associations/has_many_through_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb
@@ -188,7 +188,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
assert post.people.include?(person)
end
- assert post.reload.people(true).include?(person)
+ assert post.reload.people.reload.include?(person)
end
def test_delete_all_for_with_dependent_option_destroy
@@ -229,7 +229,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
post = posts(:thinking)
post.people.concat [person]
assert_equal 1, post.people.size
- assert_equal 1, post.people(true).size
+ assert_equal 1, post.people.reload.size
end
def test_associate_existing_record_twice_should_add_to_target_twice
@@ -285,7 +285,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
assert posts(:thinking).people.include?(new_person)
end
- assert posts(:thinking).reload.people(true).include?(new_person)
+ assert posts(:thinking).reload.people.reload.include?(new_person)
end
def test_associate_new_by_building
@@ -310,8 +310,8 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
posts(:thinking).save
end
- assert posts(:thinking).reload.people(true).collect(&:first_name).include?("Bob")
- assert posts(:thinking).reload.people(true).collect(&:first_name).include?("Ted")
+ assert posts(:thinking).reload.people.reload.collect(&:first_name).include?("Bob")
+ assert posts(:thinking).reload.people.reload.collect(&:first_name).include?("Ted")
end
def test_build_then_save_with_has_many_inverse
@@ -356,7 +356,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
assert posts(:welcome).people.empty?
end
- assert posts(:welcome).reload.people(true).empty?
+ assert posts(:welcome).reload.people.reload.empty?
end
def test_destroy_association
@@ -367,7 +367,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
end
assert posts(:welcome).reload.people.empty?
- assert posts(:welcome).people(true).empty?
+ assert posts(:welcome).people.reload.empty?
end
def test_destroy_all
@@ -378,7 +378,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
end
assert posts(:welcome).reload.people.empty?
- assert posts(:welcome).people(true).empty?
+ assert posts(:welcome).people.reload.empty?
end
def test_should_raise_exception_for_destroying_mismatching_records
@@ -539,7 +539,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
end
def test_replace_association
- assert_queries(4){posts(:welcome);people(:david);people(:michael); posts(:welcome).people(true)}
+ assert_queries(4){posts(:welcome);people(:david);people(:michael); posts(:welcome).people.reload}
# 1 query to delete the existing reader (michael)
# 1 query to associate the new reader (david)
@@ -552,8 +552,8 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
assert !posts(:welcome).people.include?(people(:michael))
}
- assert posts(:welcome).reload.people(true).include?(people(:david))
- assert !posts(:welcome).reload.people(true).include?(people(:michael))
+ assert posts(:welcome).reload.people.reload.include?(people(:david))
+ assert !posts(:welcome).reload.people.reload.include?(people(:michael))
end
def test_replace_order_is_preserved
@@ -592,7 +592,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
assert posts(:thinking).people.collect(&:first_name).include?("Jeb")
end
- assert posts(:thinking).reload.people(true).collect(&:first_name).include?("Jeb")
+ assert posts(:thinking).reload.people.reload.collect(&:first_name).include?("Jeb")
end
def test_through_record_is_built_when_created_with_where
@@ -668,7 +668,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
end
def test_clear_associations
- assert_queries(2) { posts(:welcome);posts(:welcome).people(true) }
+ assert_queries(2) { posts(:welcome);posts(:welcome).people.reload }
assert_queries(1) do
posts(:welcome).people.clear
@@ -678,7 +678,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
assert posts(:welcome).people.empty?
end
- assert posts(:welcome).reload.people(true).empty?
+ assert posts(:welcome).reload.people.reload.empty?
end
def test_association_callback_ordering
@@ -744,13 +744,14 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
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
+ assert_not_called(ActiveRecord::Associations::Preloader, :new) do
+ posts(:welcome).misc_tag_ids
+ end
end
def test_get_ids_for_loaded_associations
person = people(:michael)
- person.posts(true)
+ person.posts.reload
assert_queries(0) do
person.post_ids
person.post_ids
@@ -765,9 +766,10 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
end
def test_association_proxy_transaction_method_starts_transaction_in_association_class
- Tag.expects(:transaction)
- Post.first.tags.transaction do
- # nothing
+ assert_called(Tag, :transaction) do
+ Post.first.tags.transaction do
+ # nothing
+ end
end
end
@@ -828,14 +830,14 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
category = author.named_categories.build(:name => "Primary")
author.save
assert Categorization.exists?(:author_id => author.id, :named_category_name => category.name)
- assert author.named_categories(true).include?(category)
+ assert author.named_categories.reload.include?(category)
end
def test_collection_create_with_nonstandard_primary_key_on_belongs_to
author = authors(:mary)
category = author.named_categories.create(:name => "Primary")
assert Categorization.exists?(:author_id => author.id, :named_category_name => category.name)
- assert author.named_categories(true).include?(category)
+ assert author.named_categories.reload.include?(category)
end
def test_collection_exists
@@ -850,7 +852,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
category = author.named_categories.create(:name => "Primary")
author.named_categories.delete(category)
assert !Categorization.exists?(:author_id => author.id, :named_category_name => category.name)
- assert author.named_categories(true).empty?
+ assert author.named_categories.reload.empty?
end
def test_collection_singular_ids_getter_with_string_primary_keys
@@ -871,10 +873,10 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
assert_nothing_raised do
book = books(:awdr)
book.subscriber_ids = [subscribers(:second).nick]
- assert_equal [subscribers(:second)], book.subscribers(true)
+ assert_equal [subscribers(:second)], book.subscribers.reload
book.subscriber_ids = []
- assert_equal [], book.subscribers(true)
+ assert_equal [], book.subscribers.reload
end
end
@@ -1109,10 +1111,10 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
def test_has_many_through_with_default_scope_on_the_target
person = people(:michael)
- assert_equal [posts(:thinking)], person.first_posts
+ assert_equal [posts(:thinking).id], person.first_posts.map(&:id)
readers(:michael_authorless).update(first_post_id: 1)
- assert_equal [posts(:thinking)], person.reload.first_posts
+ assert_equal [posts(:thinking).id], person.reload.first_posts.map(&:id)
end
def test_has_many_through_with_includes_in_through_association_scope
@@ -1158,4 +1160,45 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
post_through = organization.posts.build
assert_equal post_direct.author_id, post_through.author_id
end
+
+ def test_has_many_through_with_scope_that_should_not_be_fully_merged
+ Club.has_many :distinct_memberships, -> { distinct }, class_name: "Membership"
+ Club.has_many :special_favourites, through: :distinct_memberships, source: :member
+
+ assert_nil Club.new.special_favourites.distinct_value
+ end
+
+ def test_association_force_reload_with_only_true_is_deprecated
+ post = Post.find(1)
+
+ assert_deprecated { post.people(true) }
+ end
+
+ def test_has_many_through_do_not_cache_association_reader_if_the_though_method_has_default_scopes
+ member = Member.create!
+ club = Club.create!
+ TenantMembership.create!(
+ member: member,
+ club: club
+ )
+
+ TenantMembership.current_member = member
+
+ tenant_clubs = member.tenant_clubs
+ assert_equal [club], tenant_clubs
+
+ TenantMembership.current_member = nil
+
+ other_member = Member.create!
+ other_club = Club.create!
+ TenantMembership.create!(
+ member: other_member,
+ club: other_club
+ )
+
+ tenant_clubs = other_member.tenant_clubs
+ assert_equal [other_club], tenant_clubs
+ ensure
+ TenantMembership.current_member = nil
+ 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 5c2e5e7b43..c9d9e29f09 100644
--- a/activerecord/test/cases/associations/has_one_associations_test.rb
+++ b/activerecord/test/cases/associations/has_one_associations_test.rb
@@ -107,6 +107,14 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
assert_nil Account.find(old_account_id).firm_id
end
+ def test_nullification_on_destroyed_association
+ developer = Developer.create!(name: "Someone")
+ ship = Ship.create!(name: "Planet Caravan", developer: developer)
+ ship.destroy
+ assert !ship.persisted?
+ assert !developer.persisted?
+ end
+
def test_natural_assignment_to_nil_after_destroy
firm = companies(:rails_core)
old_account_id = firm.account.id
@@ -178,6 +186,25 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
assert firm.account.present?
end
+ def test_restrict_with_error_is_deprecated_using_key_one
+ I18n.backend = I18n::Backend::Simple.new
+ I18n.backend.store_translations :en, activerecord: { errors: { messages: { restrict_dependent_destroy: { one: 'message for deprecated key' } } } }
+
+ firm = RestrictedWithErrorFirm.create!(name: 'restrict')
+ firm.create_account(credit_limit: 10)
+
+ assert_not_nil firm.account
+
+ assert_deprecated { firm.destroy }
+
+ assert !firm.errors.empty?
+ assert_equal 'message for deprecated key', firm.errors[:base].first
+ assert RestrictedWithErrorFirm.exists?(name: 'restrict')
+ assert firm.account.present?
+ ensure
+ I18n.backend.reload!
+ end
+
def test_restrict_with_error
firm = RestrictedWithErrorFirm.create!(:name => 'restrict')
firm.create_account(:credit_limit => 10)
@@ -192,6 +219,24 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
assert firm.account.present?
end
+ def test_restrict_with_error_with_locale
+ I18n.backend = I18n::Backend::Simple.new
+ I18n.backend.store_translations 'en', activerecord: {attributes: {restricted_with_error_firm: {account: 'firm account'}}}
+ firm = RestrictedWithErrorFirm.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 firm account exists", firm.errors[:base].first
+ assert RestrictedWithErrorFirm.exists?(name: 'restrict')
+ assert firm.account.present?
+ ensure
+ I18n.backend.reload!
+ end
+
def test_successful_build_association
firm = Firm.new("name" => "GlobalMegaCorp")
firm.save
@@ -332,7 +377,8 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
assert a.persisted?
assert_equal a, firm.account
assert_equal a, firm.account
- assert_equal a, firm.account(true)
+ firm.association(:account).reload
+ assert_equal a, firm.account
end
def test_save_still_works_after_accessing_nil_has_one
@@ -607,4 +653,10 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
end
end
end
+
+ def test_association_force_reload_with_only_true_is_deprecated
+ firm = Firm.find(1)
+
+ assert_deprecated { firm.account(true) }
+ end
end
diff --git a/activerecord/test/cases/associations/has_one_through_associations_test.rb b/activerecord/test/cases/associations/has_one_through_associations_test.rb
index f8772547a2..b2b46812b9 100644
--- a/activerecord/test/cases/associations/has_one_through_associations_test.rb
+++ b/activerecord/test/cases/associations/has_one_through_associations_test.rb
@@ -16,6 +16,10 @@ require 'models/owner'
require 'models/post'
require 'models/comment'
require 'models/categorization'
+require 'models/customer'
+require 'models/carrier'
+require 'models/shop_account'
+require 'models/customer_carrier'
class HasOneThroughAssociationsTest < ActiveRecord::TestCase
fixtures :member_types, :members, :clubs, :memberships, :sponsors, :organizations, :minivans,
@@ -245,12 +249,14 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
assert_not_nil @member_detail.member_type
@member_detail.destroy
assert_queries(1) do
- assert_not_nil @member_detail.member_type(true)
+ @member_detail.association(:member_type).reload
+ assert_not_nil @member_detail.member_type
end
@member_detail.member.destroy
assert_queries(1) do
- assert_nil @member_detail.member_type(true)
+ @member_detail.association(:member_type).reload
+ assert_nil @member_detail.member_type
end
end
@@ -344,4 +350,34 @@ class HasOneThroughAssociationsTest < ActiveRecord::TestCase
end
end
end
+
+ def test_has_one_through_do_not_cache_association_reader_if_the_though_method_has_default_scopes
+ customer = Customer.create!
+ carrier = Carrier.create!
+ customer_carrier = CustomerCarrier.create!(
+ customer: customer,
+ carrier: carrier,
+ )
+ account = ShopAccount.create!(customer_carrier: customer_carrier)
+
+ CustomerCarrier.current_customer = customer
+
+ account_carrier = account.carrier
+ assert_equal carrier, account_carrier
+
+ CustomerCarrier.current_customer = nil
+
+ other_carrier = Carrier.create!
+ other_customer = Customer.create!
+ other_customer_carrier = CustomerCarrier.create!(
+ customer: other_customer,
+ carrier: other_carrier,
+ )
+ other_account = ShopAccount.create!(customer_carrier: other_customer_carrier)
+
+ account_carrier = other_account.carrier
+ assert_equal other_carrier, account_carrier
+ ensure
+ CustomerCarrier.current_customer = nil
+ end
end
diff --git a/activerecord/test/cases/associations/inverse_associations_test.rb b/activerecord/test/cases/associations/inverse_associations_test.rb
index 423b8238b1..57d1c8feda 100644
--- a/activerecord/test/cases/associations/inverse_associations_test.rb
+++ b/activerecord/test/cases/associations/inverse_associations_test.rb
@@ -13,6 +13,9 @@ require 'models/mixed_case_monkey'
require 'models/admin'
require 'models/admin/account'
require 'models/admin/user'
+require 'models/developer'
+require 'models/company'
+require 'models/project'
class AutomaticInverseFindingTests < ActiveRecord::TestCase
fixtures :ratings, :comments, :cars
@@ -80,10 +83,10 @@ class AutomaticInverseFindingTests < ActiveRecord::TestCase
assert_equal rating.comment, comment, "The Rating's comment should be the original Comment"
- rating.comment.body = "Brogramming is the act of programming, like a bro."
+ rating.comment.body = "Fennec foxes are the smallest of the foxes."
assert_equal rating.comment.body, comment.body, "Changing the Comment's body on the association should change the original Comment's body"
- comment.body = "Broseiden is the king of the sea of bros."
+ comment.body = "Kittens are adorable."
assert_equal comment.body, rating.comment.body, "Changing the original Comment's body should change the Comment's body on the association"
end
@@ -94,10 +97,10 @@ class AutomaticInverseFindingTests < ActiveRecord::TestCase
assert_equal rating.comment, comment, "The Rating's comment should be the original Comment"
- rating.comment.body = "Brogramming is the act of programming, like a bro."
+ rating.comment.body = "Fennec foxes are the smallest of the foxes."
assert_equal rating.comment.body, comment.body, "Changing the Comment's body on the association should change the original Comment's body"
- comment.body = "Broseiden is the king of the sea of bros."
+ comment.body = "Kittens are adorable."
assert_equal comment.body, rating.comment.body, "Changing the original Comment's body should change the Comment's body on the association"
end
@@ -198,6 +201,16 @@ class InverseAssociationTests < ActiveRecord::TestCase
belongs_to_ref = Sponsor.reflect_on_association(:sponsor_club)
assert_nil belongs_to_ref.inverse_of
end
+
+ def test_this_inverse_stuff
+ firm = Firm.create!(name: 'Adequate Holdings')
+ Project.create!(name: 'Project 1', firm: firm)
+ Developer.create!(name: 'Gorbypuff', firm: firm)
+
+ new_project = Project.last
+ assert Project.reflect_on_association(:lead_developer).inverse_of.present?, "Expected inverse of to be present"
+ assert new_project.lead_developer.present?, "Expected lead developer to be present on the project"
+ end
end
class InverseHasOneTests < ActiveRecord::TestCase
diff --git a/activerecord/test/cases/associations/join_model_test.rb b/activerecord/test/cases/associations/join_model_test.rb
index 5575419c35..f6dddaf5b4 100644
--- a/activerecord/test/cases/associations/join_model_test.rb
+++ b/activerecord/test/cases/associations/join_model_test.rb
@@ -213,7 +213,8 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
old_count = Tagging.count
post.destroy
assert_equal old_count-1, Tagging.count
- assert_nil posts(:welcome).tagging(true)
+ posts(:welcome).association(:tagging).reload
+ assert_nil posts(:welcome).tagging
end
def test_delete_polymorphic_has_one_with_nullify
@@ -224,7 +225,8 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
old_count = Tagging.count
post.destroy
assert_equal old_count, Tagging.count
- assert_nil posts(:welcome).tagging(true)
+ posts(:welcome).association(:tagging).reload
+ assert_nil posts(:welcome).tagging
end
def test_has_many_with_piggyback
@@ -461,7 +463,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
assert saved_post.tags.include?(new_tag)
assert new_tag.persisted?
- assert saved_post.reload.tags(true).include?(new_tag)
+ assert saved_post.reload.tags.reload.include?(new_tag)
new_post = Post.new(:title => "Association replacement works!", :body => "You best believe it.")
@@ -474,7 +476,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
new_post.save!
assert new_post.persisted?
- assert new_post.reload.tags(true).include?(saved_tag)
+ assert new_post.reload.tags.reload.include?(saved_tag)
assert !posts(:thinking).tags.build.persisted?
assert !posts(:thinking).tags.new.persisted?
@@ -490,7 +492,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
assert_nil( wrong = post_thinking.taggings.detect { |t| t.class != Tagging },
message = "Expected a Tagging in taggings collection, got #{wrong.class}.")
assert_equal(count + 1, post_thinking.reload.tags.size)
- assert_equal(count + 1, post_thinking.tags(true).size)
+ assert_equal(count + 1, post_thinking.tags.reload.size)
assert_kind_of Tag, post_thinking.tags.create!(:name => 'foo')
assert_nil( wrong = post_thinking.tags.detect { |t| t.class != Tag },
@@ -498,7 +500,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
assert_nil( wrong = post_thinking.taggings.detect { |t| t.class != Tagging },
message = "Expected a Tagging in taggings collection, got #{wrong.class}.")
assert_equal(count + 2, post_thinking.reload.tags.size)
- assert_equal(count + 2, post_thinking.tags(true).size)
+ assert_equal(count + 2, post_thinking.tags.reload.size)
assert_nothing_raised { post_thinking.tags.concat(Tag.create!(:name => 'abc'), Tag.create!(:name => 'def')) }
assert_nil( wrong = post_thinking.tags.detect { |t| t.class != Tag },
@@ -506,7 +508,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
assert_nil( wrong = post_thinking.taggings.detect { |t| t.class != Tagging },
message = "Expected a Tagging in taggings collection, got #{wrong.class}.")
assert_equal(count + 4, post_thinking.reload.tags.size)
- assert_equal(count + 4, post_thinking.tags(true).size)
+ assert_equal(count + 4, post_thinking.tags.reload.size)
# Raises if the wrong reflection name is used to set the Edge belongs_to
assert_nothing_raised { vertices(:vertex_1).sinks << vertices(:vertex_5) }
@@ -544,11 +546,11 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
book = Book.create!(:name => 'Getting Real')
book_awdr = books(:awdr)
book_awdr.references << book
- assert_equal(count + 1, book_awdr.references(true).size)
+ assert_equal(count + 1, book_awdr.references.reload.size)
assert_nothing_raised { book_awdr.references.delete(book) }
assert_equal(count, book_awdr.references.size)
- assert_equal(count, book_awdr.references(true).size)
+ assert_equal(count, book_awdr.references.reload.size)
assert_equal(references_before.sort, book_awdr.references.sort)
end
@@ -558,14 +560,14 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
tag = Tag.create!(:name => 'doomed')
post_thinking = posts(:thinking)
post_thinking.tags << tag
- assert_equal(count + 1, post_thinking.taggings(true).size)
- assert_equal(count + 1, post_thinking.reload.tags(true).size)
+ assert_equal(count + 1, post_thinking.taggings.reload.size)
+ assert_equal(count + 1, post_thinking.reload.tags.reload.size)
assert_not_equal(tags_before, post_thinking.tags.sort)
assert_nothing_raised { post_thinking.tags.delete(tag) }
assert_equal(count, post_thinking.tags.size)
- assert_equal(count, post_thinking.tags(true).size)
- assert_equal(count, post_thinking.taggings(true).size)
+ assert_equal(count, post_thinking.tags.reload.size)
+ assert_equal(count, post_thinking.taggings.reload.size)
assert_equal(tags_before, post_thinking.tags.sort)
end
@@ -577,11 +579,11 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
quaked = Tag.create!(:name => 'quaked')
post_thinking = posts(:thinking)
post_thinking.tags << doomed << doomed2
- assert_equal(count + 2, post_thinking.reload.tags(true).size)
+ assert_equal(count + 2, post_thinking.reload.tags.reload.size)
assert_nothing_raised { post_thinking.tags.delete(doomed, doomed2, quaked) }
assert_equal(count, post_thinking.tags.size)
- assert_equal(count, post_thinking.tags(true).size)
+ assert_equal(count, post_thinking.tags.reload.size)
assert_equal(tags_before, post_thinking.tags.sort)
end
diff --git a/activerecord/test/cases/associations/left_outer_join_association_test.rb b/activerecord/test/cases/associations/left_outer_join_association_test.rb
new file mode 100644
index 0000000000..a362b43cc3
--- /dev/null
+++ b/activerecord/test/cases/associations/left_outer_join_association_test.rb
@@ -0,0 +1,74 @@
+require "cases/helper"
+require 'models/post'
+require 'models/comment'
+require 'models/author'
+require 'models/essay'
+require 'models/categorization'
+require 'models/person'
+
+class LeftOuterJoinAssociationTest < ActiveRecord::TestCase
+ fixtures :authors, :essays, :posts, :comments, :categorizations, :people
+
+ def test_construct_finder_sql_applies_aliases_tables_on_association_conditions
+ result = Author.left_outer_joins(:thinking_posts, :welcome_posts).to_a
+ assert_equal authors(:david), result.first
+ end
+
+ def test_construct_finder_sql_does_not_table_name_collide_on_duplicate_associations
+ assert_nothing_raised do
+ sql = capture_sql do
+ Person.left_outer_joins(:agents => {:agents => :agents})
+ .left_outer_joins(:agents => {:agents => {:primary_contact => :agents}}).to_a
+ end.first
+ assert_match(/agents_people_4/i, sql)
+ end
+ end
+
+ def test_construct_finder_sql_executes_a_left_outer_join
+ assert_not_equal Author.count, Author.joins(:posts).count
+ assert_equal Author.count, Author.left_outer_joins(:posts).count
+ end
+
+ def test_construct_finder_sql_ignores_empty_left_outer_joins_hash
+ sql = capture_sql { Author.left_outer_joins({}) }.first
+ assert_no_match(/LEFT OUTER JOIN/i, sql)
+ end
+
+ def test_construct_finder_sql_ignores_empty_left_outer_joins_array
+ sql = capture_sql { Author.left_outer_joins([]) }.first
+ assert_no_match(/LEFT OUTER JOIN/i, sql)
+ end
+
+ def test_left_outer_joins_forbids_to_use_string_as_argument
+ assert_raise(ArgumentError){ Author.left_outer_joins('LEFT OUTER JOIN "posts" ON "posts"."user_id" = "users"."id"').to_a }
+ end
+
+ def test_join_conditions_added_to_join_clause
+ sql = capture_sql { Author.left_outer_joins(:essays).to_a }.first
+ assert_match(/writer_type.*?=.*?(Author|\?|\$1)/i, sql)
+ assert_no_match(/WHERE/i, sql)
+ end
+
+ def test_find_with_sti_join
+ scope = Post.left_outer_joins(:special_comments).where(:id => posts(:sti_comments).id)
+
+ # The join should match SpecialComment and its subclasses only
+ assert scope.where("comments.type" => "Comment").empty?
+ assert !scope.where("comments.type" => "SpecialComment").empty?
+ assert !scope.where("comments.type" => "SubSpecialComment").empty?
+ end
+
+ def test_does_not_override_select
+ authors = Author.select("authors.name, #{%{(authors.author_address_id || ' ' || authors.author_address_extra_id) as addr_id}}").left_outer_joins(:posts)
+ assert authors.any?
+ assert authors.first.respond_to?(:addr_id)
+ end
+
+ test "the default scope of the target is applied when joining associations" do
+ author = Author.create! name: "Jon"
+ author.categorizations.create!
+ author.categorizations.create! special: true
+
+ assert_equal [author], Author.where(id: author).left_outer_joins(:special_categorizations)
+ end
+end
diff --git a/activerecord/test/cases/associations/nested_through_associations_test.rb b/activerecord/test/cases/associations/nested_through_associations_test.rb
index 31b68c940e..b040485d99 100644
--- a/activerecord/test/cases/associations/nested_through_associations_test.rb
+++ b/activerecord/test/cases/associations/nested_through_associations_test.rb
@@ -495,7 +495,7 @@ class NestedThroughAssociationsTest < ActiveRecord::TestCase
groucho = members(:groucho)
founding = member_types(:founding)
- assert_raises(ActiveRecord::HasManyThroughNestedAssociationsAreReadonly) do
+ assert_raises(ActiveRecord::HasOneThroughNestedAssociationsAreReadonly) do
groucho.nested_member_type = founding
end
end
diff --git a/activerecord/test/cases/associations_test.rb b/activerecord/test/cases/associations_test.rb
index 3d202a5527..01a058918a 100644
--- a/activerecord/test/cases/associations_test.rb
+++ b/activerecord/test/cases/associations_test.rb
@@ -1,7 +1,6 @@
require "cases/helper"
require 'models/computer'
require 'models/developer'
-require 'models/computer'
require 'models/project'
require 'models/company'
require 'models/categorization'
@@ -13,7 +12,6 @@ require 'models/tag'
require 'models/tagging'
require 'models/person'
require 'models/reader'
-require 'models/parrot'
require 'models/ship_part'
require 'models/ship'
require 'models/liquid'
@@ -93,8 +91,10 @@ class AssociationsTest < ActiveRecord::TestCase
assert firm.clients.empty?, "New firm should have cached no client objects"
assert_equal 0, firm.clients.size, "New firm should have cached 0 clients count"
- assert !firm.clients(true).empty?, "New firm should have reloaded client objects"
- assert_equal 1, firm.clients(true).size, "New firm should have reloaded clients count"
+ ActiveSupport::Deprecation.silence do
+ assert !firm.clients(true).empty?, "New firm should have reloaded client objects"
+ assert_equal 1, firm.clients(true).size, "New firm should have reloaded clients count"
+ end
end
def test_using_limitable_reflections_helper
@@ -110,10 +110,13 @@ class AssociationsTest < ActiveRecord::TestCase
def test_force_reload_is_uncached
firm = Firm.create!("name" => "A New Firm, Inc")
Client.create!("name" => "TheClient.com", :firm => firm)
- ActiveRecord::Base.cache do
- firm.clients.each {}
- assert_queries(0) { assert_not_nil firm.clients.each {} }
- assert_queries(1) { assert_not_nil firm.clients(true).each {} }
+
+ ActiveSupport::Deprecation.silence do
+ ActiveRecord::Base.cache do
+ firm.clients.each {}
+ assert_queries(0) { assert_not_nil firm.clients.each {} }
+ assert_queries(1) { assert_not_nil firm.clients(true).each {} }
+ end
end
end
diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb
index ea2b94cbf4..52d197718e 100644
--- a/activerecord/test/cases/attribute_methods_test.rb
+++ b/activerecord/test/cases/attribute_methods_test.rb
@@ -1,7 +1,6 @@
require "cases/helper"
require 'models/minimalistic'
require 'models/developer'
-require 'models/computer'
require 'models/auto_id'
require 'models/boolean'
require 'models/computer'
@@ -67,8 +66,9 @@ class AttributeMethodsTest < ActiveRecord::TestCase
def test_caching_nil_primary_key
klass = Class.new(Minimalistic)
- klass.expects(:reset_primary_key).returns(nil).once
- 2.times { klass.primary_key }
+ assert_called(klass, :reset_primary_key, returns: nil) do
+ 2.times { klass.primary_key }
+ end
end
def test_attribute_keys_on_new_instance
@@ -175,9 +175,9 @@ class AttributeMethodsTest < ActiveRecord::TestCase
assert_equal category_attrs , category.attributes_before_type_cast
end
- if current_adapter?(:MysqlAdapter)
+ if current_adapter?(:MysqlAdapter, :Mysql2Adapter)
def test_read_attributes_before_type_cast_on_boolean
- bool = Boolean.create({ "value" => false })
+ bool = Boolean.create!({ "value" => false })
if RUBY_PLATFORM =~ /java/
# JRuby will return the value before typecast as string
assert_equal "0", bool.reload.attributes_before_type_cast["value"]
@@ -542,9 +542,6 @@ class AttributeMethodsTest < ActiveRecord::TestCase
developer.save!
- assert_equal "50000", developer.salary_before_type_cast
- assert_equal 1337, developer.name_before_type_cast
-
assert_equal 50000, developer.salary
assert_equal "1337", developer.name
end
diff --git a/activerecord/test/cases/attribute_set_test.rb b/activerecord/test/cases/attribute_set_test.rb
index 9d927481ec..7a24b85a36 100644
--- a/activerecord/test/cases/attribute_set_test.rb
+++ b/activerecord/test/cases/attribute_set_test.rb
@@ -29,7 +29,7 @@ module ActiveRecord
assert_equal :bar, attributes[:bar].name
end
- test "duping creates a new hash and dups each attribute" do
+ test "duping creates a new hash, but does not dup the attributes" do
builder = AttributeSet::Builder.new(foo: Type::Integer.new, bar: Type::String.new)
attributes = builder.build_from_database(foo: 1, bar: 'foo')
@@ -43,6 +43,24 @@ module ActiveRecord
assert_equal 1, attributes[:foo].value
assert_equal 2, duped[:foo].value
+ assert_equal 'foobar', attributes[:bar].value
+ assert_equal 'foobar', duped[:bar].value
+ end
+
+ test "deep_duping creates a new hash and dups each attribute" do
+ builder = AttributeSet::Builder.new(foo: Type::Integer.new, bar: Type::String.new)
+ attributes = builder.build_from_database(foo: 1, bar: 'foo')
+
+ # Ensure the type cast value is cached
+ attributes[:foo].value
+ attributes[:bar].value
+
+ duped = attributes.deep_dup
+ duped.write_from_database(:foo, 2)
+ duped[:bar].value << 'bar'
+
+ assert_equal 1, attributes[:foo].value
+ assert_equal 2, duped[:foo].value
assert_equal 'foo', attributes[:bar].value
assert_equal 'foobar', duped[:bar].value
end
@@ -160,6 +178,9 @@ module ActiveRecord
return if value.nil?
value + " from database"
end
+
+ def assert_valid_value(*)
+ end
end
test "write_from_database sets the attribute with database typecasting" do
@@ -207,5 +228,26 @@ module ActiveRecord
assert_equal [:foo], attributes.accessed
end
+
+ test "#map returns a new attribute set with the changes applied" do
+ builder = AttributeSet::Builder.new(foo: Type::Integer.new, bar: Type::Integer.new)
+ attributes = builder.build_from_database(foo: "1", bar: "2")
+ new_attributes = attributes.map do |attr|
+ attr.with_cast_value(attr.value + 1)
+ end
+
+ assert_equal 2, new_attributes.fetch_value(:foo)
+ assert_equal 3, new_attributes.fetch_value(:bar)
+ end
+
+ test "comparison for equality is correctly implemented" do
+ builder = AttributeSet::Builder.new(foo: Type::Integer.new, bar: Type::Integer.new)
+ attributes = builder.build_from_database(foo: "1", bar: "2")
+ attributes2 = builder.build_from_database(foo: "1", bar: "2")
+ attributes3 = builder.build_from_database(foo: "2", bar: "2")
+
+ assert_equal attributes, attributes2
+ assert_not_equal attributes2, attributes3
+ end
end
end
diff --git a/activerecord/test/cases/attribute_test.rb b/activerecord/test/cases/attribute_test.rb
index aa419c7a67..a24a4fc6a4 100644
--- a/activerecord/test/cases/attribute_test.rb
+++ b/activerecord/test/cases/attribute_test.rb
@@ -1,11 +1,9 @@
require 'cases/helper'
-require 'minitest/mock'
module ActiveRecord
class AttributeTest < ActiveRecord::TestCase
setup do
@type = Minitest::Mock.new
- @type.expect(:==, false, [false])
end
teardown do
@@ -109,6 +107,9 @@ module ActiveRecord
def deserialize(value)
value + " from database"
end
+
+ def assert_valid_value(*)
+ end
end
test "with_value_from_user returns a new attribute with the value from the user" do
@@ -181,12 +182,65 @@ module ActiveRecord
assert attribute.has_been_read?
end
+ test "an attribute is not changed if it hasn't been assigned or mutated" do
+ attribute = Attribute.from_database(:foo, 1, Type::Value.new)
+
+ refute attribute.changed?
+ end
+
+ test "an attribute is changed if it's been assigned a new value" do
+ attribute = Attribute.from_database(:foo, 1, Type::Value.new)
+ changed = attribute.with_value_from_user(2)
+
+ assert changed.changed?
+ end
+
+ test "an attribute is not changed if it's assigned the same value" do
+ attribute = Attribute.from_database(:foo, 1, Type::Value.new)
+ unchanged = attribute.with_value_from_user(1)
+
+ refute unchanged.changed?
+ end
+
test "an attribute can not be mutated if it has not been read,
and skips expensive calculations" do
type_which_raises_from_all_methods = Object.new
attribute = Attribute.from_database(:foo, "bar", type_which_raises_from_all_methods)
- assert_not attribute.changed_in_place_from?("bar")
+ assert_not attribute.changed_in_place?
+ end
+
+ test "an attribute is changed if it has been mutated" do
+ attribute = Attribute.from_database(:foo, "bar", Type::String.new)
+ attribute.value << "!"
+
+ assert attribute.changed_in_place?
+ assert attribute.changed?
+ end
+
+ test "an attribute can forget its changes" do
+ attribute = Attribute.from_database(:foo, "bar", Type::String.new)
+ changed = attribute.with_value_from_user("foo")
+ forgotten = changed.forgetting_assignment
+
+ assert changed.changed? # sanity check
+ refute forgotten.changed?
+ end
+
+ test "with_value_from_user validates the value" do
+ type = Type::Value.new
+ type.define_singleton_method(:assert_valid_value) do |value|
+ if value == 1
+ raise ArgumentError
+ end
+ end
+
+ attribute = Attribute.from_database(:foo, 1, type)
+ assert_equal 1, attribute.value
+ assert_equal 2, attribute.with_value_from_user(2).value
+ assert_raises ArgumentError do
+ attribute.with_value_from_user(1)
+ end
end
end
end
diff --git a/activerecord/test/cases/attributes_test.rb b/activerecord/test/cases/attributes_test.rb
index eeda9335ad..264b275181 100644
--- a/activerecord/test/cases/attributes_test.rb
+++ b/activerecord/test/cases/attributes_test.rb
@@ -142,7 +142,7 @@ module ActiveRecord
end
if current_adapter?(:PostgreSQLAdapter)
- test "arrays types can be specified" do
+ test "array types can be specified" do
klass = Class.new(OverloadedType) do
attribute :my_array, :string, limit: 50, array: true
attribute :my_int_array, :integer, array: true
@@ -152,7 +152,7 @@ module ActiveRecord
Type::String.new(limit: 50))
int_array = ConnectionAdapters::PostgreSQL::OID::Array.new(
Type::Integer.new)
- refute_equal string_array, int_array
+ assert_not_equal string_array, int_array
assert_equal string_array, klass.type_for_attribute("my_array")
assert_equal int_array, klass.type_for_attribute("my_int_array")
end
@@ -167,7 +167,7 @@ module ActiveRecord
Type::String.new(limit: 50))
int_range = ConnectionAdapters::PostgreSQL::OID::Range.new(
Type::Integer.new)
- refute_equal string_range, int_range
+ assert_not_equal string_range, int_range
assert_equal string_range, klass.type_for_attribute("my_range")
assert_equal int_range, klass.type_for_attribute("my_int_range")
end
diff --git a/activerecord/test/cases/autosave_association_test.rb b/activerecord/test/cases/autosave_association_test.rb
index 80b5a0004d..0df8f1f798 100644
--- a/activerecord/test/cases/autosave_association_test.rb
+++ b/activerecord/test/cases/autosave_association_test.rb
@@ -24,6 +24,8 @@ require 'models/molecule'
require 'models/member'
require 'models/member_detail'
require 'models/organization'
+require 'models/guitar'
+require 'models/tuning_peg'
class TestAutosaveAssociationsInGeneral < ActiveRecord::TestCase
def test_autosave_validation
@@ -67,6 +69,14 @@ class TestAutosaveAssociationsInGeneral < ActiveRecord::TestCase
assert_no_difference_when_adding_callbacks_twice_for Pirate, :parrots
end
+ def test_cyclic_autosaves_do_not_add_multiple_validations
+ ship = ShipWithoutNestedAttributes.new
+ ship.prisoners.build
+
+ assert_not ship.valid?
+ assert_equal 1, ship.errors[:name].length
+ end
+
private
def assert_no_difference_when_adding_callbacks_twice_for(model, association_name)
@@ -149,7 +159,8 @@ class TestDefaultAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCas
assert_equal a, firm.account
assert firm.save
assert_equal a, firm.account
- assert_equal a, firm.account(true)
+ firm.association(:account).reload
+ assert_equal a, firm.account
end
def test_assignment_before_either_saved
@@ -162,7 +173,8 @@ class TestDefaultAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCas
assert firm.persisted?
assert a.persisted?
assert_equal a, firm.account
- assert_equal a, firm.account(true)
+ firm.association(:account).reload
+ assert_equal a, firm.account
end
def test_not_resaved_when_unchanged
@@ -248,7 +260,8 @@ class TestDefaultAutosaveAssociationOnABelongsToAssociation < ActiveRecord::Test
assert apple.save
assert apple.persisted?
assert_equal apple, client.firm
- assert_equal apple, client.firm(true)
+ client.association(:firm).reload
+ assert_equal apple, client.firm
end
def test_assignment_before_either_saved
@@ -261,7 +274,8 @@ class TestDefaultAutosaveAssociationOnABelongsToAssociation < ActiveRecord::Test
assert final_cut.persisted?
assert apple.persisted?
assert_equal apple, final_cut.firm
- assert_equal apple, final_cut.firm(true)
+ final_cut.association(:firm).reload
+ assert_equal apple, final_cut.firm
end
def test_store_two_association_with_one_save
@@ -385,6 +399,40 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociationWithAcceptsNestedAttrib
assert_not molecule.persisted?, 'Molecule should not be persisted when its electrons are invalid'
end
+ def test_errors_should_be_indexed_when_passed_as_array
+ guitar = Guitar.new
+ tuning_peg_valid = TuningPeg.new
+ tuning_peg_valid.pitch = 440.0
+ tuning_peg_invalid = TuningPeg.new
+
+ guitar.tuning_pegs = [tuning_peg_valid, tuning_peg_invalid]
+
+ assert_not tuning_peg_invalid.valid?
+ assert tuning_peg_valid.valid?
+ assert_not guitar.valid?
+ assert_equal ["is not a number"], guitar.errors["tuning_pegs[1].pitch"]
+ assert_not_equal ["is not a number"], guitar.errors["tuning_pegs.pitch"]
+ end
+
+ def test_errors_should_be_indexed_when_global_flag_is_set
+ old_attribute_config = ActiveRecord::Base.index_nested_attribute_errors
+ ActiveRecord::Base.index_nested_attribute_errors = true
+
+ molecule = Molecule.new
+ valid_electron = Electron.new(name: 'electron')
+ invalid_electron = Electron.new
+
+ molecule.electrons = [valid_electron, invalid_electron]
+
+ assert_not invalid_electron.valid?
+ assert valid_electron.valid?
+ assert_not molecule.valid?
+ assert_equal ["can't be blank"], molecule.errors["electrons[1].name"]
+ assert_not_equal ["can't be blank"], molecule.errors["electrons.name"]
+ ensure
+ ActiveRecord::Base.index_nested_attribute_errors = old_attribute_config
+ end
+
def test_valid_adding_with_nested_attributes
molecule = Molecule.new
valid_electron = Electron.new(name: 'electron')
@@ -456,7 +504,7 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociation < ActiveRecord::TestCa
assert_equal new_client, companies(:first_firm).clients_of_firm.last
assert !companies(:first_firm).save
assert !new_client.persisted?
- assert_equal 2, companies(:first_firm).clients_of_firm(true).size
+ assert_equal 2, companies(:first_firm).clients_of_firm.reload.size
end
def test_adding_before_save
@@ -481,7 +529,7 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociation < ActiveRecord::TestCa
assert_equal no_of_clients + 2, Client.count # Clients were saved to database.
assert_equal 2, new_firm.clients_of_firm.size
- assert_equal 2, new_firm.clients_of_firm(true).size
+ assert_equal 2, new_firm.clients_of_firm.reload.size
end
def test_assign_ids
@@ -510,7 +558,7 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociation < ActiveRecord::TestCa
company.name += '-changed'
assert_queries(2) { assert company.save }
assert new_client.persisted?
- assert_equal 3, company.clients_of_firm(true).size
+ assert_equal 3, company.clients_of_firm.reload.size
end
def test_build_many_before_save
@@ -519,7 +567,7 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociation < ActiveRecord::TestCa
company.name += '-changed'
assert_queries(3) { assert company.save }
- assert_equal 4, company.clients_of_firm(true).size
+ assert_equal 4, company.clients_of_firm.reload.size
end
def test_build_via_block_before_save
@@ -530,7 +578,7 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociation < ActiveRecord::TestCa
company.name += '-changed'
assert_queries(2) { assert company.save }
assert new_client.persisted?
- assert_equal 3, company.clients_of_firm(true).size
+ assert_equal 3, company.clients_of_firm.reload.size
end
def test_build_many_via_block_before_save
@@ -543,7 +591,7 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociation < ActiveRecord::TestCa
company.name += '-changed'
assert_queries(3) { assert company.save }
- assert_equal 4, company.clients_of_firm(true).size
+ assert_equal 4, company.clients_of_firm.reload.size
end
def test_replace_on_new_object
@@ -1142,6 +1190,13 @@ class TestAutosaveAssociationOnAHasOneAssociation < ActiveRecord::TestCase
def test_should_not_load_the_associated_model
assert_queries(1) { @pirate.catchphrase = 'Arr'; @pirate.save! }
end
+
+ def test_mark_for_destruction_is_ignored_without_autosave_true
+ ship = ShipWithoutNestedAttributes.new(name: "The Black Flag")
+ ship.parts.build.mark_for_destruction
+
+ assert_not ship.valid?
+ end
end
class TestAutosaveAssociationOnAHasOneThroughAssociation < ActiveRecord::TestCase
diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb
index 31c31e4329..dbbcaa075d 100644
--- a/activerecord/test/cases/base_test.rb
+++ b/activerecord/test/cases/base_test.rb
@@ -1,7 +1,4 @@
-# -*- coding: utf-8 -*-
-
require "cases/helper"
-require 'active_support/concurrency/latch'
require 'models/post'
require 'models/author'
require 'models/topic'
@@ -29,6 +26,7 @@ require 'models/bird'
require 'models/car'
require 'models/bulb'
require 'rexml/document'
+require 'concurrent/atomics'
class FirstAbstractClass < ActiveRecord::Base
self.abstract_class = true
@@ -206,7 +204,7 @@ class BasicsTest < ActiveRecord::TestCase
)
# For adapters which support microsecond resolution.
- if current_adapter?(:PostgreSQLAdapter, :SQLite3Adapter) || mysql_56?
+ if subsecond_precision_supported?
assert_equal 11, Topic.find(1).written_on.sec
assert_equal 223300, Topic.find(1).written_on.usec
assert_equal 9900, Topic.find(2).written_on.usec
@@ -946,6 +944,34 @@ class BasicsTest < ActiveRecord::TestCase
assert_equal BigDecimal("1000234000567.95"), m1.big_bank_balance
end
+ def test_numeric_fields_with_scale
+ m = NumericData.new(
+ :bank_balance => 1586.43122334,
+ :big_bank_balance => BigDecimal("234000567.952344"),
+ :world_population => 6000000000,
+ :my_house_population => 3
+ )
+ assert m.save
+
+ m1 = NumericData.find(m.id)
+ assert_not_nil m1
+
+ # As with migration_test.rb, we should make world_population >= 2**62
+ # to cover 64-bit platforms and test it is a Bignum, but the main thing
+ # is that it's an Integer.
+ assert_kind_of Integer, m1.world_population
+ assert_equal 6000000000, m1.world_population
+
+ assert_kind_of Fixnum, m1.my_house_population
+ assert_equal 3, m1.my_house_population
+
+ assert_kind_of BigDecimal, m1.bank_balance
+ assert_equal BigDecimal("1586.43"), m1.bank_balance
+
+ assert_kind_of BigDecimal, m1.big_bank_balance
+ assert_equal BigDecimal("234000567.95"), m1.big_bank_balance
+ end
+
def test_auto_id
auto = AutoId.new
auto.save
@@ -1271,9 +1297,10 @@ class BasicsTest < ActiveRecord::TestCase
end
def test_compute_type_no_method_error
- ActiveSupport::Dependencies.stubs(:safe_constantize).raises(NoMethodError)
- assert_raises NoMethodError do
- ActiveRecord::Base.send :compute_type, 'InvalidModel'
+ ActiveSupport::Dependencies.stub(:safe_constantize, proc{ raise NoMethodError }) do
+ assert_raises NoMethodError do
+ ActiveRecord::Base.send :compute_type, 'InvalidModel'
+ end
end
end
@@ -1287,18 +1314,20 @@ class BasicsTest < ActiveRecord::TestCase
error = e
end
- ActiveSupport::Dependencies.stubs(:safe_constantize).raises(e)
+ ActiveSupport::Dependencies.stub(:safe_constantize, proc{ raise e }) do
- exception = assert_raises NameError do
- ActiveRecord::Base.send :compute_type, 'InvalidModel'
+ exception = assert_raises NameError do
+ ActiveRecord::Base.send :compute_type, 'InvalidModel'
+ end
+ assert_equal error.message, exception.message
end
- assert_equal error.message, exception.message
end
def test_compute_type_argument_error
- ActiveSupport::Dependencies.stubs(:safe_constantize).raises(ArgumentError)
- assert_raises ArgumentError do
- ActiveRecord::Base.send :compute_type, 'InvalidModel'
+ ActiveSupport::Dependencies.stub(:safe_constantize, proc{ raise ArgumentError }) do
+ assert_raises ArgumentError do
+ ActiveRecord::Base.send :compute_type, 'InvalidModel'
+ end
end
end
@@ -1506,20 +1535,20 @@ class BasicsTest < ActiveRecord::TestCase
orig_handler = klass.connection_handler
new_handler = ActiveRecord::ConnectionAdapters::ConnectionHandler.new
after_handler = nil
- latch1 = ActiveSupport::Concurrency::Latch.new
- latch2 = ActiveSupport::Concurrency::Latch.new
+ latch1 = Concurrent::CountDownLatch.new
+ latch2 = Concurrent::CountDownLatch.new
t = Thread.new do
klass.connection_handler = new_handler
- latch1.release
- latch2.await
+ latch1.count_down
+ latch2.wait
after_handler = klass.connection_handler
end
- latch1.await
+ latch1.wait
klass.connection_handler = orig_handler
- latch2.release
+ latch2.count_down
t.join
assert_equal after_handler, new_handler
@@ -1542,4 +1571,22 @@ class BasicsTest < ActiveRecord::TestCase
assert_not topic.id_changed?
end
+
+ test "ignored columns are not present in columns_hash" do
+ cache_columns = Developer.connection.schema_cache.columns_hash(Developer.table_name)
+ assert_includes cache_columns.keys, 'first_name'
+ refute_includes Developer.columns_hash.keys, 'first_name'
+ end
+
+ test "ignored columns have no attribute methods" do
+ refute Developer.new.respond_to?(:first_name)
+ refute Developer.new.respond_to?(:first_name=)
+ refute Developer.new.respond_to?(:first_name?)
+ end
+
+ test "ignored columns don't prevent explicit declaration of attribute methods" do
+ assert Developer.new.respond_to?(:last_name)
+ assert Developer.new.respond_to?(:last_name=)
+ assert Developer.new.respond_to?(:last_name?)
+ end
end
diff --git a/activerecord/test/cases/batches_test.rb b/activerecord/test/cases/batches_test.rb
index 0791dde1f2..da65336305 100644
--- a/activerecord/test/cases/batches_test.rb
+++ b/activerecord/test/cases/batches_test.rb
@@ -53,7 +53,7 @@ class EachTest < ActiveRecord::TestCase
end
def test_each_should_raise_if_select_is_set_without_id
- assert_raise(RuntimeError) do
+ assert_raise(ArgumentError) do
Post.select(:title).find_each(batch_size: 1) { |post|
flunk "should not call this block"
}
@@ -69,13 +69,15 @@ class EachTest < ActiveRecord::TestCase
end
def test_warn_if_limit_scope_is_set
- ActiveRecord::Base.logger.expects(:warn)
- Post.limit(1).find_each { |post| post }
+ assert_called(ActiveRecord::Base.logger, :warn) do
+ Post.limit(1).find_each { |post| post }
+ end
end
def test_warn_if_order_scope_is_set
- ActiveRecord::Base.logger.expects(:warn)
- Post.order("title").find_each { |post| post }
+ assert_called(ActiveRecord::Base.logger, :warn) do
+ Post.order("title").find_each { |post| post }
+ end
end
def test_logger_not_required
@@ -137,14 +139,15 @@ class EachTest < ActiveRecord::TestCase
def test_find_in_batches_should_not_use_records_after_yielding_them_in_case_original_array_is_modified
not_a_post = "not a post"
- not_a_post.stubs(:id).raises(StandardError, "not_a_post had #id called on it")
-
- assert_nothing_raised do
- Post.find_in_batches(:batch_size => 1) do |batch|
- assert_kind_of Array, batch
- assert_kind_of Post, batch.first
+ def not_a_post.id; end
+ not_a_post.stub(:id, ->{ raise StandardError.new("not_a_post had #id called on it") }) do
+ assert_nothing_raised do
+ Post.find_in_batches(:batch_size => 1) do |batch|
+ assert_kind_of Array, batch
+ assert_kind_of Post, batch.first
- batch.map! { not_a_post }
+ batch.map! { not_a_post }
+ end
end
end
end
@@ -158,7 +161,7 @@ class EachTest < ActiveRecord::TestCase
end
# posts.first will be ordered using id only. Title order scope should not apply here
assert_not_equal first_post, posts.first
- assert_equal posts(:welcome), posts.first
+ assert_equal posts(:welcome).id, posts.first.id
end
def test_find_in_batches_should_not_ignore_the_default_scope_if_it_is_other_then_order
@@ -199,7 +202,7 @@ class EachTest < ActiveRecord::TestCase
def test_find_in_batches_should_return_an_enumerator
enum = nil
- assert_queries(0) do
+ assert_no_queries do
enum = Post.find_in_batches(:batch_size => 1)
end
assert_queries(4) do
@@ -210,6 +213,234 @@ class EachTest < ActiveRecord::TestCase
end
end
+ def test_in_batches_should_not_execute_any_query
+ assert_no_queries do
+ assert_kind_of ActiveRecord::Batches::BatchEnumerator, Post.in_batches(of: 2)
+ end
+ end
+
+ def test_in_batches_should_yield_relation_if_block_given
+ assert_queries(6) do
+ Post.in_batches(of: 2) do |relation|
+ assert_kind_of ActiveRecord::Relation, relation
+ end
+ end
+ end
+
+ def test_in_batches_should_be_enumerable_if_no_block_given
+ assert_queries(6) do
+ Post.in_batches(of: 2).each do |relation|
+ assert_kind_of ActiveRecord::Relation, relation
+ end
+ end
+ end
+
+ def test_in_batches_each_record_should_yield_record_if_block_is_given
+ assert_queries(6) do
+ Post.in_batches(of: 2).each_record do |post|
+ assert post.title.present?
+ assert_kind_of Post, post
+ end
+ end
+ end
+
+ def test_in_batches_each_record_should_return_enumerator_if_no_block_given
+ assert_queries(6) do
+ Post.in_batches(of: 2).each_record.with_index do |post, i|
+ assert post.title.present?
+ assert_kind_of Post, post
+ end
+ end
+ end
+
+ def test_in_batches_each_record_should_be_ordered_by_id
+ ids = Post.order('id ASC').pluck(:id)
+ assert_queries(6) do
+ Post.in_batches(of: 2).each_record.with_index do |post, i|
+ assert_equal ids[i], post.id
+ end
+ end
+ end
+
+ def test_in_batches_update_all_affect_all_records
+ assert_queries(6 + 6) do # 6 selects, 6 updates
+ Post.in_batches(of: 2).update_all(title: "updated-title")
+ end
+ assert_equal Post.all.pluck(:title), ["updated-title"] * Post.count
+ end
+
+ def test_in_batches_delete_all_should_not_delete_records_in_other_batches
+ not_deleted_count = Post.where('id <= 2').count
+ Post.where('id > 2').in_batches(of: 2).delete_all
+ assert_equal 0, Post.where('id > 2').count
+ assert_equal not_deleted_count, Post.count
+ end
+
+ def test_in_batches_should_not_be_loaded
+ Post.in_batches(of: 1) do |relation|
+ assert_not relation.loaded?
+ end
+
+ Post.in_batches(of: 1, load: false) do |relation|
+ assert_not relation.loaded?
+ end
+ end
+
+ def test_in_batches_should_be_loaded
+ Post.in_batches(of: 1, load: true) do |relation|
+ assert relation.loaded?
+ end
+ end
+
+ def test_in_batches_if_not_loaded_executes_more_queries
+ assert_queries(@total + 1) do
+ Post.in_batches(of: 1, load: false) do |relation|
+ assert_not relation.loaded?
+ end
+ end
+ end
+
+ def test_in_batches_should_return_relations
+ assert_queries(@total + 1) do
+ Post.in_batches(of: 1) do |relation|
+ assert_kind_of ActiveRecord::Relation, relation
+ end
+ end
+ end
+
+ def test_in_batches_should_start_from_the_start_option
+ post = Post.order('id ASC').where('id >= ?', 2).first
+ assert_queries(2) do
+ relation = Post.in_batches(of: 1, begin_at: 2).first
+ assert_equal post, relation.first
+ end
+ end
+
+ def test_in_batches_should_end_at_the_end_option
+ post = Post.order('id DESC').where('id <= ?', 5).first
+ assert_queries(7) do
+ relation = Post.in_batches(of: 1, end_at: 5, load: true).reverse_each.first
+ assert_equal post, relation.last
+ end
+ end
+
+ def test_in_batches_shouldnt_execute_query_unless_needed
+ assert_queries(2) do
+ Post.in_batches(of: @total) { |relation| assert_kind_of ActiveRecord::Relation, relation }
+ end
+
+ assert_queries(1) do
+ Post.in_batches(of: @total + 1) { |relation| assert_kind_of ActiveRecord::Relation, relation }
+ end
+ end
+
+ def test_in_batches_should_quote_batch_order
+ c = Post.connection
+ assert_sql(/ORDER BY #{c.quote_table_name('posts')}.#{c.quote_column_name('id')}/) do
+ Post.in_batches(of: 1) do |relation|
+ assert_kind_of ActiveRecord::Relation, relation
+ assert_kind_of Post, relation.first
+ end
+ end
+ end
+
+ def test_in_batches_should_not_use_records_after_yielding_them_in_case_original_array_is_modified
+ not_a_post = "not a post"
+ def not_a_post.id
+ raise StandardError.new("not_a_post had #id called on it")
+ end
+
+ assert_nothing_raised do
+ Post.in_batches(of: 1) do |relation|
+ assert_kind_of ActiveRecord::Relation, relation
+ assert_kind_of Post, relation.first
+
+ relation = [not_a_post] * relation.count
+ end
+ end
+ end
+
+ def test_in_batches_should_not_ignore_default_scope_without_order_statements
+ special_posts_ids = SpecialPostWithDefaultScope.all.map(&:id).sort
+ posts = []
+ SpecialPostWithDefaultScope.in_batches do |relation|
+ posts.concat(relation)
+ end
+ assert_equal special_posts_ids, posts.map(&:id)
+ end
+
+ def test_in_batches_should_not_modify_passed_options
+ assert_nothing_raised do
+ Post.in_batches({ of: 42, begin_at: 1 }.freeze){}
+ end
+ end
+
+ def test_in_batches_should_use_any_column_as_primary_key
+ nick_order_subscribers = Subscriber.order('nick asc')
+ start_nick = nick_order_subscribers.second.nick
+
+ subscribers = []
+ Subscriber.in_batches(of: 1, begin_at: start_nick) do |relation|
+ subscribers.concat(relation)
+ end
+
+ assert_equal nick_order_subscribers[1..-1].map(&:id), subscribers.map(&:id)
+ end
+
+ def test_in_batches_should_use_any_column_as_primary_key_when_start_is_not_specified
+ assert_queries(Subscriber.count + 1) do
+ Subscriber.in_batches(of: 1, load: true) do |relation|
+ assert_kind_of ActiveRecord::Relation, relation
+ assert_kind_of Subscriber, relation.first
+ end
+ end
+ end
+
+ def test_in_batches_should_return_an_enumerator
+ enum = nil
+ assert_no_queries do
+ enum = Post.in_batches(of: 1)
+ end
+ assert_queries(4) do
+ enum.first(4) do |relation|
+ assert_kind_of ActiveRecord::Relation, relation
+ assert_kind_of Post, relation.first
+ end
+ end
+ end
+
+ def test_in_batches_relations_should_not_overlap_with_each_other
+ seen_posts = []
+ Post.in_batches(of: 2, load: true) do |relation|
+ relation.to_a.each do |post|
+ assert_not seen_posts.include?(post)
+ seen_posts << post
+ end
+ end
+ end
+
+ def test_in_batches_relations_with_condition_should_not_overlap_with_each_other
+ seen_posts = []
+ author_id = Post.first.author_id
+ posts_by_author = Post.where(author_id: author_id)
+ Post.in_batches(of: 2) do |batch|
+ seen_posts += batch.where(author_id: author_id)
+ end
+
+ assert_equal posts_by_author.pluck(:id).sort, seen_posts.map(&:id).sort
+ end
+
+ def test_in_batches_relations_update_all_should_not_affect_matching_records_in_other_batches
+ Post.update_all(author_id: 0)
+ person = Post.last
+ person.update_attributes(author_id: 1)
+
+ Post.in_batches(of: 2) do |batch|
+ batch.where('author_id >= 1').update_all('author_id = author_id + 1')
+ end
+ assert_equal 2, person.reload.author_id # incremented only once
+ end
+
def test_find_in_batches_start_deprecated
assert_deprecated do
assert_queries(@total) do
diff --git a/activerecord/test/cases/cache_key_test.rb b/activerecord/test/cases/cache_key_test.rb
new file mode 100644
index 0000000000..bb2829b3c1
--- /dev/null
+++ b/activerecord/test/cases/cache_key_test.rb
@@ -0,0 +1,25 @@
+require "cases/helper"
+
+module ActiveRecord
+ class CacheKeyTest < ActiveRecord::TestCase
+ self.use_transactional_tests = false
+
+ class CacheMe < ActiveRecord::Base; end
+
+ setup do
+ @connection = ActiveRecord::Base.connection
+ @connection.create_table(:cache_mes) { |t| t.timestamps }
+ end
+
+ teardown do
+ @connection.drop_table :cache_mes, if_exists: true
+ end
+
+ test "test_cache_key_format_is_not_too_precise" do
+ record = CacheMe.create
+ key = record.cache_key
+
+ assert_equal key, record.reload.cache_key
+ end
+ end
+end
diff --git a/activerecord/test/cases/calculations_test.rb b/activerecord/test/cases/calculations_test.rb
index aa10817527..4a0e6f497f 100644
--- a/activerecord/test/cases/calculations_test.rb
+++ b/activerecord/test/cases/calculations_test.rb
@@ -31,11 +31,20 @@ class CalculationsTest < ActiveRecord::TestCase
assert_equal 318, Account.sum(:credit_limit)
end
+ def test_should_sum_arel_attribute
+ assert_equal 318, Account.sum(Account.arel_table[:credit_limit])
+ end
+
def test_should_average_field
value = Account.average(:credit_limit)
assert_equal 53.0, value
end
+ def test_should_average_arel_attribute
+ value = Account.average(Account.arel_table[:credit_limit])
+ assert_equal 53.0, value
+ end
+
def test_should_resolve_aliased_attributes
assert_equal 318, Account.sum(:available_credit)
end
@@ -60,14 +69,26 @@ class CalculationsTest < ActiveRecord::TestCase
assert_equal 60, Account.maximum(:credit_limit)
end
+ def test_should_get_maximum_of_arel_attribute
+ assert_equal 60, Account.maximum(Account.arel_table[:credit_limit])
+ end
+
def test_should_get_maximum_of_field_with_include
assert_equal 55, Account.where("companies.name != 'Summit'").references(:companies).includes(:firm).maximum(:credit_limit)
end
+ def test_should_get_maximum_of_arel_attribute_with_include
+ assert_equal 55, Account.where("companies.name != 'Summit'").references(:companies).includes(:firm).maximum(Account.arel_table[:credit_limit])
+ end
+
def test_should_get_minimum_of_field
assert_equal 50, Account.minimum(:credit_limit)
end
+ def test_should_get_minimum_of_arel_attribute
+ assert_equal 50, Account.minimum(Account.arel_table[:credit_limit])
+ end
+
def test_should_group_by_field
c = Account.group(:firm_id).sum(:credit_limit)
[1,6,2].each do |firm_id|
@@ -102,6 +123,25 @@ class CalculationsTest < ActiveRecord::TestCase
assert_equal 60, c[2]
end
+ def test_should_generate_valid_sql_with_joins_and_group
+ assert_nothing_raised ActiveRecord::StatementInvalid do
+ AuditLog.joins(:developer).group(:id).count
+ end
+ end
+
+ def test_should_calculate_against_given_relation
+ developer = Developer.create!(name: "developer")
+ developer.audit_logs.create!(message: "first log")
+ developer.audit_logs.create!(message: "second log")
+
+ c = developer.audit_logs.joins(:developer).group(:id).count
+
+ assert_equal developer.audit_logs.count, c.size
+ developer.audit_logs.each do |log|
+ assert_equal 1, c[log.id]
+ end
+ end
+
def test_should_order_by_grouped_field
c = Account.group(:firm_id).order("firm_id").sum(:credit_limit)
assert_equal [1, 2, 6, 9], c.keys.compact
@@ -131,6 +171,14 @@ class CalculationsTest < ActiveRecord::TestCase
assert_equal 3, accounts.select(:firm_id).count
end
+ def test_limit_should_apply_before_count_arel_attribute
+ accounts = Account.limit(3).where('firm_id IS NOT NULL')
+
+ firm_id_attribute = Account.arel_table[:firm_id]
+ assert_equal 3, accounts.count(firm_id_attribute)
+ assert_equal 3, accounts.select(firm_id_attribute).count
+ end
+
def test_count_should_shortcut_with_limit_zero
accounts = Account.limit(0)
@@ -353,10 +401,23 @@ class CalculationsTest < ActiveRecord::TestCase
assert_equal 6, Account.select("DISTINCT accounts.id").includes(:firm).count
end
+ def test_count_selected_arel_attribute
+ assert_equal 5, Account.select(Account.arel_table[:firm_id]).count
+ assert_equal 4, Account.distinct.select(Account.arel_table[:firm_id]).count
+ end
+
def test_count_with_column_parameter
assert_equal 5, Account.count(:firm_id)
end
+ def test_count_with_arel_attribute
+ assert_equal 5, Account.count(Account.arel_table[:firm_id])
+ end
+
+ def test_count_with_arel_star
+ assert_equal 6, Account.count(Arel.star)
+ end
+
def test_count_with_distinct
assert_equal 4, Account.select(:credit_limit).distinct.count
@@ -378,12 +439,27 @@ class CalculationsTest < ActiveRecord::TestCase
assert_equal 4, Account.joins(:firm).distinct.count('companies.id')
end
+ def test_count_arel_attribute_in_joined_table_with
+ assert_equal 5, Account.joins(:firm).count(Company.arel_table[:id])
+ assert_equal 4, Account.joins(:firm).distinct.count(Company.arel_table[:id])
+ end
+
+ def test_count_selected_arel_attribute_in_joined_table
+ assert_equal 5, Account.joins(:firm).select(Company.arel_table[:id]).count
+ assert_equal 4, Account.joins(:firm).distinct.select(Company.arel_table[:id]).count
+ end
+
def test_should_count_field_in_joined_table_with_group_by
c = Account.group('accounts.firm_id').joins(:firm).count('companies.id')
[1,6,2,9].each { |firm_id| assert c.keys.include?(firm_id) }
end
+ def test_should_count_field_of_root_table_with_conflicting_group_by_column
+ assert_equal({ 1 => 1 }, Firm.joins(:accounts).group(:firm_id).count)
+ assert_equal({ 1 => 1 }, Firm.joins(:accounts).group('accounts.firm_id').count)
+ end
+
def test_count_with_no_parameters_isnt_deprecated
assert_not_deprecated { Account.count }
end
@@ -681,4 +757,36 @@ class CalculationsTest < ActiveRecord::TestCase
end
assert block_called
end
+
+ def test_having_with_strong_parameters
+ protected_params = Class.new do
+ attr_reader :permitted
+ alias :permitted? :permitted
+
+ def initialize(parameters)
+ @parameters = parameters
+ @permitted = false
+ end
+
+ def to_h
+ @parameters
+ end
+
+ def permit!
+ @permitted = true
+ self
+ end
+ end
+
+ params = protected_params.new(credit_limit: '50')
+
+ assert_raises(ActiveModel::ForbiddenAttributesError) do
+ Account.group(:id).having(params)
+ end
+
+ result = Account.group(:id).having(params.permit!)
+ assert_equal 50, result[0].credit_limit
+ assert_equal 50, result[1].credit_limit
+ assert_equal 50, result[2].credit_limit
+ end
end
diff --git a/activerecord/test/cases/collection_cache_key_test.rb b/activerecord/test/cases/collection_cache_key_test.rb
new file mode 100644
index 0000000000..53058c5a4a
--- /dev/null
+++ b/activerecord/test/cases/collection_cache_key_test.rb
@@ -0,0 +1,70 @@
+require "cases/helper"
+require "models/computer"
+require "models/developer"
+require "models/project"
+require "models/topic"
+require "models/post"
+require "models/comment"
+
+module ActiveRecord
+ class CollectionCacheKeyTest < ActiveRecord::TestCase
+ fixtures :developers, :projects, :developers_projects, :topics, :comments, :posts
+
+ test "collection_cache_key on model" do
+ assert_match(/\Adevelopers\/query-(\h+)-(\d+)-(\d+)\Z/, Developer.collection_cache_key)
+ end
+
+ test "cache_key for relation" do
+ developers = Developer.where(name: "David")
+ last_developer_timestamp = developers.order(updated_at: :desc).first.updated_at
+
+ assert_match(/\Adevelopers\/query-(\h+)-(\d+)-(\d+)\Z/, developers.cache_key)
+
+ /\Adevelopers\/query-(\h+)-(\d+)-(\d+)\Z/ =~ developers.cache_key
+
+ assert_equal Digest::MD5.hexdigest(developers.to_sql), $1
+ assert_equal developers.count.to_s, $2
+ assert_equal last_developer_timestamp.to_s(ActiveRecord::Base.cache_timestamp_format), $3
+ end
+
+ test "it triggers at most one query" do
+ developers = Developer.where(name: "David")
+
+ assert_queries(1) { developers.cache_key }
+ assert_queries(0) { developers.cache_key }
+ end
+
+ test "it doesn't trigger any query if the relation is already loaded" do
+ developers = Developer.where(name: "David").load
+ assert_queries(0) { developers.cache_key }
+ end
+
+ test "relation cache_key changes when the sql query changes" do
+ developers = Developer.where(name: "David")
+ other_relation = Developer.where(name: "David").where("1 = 1")
+
+ assert_not_equal developers.cache_key, other_relation.cache_key
+ end
+
+ test "cache_key for empty relation" do
+ developers = Developer.where(name: "Non Existent Developer")
+ assert_match(/\Adevelopers\/query-(\h+)-0\Z/, developers.cache_key)
+ end
+
+ test "cache_key with custom timestamp column" do
+ topics = Topic.where("title like ?", "%Topic%")
+ last_topic_timestamp = topics(:fifth).written_on.utc.to_s(:usec)
+ assert_match(last_topic_timestamp, topics.cache_key(:written_on))
+ end
+
+ test "cache_key with unknown timestamp column" do
+ topics = Topic.where("title like ?", "%Topic%")
+ assert_raises(ActiveRecord::StatementInvalid) { topics.cache_key(:published_at) }
+ end
+
+ test "collection proxy provides a cache_key" do
+ developers = projects(:active_record).developers
+ assert_match(/\Adevelopers\/query-(\h+)-(\d+)-(\d+)\Z/, developers.cache_key)
+ end
+ end
+end
diff --git a/activerecord/test/cases/connection_adapters/mysql_type_lookup_test.rb b/activerecord/test/cases/connection_adapters/mysql_type_lookup_test.rb
index 80244d1439..2749273884 100644
--- a/activerecord/test/cases/connection_adapters/mysql_type_lookup_test.rb
+++ b/activerecord/test/cases/connection_adapters/mysql_type_lookup_test.rb
@@ -22,6 +22,10 @@ module ActiveRecord
assert_lookup_type :string, "SET('one', 'two', 'three')"
end
+ def test_set_type_with_value_matching_other_type
+ assert_lookup_type :string, "SET('unicode', '8bit', 'none', 'time')"
+ end
+
def test_enum_type_with_value_matching_other_type
assert_lookup_type :string, "ENUM('unicode', '8bit', 'none')"
end
diff --git a/activerecord/test/cases/connection_adapters/schema_cache_test.rb b/activerecord/test/cases/connection_adapters/schema_cache_test.rb
index c7531f5418..db832fe55d 100644
--- a/activerecord/test/cases/connection_adapters/schema_cache_test.rb
+++ b/activerecord/test/cases/connection_adapters/schema_cache_test.rb
@@ -29,7 +29,7 @@ module ActiveRecord
def test_clearing
@cache.columns('posts')
@cache.columns_hash('posts')
- @cache.tables('posts')
+ @cache.data_sources('posts')
@cache.primary_keys('posts')
@cache.clear!
@@ -40,17 +40,22 @@ module ActiveRecord
def test_dump_and_load
@cache.columns('posts')
@cache.columns_hash('posts')
- @cache.tables('posts')
+ @cache.data_sources('posts')
@cache.primary_keys('posts')
@cache = Marshal.load(Marshal.dump(@cache))
assert_equal 11, @cache.columns('posts').size
assert_equal 11, @cache.columns_hash('posts').size
- assert @cache.tables('posts')
+ assert @cache.data_sources('posts')
assert_equal 'id', @cache.primary_keys('posts')
end
+ def test_table_methods_deprecation
+ assert_deprecated { assert @cache.table_exists?('posts') }
+ assert_deprecated { assert @cache.tables('posts') }
+ assert_deprecated { @cache.clear_table_cache!('posts') }
+ end
end
end
end
diff --git a/activerecord/test/cases/connection_pool_test.rb b/activerecord/test/cases/connection_pool_test.rb
index c905772193..7ef5c93a48 100644
--- a/activerecord/test/cases/connection_pool_test.rb
+++ b/activerecord/test/cases/connection_pool_test.rb
@@ -1,5 +1,5 @@
require "cases/helper"
-require 'active_support/concurrency/latch'
+require 'concurrent/atomics'
module ActiveRecord
module ConnectionAdapters
@@ -133,15 +133,15 @@ module ActiveRecord
end
def test_reap_inactive
- ready = ActiveSupport::Concurrency::Latch.new
+ ready = Concurrent::CountDownLatch.new
@pool.checkout
child = Thread.new do
@pool.checkout
@pool.checkout
- ready.release
+ ready.count_down
Thread.stop
end
- ready.await
+ ready.wait
assert_equal 3, active_connections(@pool).size
@@ -360,13 +360,13 @@ module ActiveRecord
def test_concurrent_connection_establishment
assert_operator @pool.connections.size, :<=, 1
- all_threads_in_new_connection = ActiveSupport::Concurrency::Latch.new(@pool.size - @pool.connections.size)
- all_go = ActiveSupport::Concurrency::Latch.new
+ all_threads_in_new_connection = Concurrent::CountDownLatch.new(@pool.size - @pool.connections.size)
+ all_go = Concurrent::CountDownLatch.new
@pool.singleton_class.class_eval do
define_method(:new_connection) do
- all_threads_in_new_connection.release
- all_go.await
+ all_threads_in_new_connection.count_down
+ all_go.wait
super()
end
end
@@ -381,14 +381,14 @@ module ActiveRecord
# the kernel of the whole test is here, everything else is just scaffolding,
# this latch will not be released unless conn. pool allows for concurrent
# connection creation
- all_threads_in_new_connection.await
+ all_threads_in_new_connection.wait
end
rescue Timeout::Error
flunk 'pool unable to establish connections concurrently or implementation has ' <<
'changed, this test then needs to patch a different :new_connection method'
ensure
# clean up the threads
- all_go.release
+ all_go.count_down
connecting_threads.map(&:join)
end
end
@@ -441,11 +441,11 @@ module ActiveRecord
with_single_connection_pool do |pool|
[:disconnect, :disconnect!, :clear_reloadable_connections, :clear_reloadable_connections!].each do |group_action_method|
conn = pool.connection # drain the only available connection
- second_thread_done = ActiveSupport::Concurrency::Latch.new
+ second_thread_done = Concurrent::CountDownLatch.new
# create a first_thread and let it get into the FIFO queue first
first_thread = Thread.new do
- pool.with_connection { second_thread_done.await }
+ pool.with_connection { second_thread_done.wait }
end
# wait for first_thread to get in queue
@@ -456,7 +456,7 @@ module ActiveRecord
# first_thread when a connection is made available
second_thread = Thread.new do
pool.send(group_action_method)
- second_thread_done.release
+ second_thread_done.count_down
end
# wait for second_thread to get in queue
@@ -471,7 +471,7 @@ module ActiveRecord
failed = true unless second_thread.join(2)
#--- post test clean up start
- second_thread_done.release if failed
+ second_thread_done.count_down if failed
# after `pool.disconnect()` the first thread will be left stuck in queue, no need to wait for
# it to timeout with ConnectionTimeoutError
diff --git a/activerecord/test/cases/counter_cache_test.rb b/activerecord/test/cases/counter_cache_test.rb
index 1f5055b2a2..922cb59280 100644
--- a/activerecord/test/cases/counter_cache_test.rb
+++ b/activerecord/test/cases/counter_cache_test.rb
@@ -1,6 +1,7 @@
require 'cases/helper'
require 'models/topic'
require 'models/car'
+require 'models/aircraft'
require 'models/wheel'
require 'models/engine'
require 'models/reply'
@@ -198,4 +199,16 @@ class CounterCacheTest < ActiveRecord::TestCase
assert_equal 2, car.engines_count
assert_equal 2, car.reload.engines_count
end
+
+ test "update counters in a polymorphic relationship" do
+ aircraft = Aircraft.create!
+
+ assert_difference 'aircraft.reload.wheels_count' do
+ aircraft.wheels << Wheel.create!
+ end
+
+ assert_difference 'aircraft.reload.wheels_count', -1 do
+ aircraft.wheels.first.destroy
+ end
+ end
end
diff --git a/activerecord/test/cases/dirty_test.rb b/activerecord/test/cases/dirty_test.rb
index f5aaf22e13..cd1967c373 100644
--- a/activerecord/test/cases/dirty_test.rb
+++ b/activerecord/test/cases/dirty_test.rb
@@ -89,7 +89,7 @@ class DirtyTest < ActiveRecord::TestCase
target = Class.new(ActiveRecord::Base)
target.table_name = 'pirates'
- pirate = target.create
+ pirate = target.create!
pirate.created_on = pirate.created_on
assert !pirate.created_on_changed?
end
@@ -467,8 +467,10 @@ class DirtyTest < ActiveRecord::TestCase
topic.save!
updated_at = topic.updated_at
- topic.content[:hello] = 'world'
- topic.save!
+ travel(1.second) do
+ topic.content[:hello] = 'world'
+ topic.save!
+ end
assert_not_equal updated_at, topic.updated_at
assert_equal 'world', topic.content[:hello]
@@ -521,6 +523,9 @@ class DirtyTest < ActiveRecord::TestCase
assert_equal Hash.new, pirate.previous_changes
pirate = Pirate.find_by_catchphrase("arrr")
+
+ travel(1.second)
+
pirate.catchphrase = "Me Maties!"
pirate.save!
@@ -532,6 +537,9 @@ class DirtyTest < ActiveRecord::TestCase
assert !pirate.previous_changes.key?('created_on')
pirate = Pirate.find_by_catchphrase("Me Maties!")
+
+ travel(1.second)
+
pirate.catchphrase = "Thar She Blows!"
pirate.save
@@ -542,6 +550,8 @@ class DirtyTest < ActiveRecord::TestCase
assert !pirate.previous_changes.key?('parrot_id')
assert !pirate.previous_changes.key?('created_on')
+ travel(1.second)
+
pirate = Pirate.find_by_catchphrase("Thar She Blows!")
pirate.update(catchphrase: "Ahoy!")
@@ -552,6 +562,8 @@ class DirtyTest < ActiveRecord::TestCase
assert !pirate.previous_changes.key?('parrot_id')
assert !pirate.previous_changes.key?('created_on')
+ travel(1.second)
+
pirate = Pirate.find_by_catchphrase("Ahoy!")
pirate.update_attribute(:catchphrase, "Ninjas suck!")
@@ -561,6 +573,8 @@ class DirtyTest < ActiveRecord::TestCase
assert_not_nil pirate.previous_changes['updated_on'][1]
assert !pirate.previous_changes.key?('parrot_id')
assert !pirate.previous_changes.key?('created_on')
+ ensure
+ travel_back
end
if ActiveRecord::Base.connection.supports_migrations?
@@ -578,6 +592,7 @@ class DirtyTest < ActiveRecord::TestCase
end
def test_datetime_attribute_can_be_updated_with_fractional_seconds
+ skip "Fractional seconds are not supported" unless subsecond_precision_supported?
in_time_zone 'Paris' do
target = Class.new(ActiveRecord::Base)
target.table_name = 'topics'
diff --git a/activerecord/test/cases/enum_test.rb b/activerecord/test/cases/enum_test.rb
index 769b171717..7c930de97b 100644
--- a/activerecord/test/cases/enum_test.rb
+++ b/activerecord/test/cases/enum_test.rb
@@ -40,43 +40,43 @@ class EnumTest < ActiveRecord::TestCase
published, written = Book.statuses[:published], Book.statuses[:written]
assert_equal @book, Book.where(status: published).first
- refute_equal @book, Book.where(status: written).first
+ assert_not_equal @book, Book.where(status: written).first
assert_equal @book, Book.where(status: [published]).first
- refute_equal @book, Book.where(status: [written]).first
- refute_equal @book, Book.where("status <> ?", published).first
+ assert_not_equal @book, Book.where(status: [written]).first
+ assert_not_equal @book, Book.where("status <> ?", published).first
assert_equal @book, Book.where("status <> ?", written).first
end
test "find via where with symbols" do
assert_equal @book, Book.where(status: :published).first
- refute_equal @book, Book.where(status: :written).first
+ assert_not_equal @book, Book.where(status: :written).first
assert_equal @book, Book.where(status: [:published]).first
- refute_equal @book, Book.where(status: [:written]).first
- refute_equal @book, Book.where.not(status: :published).first
+ assert_not_equal @book, Book.where(status: [:written]).first
+ assert_not_equal @book, Book.where.not(status: :published).first
assert_equal @book, Book.where.not(status: :written).first
end
test "find via where with strings" do
assert_equal @book, Book.where(status: "published").first
- refute_equal @book, Book.where(status: "written").first
+ assert_not_equal @book, Book.where(status: "written").first
assert_equal @book, Book.where(status: ["published"]).first
- refute_equal @book, Book.where(status: ["written"]).first
- refute_equal @book, Book.where.not(status: "published").first
+ assert_not_equal @book, Book.where(status: ["written"]).first
+ assert_not_equal @book, Book.where.not(status: "published").first
assert_equal @book, Book.where.not(status: "written").first
end
test "build from scope" do
assert Book.written.build.written?
- refute Book.written.build.proposed?
+ assert_not Book.written.build.proposed?
end
test "build from where" do
assert Book.where(status: Book.statuses[:written]).build.written?
- refute Book.where(status: Book.statuses[:written]).build.proposed?
+ assert_not Book.where(status: Book.statuses[:written]).build.proposed?
assert Book.where(status: :written).build.written?
- refute Book.where(status: :written).build.proposed?
+ assert_not Book.where(status: :written).build.proposed?
assert Book.where(status: "written").build.written?
- refute Book.where(status: "written").build.proposed?
+ assert_not Book.where(status: "written").build.proposed?
end
test "update by declaration" do
@@ -405,4 +405,10 @@ class EnumTest < ActiveRecord::TestCase
assert_not @book.in_spanish?
assert_not @book.in_french?
end
+
+ test "uses default status when no status is provided in fixtures" do
+ book = books(:tlg)
+ assert book.proposed?, "expected fixture to default to proposed status"
+ assert book.in_english?, "expected fixture to default to english language"
+ end
end
diff --git a/activerecord/test/cases/errors_test.rb b/activerecord/test/cases/errors_test.rb
new file mode 100644
index 0000000000..0711a372f2
--- /dev/null
+++ b/activerecord/test/cases/errors_test.rb
@@ -0,0 +1,16 @@
+require_relative "../cases/helper"
+
+class ErrorsTest < ActiveRecord::TestCase
+ def test_can_be_instantiated_with_no_args
+ base = ActiveRecord::ActiveRecordError
+ error_klasses = ObjectSpace.each_object(Class).select { |klass| klass < base }
+
+ error_klasses.each do |error_klass|
+ begin
+ error_klass.new.inspect
+ rescue ArgumentError
+ raise "Instance of #{error_klass} can't be initialized with no arguments"
+ end
+ end
+ end
+end
diff --git a/activerecord/test/cases/explain_test.rb b/activerecord/test/cases/explain_test.rb
index f1d5511bb8..64dfd86ce2 100644
--- a/activerecord/test/cases/explain_test.rb
+++ b/activerecord/test/cases/explain_test.rb
@@ -39,38 +39,49 @@ if ActiveRecord::Base.connection.supports_explain?
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)
+ stub_explain_for_query_plans do
+ expected = sqls.map {|sql| "EXPLAIN for: #{sql}\nquery plan #{sql}"}.join("\n")
+ assert_equal expected, base.exec_explain(queries)
+ end
end
def test_exec_explain_with_binds
- cols = [Object.new, Object.new]
- cols[0].expects(:name).returns('wadus')
- cols[1].expects(:name).returns('chaflan')
+ object = Struct.new(:name)
+ cols = [object.new('wadus'), object.new('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
+ stub_explain_for_query_plans(["query plan foo\n", "query plan bar\n"]) do
+ 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)
+ EXPLAIN for: #{sqls[1]} [["chaflan", 2]]
+ query plan bar
+ SQL
+ assert_equal expected, base.exec_explain(queries)
+ end
end
def test_unsupported_connection_adapter
- connection.stubs(:supports_explain?).returns(false)
+ connection.stub(:supports_explain?, false) do
+ assert_not_called(base.logger, :warn) do
+ Car.where(:name => 'honda').to_a
+ end
+ end
+ end
- base.logger.expects(:warn).never
+ private
- Car.where(:name => 'honda').to_a
- end
+ def stub_explain_for_query_plans(query_plans = ['query plan foo', 'query plan bar'])
+ explain_called = 0
+
+ connection.stub(:explain, proc{ explain_called += 1; query_plans[explain_called - 1] }) do
+ yield
+ end
+ end
end
end
diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb
index 4b819a82e8..6686ce012d 100644
--- a/activerecord/test/cases/finder_test.rb
+++ b/activerecord/test/cases/finder_test.rb
@@ -178,8 +178,9 @@ class FinderTest < ActiveRecord::TestCase
end
def test_exists_does_not_instantiate_records
- Developer.expects(:instantiate).never
- Developer.exists?
+ assert_not_called(Developer, :instantiate) do
+ Developer.exists?
+ end
end
def test_find_by_array_of_one_id
@@ -264,6 +265,12 @@ class FinderTest < ActiveRecord::TestCase
assert_equal [Account], accounts.collect(&:class).uniq
end
+ def test_find_by_association_subquery
+ author = authors(:david)
+ assert_equal author.post, Post.find_by(author: Author.where(id: author))
+ assert_equal author.post, Post.find_by(author_id: Author.where(id: author))
+ end
+
def test_take
assert_equal topics(:first), Topic.take
end
@@ -700,12 +707,12 @@ class FinderTest < ActiveRecord::TestCase
end
def test_bind_arity
- assert_nothing_raised { bind '' }
+ assert_nothing_raised { bind '' }
assert_raise(ActiveRecord::PreparedStatementInvalid) { bind '', 1 }
assert_raise(ActiveRecord::PreparedStatementInvalid) { bind '?' }
- assert_nothing_raised { bind '?', 1 }
- assert_raise(ActiveRecord::PreparedStatementInvalid) { bind '?', 1, 1 }
+ assert_nothing_raised { bind '?', 1 }
+ assert_raise(ActiveRecord::PreparedStatementInvalid) { bind '?', 1, 1 }
end
def test_named_bind_variables
@@ -720,6 +727,12 @@ class FinderTest < ActiveRecord::TestCase
assert_kind_of Time, Topic.where(["id = :id", { id: 1 }]).first.written_on
end
+ def test_named_bind_arity
+ assert_nothing_raised { bind "name = :name", { name: "37signals" } }
+ assert_nothing_raised { bind "name = :name", { name: "37signals", id: 1 } }
+ assert_raise(ActiveRecord::PreparedStatementInvalid) { bind "name = :name", { id: 1 } }
+ end
+
class SimpleEnumerable
include Enumerable
diff --git a/activerecord/test/cases/fixture_set/file_test.rb b/activerecord/test/cases/fixture_set/file_test.rb
index 92efa8aca7..242e7a9bec 100644
--- a/activerecord/test/cases/fixture_set/file_test.rb
+++ b/activerecord/test/cases/fixture_set/file_test.rb
@@ -123,6 +123,18 @@ END
end
end
+ def test_removes_fixture_config_row
+ File.open(::File.join(FIXTURES_ROOT, 'other_posts.yml')) do |fh|
+ assert_equal(['second_welcome'], fh.each.map { |name, _| name })
+ end
+ end
+
+ def test_extracts_model_class_from_config_row
+ File.open(::File.join(FIXTURES_ROOT, 'other_posts.yml')) do |fh|
+ assert_equal 'Post', fh.model_class
+ end
+ end
+
private
def tmp_yaml(name, contents)
t = Tempfile.new name
diff --git a/activerecord/test/cases/fixtures_test.rb b/activerecord/test/cases/fixtures_test.rb
index 03a187ae92..f30ed4fcc8 100644
--- a/activerecord/test/cases/fixtures_test.rb
+++ b/activerecord/test/cases/fixtures_test.rb
@@ -7,16 +7,16 @@ require 'models/binary'
require 'models/book'
require 'models/bulb'
require 'models/category'
+require 'models/comment'
require 'models/company'
require 'models/computer'
require 'models/course'
require 'models/developer'
-require 'models/computer'
+require 'models/doubloon'
require 'models/joke'
require 'models/matey'
require 'models/parrot'
require 'models/pirate'
-require 'models/doubloon'
require 'models/post'
require 'models/randomly_named_c1'
require 'models/reply'
@@ -217,6 +217,13 @@ class FixturesTest < ActiveRecord::TestCase
end
end
+ def test_yaml_file_with_invalid_column
+ e = assert_raise(ActiveRecord::Fixture::FixtureError) do
+ ActiveRecord::FixtureSet.create_fixtures(FIXTURES_ROOT + "/naked/yml", "parrots")
+ end
+ assert_equal(%(table "parrots" has no column named "arrr".), e.message)
+ end
+
def test_omap_fixtures
assert_nothing_raised do
fixtures = ActiveRecord::FixtureSet.new(Account.connection, 'categories', Category, FIXTURES_ROOT + "/categories_ordered")
@@ -252,18 +259,19 @@ class FixturesTest < ActiveRecord::TestCase
def test_fixtures_are_set_up_with_database_env_variable
db_url_tmp = ENV['DATABASE_URL']
ENV['DATABASE_URL'] = "sqlite3::memory:"
- ActiveRecord::Base.stubs(:configurations).returns({})
- test_case = Class.new(ActiveRecord::TestCase) do
- fixtures :accounts
+ ActiveRecord::Base.stub(:configurations, {}) do
+ test_case = Class.new(ActiveRecord::TestCase) do
+ fixtures :accounts
- def test_fixtures
- assert accounts(:signals37)
+ def test_fixtures
+ assert accounts(:signals37)
+ end
end
- end
- result = test_case.new(:test_fixtures).run
+ result = test_case.new(:test_fixtures).run
- assert result.passed?, "Expected #{result.name} to pass:\n#{result}"
+ assert result.passed?, "Expected #{result.name} to pass:\n#{result}"
+ end
ensure
ENV['DATABASE_URL'] = db_url_tmp
end
@@ -401,9 +409,11 @@ class FixturesWithoutInstantiationTest < ActiveRecord::TestCase
end
def test_reloading_fixtures_through_accessor_methods
+ topic = Struct.new(:title)
assert_equal "The First Topic", topics(:first).title
- @loaded_fixtures['topics']['first'].expects(:find).returns(stub(:title => "Fresh Topic!"))
- assert_equal "Fresh Topic!", topics(:first, true).title
+ assert_called(@loaded_fixtures['topics']['first'], :find, returns: topic.new("Fresh Topic!")) do
+ assert_equal "Fresh Topic!", topics(:first, true).title
+ end
end
end
@@ -507,6 +517,38 @@ class OverRideFixtureMethodTest < ActiveRecord::TestCase
end
end
+class FixtureWithSetModelClassTest < ActiveRecord::TestCase
+ fixtures :other_posts, :other_comments
+
+ # Set to false to blow away fixtures cache and ensure our fixtures are loaded
+ # and thus takes into account the +set_model_class+.
+ self.use_transactional_tests = false
+
+ def test_uses_fixture_class_defined_in_yaml
+ assert_kind_of Post, other_posts(:second_welcome)
+ end
+
+ def test_loads_the_associations_to_fixtures_with_set_model_class
+ post = other_posts(:second_welcome)
+ comment = other_comments(:second_greetings)
+ assert_equal [comment], post.comments
+ assert_equal post, comment.post
+ end
+end
+
+class SetFixtureClassPrevailsTest < ActiveRecord::TestCase
+ set_fixture_class bad_posts: Post
+ fixtures :bad_posts
+
+ # Set to false to blow away fixtures cache and ensure our fixtures are loaded
+ # and thus takes into account the +set_model_class+.
+ self.use_transactional_tests = false
+
+ def test_uses_set_fixture_class
+ assert_kind_of Post, bad_posts(:bad_welcome)
+ end
+end
+
class CheckSetTableNameFixturesTest < ActiveRecord::TestCase
set_fixture_class :funny_jokes => Joke
fixtures :funny_jokes
@@ -913,3 +955,17 @@ class FixturesWithAbstractBelongsTo < ActiveRecord::TestCase
assert_equal pirates(:blackbeard), doubloons(:blackbeards_doubloon).pirate
end
end
+
+class FixtureClassNamesTest < ActiveRecord::TestCase
+ def setup
+ @saved_cache = self.fixture_class_names.dup
+ end
+
+ def teardown
+ self.fixture_class_names.replace(@saved_cache)
+ end
+
+ test "fixture_class_names returns nil for unregistered identifier" do
+ assert_nil self.fixture_class_names['unregistered_identifier']
+ end
+end
diff --git a/activerecord/test/cases/helper.rb b/activerecord/test/cases/helper.rb
index 12c793c408..d82a3040fc 100644
--- a/activerecord/test/cases/helper.rb
+++ b/activerecord/test/cases/helper.rb
@@ -3,6 +3,7 @@ require File.expand_path('../../../../load_paths', __FILE__)
require 'config'
require 'active_support/testing/autorun'
+require 'active_support/testing/method_call_assertions'
require 'stringio'
require 'active_record'
@@ -45,9 +46,8 @@ def in_memory_db?
ActiveRecord::Base.connection_pool.spec.config[:database] == ":memory:"
end
-def mysql_56?
- current_adapter?(:MysqlAdapter, :Mysql2Adapter) &&
- ActiveRecord::Base.connection.send(:version).join(".") >= "5.6.0"
+def subsecond_precision_supported?
+ !current_adapter?(:MysqlAdapter, :Mysql2Adapter) || ActiveRecord::Base.connection.version >= '5.6.4'
end
def mysql_enforcing_gtid_consistency?
@@ -140,6 +140,7 @@ require "cases/validations_repair_helper"
class ActiveSupport::TestCase
include ActiveRecord::TestFixtures
include ActiveRecord::ValidationsRepairHelper
+ include ActiveSupport::Testing::MethodCallAssertions
self.fixture_path = FIXTURES_ROOT
self.use_instantiated_fixtures = false
diff --git a/activerecord/test/cases/integration_test.rb b/activerecord/test/cases/integration_test.rb
index 018b7b0d8f..08a186ae07 100644
--- a/activerecord/test/cases/integration_test.rb
+++ b/activerecord/test/cases/integration_test.rb
@@ -81,7 +81,7 @@ class IntegrationTest < ActiveRecord::TestCase
def test_cache_key_format_for_existing_record_with_updated_at
dev = Developer.first
- assert_equal "developers/#{dev.id}-#{dev.updated_at.utc.to_s(:nsec)}", dev.cache_key
+ assert_equal "developers/#{dev.id}-#{dev.updated_at.utc.to_s(:usec)}", dev.cache_key
end
def test_cache_key_format_for_existing_record_with_updated_at_and_custom_cache_timestamp_format
@@ -96,7 +96,9 @@ class IntegrationTest < ActiveRecord::TestCase
owner.update_column :updated_at, Time.current
key = owner.cache_key
- assert pet.touch
+ travel(1.second) do
+ assert pet.touch
+ end
assert_not_equal key, owner.reload.cache_key
end
@@ -109,30 +111,39 @@ class IntegrationTest < ActiveRecord::TestCase
def test_cache_key_for_updated_on
dev = Developer.first
dev.updated_at = nil
- assert_equal "developers/#{dev.id}-#{dev.updated_on.utc.to_s(:nsec)}", dev.cache_key
+ assert_equal "developers/#{dev.id}-#{dev.updated_on.utc.to_s(:usec)}", dev.cache_key
end
def test_cache_key_for_newer_updated_at
dev = Developer.first
dev.updated_at += 3600
- assert_equal "developers/#{dev.id}-#{dev.updated_at.utc.to_s(:nsec)}", dev.cache_key
+ assert_equal "developers/#{dev.id}-#{dev.updated_at.utc.to_s(:usec)}", dev.cache_key
end
def test_cache_key_for_newer_updated_on
dev = Developer.first
dev.updated_on += 3600
- assert_equal "developers/#{dev.id}-#{dev.updated_on.utc.to_s(:nsec)}", dev.cache_key
+ assert_equal "developers/#{dev.id}-#{dev.updated_on.utc.to_s(:usec)}", dev.cache_key
end
def test_cache_key_format_is_precise_enough
+ skip("Subsecond precision is not supported") unless subsecond_precision_supported?
dev = Developer.first
key = dev.cache_key
dev.touch
assert_not_equal key, dev.cache_key
end
+ def test_cache_key_format_is_not_too_precise
+ skip("Subsecond precision is not supported") unless subsecond_precision_supported?
+ dev = Developer.first
+ dev.touch
+ key = dev.cache_key
+ assert_equal key, dev.reload.cache_key
+ end
+
def test_named_timestamps_for_cache_key
owner = owners(:blackbeard)
- assert_equal "owners/#{owner.id}-#{owner.happy_at.utc.to_s(:nsec)}", owner.cache_key(:updated_at, :happy_at)
+ assert_equal "owners/#{owner.id}-#{owner.happy_at.utc.to_s(:usec)}", owner.cache_key(:updated_at, :happy_at)
end
end
diff --git a/activerecord/test/cases/invertible_migration_test.rb b/activerecord/test/cases/invertible_migration_test.rb
index 99230aa3d5..84b0ff8fcb 100644
--- a/activerecord/test/cases/invertible_migration_test.rb
+++ b/activerecord/test/cases/invertible_migration_test.rb
@@ -1,5 +1,8 @@
require "cases/helper"
+class Horse < ActiveRecord::Base
+end
+
module ActiveRecord
class InvertibleMigrationTest < ActiveRecord::TestCase
class SilentMigration < ActiveRecord::Migration
@@ -76,6 +79,32 @@ module ActiveRecord
end
end
+ class ChangeColumnDefault1 < SilentMigration
+ def change
+ create_table("horses") do |t|
+ t.column :name, :string, default: "Sekitoba"
+ end
+ end
+ end
+
+ class ChangeColumnDefault2 < SilentMigration
+ def change
+ change_column_default :horses, :name, from: "Sekitoba", to: "Diomed"
+ end
+ end
+
+ class DisableExtension1 < SilentMigration
+ def change
+ enable_extension "hstore"
+ end
+ end
+
+ class DisableExtension2 < SilentMigration
+ def change
+ disable_extension "hstore"
+ end
+ end
+
class LegacyMigration < ActiveRecord::Migration
def self.up
create_table("horses") do |t|
@@ -223,6 +252,42 @@ module ActiveRecord
assert !revert.connection.table_exists?("horses")
end
+ def test_migrate_revert_change_column_default
+ migration1 = ChangeColumnDefault1.new
+ migration1.migrate(:up)
+ assert_equal "Sekitoba", Horse.new.name
+
+ migration2 = ChangeColumnDefault2.new
+ migration2.migrate(:up)
+ Horse.reset_column_information
+ assert_equal "Diomed", Horse.new.name
+
+ migration2.migrate(:down)
+ Horse.reset_column_information
+ assert_equal "Sekitoba", Horse.new.name
+ end
+
+ if current_adapter?(:PostgreSQLAdapter)
+ def test_migrate_enable_and_disable_extension
+ migration1 = InvertibleMigration.new
+ migration2 = DisableExtension1.new
+ migration3 = DisableExtension2.new
+
+ migration1.migrate(:up)
+ migration2.migrate(:up)
+ assert_equal true, Horse.connection.extension_enabled?('hstore')
+
+ migration3.migrate(:up)
+ assert_equal false, Horse.connection.extension_enabled?('hstore')
+
+ migration3.migrate(:down)
+ assert_equal true, Horse.connection.extension_enabled?('hstore')
+
+ migration2.migrate(:down)
+ assert_equal false, Horse.connection.extension_enabled?('hstore')
+ end
+ end
+
def test_revert_order
block = Proc.new{|t| t.string :name }
recorder = ActiveRecord::Migration::CommandRecorder.new(ActiveRecord::Base.connection)
diff --git a/activerecord/test/cases/locking_test.rb b/activerecord/test/cases/locking_test.rb
index dbdcc84b7d..2e1363334d 100644
--- a/activerecord/test/cases/locking_test.rb
+++ b/activerecord/test/cases/locking_test.rb
@@ -270,7 +270,7 @@ class OptimisticLockingTest < ActiveRecord::TestCase
car.wheels << Wheel.create!
end
assert_difference 'car.wheels.count', -1 do
- car.destroy
+ car.reload.destroy
end
assert car.destroyed?
end
diff --git a/activerecord/test/cases/log_subscriber_test.rb b/activerecord/test/cases/log_subscriber_test.rb
index 4192d12ff4..3846ba8e7f 100644
--- a/activerecord/test/cases/log_subscriber_test.rb
+++ b/activerecord/test/cases/log_subscriber_test.rb
@@ -7,6 +7,20 @@ require "active_support/log_subscriber/test_helper"
class LogSubscriberTest < ActiveRecord::TestCase
include ActiveSupport::LogSubscriber::TestHelper
include ActiveSupport::Logger::Severity
+ REGEXP_CLEAR = Regexp.escape(ActiveRecord::LogSubscriber::CLEAR)
+ REGEXP_BOLD = Regexp.escape(ActiveRecord::LogSubscriber::BOLD)
+ REGEXP_MAGENTA = Regexp.escape(ActiveRecord::LogSubscriber::MAGENTA)
+ REGEXP_CYAN = Regexp.escape(ActiveRecord::LogSubscriber::CYAN)
+ SQL_COLORINGS = {
+ SELECT: Regexp.escape(ActiveRecord::LogSubscriber::BLUE),
+ INSERT: Regexp.escape(ActiveRecord::LogSubscriber::GREEN),
+ UPDATE: Regexp.escape(ActiveRecord::LogSubscriber::YELLOW),
+ DELETE: Regexp.escape(ActiveRecord::LogSubscriber::RED),
+ LOCK: Regexp.escape(ActiveRecord::LogSubscriber::WHITE),
+ ROLLBACK: Regexp.escape(ActiveRecord::LogSubscriber::RED),
+ TRANSACTION: REGEXP_CYAN,
+ OTHER: REGEXP_MAGENTA
+ }
class TestDebugLogSubscriber < ActiveRecord::LogSubscriber
attr_reader :debugs
@@ -71,6 +85,90 @@ class LogSubscriberTest < ActiveRecord::TestCase
assert_match(/SELECT .*?FROM .?developers.?/i, @logger.logged(:debug).last)
end
+ def test_basic_query_logging_coloration
+ event = Struct.new(:duration, :payload)
+ logger = TestDebugLogSubscriber.new
+ logger.colorize_logging = true
+ SQL_COLORINGS.each do |verb, color_regex|
+ logger.sql(event.new(0, sql: verb.to_s))
+ assert_match(/#{REGEXP_BOLD}#{color_regex}#{verb}#{REGEXP_CLEAR}/i, logger.debugs.last)
+ end
+ end
+
+ def test_basic_payload_name_logging_coloration_generic_sql
+ event = Struct.new(:duration, :payload)
+ logger = TestDebugLogSubscriber.new
+ logger.colorize_logging = true
+ SQL_COLORINGS.each do |verb, _|
+ logger.sql(event.new(0, sql: verb.to_s))
+ assert_match(/#{REGEXP_BOLD}#{REGEXP_MAGENTA} \(0.0ms\)#{REGEXP_CLEAR}/i, logger.debugs.last)
+
+ logger.sql(event.new(0, {sql: verb.to_s, name: "SQL"}))
+ assert_match(/#{REGEXP_BOLD}#{REGEXP_MAGENTA}SQL \(0.0ms\)#{REGEXP_CLEAR}/i, logger.debugs.last)
+ end
+ end
+
+ def test_basic_payload_name_logging_coloration_named_sql
+ event = Struct.new(:duration, :payload)
+ logger = TestDebugLogSubscriber.new
+ logger.colorize_logging = true
+ SQL_COLORINGS.each do |verb, _|
+ logger.sql(event.new(0, {sql: verb.to_s, name: "Model Load"}))
+ assert_match(/#{REGEXP_BOLD}#{REGEXP_CYAN}Model Load \(0.0ms\)#{REGEXP_CLEAR}/i, logger.debugs.last)
+
+ logger.sql(event.new(0, {sql: verb.to_s, name: "Model Exists"}))
+ assert_match(/#{REGEXP_BOLD}#{REGEXP_CYAN}Model Exists \(0.0ms\)#{REGEXP_CLEAR}/i, logger.debugs.last)
+
+ logger.sql(event.new(0, {sql: verb.to_s, name: "ANY SPECIFIC NAME"}))
+ assert_match(/#{REGEXP_BOLD}#{REGEXP_CYAN}ANY SPECIFIC NAME \(0.0ms\)#{REGEXP_CLEAR}/i, logger.debugs.last)
+ end
+ end
+
+ def test_query_logging_coloration_with_nested_select
+ event = Struct.new(:duration, :payload)
+ logger = TestDebugLogSubscriber.new
+ logger.colorize_logging = true
+ SQL_COLORINGS.slice(:SELECT, :INSERT, :UPDATE, :DELETE).each do |verb, color_regex|
+ logger.sql(event.new(0, sql: "#{verb} WHERE ID IN SELECT"))
+ assert_match(/#{REGEXP_BOLD}#{REGEXP_MAGENTA} \(0.0ms\)#{REGEXP_CLEAR} #{REGEXP_BOLD}#{color_regex}#{verb} WHERE ID IN SELECT#{REGEXP_CLEAR}/i, logger.debugs.last)
+ end
+ end
+
+ def test_query_logging_coloration_with_multi_line_nested_select
+ event = Struct.new(:duration, :payload)
+ logger = TestDebugLogSubscriber.new
+ logger.colorize_logging = true
+ SQL_COLORINGS.slice(:SELECT, :INSERT, :UPDATE, :DELETE).each do |verb, color_regex|
+ sql = <<-EOS
+ #{verb}
+ WHERE ID IN (
+ SELECT ID FROM THINGS
+ )
+ EOS
+ logger.sql(event.new(0, sql: sql))
+ assert_match(/#{REGEXP_BOLD}#{REGEXP_MAGENTA} \(0.0ms\)#{REGEXP_CLEAR} #{REGEXP_BOLD}#{color_regex}.*#{verb}.*#{REGEXP_CLEAR}/mi, logger.debugs.last)
+ end
+ end
+
+ def test_query_logging_coloration_with_lock
+ event = Struct.new(:duration, :payload)
+ logger = TestDebugLogSubscriber.new
+ logger.colorize_logging = true
+ sql = <<-EOS
+ SELECT * FROM
+ (SELECT * FROM mytable FOR UPDATE) ss
+ WHERE col1 = 5;
+ EOS
+ logger.sql(event.new(0, sql: sql))
+ assert_match(/#{REGEXP_BOLD}#{REGEXP_MAGENTA} \(0.0ms\)#{REGEXP_CLEAR} #{REGEXP_BOLD}#{SQL_COLORINGS[:LOCK]}.*FOR UPDATE.*#{REGEXP_CLEAR}/mi, logger.debugs.last)
+
+ sql = <<-EOS
+ LOCK TABLE films IN SHARE MODE;
+ EOS
+ logger.sql(event.new(0, sql: sql))
+ assert_match(/#{REGEXP_BOLD}#{REGEXP_MAGENTA} \(0.0ms\)#{REGEXP_CLEAR} #{REGEXP_BOLD}#{SQL_COLORINGS[:LOCK]}.*LOCK TABLE.*#{REGEXP_CLEAR}/mi, logger.debugs.last)
+ end
+
def test_exists_query_logging
Developer.exists? 1
wait
diff --git a/activerecord/test/cases/migration/change_table_test.rb b/activerecord/test/cases/migration/change_table_test.rb
index 2ffe7a1b0d..2f9c50141f 100644
--- a/activerecord/test/cases/migration/change_table_test.rb
+++ b/activerecord/test/cases/migration/change_table_test.rb
@@ -1,5 +1,4 @@
require "cases/migration/helper"
-require "minitest/mock"
module ActiveRecord
class Migration
diff --git a/activerecord/test/cases/migration/columns_test.rb b/activerecord/test/cases/migration/columns_test.rb
index 5fc7702dfa..ab3f584350 100644
--- a/activerecord/test/cases/migration/columns_test.rb
+++ b/activerecord/test/cases/migration/columns_test.rb
@@ -267,6 +267,13 @@ module ActiveRecord
assert_nil TestModel.new.first_name
end
+ def test_change_column_default_with_from_and_to
+ add_column "test_models", "first_name", :string
+ connection.change_column_default "test_models", "first_name", from: nil, to: "Tester"
+
+ assert_equal "Tester", TestModel.new.first_name
+ end
+
def test_remove_column_no_second_parameter_raises_exception
assert_raise(ArgumentError) { connection.remove_column("funny") }
end
diff --git a/activerecord/test/cases/migration/command_recorder_test.rb b/activerecord/test/cases/migration/command_recorder_test.rb
index 90b7c6b38a..1e3529db54 100644
--- a/activerecord/test/cases/migration/command_recorder_test.rb
+++ b/activerecord/test/cases/migration/command_recorder_test.rb
@@ -31,7 +31,8 @@ module ActiveRecord
end
def test_unknown_commands_delegate
- recorder = CommandRecorder.new(stub(:foo => 'bar'))
+ recorder = Struct.new(:foo)
+ recorder = CommandRecorder.new(recorder.new('bar'))
assert_equal 'bar', recorder.foo
end
@@ -169,6 +170,16 @@ module ActiveRecord
end
end
+ def test_invert_change_column_default_with_from_and_to
+ change = @recorder.inverse_of :change_column_default, [:table, :column, from: "old_value", to: "new_value"]
+ assert_equal [:change_column_default, [:table, :column, from: "new_value", to: "old_value"]], change
+ end
+
+ def test_invert_change_column_default_with_from_and_to_with_boolean
+ change = @recorder.inverse_of :change_column_default, [:table, :column, from: true, to: false]
+ assert_equal [:change_column_default, [:table, :column, from: false, to: true]], change
+ end
+
def test_invert_change_column_null
add = @recorder.inverse_of :change_column_null, [:table, :column, true]
assert_equal [:change_column_null, [:table, :column, false]], add
diff --git a/activerecord/test/cases/migration/foreign_key_test.rb b/activerecord/test/cases/migration/foreign_key_test.rb
index 7f4790bf3e..72f2fa95f1 100644
--- a/activerecord/test/cases/migration/foreign_key_test.rb
+++ b/activerecord/test/cases/migration/foreign_key_test.rb
@@ -243,6 +243,37 @@ module ActiveRecord
silence_stream($stdout) { migration.migrate(:down) }
end
+ class CreateSchoolsAndClassesMigration < ActiveRecord::Migration
+ def change
+ create_table(:schools)
+
+ create_table(:classes) do |t|
+ t.column :school_id, :integer
+ end
+ add_foreign_key :classes, :schools
+ end
+ end
+
+ def test_add_foreign_key_with_prefix
+ ActiveRecord::Base.table_name_prefix = 'p_'
+ migration = CreateSchoolsAndClassesMigration.new
+ silence_stream($stdout) { migration.migrate(:up) }
+ assert_equal 1, @connection.foreign_keys("p_classes").size
+ ensure
+ silence_stream($stdout) { migration.migrate(:down) }
+ ActiveRecord::Base.table_name_prefix = nil
+ end
+
+ def test_add_foreign_key_with_suffix
+ ActiveRecord::Base.table_name_suffix = '_s'
+ migration = CreateSchoolsAndClassesMigration.new
+ silence_stream($stdout) { migration.migrate(:up) }
+ assert_equal 1, @connection.foreign_keys("classes_s").size
+ ensure
+ silence_stream($stdout) { migration.migrate(:down) }
+ ActiveRecord::Base.table_name_suffix = nil
+ end
+
end
end
end
diff --git a/activerecord/test/cases/migration/helper.rb b/activerecord/test/cases/migration/helper.rb
index 5bc0898f33..ad85684c0b 100644
--- a/activerecord/test/cases/migration/helper.rb
+++ b/activerecord/test/cases/migration/helper.rb
@@ -28,7 +28,7 @@ module ActiveRecord
super
TestModel.reset_table_name
TestModel.reset_sequence_name
- connection.drop_table :test_models rescue nil
+ connection.drop_table :test_models, if_exists: true
end
private
diff --git a/activerecord/test/cases/migration/pending_migrations_test.rb b/activerecord/test/cases/migration/pending_migrations_test.rb
index 7afac83bd2..4f5589f32a 100644
--- a/activerecord/test/cases/migration/pending_migrations_test.rb
+++ b/activerecord/test/cases/migration/pending_migrations_test.rb
@@ -1,5 +1,4 @@
require 'cases/helper'
-require "minitest/mock"
module ActiveRecord
class Migration
diff --git a/activerecord/test/cases/migration/postgresql_geometric_types_test.rb b/activerecord/test/cases/migration/postgresql_geometric_types_test.rb
new file mode 100644
index 0000000000..e4772905bb
--- /dev/null
+++ b/activerecord/test/cases/migration/postgresql_geometric_types_test.rb
@@ -0,0 +1,93 @@
+require 'cases/helper'
+
+module ActiveRecord
+ class Migration
+ class PostgreSQLGeometricTypesTest < ActiveRecord::TestCase
+ attr_reader :connection, :table_name
+
+ def setup
+ super
+ @connection = ActiveRecord::Base.connection
+ @table_name = :testings
+ end
+
+ if current_adapter?(:PostgreSQLAdapter)
+ def test_creating_column_with_point_type
+ connection.create_table(table_name) do |t|
+ t.point :foo_point
+ end
+
+ assert_column_exists(:foo_point)
+ assert_type_correct(:foo_point, :point)
+ end
+
+ def test_creating_column_with_line_type
+ connection.create_table(table_name) do |t|
+ t.line :foo_line
+ end
+
+ assert_column_exists(:foo_line)
+ assert_type_correct(:foo_line, :line)
+ end
+
+ def test_creating_column_with_lseg_type
+ connection.create_table(table_name) do |t|
+ t.lseg :foo_lseg
+ end
+
+ assert_column_exists(:foo_lseg)
+ assert_type_correct(:foo_lseg, :lseg)
+ end
+
+ def test_creating_column_with_box_type
+ connection.create_table(table_name) do |t|
+ t.box :foo_box
+ end
+
+ assert_column_exists(:foo_box)
+ assert_type_correct(:foo_box, :box)
+ end
+
+ def test_creating_column_with_path_type
+ connection.create_table(table_name) do |t|
+ t.path :foo_path
+ end
+
+ assert_column_exists(:foo_path)
+ assert_type_correct(:foo_path, :path)
+ end
+
+ def test_creating_column_with_polygon_type
+ connection.create_table(table_name) do |t|
+ t.polygon :foo_polygon
+ end
+
+ assert_column_exists(:foo_polygon)
+ assert_type_correct(:foo_polygon, :polygon)
+ end
+
+ def test_creating_column_with_circle_type
+ connection.create_table(table_name) do |t|
+ t.circle :foo_circle
+ end
+
+ assert_column_exists(:foo_circle)
+ assert_type_correct(:foo_circle, :circle)
+ end
+ end
+
+ private
+ def assert_column_exists(column_name)
+ columns = connection.columns(table_name)
+ assert columns.map(&:name).include?(column_name.to_s)
+ end
+
+ def assert_type_correct(column_name, type)
+ columns = connection.columns(table_name)
+ column = columns.select{ |c| c.name == column_name.to_s }.first
+ assert_equal type.to_s, column.sql_type
+ end
+
+ end
+ end
+end \ No newline at end of file
diff --git a/activerecord/test/cases/migration/references_foreign_key_test.rb b/activerecord/test/cases/migration/references_foreign_key_test.rb
index 1594f99852..84ec657398 100644
--- a/activerecord/test/cases/migration/references_foreign_key_test.rb
+++ b/activerecord/test/cases/migration/references_foreign_key_test.rb
@@ -32,6 +32,14 @@ module ActiveRecord
assert_equal [], @connection.foreign_keys("testings")
end
+ test "foreign keys can be created in one query" do
+ assert_queries(1) do
+ @connection.create_table :testings do |t|
+ t.references :testing_parent, foreign_key: true
+ end
+ end
+ end
+
test "options hash can be passed" do
@connection.change_table :testing_parents do |t|
t.integer :other_id
@@ -45,6 +53,15 @@ module ActiveRecord
assert_equal "other_id", fk.primary_key
end
+ test "to_table option can be passed" do
+ @connection.create_table :testings do |t|
+ t.references :parent, foreign_key: { to_table: :testing_parents }
+ end
+ fks = @connection.foreign_keys("testings")
+ assert_equal([["testings", "testing_parents", "parent_id"]],
+ fks.map {|fk| [fk.from_table, fk.to_table, fk.column] })
+ end
+
test "foreign keys cannot be added to polymorphic relations when creating the table" do
@connection.create_table :testings do |t|
assert_raises(ArgumentError) do
diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb
index b2f209fe97..10f1c7216f 100644
--- a/activerecord/test/cases/migration_test.rb
+++ b/activerecord/test/cases/migration_test.rb
@@ -75,15 +75,13 @@ class MigrationTest < ActiveRecord::TestCase
ActiveRecord::Migrator.up(migrations_path)
assert_equal 3, ActiveRecord::Migrator.current_version
- assert_equal 3, ActiveRecord::Migrator.last_version
assert_equal false, ActiveRecord::Migrator.needs_migration?
ActiveRecord::Migrator.down(MIGRATIONS_ROOT + "/valid")
assert_equal 0, ActiveRecord::Migrator.current_version
- assert_equal 3, ActiveRecord::Migrator.last_version
assert_equal true, ActiveRecord::Migrator.needs_migration?
- ActiveRecord::SchemaMigration.create!(:version => ActiveRecord::Migrator.last_version)
+ ActiveRecord::SchemaMigration.create!(version: 3)
assert_equal true, ActiveRecord::Migrator.needs_migration?
ensure
ActiveRecord::Migrator.migrations_paths = old_path
@@ -115,7 +113,7 @@ class MigrationTest < ActiveRecord::TestCase
end
def test_migration_version
- ActiveRecord::Migrator.run(:up, MIGRATIONS_ROOT + "/version_check", 20131219224947)
+ assert_nothing_raised { ActiveRecord::Migrator.run(:up, MIGRATIONS_ROOT + "/version_check", 20131219224947) }
end
def test_create_table_with_force_true_does_not_drop_nonexisting_table
@@ -132,13 +130,9 @@ class MigrationTest < ActiveRecord::TestCase
Person.connection.drop_table :testings2, if_exists: true
end
- def connection
- ActiveRecord::Base.connection
- end
-
def test_migration_instance_has_connection
migration = Class.new(ActiveRecord::Migration).new
- assert_equal connection, migration.connection
+ assert_equal ActiveRecord::Base.connection, migration.connection
end
def test_method_missing_delegates_to_connection
diff --git a/activerecord/test/cases/nested_attributes_test.rb b/activerecord/test/cases/nested_attributes_test.rb
index 6b4addd52f..93cb631a04 100644
--- a/activerecord/test/cases/nested_attributes_test.rb
+++ b/activerecord/test/cases/nested_attributes_test.rb
@@ -273,10 +273,11 @@ class TestNestedAttributesOnAHasOneAssociation < ActiveRecord::TestCase
end
def test_should_modify_an_existing_record_if_there_is_a_matching_composite_id
- @ship.stubs(:id).returns('ABC1X')
- @pirate.ship_attributes = { :id => @ship.id, :name => 'Davy Jones Gold Dagger' }
+ @ship.stub(:id, 'ABC1X') do
+ @pirate.ship_attributes = { :id => @ship.id, :name => 'Davy Jones Gold Dagger' }
- assert_equal 'Davy Jones Gold Dagger', @pirate.ship.name
+ assert_equal 'Davy Jones Gold Dagger', @pirate.ship.name
+ end
end
def test_should_destroy_an_existing_record_if_there_is_a_matching_id_and_destroy_is_truthy
@@ -457,10 +458,11 @@ class TestNestedAttributesOnABelongsToAssociation < ActiveRecord::TestCase
end
def test_should_modify_an_existing_record_if_there_is_a_matching_composite_id
- @pirate.stubs(:id).returns('ABC1X')
- @ship.pirate_attributes = { :id => @pirate.id, :catchphrase => 'Arr' }
+ @pirate.stub(:id, 'ABC1X') do
+ @ship.pirate_attributes = { :id => @pirate.id, :catchphrase => 'Arr' }
- assert_equal 'Arr', @ship.pirate.catchphrase
+ assert_equal 'Arr', @ship.pirate.catchphrase
+ end
end
def test_should_destroy_an_existing_record_if_there_is_a_matching_id_and_destroy_is_truthy
@@ -638,17 +640,19 @@ module NestedAttributesOnACollectionAssociationTests
end
def test_should_take_a_hash_with_composite_id_keys_and_assign_the_attributes_to_the_associated_models
- @child_1.stubs(:id).returns('ABC1X')
- @child_2.stubs(:id).returns('ABC2X')
-
- @pirate.attributes = {
- association_getter => [
- { :id => @child_1.id, :name => 'Grace OMalley' },
- { :id => @child_2.id, :name => 'Privateers Greed' }
- ]
- }
+ @child_1.stub(:id, 'ABC1X') do
+ @child_2.stub(:id, 'ABC2X') do
+
+ @pirate.attributes = {
+ association_getter => [
+ { :id => @child_1.id, :name => 'Grace OMalley' },
+ { :id => @child_2.id, :name => 'Privateers Greed' }
+ ]
+ }
- assert_equal ['Grace OMalley', 'Privateers Greed'], [@child_1.name, @child_2.name]
+ assert_equal ['Grace OMalley', 'Privateers Greed'], [@child_1.name, @child_2.name]
+ end
+ end
end
def test_should_raise_RecordNotFound_if_an_id_is_given_but_doesnt_return_a_record
@@ -658,6 +662,16 @@ module NestedAttributesOnACollectionAssociationTests
assert_equal "Couldn't find #{@child_1.class.name} with ID=1234567890 for Pirate with ID=#{@pirate.id}", exception.message
end
+ def test_should_raise_RecordNotFound_if_an_id_belonging_to_a_different_record_is_given
+ other_pirate = Pirate.create! catchphrase: 'Ahoy!'
+ other_child = other_pirate.send(@association_name).create! name: 'Buccaneers Servant'
+
+ exception = assert_raise ActiveRecord::RecordNotFound do
+ @pirate.attributes = { association_getter => [{ id: other_child.id }] }
+ end
+ assert_equal "Couldn't find #{@child_1.class.name} with ID=#{other_child.id} for Pirate with ID=#{@pirate.id}", exception.message
+ end
+
def test_should_automatically_build_new_associated_models_for_each_entry_in_a_hash_where_the_id_is_missing
@pirate.send(@association_name).destroy_all
@pirate.reload.attributes = {
@@ -1054,4 +1068,39 @@ class TestHasManyAutosaveAssociationWhichItselfHasAutosaveAssociations < ActiveR
assert_not part.valid?
assert_equal ["Ship name can't be blank"], part.errors.full_messages
end
+
+ class ProtectedParameters
+ def initialize(hash)
+ @hash = hash
+ end
+
+ def permitted?
+ true
+ end
+
+ def [](key)
+ @hash[key]
+ end
+
+ def to_h
+ @hash
+ end
+ end
+
+ test "strong params style objects can be assigned for singular associations" do
+ params = { name: "Stern", ship_attributes:
+ ProtectedParameters.new(name: "The Black Rock") }
+ part = ShipPart.new(params)
+
+ assert_equal "Stern", part.name
+ assert_equal "The Black Rock", part.ship.name
+ end
+
+ test "strong params style objects can be assigned for collection associations" do
+ params = { trinkets_attributes: ProtectedParameters.new("0" => ProtectedParameters.new(name: "Necklace"), "1" => ProtectedParameters.new(name: "Spoon")) }
+ part = ShipPart.new(params)
+
+ assert_equal "Necklace", part.trinkets[0].name
+ assert_equal "Spoon", part.trinkets[1].name
+ end
end
diff --git a/activerecord/test/cases/persistence_test.rb b/activerecord/test/cases/persistence_test.rb
index 42e7507631..31686bde3f 100644
--- a/activerecord/test/cases/persistence_test.rb
+++ b/activerecord/test/cases/persistence_test.rb
@@ -17,6 +17,7 @@ require 'models/minivan'
require 'models/owner'
require 'models/person'
require 'models/pet'
+require 'models/ship'
require 'models/toy'
require 'rexml/document'
@@ -119,13 +120,22 @@ class PersistenceTest < ActiveRecord::TestCase
assert_equal 59, accounts(:signals37, :reload).credit_limit
end
+ def test_increment_updates_counter_in_db_using_offset
+ a1 = accounts(:signals37)
+ initial_credit = a1.credit_limit
+ a2 = Account.find(accounts(:signals37).id)
+ a1.increment!(:credit_limit)
+ a2.increment!(:credit_limit)
+ assert_equal initial_credit + 2, a1.reload.credit_limit
+ end
+
def test_destroy_all
conditions = "author_name = 'Mary'"
topics_by_mary = Topic.all.merge!(:where => conditions, :order => 'id').to_a
assert ! topics_by_mary.empty?
assert_difference('Topic.count', -topics_by_mary.size) do
- destroyed = Topic.destroy_all(conditions).sort_by(&:id)
+ destroyed = Topic.where(conditions).destroy_all.sort_by(&:id)
assert_equal topics_by_mary, destroyed
assert destroyed.all?(&:frozen?), "destroyed topics should be frozen"
end
diff --git a/activerecord/test/cases/primary_keys_test.rb b/activerecord/test/cases/primary_keys_test.rb
index 0745a37ee9..5e4ba47988 100644
--- a/activerecord/test/cases/primary_keys_test.rb
+++ b/activerecord/test/cases/primary_keys_test.rb
@@ -241,6 +241,33 @@ class PrimaryKeyAnyTypeTest < ActiveRecord::TestCase
end
end
+class CompositePrimaryKeyTest < ActiveRecord::TestCase
+ include SchemaDumpingHelper
+
+ self.use_transactional_tests = false
+
+ def setup
+ @connection = ActiveRecord::Base.connection
+ @connection.create_table(:barcodes, primary_key: ["region", "code"], force: true) do |t|
+ t.string :region
+ t.integer :code
+ end
+ end
+
+ def teardown
+ @connection.drop_table(:barcodes, if_exists: true)
+ end
+
+ def test_composite_primary_key
+ assert_equal ["region", "code"], @connection.primary_keys("barcodes")
+ end
+
+ def test_collectly_dump_composite_primary_key
+ schema = dump_table_schema "barcodes"
+ assert_match %r{create_table "barcodes", primary_key: \["region", "code"\]}, schema
+ end
+end
+
if current_adapter?(:MysqlAdapter, :Mysql2Adapter)
class PrimaryKeyWithAnsiQuotesTest < ActiveRecord::TestCase
self.use_transactional_tests = false
@@ -300,11 +327,12 @@ if current_adapter?(:PostgreSQLAdapter, :MysqlAdapter, :Mysql2Adapter)
if current_adapter?(:MysqlAdapter, :Mysql2Adapter)
test "primary key column type with options" do
- @connection.create_table(:widgets, id: :primary_key, limit: 8, force: true)
+ @connection.create_table(:widgets, id: :primary_key, limit: 8, unsigned: true, force: true)
column = @connection.columns(:widgets).find { |c| c.name == 'id' }
assert column.auto_increment?
assert_equal :integer, column.type
assert_equal 8, column.limit
+ assert column.unsigned?
end
end
end
diff --git a/activerecord/test/cases/query_cache_test.rb b/activerecord/test/cases/query_cache_test.rb
index 2f0b5df286..d84653e4c9 100644
--- a/activerecord/test/cases/query_cache_test.rb
+++ b/activerecord/test/cases/query_cache_test.rb
@@ -262,61 +262,66 @@ class QueryCacheExpiryTest < ActiveRecord::TestCase
end
def test_find
- Task.connection.expects(:clear_query_cache).times(1)
+ assert_called(Task.connection, :clear_query_cache) do
+ assert !Task.connection.query_cache_enabled
+ Task.cache do
+ assert Task.connection.query_cache_enabled
+ Task.find(1)
- assert !Task.connection.query_cache_enabled
- Task.cache do
- assert Task.connection.query_cache_enabled
- Task.find(1)
+ Task.uncached do
+ assert !Task.connection.query_cache_enabled
+ Task.find(1)
+ end
- Task.uncached do
- assert !Task.connection.query_cache_enabled
- Task.find(1)
+ assert Task.connection.query_cache_enabled
end
-
- assert Task.connection.query_cache_enabled
+ assert !Task.connection.query_cache_enabled
end
- assert !Task.connection.query_cache_enabled
end
def test_update
- Task.connection.expects(:clear_query_cache).times(2)
- Task.cache do
- task = Task.find(1)
- task.starting = Time.now.utc
- task.save!
+ assert_called(Task.connection, :clear_query_cache, times: 2) do
+ Task.cache do
+ task = Task.find(1)
+ task.starting = Time.now.utc
+ task.save!
+ end
end
end
def test_destroy
- Task.connection.expects(:clear_query_cache).times(2)
- Task.cache do
- Task.find(1).destroy
+ assert_called(Task.connection, :clear_query_cache, times: 2) do
+ Task.cache do
+ Task.find(1).destroy
+ end
end
end
def test_insert
- ActiveRecord::Base.connection.expects(:clear_query_cache).times(2)
- Task.cache do
- Task.create!
+ assert_called(ActiveRecord::Base.connection, :clear_query_cache, times: 2) do
+ Task.cache do
+ Task.create!
+ end
end
end
def test_cache_is_expired_by_habtm_update
- ActiveRecord::Base.connection.expects(:clear_query_cache).times(2)
- ActiveRecord::Base.cache do
- c = Category.first
- p = Post.first
- p.categories << c
+ assert_called(ActiveRecord::Base.connection, :clear_query_cache, times: 2) do
+ ActiveRecord::Base.cache do
+ c = Category.first
+ p = Post.first
+ p.categories << c
+ end
end
end
def test_cache_is_expired_by_habtm_delete
- ActiveRecord::Base.connection.expects(:clear_query_cache).times(2)
- ActiveRecord::Base.cache do
- p = Post.find(1)
- assert p.categories.any?
- p.categories.delete_all
+ assert_called(ActiveRecord::Base.connection, :clear_query_cache, times: 2) do
+ ActiveRecord::Base.cache do
+ p = Post.find(1)
+ assert p.categories.any?
+ p.categories.delete_all
+ end
end
end
end
diff --git a/activerecord/test/cases/readonly_test.rb b/activerecord/test/cases/readonly_test.rb
index 1c919f0b57..5f6eb41240 100644
--- a/activerecord/test/cases/readonly_test.rb
+++ b/activerecord/test/cases/readonly_test.rb
@@ -7,6 +7,7 @@ require 'models/computer'
require 'models/project'
require 'models/reader'
require 'models/person'
+require 'models/ship'
class ReadOnlyTest < ActiveRecord::TestCase
fixtures :authors, :posts, :comments, :developers, :projects, :developers_projects, :people, :readers
diff --git a/activerecord/test/cases/reflection_test.rb b/activerecord/test/cases/reflection_test.rb
index 7b47c80331..9c04a41e69 100644
--- a/activerecord/test/cases/reflection_test.rb
+++ b/activerecord/test/cases/reflection_test.rb
@@ -393,12 +393,14 @@ class ReflectionTest < ActiveRecord::TestCase
product = Struct.new(:table_name, :pluralize_table_names).new('products', true)
reflection = ActiveRecord::Reflection.create(:has_many, :categories, nil, {}, product)
- reflection.stubs(:klass).returns(category)
- assert_equal 'categories_products', reflection.join_table
+ reflection.stub(:klass, category) do
+ assert_equal 'categories_products', reflection.join_table
+ end
reflection = ActiveRecord::Reflection.create(:has_many, :products, nil, {}, category)
- reflection.stubs(:klass).returns(product)
- assert_equal 'categories_products', reflection.join_table
+ reflection.stub(:klass, product) do
+ assert_equal 'categories_products', reflection.join_table
+ end
end
def test_join_table_with_common_prefix
@@ -406,12 +408,14 @@ class ReflectionTest < ActiveRecord::TestCase
product = Struct.new(:table_name, :pluralize_table_names).new('catalog_products', true)
reflection = ActiveRecord::Reflection.create(:has_many, :categories, nil, {}, product)
- reflection.stubs(:klass).returns(category)
- assert_equal 'catalog_categories_products', reflection.join_table
+ reflection.stub(:klass, category) do
+ assert_equal 'catalog_categories_products', reflection.join_table
+ end
reflection = ActiveRecord::Reflection.create(:has_many, :products, nil, {}, category)
- reflection.stubs(:klass).returns(product)
- assert_equal 'catalog_categories_products', reflection.join_table
+ reflection.stub(:klass, product) do
+ assert_equal 'catalog_categories_products', reflection.join_table
+ end
end
def test_join_table_with_different_prefix
@@ -419,12 +423,14 @@ class ReflectionTest < ActiveRecord::TestCase
page = Struct.new(:table_name, :pluralize_table_names).new('content_pages', true)
reflection = ActiveRecord::Reflection.create(:has_many, :categories, nil, {}, page)
- reflection.stubs(:klass).returns(category)
- assert_equal 'catalog_categories_content_pages', reflection.join_table
+ reflection.stub(:klass, category) do
+ assert_equal 'catalog_categories_content_pages', reflection.join_table
+ end
reflection = ActiveRecord::Reflection.create(:has_many, :pages, nil, {}, category)
- reflection.stubs(:klass).returns(page)
- assert_equal 'catalog_categories_content_pages', reflection.join_table
+ reflection.stub(:klass, page) do
+ assert_equal 'catalog_categories_content_pages', reflection.join_table
+ end
end
def test_join_table_can_be_overridden
@@ -432,12 +438,14 @@ class ReflectionTest < ActiveRecord::TestCase
product = Struct.new(:table_name, :pluralize_table_names).new('products', true)
reflection = ActiveRecord::Reflection.create(:has_many, :categories, nil, { :join_table => 'product_categories' }, product)
- reflection.stubs(:klass).returns(category)
- assert_equal 'product_categories', reflection.join_table
+ reflection.stub(:klass, category) do
+ assert_equal 'product_categories', reflection.join_table
+ end
reflection = ActiveRecord::Reflection.create(:has_many, :products, nil, { :join_table => 'product_categories' }, category)
- reflection.stubs(:klass).returns(product)
- assert_equal 'product_categories', reflection.join_table
+ reflection.stub(:klass, product) do
+ assert_equal 'product_categories', reflection.join_table
+ end
end
def test_includes_accepts_symbols
diff --git a/activerecord/test/cases/relation/mutation_test.rb b/activerecord/test/cases/relation/mutation_test.rb
index ba4d9d2503..88d2dd55ab 100644
--- a/activerecord/test/cases/relation/mutation_test.rb
+++ b/activerecord/test/cases/relation/mutation_test.rb
@@ -55,9 +55,10 @@ module ActiveRecord
test '#order! on non-string does not attempt regexp match for references' do
obj = Object.new
- obj.expects(:=~).never
- assert relation.order!(obj)
- assert_equal [obj], relation.order_values
+ assert_not_called(obj, :=~) do
+ assert relation.order!(obj)
+ assert_equal [obj], relation.order_values
+ end
end
test '#references!' do
diff --git a/activerecord/test/cases/relation/where_test.rb b/activerecord/test/cases/relation/where_test.rb
index 6af31017d6..bc6378b90e 100644
--- a/activerecord/test/cases/relation/where_test.rb
+++ b/activerecord/test/cases/relation/where_test.rb
@@ -276,5 +276,35 @@ module ActiveRecord
assert_equal essays(:david_modest_proposal), essay
end
+
+ def test_where_with_strong_parameters
+ protected_params = Class.new do
+ attr_reader :permitted
+ alias :permitted? :permitted
+
+ def initialize(parameters)
+ @parameters = parameters
+ @permitted = false
+ end
+
+ def to_h
+ @parameters
+ end
+
+ def permit!
+ @permitted = true
+ self
+ end
+ end
+
+ author = authors(:david)
+ params = protected_params.new(name: author.name)
+ assert_raises(ActiveModel::ForbiddenAttributesError) { Author.where(params) }
+ assert_equal author, Author.where(params.permit!).first
+ end
+
+ def test_where_with_unsupported_arguments
+ assert_raises(ArgumentError) { Author.where(42) }
+ end
end
end
diff --git a/activerecord/test/cases/relation_test.rb b/activerecord/test/cases/relation_test.rb
index 37d3965022..675149556f 100644
--- a/activerecord/test/cases/relation_test.rb
+++ b/activerecord/test/cases/relation_test.rb
@@ -57,9 +57,6 @@ module ActiveRecord
def test_empty_where_values_hash
relation = Relation.new(FakeKlass, :b, nil)
assert_equal({}, relation.where_values_hash)
-
- relation.where! :hello
- assert_equal({}, relation.where_values_hash)
end
def test_has_values
@@ -153,10 +150,10 @@ module ActiveRecord
end
test 'merging a hash into a relation' do
- relation = Relation.new(FakeKlass, :b, nil)
- relation = relation.merge where: :lol, readonly: true
+ relation = Relation.new(Post, Post.arel_table, Post.predicate_builder)
+ relation = relation.merge where: {name: :lol}, readonly: true
- assert_equal Relation::WhereClause.new([:lol], []), relation.where_clause
+ assert_equal({"name"=>:lol}, relation.where_clause.to_h)
assert_equal true, relation.readonly_value
end
@@ -185,7 +182,7 @@ module ActiveRecord
end
test '#values returns a dup of the values' do
- relation = Relation.new(FakeKlass, :b, nil).where! :foo
+ relation = Relation.new(Post, Post.arel_table, Post.predicate_builder).where!(name: :foo)
values = relation.values
values[:where] = nil
@@ -243,18 +240,23 @@ module ActiveRecord
end
def test_select_quotes_when_using_from_clause
- if sqlite3_version_includes_quoting_bug?
- skip <<-ERROR.squish
- You are using an outdated version of SQLite3 which has a bug in
- quoted column names. Please update SQLite3 and rebuild the sqlite3
- ruby gem
- ERROR
- end
+ skip_if_sqlite3_version_includes_quoting_bug
quoted_join = ActiveRecord::Base.connection.quote_table_name("join")
selected = Post.select(:join).from(Post.select("id as #{quoted_join}")).map(&:join)
assert_equal Post.pluck(:id), selected
end
+ def test_selecting_aliased_attribute_quotes_column_name_when_from_is_used
+ skip_if_sqlite3_version_includes_quoting_bug
+ klass = Class.new(ActiveRecord::Base) do
+ self.table_name = :test_with_keyword_column_name
+ alias_attribute :description, :desc
+ end
+ klass.create!(description: "foo")
+
+ assert_equal ["foo"], klass.select(:description).from(klass.all).map(&:desc)
+ end
+
def test_relation_merging_with_merged_joins_as_strings
join_string = "LEFT OUTER JOIN #{Rating.quoted_table_name} ON #{SpecialComment.quoted_table_name}.id = #{Rating.quoted_table_name}.comment_id"
special_comments_with_ratings = SpecialComment.joins join_string
@@ -292,6 +294,16 @@ module ActiveRecord
private
+ def skip_if_sqlite3_version_includes_quoting_bug
+ if sqlite3_version_includes_quoting_bug?
+ skip <<-ERROR.squish
+ You are using an outdated version of SQLite3 which has a bug in
+ quoted column names. Please update SQLite3 and rebuild the sqlite3
+ ruby gem
+ ERROR
+ end
+ end
+
def sqlite3_version_includes_quoting_bug?
if current_adapter?(:SQLite3Adapter)
selected_quoted_column_names = ActiveRecord::Base.connection.exec_query(
diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb
index acbf85d398..cd23c1b3e1 100644
--- a/activerecord/test/cases/relations_test.rb
+++ b/activerecord/test/cases/relations_test.rb
@@ -297,6 +297,11 @@ class RelationTest < ActiveRecord::TestCase
assert_equal 3, tags.length
end
+ def test_finding_with_sanitized_order
+ query = Tag.order(["field(id, ?)", [1,3,2]]).to_sql
+ assert_match(/field\(id, 1,3,2\)/, query)
+ end
+
def test_finding_with_order_limit_and_offset
entrants = Entrant.order("id ASC").limit(2).offset(1)
@@ -647,6 +652,25 @@ class RelationTest < ActiveRecord::TestCase
end
end
+ def test_preloading_with_associations_default_scopes_and_merges
+ post = Post.create! title: 'Uhuu', body: 'body'
+ reader = Reader.create! post_id: post.id, person_id: 1
+
+ post_rel = PostWithPreloadDefaultScope.preload(:readers).joins(:readers).where(title: 'Uhuu')
+ result_post = PostWithPreloadDefaultScope.all.merge(post_rel).to_a.first
+
+ assert_no_queries do
+ assert_equal [reader], result_post.readers.to_a
+ end
+
+ post_rel = PostWithIncludesDefaultScope.includes(:readers).where(title: 'Uhuu')
+ result_post = PostWithIncludesDefaultScope.all.merge(post_rel).to_a.first
+
+ assert_no_queries do
+ assert_equal [reader], result_post.readers.to_a
+ end
+ end
+
def test_loading_with_one_association
posts = Post.preload(:comments)
post = posts.find { |p| p.id == 1 }
@@ -912,6 +936,12 @@ class RelationTest < ActiveRecord::TestCase
assert davids.loaded?
end
+ def test_destroy_all_with_conditions_is_deprecated
+ assert_deprecated do
+ assert_difference('Author.count', -1) { Author.destroy_all(name: 'David') }
+ end
+ end
+
def test_delete_all
davids = Author.where(:name => 'David')
@@ -919,6 +949,12 @@ class RelationTest < ActiveRecord::TestCase
assert ! davids.loaded?
end
+ def test_delete_all_with_conditions_is_deprecated
+ assert_deprecated do
+ assert_difference('Author.count', -1) { Author.delete_all(name: 'David') }
+ end
+ end
+
def test_delete_all_loaded
davids = Author.where(:name => 'David')
@@ -1510,6 +1546,13 @@ class RelationTest < ActiveRecord::TestCase
assert_equal 'David', topic2.reload.author_name
end
+ def test_update_on_relation_passing_active_record_object_is_deprecated
+ topic = Topic.create!(title: 'Foo', author_name: nil)
+ assert_deprecated(/update/) do
+ Topic.where(id: topic.id).update(topic, title: 'Bar')
+ end
+ end
+
def test_distinct
tag1 = Tag.create(:name => 'Foo')
tag2 = Tag.create(:name => 'Foo')
diff --git a/activerecord/test/cases/sanitize_test.rb b/activerecord/test/cases/sanitize_test.rb
index 262e0abc22..14e392ac30 100644
--- a/activerecord/test/cases/sanitize_test.rb
+++ b/activerecord/test/cases/sanitize_test.rb
@@ -9,11 +9,11 @@ class SanitizeTest < ActiveRecord::TestCase
def test_sanitize_sql_array_handles_string_interpolation
quoted_bambi = ActiveRecord::Base.connection.quote_string("Bambi")
- assert_equal "name=#{quoted_bambi}", Binary.send(:sanitize_sql_array, ["name=%s", "Bambi"])
- assert_equal "name=#{quoted_bambi}", Binary.send(:sanitize_sql_array, ["name=%s", "Bambi".mb_chars])
+ assert_equal "name='#{quoted_bambi}'", Binary.send(:sanitize_sql_array, ["name='%s'", "Bambi"])
+ assert_equal "name='#{quoted_bambi}'", Binary.send(:sanitize_sql_array, ["name='%s'", "Bambi".mb_chars])
quoted_bambi_and_thumper = ActiveRecord::Base.connection.quote_string("Bambi\nand\nThumper")
- assert_equal "name=#{quoted_bambi_and_thumper}",Binary.send(:sanitize_sql_array, ["name=%s", "Bambi\nand\nThumper"])
- assert_equal "name=#{quoted_bambi_and_thumper}",Binary.send(:sanitize_sql_array, ["name=%s", "Bambi\nand\nThumper".mb_chars])
+ assert_equal "name='#{quoted_bambi_and_thumper}'",Binary.send(:sanitize_sql_array, ["name='%s'", "Bambi\nand\nThumper"])
+ assert_equal "name='#{quoted_bambi_and_thumper}'",Binary.send(:sanitize_sql_array, ["name='%s'", "Bambi\nand\nThumper".mb_chars])
end
def test_sanitize_sql_array_handles_bind_variables
diff --git a/activerecord/test/cases/schema_dumper_test.rb b/activerecord/test/cases/schema_dumper_test.rb
index 51170c533b..2a2c2bc8d0 100644
--- a/activerecord/test/cases/schema_dumper_test.rb
+++ b/activerecord/test/cases/schema_dumper_test.rb
@@ -215,7 +215,7 @@ class SchemaDumperTest < ActiveRecord::TestCase
def test_schema_dump_includes_length_for_mysql_blob_and_text_fields
output = standard_dump
- assert_match %r{t\.binary\s+"tiny_blob",\s+limit: 255$}, output
+ assert_match %r{t\.blob\s+"tiny_blob",\s+limit: 255$}, output
assert_match %r{t\.binary\s+"normal_blob",\s+limit: 65535$}, output
assert_match %r{t\.binary\s+"medium_blob",\s+limit: 16777215$}, output
assert_match %r{t\.binary\s+"long_blob",\s+limit: 4294967295$}, output
@@ -239,7 +239,7 @@ class SchemaDumperTest < ActiveRecord::TestCase
def test_schema_dump_includes_decimal_options
output = dump_all_table_schema([/^[^n]/])
- assert_match %r{precision: 3,[[:space:]]+scale: 2,[[:space:]]+default: 2\.78}, output
+ assert_match %r{precision: 3,[[:space:]]+scale: 2,[[:space:]]+default: "2\.78"}, output
end
if current_adapter?(:PostgreSQLAdapter)
@@ -255,7 +255,7 @@ class SchemaDumperTest < ActiveRecord::TestCase
def test_schema_dump_allows_array_of_decimal_defaults
output = standard_dump
- assert_match %r{t\.decimal\s+"decimal_array_default",\s+default: \[1.23, 3.45\],\s+array: true}, output
+ assert_match %r{t\.decimal\s+"decimal_array_default",\s+default: \["1.23", "3.45"\],\s+array: true}, output
end
if ActiveRecord::Base.connection.supports_extensions?
diff --git a/activerecord/test/cases/scoping/default_scoping_test.rb b/activerecord/test/cases/scoping/default_scoping_test.rb
index 0dbc60940e..86316ab476 100644
--- a/activerecord/test/cases/scoping/default_scoping_test.rb
+++ b/activerecord/test/cases/scoping/default_scoping_test.rb
@@ -3,6 +3,7 @@ require 'models/post'
require 'models/comment'
require 'models/developer'
require 'models/computer'
+require 'models/vehicle'
class DefaultScopingTest < ActiveRecord::TestCase
fixtures :developers, :posts, :comments
@@ -453,4 +454,9 @@ class DefaultScopingTest < ActiveRecord::TestCase
assert_equal 1, scope.where_clause.ast.children.length
assert_equal Developer.where(name: "David"), scope
end
+
+ def test_with_abstract_class_where_clause_should_not_be_duplicated
+ scope = Bus.all
+ assert_equal scope.where_clause.ast.children.length, 1
+ end
end
diff --git a/activerecord/test/cases/scoping/named_scoping_test.rb b/activerecord/test/cases/scoping/named_scoping_test.rb
index e4cc533517..7a8eaeccb7 100644
--- a/activerecord/test/cases/scoping/named_scoping_test.rb
+++ b/activerecord/test/cases/scoping/named_scoping_test.rb
@@ -188,8 +188,9 @@ class NamedScopingTest < ActiveRecord::TestCase
def test_any_should_call_proxy_found_if_using_a_block
topics = Topic.base
assert_queries(1) do
- topics.expects(:empty?).never
- topics.any? { true }
+ assert_not_called(topics, :empty?) do
+ topics.any? { true }
+ end
end
end
@@ -217,8 +218,9 @@ class NamedScopingTest < ActiveRecord::TestCase
def test_many_should_call_proxy_found_if_using_a_block
topics = Topic.base
assert_queries(1) do
- topics.expects(:size).never
- topics.many? { true }
+ assert_not_called(topics, :size) do
+ topics.many? { true }
+ end
end
end
diff --git a/activerecord/test/cases/serialization_test.rb b/activerecord/test/cases/serialization_test.rb
index 35b13ea247..14b80f4df4 100644
--- a/activerecord/test/cases/serialization_test.rb
+++ b/activerecord/test/cases/serialization_test.rb
@@ -8,7 +8,7 @@ require 'models/post'
class SerializationTest < ActiveRecord::TestCase
fixtures :books
- FORMATS = [ :xml, :json ]
+ FORMATS = [ :json ]
def setup
@contact_attributes = {
diff --git a/activerecord/test/cases/tasks/database_tasks_test.rb b/activerecord/test/cases/tasks/database_tasks_test.rb
index 38164b2228..c8f4179313 100644
--- a/activerecord/test/cases/tasks/database_tasks_test.rb
+++ b/activerecord/test/cases/tasks/database_tasks_test.rb
@@ -277,12 +277,14 @@ module ActiveRecord
def test_migrate_receives_correct_env_vars
verbose, version = ENV['VERBOSE'], ENV['VERSION']
+ ActiveRecord::Tasks::DatabaseTasks.migrations_paths = 'custom/path'
ENV['VERBOSE'] = 'false'
ENV['VERSION'] = '4'
- ActiveRecord::Migrator.expects(:migrate).with(ActiveRecord::Migrator.migrations_paths, 4)
+ ActiveRecord::Migrator.expects(:migrate).with('custom/path', 4)
ActiveRecord::Tasks::DatabaseTasks.migrate
ensure
+ ActiveRecord::Tasks::DatabaseTasks.migrations_paths = nil
ENV['VERBOSE'], ENV['VERSION'] = verbose, version
end
end
diff --git a/activerecord/test/cases/tasks/mysql_rake_test.rb b/activerecord/test/cases/tasks/mysql_rake_test.rb
index d0deb4c273..a93fa57257 100644
--- a/activerecord/test/cases/tasks/mysql_rake_test.rb
+++ b/activerecord/test/cases/tasks/mysql_rake_test.rb
@@ -270,15 +270,16 @@ module ActiveRecord
ActiveRecord::Tasks::DatabaseTasks.structure_dump(@configuration, filename)
end
- def test_warn_when_external_structure_dump_fails
+ def test_warn_when_external_structure_dump_command_execution_fails
filename = "awesome-file.sql"
- Kernel.expects(:system).with("mysqldump", "--result-file", filename, "--no-data", "--routines", "test-db").returns(false)
+ Kernel.expects(:system)
+ .with("mysqldump", "--result-file", filename, "--no-data", "--routines", "test-db")
+ .returns(false)
- warnings = capture(:stderr) do
+ e = assert_raise(RuntimeError) {
ActiveRecord::Tasks::DatabaseTasks.structure_dump(@configuration, filename)
- end
-
- assert_match(/Could not dump the database structure/, warnings)
+ }
+ assert_match(/^failed to execute: `mysqldump`$/, e.message)
end
def test_structure_dump_with_port_number
@@ -311,6 +312,7 @@ module ActiveRecord
def test_structure_load
filename = "awesome-file.sql"
Kernel.expects(:system).with('mysql', '--execute', %{SET FOREIGN_KEY_CHECKS = 0; SOURCE #{filename}; SET FOREIGN_KEY_CHECKS = 1}, "--database", "test-db")
+ .returns(true)
ActiveRecord::Tasks::DatabaseTasks.structure_load(@configuration, filename)
end
diff --git a/activerecord/test/cases/tasks/postgresql_rake_test.rb b/activerecord/test/cases/tasks/postgresql_rake_test.rb
index 084302cde5..c31f94b2f2 100644
--- a/activerecord/test/cases/tasks/postgresql_rake_test.rb
+++ b/activerecord/test/cases/tasks/postgresql_rake_test.rb
@@ -60,7 +60,7 @@ module ActiveRecord
$stderr.expects(:puts).
with("Couldn't create database for #{@configuration.inspect}")
- ActiveRecord::Tasks::DatabaseTasks.create @configuration
+ assert_raises(Exception) { ActiveRecord::Tasks::DatabaseTasks.create @configuration }
end
def test_create_when_database_exists_outputs_info_to_stderr
@@ -204,7 +204,7 @@ module ActiveRecord
end
def test_structure_dump
- Kernel.expects(:system).with("pg_dump -i -s -x -O -f #{@filename} my-app-db").returns(true)
+ Kernel.expects(:system).with('pg_dump', '-s', '-x', '-O', '-f', @filename, 'my-app-db').returns(true)
ActiveRecord::Tasks::DatabaseTasks.structure_dump(@configuration, @filename)
end
@@ -212,7 +212,7 @@ module ActiveRecord
def test_structure_dump_with_schema_search_path
@configuration['schema_search_path'] = 'foo,bar'
- Kernel.expects(:system).with("pg_dump -i -s -x -O -f #{@filename} --schema=foo --schema=bar my-app-db").returns(true)
+ Kernel.expects(:system).with('pg_dump', '-s', '-x', '-O', '-f', @filename, '--schema=foo --schema=bar', 'my-app-db').returns(true)
ActiveRecord::Tasks::DatabaseTasks.structure_dump(@configuration, @filename)
end
@@ -220,7 +220,7 @@ module ActiveRecord
def test_structure_dump_with_schema_search_path_and_dump_schemas_all
@configuration['schema_search_path'] = 'foo,bar'
- Kernel.expects(:system).with("pg_dump -i -s -x -O -f #{@filename} my-app-db").returns(true)
+ Kernel.expects(:system).with("pg_dump", '-s', '-x', '-O', '-f', @filename, 'my-app-db').returns(true)
with_dump_schemas(:all) do
ActiveRecord::Tasks::DatabaseTasks.structure_dump(@configuration, @filename)
@@ -228,7 +228,7 @@ module ActiveRecord
end
def test_structure_dump_with_dump_schemas_string
- Kernel.expects(:system).with("pg_dump -i -s -x -O -f #{@filename} --schema=foo --schema=bar my-app-db").returns(true)
+ Kernel.expects(:system).with("pg_dump", '-s', '-x', '-O', '-f', @filename, '--schema=foo --schema=bar', "my-app-db").returns(true)
with_dump_schemas('foo,bar') do
ActiveRecord::Tasks::DatabaseTasks.structure_dump(@configuration, @filename)
@@ -261,14 +261,14 @@ module ActiveRecord
def test_structure_load
filename = "awesome-file.sql"
- Kernel.expects(:system).with("psql -X -q -f #{filename} my-app-db")
+ Kernel.expects(:system).with('psql', '-q', '-f', filename, @configuration['database']).returns(true)
ActiveRecord::Tasks::DatabaseTasks.structure_load(@configuration, filename)
end
def test_structure_load_accepts_path_with_spaces
filename = "awesome file.sql"
- Kernel.expects(:system).with("psql -X -q -f awesome\\ file.sql my-app-db")
+ Kernel.expects(:system).with('psql', '-q', '-f', filename, @configuration['database']).returns(true)
ActiveRecord::Tasks::DatabaseTasks.structure_load(@configuration, filename)
end
diff --git a/activerecord/test/cases/tasks/sqlite_rake_test.rb b/activerecord/test/cases/tasks/sqlite_rake_test.rb
index 750d5e42dc..0aea0c3b38 100644
--- a/activerecord/test/cases/tasks/sqlite_rake_test.rb
+++ b/activerecord/test/cases/tasks/sqlite_rake_test.rb
@@ -53,7 +53,7 @@ module ActiveRecord
$stderr.expects(:puts).
with("Couldn't create database for #{@configuration.inspect}")
- ActiveRecord::Tasks::DatabaseTasks.create @configuration, '/rails/root'
+ assert_raises(Exception) { ActiveRecord::Tasks::DatabaseTasks.create @configuration, '/rails/root' }
end
end
diff --git a/activerecord/test/cases/test_case.rb b/activerecord/test/cases/test_case.rb
index 7761ea5612..47e664f4e7 100644
--- a/activerecord/test/cases/test_case.rb
+++ b/activerecord/test/cases/test_case.rb
@@ -103,7 +103,7 @@ module ActiveRecord
# ignored SQL, or better yet, use a different notification for the queries
# instead examining the SQL content.
oracle_ignored = [/^select .*nextval/i, /^SAVEPOINT/, /^ROLLBACK TO/, /^\s*select .* from all_triggers/im, /^\s*select .* from all_constraints/im, /^\s*select .* from all_tab_cols/im]
- mysql_ignored = [/^SHOW TABLES/i, /^SHOW FULL FIELDS/, /^SHOW CREATE TABLE /i, /^SHOW VARIABLES /]
+ mysql_ignored = [/^SHOW FULL TABLES/i, /^SHOW FULL FIELDS/, /^SHOW CREATE TABLE /i, /^SHOW VARIABLES /, /^\s*SELECT (?:column_name|table_name)\b.*\bFROM information_schema\.(?:key_column_usage|tables)\b/im]
postgresql_ignored = [/^\s*select\b.*\bfrom\b.*pg_namespace\b/im, /^\s*select tablename\b.*from pg_tables\b/im, /^\s*select\b.*\battname\b.*\bfrom\b.*\bpg_attribute\b/im, /^SHOW search_path/i]
sqlite3_ignored = [/^\s*SELECT name\b.*\bFROM sqlite_master/im, /^\s*SELECT sql\b.*\bFROM sqlite_master/im]
diff --git a/activerecord/test/cases/test_fixtures_test.rb b/activerecord/test/cases/test_fixtures_test.rb
index 3f4baf8378..1970fe82d0 100644
--- a/activerecord/test/cases/test_fixtures_test.rb
+++ b/activerecord/test/cases/test_fixtures_test.rb
@@ -28,7 +28,7 @@ class TestFixturesTest < ActiveRecord::TestCase
assert_equal true, @klass.use_transactional_tests
end
- def test_use_transactional_tests_can_be_overriden
+ def test_use_transactional_tests_can_be_overridden
@klass.use_transactional_tests = "foobar"
assert_equal "foobar", @klass.use_transactional_tests
diff --git a/activerecord/test/cases/timestamp_test.rb b/activerecord/test/cases/timestamp_test.rb
index 5dab32995c..970f6bcf4a 100644
--- a/activerecord/test/cases/timestamp_test.rb
+++ b/activerecord/test/cases/timestamp_test.rb
@@ -84,7 +84,9 @@ class TimestampTest < ActiveRecord::TestCase
def test_touching_an_attribute_updates_timestamp
previously_created_at = @developer.created_at
- @developer.touch(:created_at)
+ travel(1.second) do
+ @developer.touch(:created_at)
+ end
assert !@developer.created_at_changed? , 'created_at should not be changed'
assert !@developer.changed?, 'record should not be changed'
@@ -199,8 +201,10 @@ class TimestampTest < ActiveRecord::TestCase
owner = pet.owner
previously_owner_updated_at = owner.updated_at
- pet.name = "Fluffy the Third"
- pet.save
+ travel(1.second) do
+ pet.name = "Fluffy the Third"
+ pet.save
+ end
assert_not_equal previously_owner_updated_at, pet.owner.updated_at
end
@@ -210,7 +214,9 @@ class TimestampTest < ActiveRecord::TestCase
owner = pet.owner
previously_owner_updated_at = owner.updated_at
- pet.destroy
+ travel(1.second) do
+ pet.destroy
+ end
assert_not_equal previously_owner_updated_at, pet.owner.updated_at
end
@@ -254,8 +260,10 @@ class TimestampTest < ActiveRecord::TestCase
owner.update_columns(happy_at: 3.days.ago)
previously_owner_updated_at = owner.updated_at
- pet.name = "I'm a parrot"
- pet.save
+ travel(1.second) do
+ pet.name = "I'm a parrot"
+ pet.save
+ end
assert_not_equal previously_owner_updated_at, pet.owner.updated_at
end
diff --git a/activerecord/test/cases/touch_later_test.rb b/activerecord/test/cases/touch_later_test.rb
index 11804ff90b..7058f4fbe2 100644
--- a/activerecord/test/cases/touch_later_test.rb
+++ b/activerecord/test/cases/touch_later_test.rb
@@ -2,13 +2,16 @@ require 'cases/helper'
require 'models/invoice'
require 'models/line_item'
require 'models/topic'
+require 'models/node'
+require 'models/tree'
class TouchLaterTest < ActiveRecord::TestCase
+ fixtures :nodes, :trees
def test_touch_laster_raise_if_non_persisted
invoice = Invoice.new
Invoice.transaction do
- refute invoice.persisted?
+ assert_not invoice.persisted?
assert_raises(ActiveRecord::ActiveRecordError) do
invoice.touch_later
end
@@ -18,7 +21,7 @@ class TouchLaterTest < ActiveRecord::TestCase
def test_touch_later_dont_set_dirty_attributes
invoice = Invoice.create!
invoice.touch_later
- refute invoice.changed?
+ assert_not invoice.changed?
end
def test_touch_later_update_the_attributes
@@ -69,7 +72,7 @@ class TouchLaterTest < ActiveRecord::TestCase
end
def test_touch_touches_immediately_with_a_custom_time
- time = Time.now.utc - 25.days
+ time = (Time.now.utc - 25.days).change(nsec: 0)
topic = Topic.create!(updated_at: time, created_at: time)
assert_equal time, topic.updated_at
assert_equal time, topic.created_at
@@ -90,4 +93,22 @@ class TouchLaterTest < ActiveRecord::TestCase
invoice.touch_later
end
end
+
+ def test_touching_three_deep
+ skip "Pending from #19324"
+
+ previous_tree_updated_at = trees(:root).updated_at
+ previous_grandparent_updated_at = nodes(:grandparent).updated_at
+ previous_parent_updated_at = nodes(:parent_a).updated_at
+ previous_child_updated_at = nodes(:child_one_of_a).updated_at
+
+ travel 5.seconds
+
+ Node.create! parent: nodes(:child_one_of_a), tree: trees(:root)
+
+ assert_not_equal nodes(:child_one_of_a).reload.updated_at, previous_child_updated_at
+ assert_not_equal nodes(:parent_a).reload.updated_at, previous_parent_updated_at
+ assert_not_equal nodes(:grandparent).reload.updated_at, previous_grandparent_updated_at
+ assert_not_equal trees(:root).reload.updated_at, previous_tree_updated_at
+ end
end
diff --git a/activerecord/test/cases/transactions_test.rb b/activerecord/test/cases/transactions_test.rb
index 2468a91969..ec5bdfd725 100644
--- a/activerecord/test/cases/transactions_test.rb
+++ b/activerecord/test/cases/transactions_test.rb
@@ -175,13 +175,20 @@ class TransactionTest < ActiveRecord::TestCase
assert topic.new_record?, "#{topic.inspect} should be new record"
end
+ def test_transaction_state_is_cleared_when_record_is_persisted
+ author = Author.create! name: 'foo'
+ author.name = nil
+ assert_not author.save
+ assert_not author.new_record?
+ end
+
def test_update_should_rollback_on_failure
author = Author.find(1)
posts_count = author.posts.size
assert posts_count > 0
status = author.update(name: nil, post_ids: [])
assert !status
- assert_equal posts_count, author.posts(true).size
+ assert_equal posts_count, author.posts.reload.size
end
def test_update_should_rollback_on_failure!
@@ -191,7 +198,7 @@ class TransactionTest < ActiveRecord::TestCase
assert_raise(ActiveRecord::RecordInvalid) do
author.update!(name: nil, post_ids: [])
end
- assert_equal posts_count, author.posts(true).size
+ assert_equal posts_count, author.posts.reload.size
end
def test_cancellation_from_returning_false_in_before_filter
@@ -480,13 +487,17 @@ class TransactionTest < ActiveRecord::TestCase
end
def test_rollback_when_commit_raises
- Topic.connection.expects(:begin_db_transaction)
- Topic.connection.expects(:commit_db_transaction).raises('OH NOES')
- Topic.connection.expects(:rollback_db_transaction)
+ assert_called(Topic.connection, :begin_db_transaction) do
+ Topic.connection.stub(:commit_db_transaction, ->{ raise('OH NOES') }) do
+ assert_called(Topic.connection, :rollback_db_transaction) do
- assert_raise RuntimeError do
- Topic.transaction do
- # do nothing
+ e = assert_raise RuntimeError do
+ Topic.transaction do
+ # do nothing
+ end
+ end
+ assert_equal 'OH NOES', e.message
+ end
end
end
end
diff --git a/activerecord/test/cases/type/date_time_test.rb b/activerecord/test/cases/type/date_time_test.rb
new file mode 100644
index 0000000000..bc4900e1c2
--- /dev/null
+++ b/activerecord/test/cases/type/date_time_test.rb
@@ -0,0 +1,14 @@
+require "cases/helper"
+require "models/task"
+
+module ActiveRecord
+ module Type
+ class IntegerTest < ActiveRecord::TestCase
+ def test_datetime_seconds_precision_applied_to_timestamp
+ skip "This test is invalid if subsecond precision isn't supported" unless subsecond_precision_supported?
+ p = Task.create!(starting: ::Time.now)
+ assert_equal p.starting.usec, p.reload.starting.usec
+ end
+ end
+ end
+end
diff --git a/activerecord/test/cases/type/decimal_test.rb b/activerecord/test/cases/type/decimal_test.rb
deleted file mode 100644
index fe49d0e79a..0000000000
--- a/activerecord/test/cases/type/decimal_test.rb
+++ /dev/null
@@ -1,51 +0,0 @@
-require "cases/helper"
-
-module ActiveRecord
- module Type
- class DecimalTest < ActiveRecord::TestCase
- def test_type_cast_decimal
- type = Decimal.new
- assert_equal BigDecimal.new("0"), type.cast(BigDecimal.new("0"))
- assert_equal BigDecimal.new("123"), type.cast(123.0)
- assert_equal BigDecimal.new("1"), type.cast(:"1")
- end
-
- def test_type_cast_decimal_from_float_with_large_precision
- type = Decimal.new(precision: ::Float::DIG + 2)
- assert_equal BigDecimal.new("123.0"), type.cast(123.0)
- end
-
- def test_type_cast_from_float_with_unspecified_precision
- type = Decimal.new
- assert_equal 22.68.to_d, type.cast(22.68)
- end
-
- def test_type_cast_decimal_from_rational_with_precision
- type = Decimal.new(precision: 2)
- assert_equal BigDecimal("0.33"), type.cast(Rational(1, 3))
- end
-
- def test_type_cast_decimal_from_rational_without_precision_defaults_to_18_36
- type = Decimal.new
- assert_equal BigDecimal("0.333333333333333333E0"), type.cast(Rational(1, 3))
- end
-
- def test_type_cast_decimal_from_object_responding_to_d
- value = Object.new
- def value.to_d
- BigDecimal.new("1")
- end
- type = Decimal.new
- assert_equal BigDecimal("1"), type.cast(value)
- end
-
- def test_changed?
- type = Decimal.new
-
- assert type.changed?(5.0, 5.0, '5.0wibble')
- assert_not type.changed?(5.0, 5.0, '5.0')
- assert_not type.changed?(-5.0, -5.0, '-5.0')
- end
- end
- end
-end
diff --git a/activerecord/test/cases/type/integer_test.rb b/activerecord/test/cases/type/integer_test.rb
index 0dcdbd0667..c0932d5357 100644
--- a/activerecord/test/cases/type/integer_test.rb
+++ b/activerecord/test/cases/type/integer_test.rb
@@ -4,112 +4,12 @@ require "models/company"
module ActiveRecord
module Type
class IntegerTest < ActiveRecord::TestCase
- test "simple values" do
- type = Type::Integer.new
- assert_equal 1, type.cast(1)
- assert_equal 1, type.cast('1')
- assert_equal 1, type.cast('1ignore')
- assert_equal 0, type.cast('bad1')
- assert_equal 0, type.cast('bad')
- assert_equal 1, type.cast(1.7)
- assert_equal 0, type.cast(false)
- assert_equal 1, type.cast(true)
- assert_nil type.cast(nil)
- end
-
- test "random objects cast to nil" do
- type = Type::Integer.new
- assert_nil type.cast([1,2])
- assert_nil type.cast({1 => 2})
- assert_nil type.cast(1..2)
- end
-
test "casting ActiveRecord models" do
type = Type::Integer.new
firm = Firm.create(:name => 'Apple')
assert_nil type.cast(firm)
end
- test "casting objects without to_i" do
- type = Type::Integer.new
- assert_nil type.cast(::Object.new)
- end
-
- test "casting nan and infinity" do
- type = Type::Integer.new
- assert_nil type.cast(::Float::NAN)
- assert_nil type.cast(1.0/0.0)
- end
-
- test "casting booleans for database" do
- type = Type::Integer.new
- assert_equal 1, type.serialize(true)
- assert_equal 0, type.serialize(false)
- end
-
- test "changed?" do
- type = Type::Integer.new
-
- assert type.changed?(5, 5, '5wibble')
- assert_not type.changed?(5, 5, '5')
- assert_not type.changed?(5, 5, '5.0')
- assert_not type.changed?(-5, -5, '-5')
- assert_not type.changed?(-5, -5, '-5.0')
- assert_not type.changed?(nil, nil, nil)
- end
-
- test "values below int min value are out of range" do
- assert_raises(::RangeError) do
- Integer.new.serialize(-2147483649)
- end
- end
-
- test "values above int max value are out of range" do
- assert_raises(::RangeError) do
- Integer.new.serialize(2147483648)
- end
- end
-
- test "very small numbers are out of range" do
- assert_raises(::RangeError) do
- Integer.new.serialize(-9999999999999999999999999999999)
- end
- end
-
- test "very large numbers are out of range" do
- assert_raises(::RangeError) do
- Integer.new.serialize(9999999999999999999999999999999)
- end
- end
-
- test "normal numbers are in range" do
- type = Integer.new
- assert_equal(0, type.serialize(0))
- assert_equal(-1, type.serialize(-1))
- assert_equal(1, type.serialize(1))
- end
-
- test "int max value is in range" do
- assert_equal(2147483647, Integer.new.serialize(2147483647))
- end
-
- test "int min value is in range" do
- assert_equal(-2147483648, Integer.new.serialize(-2147483648))
- end
-
- test "columns with a larger limit have larger ranges" do
- type = Integer.new(limit: 8)
-
- assert_equal(9223372036854775807, type.serialize(9223372036854775807))
- assert_equal(-9223372036854775808, type.serialize(-9223372036854775808))
- assert_raises(::RangeError) do
- type.serialize(-9999999999999999999999999999999)
- end
- assert_raises(::RangeError) do
- type.serialize(9999999999999999999999999999999)
- end
- end
-
test "values which are out of range can be re-assigned" do
klass = Class.new(ActiveRecord::Base) do
self.table_name = 'posts'
diff --git a/activerecord/test/cases/type/string_test.rb b/activerecord/test/cases/type/string_test.rb
index 56e9bf434d..6fe6d46711 100644
--- a/activerecord/test/cases/type/string_test.rb
+++ b/activerecord/test/cases/type/string_test.rb
@@ -2,20 +2,6 @@ require 'cases/helper'
module ActiveRecord
class StringTypeTest < ActiveRecord::TestCase
- test "type casting" do
- type = Type::String.new
- assert_equal "t", type.cast(true)
- assert_equal "f", type.cast(false)
- assert_equal "123", type.cast(123)
- end
-
- test "values are duped coming out" do
- s = "foo"
- type = Type::String.new
- assert_not_same s, type.cast(s)
- assert_not_same s, type.deserialize(s)
- end
-
test "string mutations are detected" do
klass = Class.new(Base)
klass.table_name = 'authors'
diff --git a/activerecord/test/cases/type/unsigned_integer_test.rb b/activerecord/test/cases/type/unsigned_integer_test.rb
deleted file mode 100644
index f2c910eade..0000000000
--- a/activerecord/test/cases/type/unsigned_integer_test.rb
+++ /dev/null
@@ -1,17 +0,0 @@
-require "cases/helper"
-
-module ActiveRecord
- module Type
- class UnsignedIntegerTest < ActiveRecord::TestCase
- test "unsigned int max value is in range" do
- assert_equal(4294967295, UnsignedInteger.new.serialize(4294967295))
- end
-
- test "minus value is out of range" do
- assert_raises(::RangeError) do
- UnsignedInteger.new.serialize(-1)
- end
- end
- end
- end
-end
diff --git a/activerecord/test/cases/types_test.rb b/activerecord/test/cases/types_test.rb
index 9b1859c2ce..81fcf04a27 100644
--- a/activerecord/test/cases/types_test.rb
+++ b/activerecord/test/cases/types_test.rb
@@ -3,111 +3,6 @@ require "cases/helper"
module ActiveRecord
module ConnectionAdapters
class TypesTest < ActiveRecord::TestCase
- def test_type_cast_boolean
- type = Type::Boolean.new
- assert type.cast('').nil?
- assert type.cast(nil).nil?
-
- assert type.cast(true)
- assert type.cast(1)
- assert type.cast('1')
- assert type.cast('t')
- assert type.cast('T')
- assert type.cast('true')
- assert type.cast('TRUE')
- assert type.cast('on')
- assert type.cast('ON')
- assert type.cast(' ')
- assert type.cast("\u3000\r\n")
- assert type.cast("\u0000")
- assert type.cast('SOMETHING RANDOM')
-
- # explicitly check for false vs nil
- assert_equal false, type.cast(false)
- assert_equal false, type.cast(0)
- assert_equal false, type.cast('0')
- assert_equal false, type.cast('f')
- assert_equal false, type.cast('F')
- assert_equal false, type.cast('false')
- assert_equal false, type.cast('FALSE')
- assert_equal false, type.cast('off')
- assert_equal false, type.cast('OFF')
- end
-
- def test_type_cast_float
- type = Type::Float.new
- assert_equal 1.0, type.cast("1")
- end
-
- def test_changing_float
- type = Type::Float.new
-
- assert type.changed?(5.0, 5.0, '5wibble')
- assert_not type.changed?(5.0, 5.0, '5')
- assert_not type.changed?(5.0, 5.0, '5.0')
- assert_not type.changed?(nil, nil, nil)
- end
-
- def test_type_cast_binary
- type = Type::Binary.new
- assert_equal nil, type.cast(nil)
- assert_equal "1", type.cast("1")
- assert_equal 1, type.cast(1)
- end
-
- def test_type_cast_time
- type = Type::Time.new
- assert_equal nil, type.cast(nil)
- assert_equal nil, type.cast('')
- assert_equal nil, type.cast('ABC')
-
- time_string = Time.now.utc.strftime("%T")
- assert_equal time_string, type.cast(time_string).strftime("%T")
- end
-
- def test_type_cast_datetime_and_timestamp
- type = Type::DateTime.new
- assert_equal nil, type.cast(nil)
- assert_equal nil, type.cast('')
- assert_equal nil, type.cast(' ')
- assert_equal nil, type.cast('ABC')
-
- datetime_string = Time.now.utc.strftime("%FT%T")
- assert_equal datetime_string, type.cast(datetime_string).strftime("%FT%T")
- end
-
- def test_type_cast_date
- type = Type::Date.new
- assert_equal nil, type.cast(nil)
- assert_equal nil, type.cast('')
- assert_equal nil, type.cast(' ')
- assert_equal nil, type.cast('ABC')
-
- date_string = Time.now.utc.strftime("%F")
- assert_equal date_string, type.cast(date_string).strftime("%F")
- end
-
- def test_type_cast_duration_to_integer
- type = Type::Integer.new
- assert_equal 1800, type.cast(30.minutes)
- assert_equal 7200, type.cast(2.hours)
- end
-
- def test_string_to_time_with_timezone
- [:utc, :local].each do |zone|
- with_timezone_config default: zone do
- type = Type::DateTime.new
- assert_equal Time.utc(2013, 9, 4, 0, 0, 0), type.cast("Wed, 04 Sep 2013 03:00:00 EAT")
- end
- end
- end
-
- def test_type_equality
- assert_equal Type::Value.new, Type::Value.new
- assert_not_equal Type::Value.new, Type::Integer.new
- assert_not_equal Type::Value.new(precision: 1), Type::Value.new(precision: 2)
- end
-
def test_attributes_which_are_invalid_for_database_can_still_be_reassigned
type_which_cannot_go_to_the_database = Type::Value.new
def type_which_cannot_go_to_the_database.serialize(*)
diff --git a/activerecord/test/cases/validations/i18n_validation_test.rb b/activerecord/test/cases/validations/i18n_validation_test.rb
index 268d7914b5..981239c4d6 100644
--- a/activerecord/test/cases/validations/i18n_validation_test.rb
+++ b/activerecord/test/cases/validations/i18n_validation_test.rb
@@ -53,8 +53,9 @@ class I18nValidationTest < ActiveRecord::TestCase
test "validates_uniqueness_of on generated message #{name}" do
Topic.validates_uniqueness_of :title, validation_options
@topic.title = unique_topic.title
- @topic.errors.expects(:generate_message).with(:title, :taken, generate_message_options.merge(:value => 'unique!'))
- @topic.valid?
+ assert_called_with(@topic.errors, :generate_message, [:title, :taken, generate_message_options.merge(:value => 'unique!')]) do
+ @topic.valid?
+ end
end
end
@@ -63,8 +64,9 @@ class I18nValidationTest < ActiveRecord::TestCase
COMMON_CASES.each do |name, validation_options, generate_message_options|
test "validates_associated on generated message #{name}" do
Topic.validates_associated :replies, validation_options
- replied_topic.errors.expects(:generate_message).with(:replies, :invalid, generate_message_options.merge(:value => replied_topic.replies))
- replied_topic.save
+ assert_called_with(replied_topic.errors, :generate_message, [:replies, :invalid, generate_message_options.merge(:value => replied_topic.replies)]) do
+ replied_topic.save
+ end
end
end
diff --git a/activerecord/test/cases/validations/length_validation_test.rb b/activerecord/test/cases/validations/length_validation_test.rb
index f95f8f0b8f..c5d8f8895c 100644
--- a/activerecord/test/cases/validations/length_validation_test.rb
+++ b/activerecord/test/cases/validations/length_validation_test.rb
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
require "cases/helper"
require 'models/owner'
require 'models/pet'
diff --git a/activerecord/test/cases/validations/uniqueness_validation_test.rb b/activerecord/test/cases/validations/uniqueness_validation_test.rb
index 2608c84be2..7502a55391 100644
--- a/activerecord/test/cases/validations/uniqueness_validation_test.rb
+++ b/activerecord/test/cases/validations/uniqueness_validation_test.rb
@@ -4,6 +4,7 @@ require 'models/reply'
require 'models/warehouse_thing'
require 'models/guid'
require 'models/event'
+require 'models/dashboard'
class Wizard < ActiveRecord::Base
self.abstract_class = true
@@ -427,4 +428,45 @@ class UniquenessValidationTest < ActiveRecord::TestCase
assert reply.valid?
assert topic.valid?
end
+
+ def test_validate_uniqueness_of_custom_primary_key
+ klass = Class.new(ActiveRecord::Base) do
+ self.table_name = "keyboards"
+ self.primary_key = :key_number
+
+ validates_uniqueness_of :key_number
+
+ def self.name
+ "Keyboard"
+ end
+ end
+
+ klass.create!(key_number: 10)
+ key2 = klass.create!(key_number: 11)
+
+ key2.key_number = 10
+ assert_not key2.valid?
+ end
+
+ def test_validate_uniqueness_without_primary_key
+ klass = Class.new(ActiveRecord::Base) do
+ self.table_name = "dashboards"
+
+ validates_uniqueness_of :dashboard_id
+
+ def self.name; "Dashboard" end
+ end
+
+ abc = klass.create!(dashboard_id: "abc")
+ assert klass.new(dashboard_id: "xyz").valid?
+ assert_not klass.new(dashboard_id: "abc").valid?
+
+ abc.dashboard_id = "def"
+
+ e = assert_raises ActiveRecord::UnknownPrimaryKey do
+ abc.save!
+ end
+ assert_match(/\AUnknown primary key for table dashboards in model/, e.message)
+ assert_match(/Can not validate uniqueness for persisted record without primary key.\z/, e.message)
+ end
end
diff --git a/activerecord/test/cases/validations_test.rb b/activerecord/test/cases/validations_test.rb
index f4f316f393..d04f4f7ce7 100644
--- a/activerecord/test/cases/validations_test.rb
+++ b/activerecord/test/cases/validations_test.rb
@@ -52,6 +52,13 @@ class ValidationsTest < ActiveRecord::TestCase
assert r.valid?(:special_case)
end
+ def test_invalid_using_multiple_contexts
+ r = WrongReply.new(:title => 'Wrong Create')
+ assert r.invalid?([:special_case, :create])
+ assert_equal "Invalid", r.errors[:author_name].join
+ assert_equal "is Wrong Create", r.errors[:title].join
+ end
+
def test_validate
r = WrongReply.new
@@ -161,4 +168,15 @@ class ValidationsTest < ActiveRecord::TestCase
ensure
Topic.reset_column_information
end
+
+ def test_acceptance_validator_doesnt_require_db_connection
+ klass = Class.new(ActiveRecord::Base) do
+ self.table_name = 'posts'
+ end
+ klass.reset_column_information
+
+ assert_no_queries do
+ klass.validates_acceptance_of(:foo)
+ end
+ end
end
diff --git a/activerecord/test/cases/view_test.rb b/activerecord/test/cases/view_test.rb
index f9dca1e196..e80d8bd584 100644
--- a/activerecord/test/cases/view_test.rb
+++ b/activerecord/test/cases/view_test.rb
@@ -1,7 +1,9 @@
require "cases/helper"
require "models/book"
+require "support/schema_dumping_helper"
module ViewBehavior
+ include SchemaDumpingHelper
extend ActiveSupport::Concern
included do
@@ -31,11 +33,26 @@ module ViewBehavior
assert_equal ["Ruby for Rails"], books.map(&:name)
end
+ def test_views
+ assert_equal [Ebook.table_name], @connection.views
+ end
+
+ def test_view_exists
+ view_name = Ebook.table_name
+ assert @connection.view_exists?(view_name), "'#{view_name}' view should exist"
+ end
+
def test_table_exists
view_name = Ebook.table_name
+ # TODO: switch this assertion around once we changed #tables to not return views.
assert @connection.table_exists?(view_name), "'#{view_name}' table should exist"
end
+ def test_views_ara_valid_data_sources
+ view_name = Ebook.table_name
+ assert @connection.data_source_exists?(view_name), "'#{view_name}' should be a data source"
+ end
+
def test_column_definitions
assert_equal([["id", :integer],
["name", :string],
@@ -53,6 +70,11 @@ module ViewBehavior
end
assert_nil model.primary_key
end
+
+ def test_does_not_dump_view_as_table
+ schema = dump_table_schema "ebooks"
+ assert_no_match %r{create_table "ebooks"}, schema
+ end
end
if ActiveRecord::Base.connection.supports_views?
@@ -70,6 +92,7 @@ class ViewWithPrimaryKeyTest < ActiveRecord::TestCase
end
class ViewWithoutPrimaryKeyTest < ActiveRecord::TestCase
+ include SchemaDumpingHelper
fixtures :books
class Paperback < ActiveRecord::Base; end
@@ -91,6 +114,15 @@ class ViewWithoutPrimaryKeyTest < ActiveRecord::TestCase
assert_equal ["Agile Web Development with Rails"], books.map(&:name)
end
+ def test_views
+ assert_equal [Paperback.table_name], @connection.views
+ end
+
+ def test_view_exists
+ view_name = Paperback.table_name
+ assert @connection.view_exists?(view_name), "'#{view_name}' view should exist"
+ end
+
def test_table_exists
view_name = Paperback.table_name
assert @connection.table_exists?(view_name), "'#{view_name}' table should exist"
@@ -109,5 +141,76 @@ class ViewWithoutPrimaryKeyTest < ActiveRecord::TestCase
def test_does_not_have_a_primary_key
assert_nil Paperback.primary_key
end
+
+ def test_does_not_dump_view_as_table
+ schema = dump_table_schema "paperbacks"
+ assert_no_match %r{create_table "paperbacks"}, schema
+ end
+end
+
+# sqlite dose not support CREATE, INSERT, and DELETE for VIEW
+if current_adapter?(:MysqlAdapter, :Mysql2Adapter, :PostgreSQLAdapter)
+class UpdateableViewTest < ActiveRecord::TestCase
+ self.use_transactional_tests = false
+ fixtures :books
+
+ class PrintedBook < ActiveRecord::Base
+ self.primary_key = "id"
+ end
+
+ setup do
+ @connection = ActiveRecord::Base.connection
+ @connection.execute <<-SQL
+ CREATE VIEW printed_books
+ AS SELECT id, name, status, format FROM books WHERE format = 'paperback'
+ SQL
+ end
+
+ teardown do
+ @connection.execute "DROP VIEW printed_books" if @connection.table_exists? "printed_books"
+ end
+
+ def test_update_record
+ book = PrintedBook.first
+ book.name = "AWDwR"
+ book.save!
+ book.reload
+ assert_equal "AWDwR", book.name
+ end
+
+ def test_insert_record
+ PrintedBook.create! name: "Rails in Action", status: 0, format: "paperback"
+
+ new_book = PrintedBook.last
+ assert_equal "Rails in Action", new_book.name
+ end
+
+ def test_update_record_to_fail_view_conditions
+ book = PrintedBook.first
+ book.format = "ebook"
+ book.save!
+
+ assert_raises ActiveRecord::RecordNotFound do
+ book.reload
+ end
+ end
+end
+end # end fo `if current_adapter?(:MysqlAdapter, :Mysql2Adapter, :PostgreSQLAdapter)`
+end # end fo `if ActiveRecord::Base.connection.supports_views?`
+
+if ActiveRecord::Base.connection.respond_to?(:supports_materialized_views?) &&
+ ActiveRecord::Base.connection.supports_materialized_views?
+class MaterializedViewTest < ActiveRecord::PostgreSQLTestCase
+ include ViewBehavior
+
+ private
+ def create_view(name, query)
+ @connection.execute "CREATE MATERIALIZED VIEW #{name} AS #{query}"
+ end
+
+ def drop_view(name)
+ @connection.execute "DROP MATERIALIZED VIEW #{name}" if @connection.table_exists? name
+
+ end
end
end
diff --git a/activerecord/test/cases/xml_serialization_test.rb b/activerecord/test/cases/xml_serialization_test.rb
deleted file mode 100644
index b30b50f597..0000000000
--- a/activerecord/test/cases/xml_serialization_test.rb
+++ /dev/null
@@ -1,447 +0,0 @@
-require "cases/helper"
-require "rexml/document"
-require 'models/contact'
-require 'models/post'
-require 'models/author'
-require 'models/comment'
-require 'models/company_in_module'
-require 'models/toy'
-require 'models/topic'
-require 'models/reply'
-require 'models/company'
-
-class XmlSerializationTest < ActiveRecord::TestCase
- def test_should_serialize_default_root
- @xml = Contact.new.to_xml
- assert_match %r{^<contact>}, @xml
- assert_match %r{</contact>$}, @xml
- end
-
- def test_should_serialize_default_root_with_namespace
- @xml = Contact.new.to_xml :namespace=>"http://xml.rubyonrails.org/contact"
- assert_match %r{^<contact xmlns="http://xml\.rubyonrails\.org/contact">}, @xml
- assert_match %r{</contact>$}, @xml
- end
-
- def test_should_serialize_custom_root
- @xml = Contact.new.to_xml :root => 'xml_contact'
- assert_match %r{^<xml-contact>}, @xml
- assert_match %r{</xml-contact>$}, @xml
- end
-
- def test_should_allow_undasherized_tags
- @xml = Contact.new.to_xml :root => 'xml_contact', :dasherize => false
- assert_match %r{^<xml_contact>}, @xml
- assert_match %r{</xml_contact>$}, @xml
- assert_match %r{<created_at}, @xml
- end
-
- def test_should_allow_camelized_tags
- @xml = Contact.new.to_xml :root => 'xml_contact', :camelize => true
- assert_match %r{^<XmlContact>}, @xml
- assert_match %r{</XmlContact>$}, @xml
- assert_match %r{<CreatedAt}, @xml
- end
-
- def test_should_allow_skipped_types
- @xml = Contact.new(:age => 25).to_xml :skip_types => true
- assert %r{<age>25</age>}.match(@xml)
- end
-
- def test_should_include_yielded_additions
- @xml = Contact.new.to_xml do |xml|
- xml.creator "David"
- end
- assert_match %r{<creator>David</creator>}, @xml
- end
-
- def test_to_xml_with_block
- value = "Rockin' the block"
- xml = Contact.new.to_xml(:skip_instruct => true) do |_xml|
- _xml.tag! "arbitrary-element", value
- end
- assert_equal "<contact>", xml.first(9)
- assert xml.include?(%(<arbitrary-element>#{value}</arbitrary-element>))
- end
-
- def test_should_skip_instruct_for_included_records
- @contact = Contact.new
- @contact.alternative = Contact.new(:name => 'Copa Cabana')
- @xml = @contact.to_xml(:include => [ :alternative ])
- assert_equal @xml.index('<?xml '), 0
- assert_nil @xml.index('<?xml ', 1)
- end
-end
-
-class DefaultXmlSerializationTest < ActiveRecord::TestCase
- def setup
- @contact = Contact.new(
- :name => 'aaron stack',
- :age => 25,
- :avatar => 'binarydata',
- :created_at => Time.utc(2006, 8, 1),
- :awesome => false,
- :preferences => { :gem => 'ruby' }
- )
- end
-
- def test_should_serialize_string
- assert_match %r{<name>aaron stack</name>}, @contact.to_xml
- end
-
- def test_should_serialize_integer
- assert_match %r{<age type="integer">25</age>}, @contact.to_xml
- end
-
- def test_should_serialize_binary
- xml = @contact.to_xml
- assert_match %r{YmluYXJ5ZGF0YQ==\n</avatar>}, xml
- assert_match %r{<avatar(.*)(type="binary")}, xml
- assert_match %r{<avatar(.*)(encoding="base64")}, xml
- end
-
- def test_should_serialize_datetime
- assert_match %r{<created-at type=\"dateTime\">2006-08-01T00:00:00Z</created-at>}, @contact.to_xml
- end
-
- def test_should_serialize_boolean
- assert_match %r{<awesome type=\"boolean\">false</awesome>}, @contact.to_xml
- end
-
- def test_should_serialize_hash
- assert_match %r{<preferences>\s*<gem>ruby</gem>\s*</preferences>}m, @contact.to_xml
- end
-
- def test_uses_serializable_hash_with_only_option
- def @contact.serializable_hash(options=nil)
- super(only: %w(name))
- end
-
- xml = @contact.to_xml
- assert_match %r{<name>aaron stack</name>}, xml
- assert_no_match %r{age}, xml
- assert_no_match %r{awesome}, xml
- end
-
- def test_uses_serializable_hash_with_except_option
- def @contact.serializable_hash(options=nil)
- super(except: %w(age))
- end
-
- xml = @contact.to_xml
- assert_match %r{<name>aaron stack</name>}, xml
- assert_match %r{<awesome type=\"boolean\">false</awesome>}, xml
- assert_no_match %r{age}, xml
- end
-
- def test_does_not_include_inheritance_column_from_sti
- @contact = ContactSti.new(@contact.attributes)
- assert_equal 'ContactSti', @contact.type
-
- xml = @contact.to_xml
- assert_match %r{<name>aaron stack</name>}, xml
- assert_no_match %r{<type}, xml
- assert_no_match %r{ContactSti}, xml
- end
-
- def test_serializable_hash_with_default_except_option_and_excluding_inheritance_column_from_sti
- @contact = ContactSti.new(@contact.attributes)
- assert_equal 'ContactSti', @contact.type
-
- def @contact.serializable_hash(options={})
- super({ except: %w(age) }.merge!(options))
- end
-
- xml = @contact.to_xml
- assert_match %r{<name>aaron stack</name>}, xml
- assert_no_match %r{age}, xml
- assert_no_match %r{<type}, xml
- assert_no_match %r{ContactSti}, xml
- end
-end
-
-class DefaultXmlSerializationTimezoneTest < ActiveRecord::TestCase
- def test_should_serialize_datetime_with_timezone
- with_timezone_config zone: "Pacific Time (US & Canada)" do
- toy = Toy.create(:name => 'Mickey', :updated_at => Time.utc(2006, 8, 1))
- assert_match %r{<updated-at type=\"dateTime\">2006-07-31T17:00:00-07:00</updated-at>}, toy.to_xml
- end
- end
-
- def test_should_serialize_datetime_with_timezone_reloaded
- with_timezone_config zone: "Pacific Time (US & Canada)" do
- toy = Toy.create(:name => 'Minnie', :updated_at => Time.utc(2006, 8, 1)).reload
- assert_match %r{<updated-at type=\"dateTime\">2006-07-31T17:00:00-07:00</updated-at>}, toy.to_xml
- end
- end
-end
-
-class NilXmlSerializationTest < ActiveRecord::TestCase
- def setup
- @xml = Contact.new.to_xml(:root => 'xml_contact')
- end
-
- def test_should_serialize_string
- assert_match %r{<name nil="true"/>}, @xml
- end
-
- def test_should_serialize_integer
- assert %r{<age (.*)/>}.match(@xml)
- attributes = $1
- assert_match %r{nil="true"}, attributes
- assert_match %r{type="integer"}, attributes
- end
-
- def test_should_serialize_binary
- assert %r{<avatar (.*)/>}.match(@xml)
- attributes = $1
- assert_match %r{type="binary"}, attributes
- assert_match %r{encoding="base64"}, attributes
- assert_match %r{nil="true"}, attributes
- end
-
- def test_should_serialize_datetime
- assert %r{<created-at (.*)/>}.match(@xml)
- attributes = $1
- assert_match %r{nil="true"}, attributes
- assert_match %r{type="dateTime"}, attributes
- end
-
- def test_should_serialize_boolean
- assert %r{<awesome (.*)/>}.match(@xml)
- attributes = $1
- assert_match %r{type="boolean"}, attributes
- assert_match %r{nil="true"}, attributes
- end
-
- def test_should_serialize_yaml
- assert_match %r{<preferences nil=\"true\"/>}, @xml
- end
-end
-
-class DatabaseConnectedXmlSerializationTest < ActiveRecord::TestCase
- fixtures :topics, :companies, :accounts, :authors, :posts, :projects
-
- def test_to_xml
- xml = REXML::Document.new(topics(:first).to_xml(:indent => 0))
- bonus_time_in_current_timezone = topics(:first).bonus_time.xmlschema
- written_on_in_current_timezone = topics(:first).written_on.xmlschema
-
- assert_equal "topic", xml.root.name
- assert_equal "The First Topic" , xml.elements["//title"].text
- assert_equal "David" , xml.elements["//author-name"].text
- assert_match "Have a nice day", xml.elements["//content"].text
-
- assert_equal "1", xml.elements["//id"].text
- assert_equal "integer" , xml.elements["//id"].attributes['type']
-
- assert_equal "1", xml.elements["//replies-count"].text
- assert_equal "integer" , xml.elements["//replies-count"].attributes['type']
-
- assert_equal written_on_in_current_timezone, xml.elements["//written-on"].text
- assert_equal "dateTime" , xml.elements["//written-on"].attributes['type']
-
- assert_equal "david@loudthinking.com", xml.elements["//author-email-address"].text
-
- assert_equal nil, xml.elements["//parent-id"].text
- assert_equal "integer", xml.elements["//parent-id"].attributes['type']
- assert_equal "true", xml.elements["//parent-id"].attributes['nil']
-
- # Oracle enhanced adapter allows to define Date attributes in model class (see topic.rb)
- assert_equal "2004-04-15", xml.elements["//last-read"].text
- assert_equal "date" , xml.elements["//last-read"].attributes['type']
-
- # Oracle and DB2 don't have true boolean or time-only fields
- unless current_adapter?(:OracleAdapter, :DB2Adapter)
- assert_equal "false", xml.elements["//approved"].text
- assert_equal "boolean" , xml.elements["//approved"].attributes['type']
-
- assert_equal bonus_time_in_current_timezone, xml.elements["//bonus-time"].text
- assert_equal "dateTime" , xml.elements["//bonus-time"].attributes['type']
- end
- end
-
- def test_except_option
- xml = topics(:first).to_xml(:indent => 0, :skip_instruct => true, :except => [:title, :replies_count])
- assert_equal "<topic>", xml.first(7)
- assert !xml.include?(%(<title>The First Topic</title>))
- assert xml.include?(%(<author-name>David</author-name>))
-
- xml = topics(:first).to_xml(:indent => 0, :skip_instruct => true, :except => [:title, :author_name, :replies_count])
- assert !xml.include?(%(<title>The First Topic</title>))
- assert !xml.include?(%(<author-name>David</author-name>))
- end
-
- # to_xml used to mess with the hash the user provided which
- # caused the builder to be reused. This meant the document kept
- # getting appended to.
-
- def test_modules
- projects = MyApplication::Business::Project.all
- xml = projects.to_xml
- root = projects.first.class.to_s.underscore.pluralize.tr('/','_').dasherize
- assert_match "<#{root} type=\"array\">", xml
- assert_match "</#{root}>", xml
- end
-
- def test_passing_hash_shouldnt_reuse_builder
- options = {:include=>:posts}
- david = authors(:david)
- first_xml_size = david.to_xml(options).size
- second_xml_size = david.to_xml(options).size
- assert_equal first_xml_size, second_xml_size
- end
-
- def test_include_uses_association_name
- xml = authors(:david).to_xml :include=>:hello_posts, :indent => 0
- assert_match %r{<hello-posts type="array">}, xml
- assert_match %r{<hello-post type="Post">}, xml
- assert_match %r{<hello-post type="StiPost">}, xml
- end
-
- def test_included_associations_should_skip_types
- xml = authors(:david).to_xml :include=>:hello_posts, :indent => 0, :skip_types => true
- assert_match %r{<hello-posts>}, xml
- assert_match %r{<hello-post>}, xml
- assert_match %r{<hello-post>}, xml
- end
-
- def test_including_has_many_association
- xml = topics(:first).to_xml(:indent => 0, :skip_instruct => true, :include => :replies, :except => :replies_count)
- assert_equal "<topic>", xml.first(7)
- assert xml.include?(%(<replies type="array"><reply>))
- assert xml.include?(%(<title>The Second Topic of the day</title>))
- end
-
- def test_including_belongs_to_association
- xml = companies(:first_client).to_xml(:indent => 0, :skip_instruct => true, :include => :firm)
- assert !xml.include?("<firm>")
-
- xml = companies(:second_client).to_xml(:indent => 0, :skip_instruct => true, :include => :firm)
- assert xml.include?("<firm>")
- end
-
- def test_including_multiple_associations
- xml = companies(:first_firm).to_xml(:indent => 0, :skip_instruct => true, :include => [ :clients, :account ])
- assert_equal "<firm>", xml.first(6)
- assert xml.include?(%(<account>))
- assert xml.include?(%(<clients type="array"><client>))
- end
-
- def test_including_association_with_options
- xml = companies(:first_firm).to_xml(
- :indent => 0, :skip_instruct => true,
- :include => { :clients => { :only => :name } }
- )
-
- assert_equal "<firm>", xml.first(6)
- assert xml.include?(%(<client><name>Summit</name></client>))
- assert xml.include?(%(<clients type="array"><client>))
- end
-
- def test_methods_are_called_on_object
- xml = authors(:david).to_xml :methods => :label, :indent => 0
- assert_match %r{<label>.*</label>}, xml
- end
-
- def test_should_not_call_methods_on_associations_that_dont_respond
- xml = authors(:david).to_xml :include=>:hello_posts, :methods => :label, :indent => 2
- assert !authors(:david).hello_posts.first.respond_to?(:label)
- assert_match %r{^ <label>.*</label>}, xml
- assert_no_match %r{^ <label>}, xml
- end
-
- def test_procs_are_called_on_object
- proc = Proc.new { |options| options[:builder].tag!('nationality', 'Danish') }
- xml = authors(:david).to_xml(:procs => [ proc ])
- assert_match %r{<nationality>Danish</nationality>}, xml
- end
-
- def test_dual_arity_procs_are_called_on_object
- proc = Proc.new { |options, record| options[:builder].tag!('name-reverse', record.name.reverse) }
- xml = authors(:david).to_xml(:procs => [ proc ])
- assert_match %r{<name-reverse>divaD</name-reverse>}, xml
- end
-
- def test_top_level_procs_arent_applied_to_associations
- author_proc = Proc.new { |options| options[:builder].tag!('nationality', 'Danish') }
- xml = authors(:david).to_xml(:procs => [ author_proc ], :include => :posts, :indent => 2)
-
- assert_match %r{^ <nationality>Danish</nationality>}, xml
- assert_no_match %r{^ {6}<nationality>Danish</nationality>}, xml
- end
-
- def test_procs_on_included_associations_are_called
- posts_proc = Proc.new { |options| options[:builder].tag!('copyright', 'DHH') }
- xml = authors(:david).to_xml(
- :indent => 2,
- :include => {
- :posts => { :procs => [ posts_proc ] }
- }
- )
-
- assert_no_match %r{^ <copyright>DHH</copyright>}, xml
- assert_match %r{^ {6}<copyright>DHH</copyright>}, xml
- end
-
- def test_should_include_empty_has_many_as_empty_array
- authors(:david).posts.delete_all
- xml = authors(:david).to_xml :include=>:posts, :indent => 2
-
- assert_equal [], Hash.from_xml(xml)['author']['posts']
- assert_match %r{^ <posts type="array"/>}, xml
- end
-
- def test_should_has_many_array_elements_should_include_type_when_different_from_guessed_value
- xml = authors(:david).to_xml :include=>:posts_with_comments, :indent => 2
-
- assert Hash.from_xml(xml)
- assert_match %r{^ <posts-with-comments type="array">}, xml
- assert_match %r{^ <posts-with-comment type="Post">}, xml
- assert_match %r{^ <posts-with-comment type="StiPost">}, xml
-
- types = Hash.from_xml(xml)['author']['posts_with_comments'].collect {|t| t['type'] }
- assert types.include?('SpecialPost')
- assert types.include?('Post')
- assert types.include?('StiPost')
- end
-
- def test_should_produce_xml_for_methods_returning_array
- xml = authors(:david).to_xml(:methods => :social)
- array = Hash.from_xml(xml)['author']['social']
- assert_equal 2, array.size
- assert array.include? 'twitter'
- assert array.include? 'github'
- end
-
- def test_should_support_aliased_attributes
- xml = Author.select("name as firstname").to_xml
- Author.all.each do |author|
- assert xml.include?(%(<firstname>#{author.name}</firstname>)), xml
- end
- end
-
- def test_array_to_xml_including_has_many_association
- xml = [ topics(:first), topics(:second) ].to_xml(:indent => 0, :skip_instruct => true, :include => :replies)
- assert xml.include?(%(<replies type="array"><reply>))
- end
-
- def test_array_to_xml_including_methods
- xml = [ topics(:first), topics(:second) ].to_xml(:indent => 0, :skip_instruct => true, :methods => [ :topic_id ])
- assert xml.include?(%(<topic-id type="integer">#{topics(:first).topic_id}</topic-id>)), xml
- assert xml.include?(%(<topic-id type="integer">#{topics(:second).topic_id}</topic-id>)), xml
- end
-
- def test_array_to_xml_including_has_one_association
- xml = [ companies(:first_firm), companies(:rails_core) ].to_xml(:indent => 0, :skip_instruct => true, :include => :account)
- assert xml.include?(companies(:first_firm).account.to_xml(:indent => 0, :skip_instruct => true))
- assert xml.include?(companies(:rails_core).account.to_xml(:indent => 0, :skip_instruct => true))
- end
-
- def test_array_to_xml_including_belongs_to_association
- xml = [ companies(:first_client), companies(:second_client), companies(:another_client) ].to_xml(:indent => 0, :skip_instruct => true, :include => :firm)
- assert xml.include?(companies(:first_client).to_xml(:indent => 0, :skip_instruct => true))
- assert xml.include?(companies(:second_client).firm.to_xml(:indent => 0, :skip_instruct => true))
- assert xml.include?(companies(:another_client).firm.to_xml(:indent => 0, :skip_instruct => true))
- end
-end
diff --git a/activerecord/test/fixtures/bad_posts.yml b/activerecord/test/fixtures/bad_posts.yml
new file mode 100644
index 0000000000..addee8e3bf
--- /dev/null
+++ b/activerecord/test/fixtures/bad_posts.yml
@@ -0,0 +1,9 @@
+# Please do not use this fixture without `set_fixture_class` as Post
+
+_fixture:
+ model_class: BadPostModel
+
+bad_welcome:
+ author_id: 1
+ title: Welcome to the another weblog
+ body: It's really nice today
diff --git a/activerecord/test/fixtures/books.yml b/activerecord/test/fixtures/books.yml
index 93cfabd61c..a304fba399 100644
--- a/activerecord/test/fixtures/books.yml
+++ b/activerecord/test/fixtures/books.yml
@@ -24,3 +24,8 @@ ddd:
name: "Domain-Driven Design"
format: "hardcover"
status: 2
+
+tlg:
+ author_id: 1
+ id: 4
+ name: "Thoughtleadering"
diff --git a/activerecord/test/fixtures/content.yml b/activerecord/test/fixtures/content.yml
new file mode 100644
index 0000000000..0d12ee03dc
--- /dev/null
+++ b/activerecord/test/fixtures/content.yml
@@ -0,0 +1,3 @@
+content:
+ id: 1
+ title: How to use Rails
diff --git a/activerecord/test/fixtures/content_positions.yml b/activerecord/test/fixtures/content_positions.yml
new file mode 100644
index 0000000000..9e85773f8e
--- /dev/null
+++ b/activerecord/test/fixtures/content_positions.yml
@@ -0,0 +1,3 @@
+content_positions:
+ id: 1
+ content_id: 1
diff --git a/activerecord/test/fixtures/naked/yml/parrots.yml b/activerecord/test/fixtures/naked/yml/parrots.yml
new file mode 100644
index 0000000000..3e10331105
--- /dev/null
+++ b/activerecord/test/fixtures/naked/yml/parrots.yml
@@ -0,0 +1,2 @@
+george:
+ arrr: "Curious George"
diff --git a/activerecord/test/fixtures/nodes.yml b/activerecord/test/fixtures/nodes.yml
new file mode 100644
index 0000000000..b8bb8216ee
--- /dev/null
+++ b/activerecord/test/fixtures/nodes.yml
@@ -0,0 +1,29 @@
+grandparent:
+ id: 1
+ tree_id: 1
+ name: Grand Parent
+
+parent_a:
+ id: 2
+ tree_id: 1
+ parent_id: 1
+ name: Parent A
+
+parent_b:
+ id: 3
+ tree_id: 1
+ parent_id: 1
+ name: Parent B
+
+child_one_of_a:
+ id: 4
+ tree_id: 1
+ parent_id: 2
+ name: Child one
+
+child_two_of_b:
+ id: 5
+ tree_id: 1
+ parent_id: 2
+ name: Child two
+
diff --git a/activerecord/test/fixtures/other_comments.yml b/activerecord/test/fixtures/other_comments.yml
new file mode 100644
index 0000000000..55e8216ec7
--- /dev/null
+++ b/activerecord/test/fixtures/other_comments.yml
@@ -0,0 +1,6 @@
+_fixture:
+ model_class: Comment
+
+second_greetings:
+ post: second_welcome
+ body: Thank you for the second welcome
diff --git a/activerecord/test/fixtures/other_posts.yml b/activerecord/test/fixtures/other_posts.yml
new file mode 100644
index 0000000000..39ff763547
--- /dev/null
+++ b/activerecord/test/fixtures/other_posts.yml
@@ -0,0 +1,7 @@
+_fixture:
+ model_class: Post
+
+second_welcome:
+ author_id: 1
+ title: Welcome to the another weblog
+ body: It's really nice today
diff --git a/activerecord/test/fixtures/trees.yml b/activerecord/test/fixtures/trees.yml
new file mode 100644
index 0000000000..9e030b7632
--- /dev/null
+++ b/activerecord/test/fixtures/trees.yml
@@ -0,0 +1,3 @@
+root:
+ id: 1
+ name: The Root
diff --git a/activerecord/test/models/aircraft.rb b/activerecord/test/models/aircraft.rb
index 1f35ef45da..c4404a8094 100644
--- a/activerecord/test/models/aircraft.rb
+++ b/activerecord/test/models/aircraft.rb
@@ -1,4 +1,5 @@
class Aircraft < ActiveRecord::Base
self.pluralize_table_names = false
has_many :engines, :foreign_key => "car_id"
+ has_many :wheels, as: :wheelable
end
diff --git a/activerecord/test/models/author.rb b/activerecord/test/models/author.rb
index 8c1f14bd36..0d90cbb110 100644
--- a/activerecord/test/models/author.rb
+++ b/activerecord/test/models/author.rb
@@ -144,9 +144,6 @@ class Author < ActiveRecord::Base
has_many :posts_with_signature, ->(record) { where("posts.title LIKE ?", "%by #{record.name.downcase}%") }, class_name: "Post"
- scope :relation_include_posts, -> { includes(:posts) }
- scope :relation_include_tags, -> { includes(:tags) }
-
attr_accessor :post_log
after_initialize :set_post_log
diff --git a/activerecord/test/models/book.rb b/activerecord/test/models/book.rb
index 24bfe47bbf..1927191393 100644
--- a/activerecord/test/models/book.rb
+++ b/activerecord/test/models/book.rb
@@ -10,10 +10,10 @@ class Book < ActiveRecord::Base
enum status: [:proposed, :written, :published]
enum read_status: {unread: 0, reading: 2, read: 3}
enum nullable_status: [:single, :married]
- enum language: [:english, :spanish, :french], enum_prefix: :in
- enum author_visibility: [:visible, :invisible], enum_prefix: true
- enum illustrator_visibility: [:visible, :invisible], enum_prefix: true
- enum font_size: [:small, :medium, :large], enum_prefix: :with, enum_suffix: true
+ enum language: [:english, :spanish, :french], _prefix: :in
+ enum author_visibility: [:visible, :invisible], _prefix: true
+ enum illustrator_visibility: [:visible, :invisible], _prefix: true
+ enum font_size: [:small, :medium, :large], _prefix: :with, _suffix: true
def published!
super
diff --git a/activerecord/test/models/carrier.rb b/activerecord/test/models/carrier.rb
new file mode 100644
index 0000000000..230be118c3
--- /dev/null
+++ b/activerecord/test/models/carrier.rb
@@ -0,0 +1,2 @@
+class Carrier < ActiveRecord::Base
+end
diff --git a/activerecord/test/models/categorization.rb b/activerecord/test/models/categorization.rb
index 6588531de6..4cd67c970a 100644
--- a/activerecord/test/models/categorization.rb
+++ b/activerecord/test/models/categorization.rb
@@ -1,6 +1,6 @@
class Categorization < ActiveRecord::Base
belongs_to :post
- belongs_to :category
+ belongs_to :category, counter_cache: true
belongs_to :named_category, :class_name => 'Category', :foreign_key => :named_category_name, :primary_key => :name
belongs_to :author
diff --git a/activerecord/test/models/company.rb b/activerecord/test/models/company.rb
index 67936e8e5d..1dcd9fc21e 100644
--- a/activerecord/test/models/company.rb
+++ b/activerecord/test/models/company.rb
@@ -10,7 +10,6 @@ class Company < AbstractCompany
has_one :dummy_account, :foreign_key => "firm_id", :class_name => "Account"
has_many :contracts
has_many :developers, :through => :contracts
- has_many :accounts
scope :of_first_firm, lambda {
joins(:account => :firm).
@@ -86,6 +85,9 @@ class Firm < Company
has_many :association_with_references, -> { references(:foo) }, :class_name => 'Client'
+ has_one :lead_developer, class_name: "Developer"
+ has_many :projects
+
def log
@log ||= []
end
diff --git a/activerecord/test/models/contact.rb b/activerecord/test/models/contact.rb
index 3ea17c3abf..9f2f69e1ee 100644
--- a/activerecord/test/models/contact.rb
+++ b/activerecord/test/models/contact.rb
@@ -3,7 +3,7 @@ module ContactFakeColumns
base.class_eval do
establish_connection(:adapter => 'fake')
- connection.tables = [table_name]
+ connection.data_sources = [table_name]
connection.primary_keys = {
table_name => 'id'
}
diff --git a/activerecord/test/models/content.rb b/activerecord/test/models/content.rb
new file mode 100644
index 0000000000..140e1dfc78
--- /dev/null
+++ b/activerecord/test/models/content.rb
@@ -0,0 +1,40 @@
+class Content < ActiveRecord::Base
+ self.table_name = 'content'
+ has_one :content_position, dependent: :destroy
+
+ def self.destroyed_ids
+ @destroyed_ids ||= []
+ end
+
+ before_destroy do |object|
+ Content.destroyed_ids << object.id
+ end
+end
+
+class ContentWhichRequiresTwoDestroyCalls < ActiveRecord::Base
+ self.table_name = 'content'
+ has_one :content_position, foreign_key: 'content_id', dependent: :destroy
+
+ after_initialize do
+ @destroy_count = 0
+ end
+
+ before_destroy do
+ @destroy_count += 1
+ if @destroy_count == 1
+ throw :abort
+ end
+ end
+end
+
+class ContentPosition < ActiveRecord::Base
+ belongs_to :content, dependent: :destroy
+
+ def self.destroyed_ids
+ @destroyed_ids ||= []
+ end
+
+ before_destroy do |object|
+ ContentPosition.destroyed_ids << object.id
+ end
+end
diff --git a/activerecord/test/models/customer_carrier.rb b/activerecord/test/models/customer_carrier.rb
new file mode 100644
index 0000000000..37186903ff
--- /dev/null
+++ b/activerecord/test/models/customer_carrier.rb
@@ -0,0 +1,14 @@
+class CustomerCarrier < ActiveRecord::Base
+ cattr_accessor :current_customer
+
+ belongs_to :customer
+ belongs_to :carrier
+
+ default_scope -> {
+ if current_customer
+ where(customer: current_customer)
+ else
+ all
+ end
+ }
+end
diff --git a/activerecord/test/models/developer.rb b/activerecord/test/models/developer.rb
index d2a5a7fc49..9a907273f8 100644
--- a/activerecord/test/models/developer.rb
+++ b/activerecord/test/models/developer.rb
@@ -7,12 +7,16 @@ module DeveloperProjectsAssociationExtension2
end
class Developer < ActiveRecord::Base
+ self.ignored_columns = %w(first_name last_name)
+
has_and_belongs_to_many :projects do
def find_most_recent
order("id DESC").first
end
end
+ belongs_to :mentor
+
accepts_nested_attributes_for :projects
has_and_belongs_to_many :shared_computers, class_name: "Computer"
@@ -50,6 +54,10 @@ class Developer < ActiveRecord::Base
has_many :firms, :through => :contracts, :source => :firm
has_many :comments, ->(developer) { where(body: "I'm #{developer.name}") }
has_many :ratings, through: :comments
+ has_one :ship, dependent: :nullify
+
+ belongs_to :firm
+ has_many :contracted_projects, class_name: "Project"
scope :jamises, -> { where(:name => 'Jamis') }
@@ -60,6 +68,9 @@ class Developer < ActiveRecord::Base
developer.audit_logs.build :message => "Computer created"
end
+ attr_accessor :last_name
+ define_attribute_method 'last_name'
+
def log=(message)
audit_logs.build :message => message
end
diff --git a/activerecord/test/models/face.rb b/activerecord/test/models/face.rb
index 91e46f83e5..af76fea52c 100644
--- a/activerecord/test/models/face.rb
+++ b/activerecord/test/models/face.rb
@@ -1,7 +1,7 @@
class Face < ActiveRecord::Base
belongs_to :man, :inverse_of => :face
belongs_to :polymorphic_man, :polymorphic => true, :inverse_of => :polymorphic_face
- # Oracle identifier lengh is limited to 30 bytes or less, `polymorphic` renamed `poly`
+ # Oracle identifier length is limited to 30 bytes or less, `polymorphic` renamed `poly`
belongs_to :poly_man_without_inverse, :polymorphic => true
# These is a "broken" inverse_of for the purposes of testing
belongs_to :horrible_man, :class_name => 'Man', :inverse_of => :horrible_face
diff --git a/activerecord/test/models/guitar.rb b/activerecord/test/models/guitar.rb
new file mode 100644
index 0000000000..cd068ff53d
--- /dev/null
+++ b/activerecord/test/models/guitar.rb
@@ -0,0 +1,4 @@
+class Guitar < ActiveRecord::Base
+ has_many :tuning_pegs, index_errors: true
+ accepts_nested_attributes_for :tuning_pegs
+end
diff --git a/activerecord/test/models/member.rb b/activerecord/test/models/member.rb
index dc0566d8a7..7693c6e515 100644
--- a/activerecord/test/models/member.rb
+++ b/activerecord/test/models/member.rb
@@ -26,6 +26,9 @@ class Member < ActiveRecord::Base
has_many :current_memberships, -> { where :favourite => true }
has_many :clubs, :through => :current_memberships
+ has_many :tenant_memberships
+ has_many :tenant_clubs, through: :tenant_memberships, class_name: 'Club', source: :club
+
has_one :club_through_many, :through => :current_memberships, :source => :club
belongs_to :admittable, polymorphic: true
diff --git a/activerecord/test/models/member_detail.rb b/activerecord/test/models/member_detail.rb
index 9d253aa126..157130986c 100644
--- a/activerecord/test/models/member_detail.rb
+++ b/activerecord/test/models/member_detail.rb
@@ -1,7 +1,8 @@
class MemberDetail < ActiveRecord::Base
- belongs_to :member, :inverse_of => false
+ belongs_to :member, inverse_of: false
belongs_to :organization
- has_one :member_type, :through => :member
+ has_one :member_type, through: :member
+ has_one :membership, through: :member
- has_many :organization_member_details, :through => :organization, :source => :member_details
+ has_many :organization_member_details, through: :organization, source: :member_details
end
diff --git a/activerecord/test/models/membership.rb b/activerecord/test/models/membership.rb
index df7167ee93..e181ba1f11 100644
--- a/activerecord/test/models/membership.rb
+++ b/activerecord/test/models/membership.rb
@@ -18,3 +18,18 @@ class SelectedMembership < Membership
select("'1' as foo")
end
end
+
+class TenantMembership < Membership
+ cattr_accessor :current_member
+
+ belongs_to :member
+ belongs_to :club
+
+ default_scope -> {
+ if current_member
+ where(member: current_member)
+ else
+ all
+ end
+ }
+end
diff --git a/activerecord/test/models/mentor.rb b/activerecord/test/models/mentor.rb
new file mode 100644
index 0000000000..11f1e4bff8
--- /dev/null
+++ b/activerecord/test/models/mentor.rb
@@ -0,0 +1,3 @@
+class Mentor < ActiveRecord::Base
+ has_many :developers
+end \ No newline at end of file
diff --git a/activerecord/test/models/node.rb b/activerecord/test/models/node.rb
new file mode 100644
index 0000000000..07dd2dbccb
--- /dev/null
+++ b/activerecord/test/models/node.rb
@@ -0,0 +1,5 @@
+class Node < ActiveRecord::Base
+ belongs_to :tree, touch: true
+ belongs_to :parent, class_name: 'Node', touch: true, optional: true
+ has_many :children, class_name: 'Node', foreign_key: :parent_id, dependent: :destroy
+end
diff --git a/activerecord/test/models/parrot.rb b/activerecord/test/models/parrot.rb
index b26035d944..ddc9dcaf29 100644
--- a/activerecord/test/models/parrot.rb
+++ b/activerecord/test/models/parrot.rb
@@ -21,9 +21,3 @@ end
class DeadParrot < Parrot
belongs_to :killer, :class_name => 'Pirate', foreign_key: :killer_id
end
-
-class FunkyParrot < Parrot
- before_destroy do
- raise "before_destroy was called"
- end
-end
diff --git a/activerecord/test/models/person.rb b/activerecord/test/models/person.rb
index ad12f00d42..a4a9c6b0d4 100644
--- a/activerecord/test/models/person.rb
+++ b/activerecord/test/models/person.rb
@@ -37,7 +37,6 @@ class Person < ActiveRecord::Base
has_many :essays, primary_key: "first_name", foreign_key: "writer_id"
scope :males, -> { where(:gender => 'M') }
- scope :females, -> { where(:gender => 'F') }
end
class PersonWithDependentDestroyJobs < ActiveRecord::Base
diff --git a/activerecord/test/models/post.rb b/activerecord/test/models/post.rb
index 052b1c9690..23cebe2602 100644
--- a/activerecord/test/models/post.rb
+++ b/activerecord/test/models/post.rb
@@ -98,11 +98,11 @@ class Post < ActiveRecord::Base
end
end
- has_many :taggings_with_delete_all, :class_name => 'Tagging', :as => :taggable, :dependent => :delete_all
- has_many :taggings_with_destroy, :class_name => 'Tagging', :as => :taggable, :dependent => :destroy
+ has_many :taggings_with_delete_all, :class_name => 'Tagging', :as => :taggable, :dependent => :delete_all, counter_cache: :taggings_with_delete_all_count
+ has_many :taggings_with_destroy, :class_name => 'Tagging', :as => :taggable, :dependent => :destroy, counter_cache: :taggings_with_destroy_count
- has_many :tags_with_destroy, :through => :taggings, :source => :tag, :dependent => :destroy
- has_many :tags_with_nullify, :through => :taggings, :source => :tag, :dependent => :nullify
+ has_many :tags_with_destroy, :through => :taggings, :source => :tag, :dependent => :destroy, counter_cache: :tags_with_destroy_count
+ has_many :tags_with_nullify, :through => :taggings, :source => :tag, :dependent => :nullify, counter_cache: :tags_with_nullify_count
has_many :misc_tags, -> { where :tags => { :name => 'Misc' } }, :through => :taggings, :source => :tag
has_many :funky_tags, :through => :taggings, :source => :tag
@@ -185,6 +185,7 @@ class SubStiPost < StiPost
end
class FirstPost < ActiveRecord::Base
+ self.inheritance_column = :disabled
self.table_name = 'posts'
default_scope { where(:id => 1) }
@@ -193,6 +194,7 @@ class FirstPost < ActiveRecord::Base
end
class PostWithDefaultInclude < ActiveRecord::Base
+ self.inheritance_column = :disabled
self.table_name = 'posts'
default_scope { includes(:comments) }
has_many :comments, :foreign_key => :post_id
@@ -204,16 +206,35 @@ class PostWithSpecialCategorization < Post
end
class PostWithDefaultScope < ActiveRecord::Base
+ self.inheritance_column = :disabled
self.table_name = 'posts'
default_scope { order(:title) }
end
+class PostWithPreloadDefaultScope < ActiveRecord::Base
+ self.table_name = 'posts'
+
+ has_many :readers, foreign_key: 'post_id'
+
+ default_scope { preload(:readers) }
+end
+
+class PostWithIncludesDefaultScope < ActiveRecord::Base
+ self.table_name = 'posts'
+
+ has_many :readers, foreign_key: 'post_id'
+
+ default_scope { includes(:readers) }
+end
+
class SpecialPostWithDefaultScope < ActiveRecord::Base
+ self.inheritance_column = :disabled
self.table_name = 'posts'
default_scope { where(:id => [1, 5,6]) }
end
class PostThatLoadsCommentsInAnAfterSaveHook < ActiveRecord::Base
+ self.inheritance_column = :disabled
self.table_name = 'posts'
has_many :comments, class_name: "CommentThatAutomaticallyAltersPostBody", foreign_key: :post_id
@@ -223,6 +244,7 @@ class PostThatLoadsCommentsInAnAfterSaveHook < ActiveRecord::Base
end
class PostWithAfterCreateCallback < ActiveRecord::Base
+ self.inheritance_column = :disabled
self.table_name = 'posts'
has_many :comments, foreign_key: :post_id
@@ -232,6 +254,7 @@ class PostWithAfterCreateCallback < ActiveRecord::Base
end
class PostWithCommentWithDefaultScopeReferencesAssociation < ActiveRecord::Base
+ self.inheritance_column = :disabled
self.table_name = 'posts'
has_many :comment_with_default_scope_references_associations, foreign_key: :post_id
has_one :first_comment, class_name: "CommentWithDefaultScopeReferencesAssociation", foreign_key: :post_id
diff --git a/activerecord/test/models/professor.rb b/activerecord/test/models/professor.rb
new file mode 100644
index 0000000000..7654eda0ef
--- /dev/null
+++ b/activerecord/test/models/professor.rb
@@ -0,0 +1,5 @@
+require_dependency 'models/arunit2_model'
+
+class Professor < ARUnit2Model
+ has_and_belongs_to_many :courses
+end
diff --git a/activerecord/test/models/project.rb b/activerecord/test/models/project.rb
index 7f42a4b1f8..efa8246f1e 100644
--- a/activerecord/test/models/project.rb
+++ b/activerecord/test/models/project.rb
@@ -1,4 +1,5 @@
class Project < ActiveRecord::Base
+ belongs_to :mentor
has_and_belongs_to_many :developers, -> { distinct.order 'developers.name desc, developers.id desc' }
has_and_belongs_to_many :readonly_developers, -> { readonly }, :class_name => "Developer"
has_and_belongs_to_many :non_unique_developers, -> { order 'developers.name desc, developers.id desc' }, :class_name => 'Developer'
@@ -11,6 +12,16 @@ class Project < ActiveRecord::Base
:before_remove => Proc.new {|o, r| o.developers_log << "before_removing#{r.id}"},
:after_remove => Proc.new {|o, r| o.developers_log << "after_removing#{r.id}"}
has_and_belongs_to_many :well_payed_salary_groups, -> { group("developers.salary").having("SUM(salary) > 10000").select("SUM(salary) as salary") }, :class_name => "Developer"
+ belongs_to :firm
+ has_one :lead_developer, through: :firm, inverse_of: :contracted_projects
+
+ begin
+ previous_value, ActiveRecord::Base.belongs_to_required_by_default =
+ ActiveRecord::Base.belongs_to_required_by_default, true
+ has_and_belongs_to_many :developers_required_by_default, class_name: "Developer"
+ ensure
+ ActiveRecord::Base.belongs_to_required_by_default = previous_value
+ end
attr_accessor :developers_log
after_initialize :set_developers_log
diff --git a/activerecord/test/models/ship.rb b/activerecord/test/models/ship.rb
index 312caef604..e333b964ab 100644
--- a/activerecord/test/models/ship.rb
+++ b/activerecord/test/models/ship.rb
@@ -3,6 +3,7 @@ class Ship < ActiveRecord::Base
belongs_to :pirate
belongs_to :update_only_pirate, :class_name => 'Pirate'
+ belongs_to :developer, dependent: :destroy
has_many :parts, :class_name => 'ShipPart'
has_many :treasures
@@ -19,6 +20,18 @@ class Ship < ActiveRecord::Base
end
end
+class ShipWithoutNestedAttributes < ActiveRecord::Base
+ self.table_name = "ships"
+ has_many :prisoners, inverse_of: :ship, foreign_key: :ship_id
+ has_many :parts, class_name: "ShipPart", foreign_key: :ship_id
+
+ validates :name, presence: true
+end
+
+class Prisoner < ActiveRecord::Base
+ belongs_to :ship, autosave: true, class_name: "ShipWithoutNestedAttributes", inverse_of: :prisoners
+end
+
class FamousShip < ActiveRecord::Base
self.table_name = 'ships'
belongs_to :famous_pirate
diff --git a/activerecord/test/models/shop_account.rb b/activerecord/test/models/shop_account.rb
new file mode 100644
index 0000000000..1580e8b20c
--- /dev/null
+++ b/activerecord/test/models/shop_account.rb
@@ -0,0 +1,6 @@
+class ShopAccount < ActiveRecord::Base
+ belongs_to :customer
+ belongs_to :customer_carrier
+
+ has_one :carrier, through: :customer_carrier
+end
diff --git a/activerecord/test/models/topic.rb b/activerecord/test/models/topic.rb
index f81ffe1d90..176bc79dc7 100644
--- a/activerecord/test/models/topic.rb
+++ b/activerecord/test/models/topic.rb
@@ -32,7 +32,7 @@ class Topic < ActiveRecord::Base
end
end
- has_many :replies, :dependent => :destroy, :foreign_key => "parent_id"
+ has_many :replies, dependent: :destroy, foreign_key: "parent_id", autosave: true
has_many :approved_replies, -> { approved }, class_name: 'Reply', foreign_key: "parent_id", counter_cache: 'replies_count'
has_many :unique_replies, :dependent => :destroy, :foreign_key => "parent_id"
@@ -86,7 +86,7 @@ class Topic < ActiveRecord::Base
end
def destroy_children
- self.class.delete_all "parent_id = #{id}"
+ self.class.where("parent_id = #{id}").delete_all
end
def set_email_address
diff --git a/activerecord/test/models/treasure.rb b/activerecord/test/models/treasure.rb
index ffc65466d5..63ff0c23ec 100644
--- a/activerecord/test/models/treasure.rb
+++ b/activerecord/test/models/treasure.rb
@@ -1,6 +1,7 @@
class Treasure < ActiveRecord::Base
has_and_belongs_to_many :parrots
belongs_to :looter, :polymorphic => true
+ # No counter_cache option given
belongs_to :ship
has_many :price_estimates, :as => :estimate_of
diff --git a/activerecord/test/models/tree.rb b/activerecord/test/models/tree.rb
new file mode 100644
index 0000000000..dc29cccc9c
--- /dev/null
+++ b/activerecord/test/models/tree.rb
@@ -0,0 +1,3 @@
+class Tree < ActiveRecord::Base
+ has_many :nodes, dependent: :destroy
+end
diff --git a/activerecord/test/models/tuning_peg.rb b/activerecord/test/models/tuning_peg.rb
new file mode 100644
index 0000000000..1252d6dc1d
--- /dev/null
+++ b/activerecord/test/models/tuning_peg.rb
@@ -0,0 +1,4 @@
+class TuningPeg < ActiveRecord::Base
+ belongs_to :guitar
+ validates_numericality_of :pitch
+end
diff --git a/activerecord/test/models/vehicle.rb b/activerecord/test/models/vehicle.rb
new file mode 100644
index 0000000000..ef26170f1f
--- /dev/null
+++ b/activerecord/test/models/vehicle.rb
@@ -0,0 +1,7 @@
+class Vehicle < ActiveRecord::Base
+ self.abstract_class = true
+ default_scope -> { where("tires_count IS NOT NULL") }
+end
+
+class Bus < Vehicle
+end \ No newline at end of file
diff --git a/activerecord/test/schema/mysql2_specific_schema.rb b/activerecord/test/schema/mysql2_specific_schema.rb
index 52d3290c84..92e0b197a7 100644
--- a/activerecord/test/schema/mysql2_specific_schema.rb
+++ b/activerecord/test/schema/mysql2_specific_schema.rb
@@ -2,7 +2,7 @@ ActiveRecord::Schema.define do
create_table :binary_fields, force: true do |t|
t.binary :var_binary, limit: 255
t.binary :var_binary_large, limit: 4095
- t.column :tiny_blob, 'tinyblob', limit: 255
+ t.blob :tiny_blob, limit: 255
t.binary :normal_blob, limit: 65535
t.binary :medium_blob, limit: 16777215
t.binary :long_blob, limit: 2147483647
@@ -40,6 +40,17 @@ BEGIN
END
SQL
+ ActiveRecord::Base.connection.execute <<-SQL
+DROP PROCEDURE IF EXISTS topics;
+SQL
+
+ ActiveRecord::Base.connection.execute <<-SQL
+CREATE PROCEDURE topics(IN num INT) SQL SECURITY INVOKER
+BEGIN
+ select * from topics limit num;
+END
+SQL
+
ActiveRecord::Base.connection.drop_table "enum_tests", if_exists: true
ActiveRecord::Base.connection.execute <<-SQL
diff --git a/activerecord/test/schema/mysql_specific_schema.rb b/activerecord/test/schema/mysql_specific_schema.rb
index 90f5a60d7b..553cb56103 100644
--- a/activerecord/test/schema/mysql_specific_schema.rb
+++ b/activerecord/test/schema/mysql_specific_schema.rb
@@ -2,7 +2,7 @@ ActiveRecord::Schema.define do
create_table :binary_fields, force: true do |t|
t.binary :var_binary, limit: 255
t.binary :var_binary_large, limit: 4095
- t.column :tiny_blob, 'tinyblob', limit: 255
+ t.blob :tiny_blob, limit: 255
t.binary :normal_blob, limit: 65535
t.binary :medium_blob, limit: 16777215
t.binary :long_blob, limit: 2147483647
@@ -45,9 +45,9 @@ DROP PROCEDURE IF EXISTS topics;
SQL
ActiveRecord::Base.connection.execute <<-SQL
-CREATE PROCEDURE topics() SQL SECURITY INVOKER
+CREATE PROCEDURE topics(IN num INT) SQL SECURITY INVOKER
BEGIN
- select * from topics limit 1;
+ select * from topics limit num;
END
SQL
diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb
index dffccc9326..66a1f5aa8a 100644
--- a/activerecord/test/schema/schema.rb
+++ b/activerecord/test/schema/schema.rb
@@ -1,4 +1,3 @@
-
ActiveRecord::Schema.define do
def except(adapter_names_to_exclude)
unless [adapter_names_to_exclude].flatten.include?(adapter_name)
@@ -37,6 +36,7 @@ ActiveRecord::Schema.define do
create_table :aircraft, force: true do |t|
t.string :name
+ t.integer :wheels_count, default: 0, null: false
end
create_table :articles, force: true do |t|
@@ -130,6 +130,8 @@ ActiveRecord::Schema.define do
t.timestamps null: false
end
+ create_table :carriers, force: true
+
create_table :categories, force: true do |t|
t.string :name, null: false
t.string :type
@@ -205,6 +207,14 @@ ActiveRecord::Schema.define do
add_index :companies, [:firm_id, :type], name: "company_partial_index", where: "rating > 10"
add_index :companies, :name, name: 'company_name_index', using: :btree
+ create_table :content, force: true do |t|
+ t.string :title
+ end
+
+ create_table :content_positions, force: true do |t|
+ t.integer :content_id
+ end
+
create_table :vegetables, force: true do |t|
t.string :name
t.integer :seller_id
@@ -236,6 +246,11 @@ ActiveRecord::Schema.define do
t.string :gps_location
end
+ create_table :customer_carriers, force: true do |t|
+ t.references :customer
+ t.references :carrier
+ end
+
create_table :dashboards, force: true, id: false do |t|
t.string :dashboard_id
t.string :name
@@ -243,11 +258,21 @@ ActiveRecord::Schema.define do
create_table :developers, force: true do |t|
t.string :name
+ t.string :first_name
t.integer :salary, default: 70000
- t.datetime :created_at
- t.datetime :updated_at
- t.datetime :created_on
- t.datetime :updated_on
+ t.integer :firm_id
+ t.integer :mentor_id
+ if subsecond_precision_supported?
+ t.datetime :created_at, precision: 6
+ t.datetime :updated_at, precision: 6
+ t.datetime :created_on, precision: 6
+ t.datetime :updated_on, precision: 6
+ else
+ t.datetime :created_at
+ t.datetime :updated_at
+ t.datetime :created_on
+ t.datetime :updated_on
+ end
end
create_table :developers_projects, force: true, id: false do |t|
@@ -331,6 +356,10 @@ ActiveRecord::Schema.define do
t.column :key, :string
end
+ create_table :guitar, force: true do |t|
+ t.string :color
+ end
+
create_table :inept_wizards, force: true do |t|
t.column :name, :string, null: false
t.column :city, :string, null: false
@@ -346,7 +375,11 @@ ActiveRecord::Schema.define do
create_table :invoices, force: true do |t|
t.integer :balance
- t.datetime :updated_at
+ if subsecond_precision_supported?
+ t.datetime :updated_at, precision: 6
+ else
+ t.datetime :updated_at
+ end
end
create_table :iris, force: true do |t|
@@ -432,6 +465,10 @@ ActiveRecord::Schema.define do
t.string :name
end
+ create_table :mentors, force: true do |t|
+ t.string :name
+ end
+
create_table :minivans, force: true, id: false do |t|
t.string :minivan_id
t.string :name
@@ -496,7 +533,11 @@ ActiveRecord::Schema.define do
create_table :owners, primary_key: :owner_id, force: true do |t|
t.string :name
- t.column :updated_at, :datetime
+ if subsecond_precision_supported?
+ t.column :updated_at, :datetime, precision: 6
+ else
+ t.column :updated_at, :datetime
+ end
t.column :happy_at, :datetime
t.string :essay_id
end
@@ -514,10 +555,17 @@ ActiveRecord::Schema.define do
t.column :color, :string
t.column :parrot_sti_class, :string
t.column :killer_id, :integer
- t.column :created_at, :datetime
- t.column :created_on, :datetime
- t.column :updated_at, :datetime
- t.column :updated_on, :datetime
+ if subsecond_precision_supported?
+ t.column :created_at, :datetime, precision: 0
+ t.column :created_on, :datetime, precision: 0
+ t.column :updated_at, :datetime, precision: 0
+ t.column :updated_on, :datetime, precision: 0
+ else
+ t.column :created_at, :datetime
+ t.column :created_on, :datetime
+ t.column :updated_at, :datetime
+ t.column :updated_on, :datetime
+ end
end
create_table :parrots_pirates, id: false, force: true do |t|
@@ -560,15 +608,24 @@ ActiveRecord::Schema.define do
create_table :pets, primary_key: :pet_id, force: true do |t|
t.string :name
t.integer :owner_id, :integer
- t.timestamps null: false
+ if subsecond_precision_supported?
+ t.timestamps null: false, precision: 6
+ else
+ t.timestamps null: false
+ end
end
create_table :pirates, force: true do |t|
t.column :catchphrase, :string
t.column :parrot_id, :integer
t.integer :non_validated_parrot_id
- t.column :created_on, :datetime
- t.column :updated_on, :datetime
+ if subsecond_precision_supported?
+ t.column :created_on, :datetime, precision: 6
+ t.column :updated_on, :datetime, precision: 6
+ else
+ t.column :created_on, :datetime
+ t.column :updated_on, :datetime
+ end
end
create_table :posts, force: true do |t|
@@ -619,6 +676,8 @@ ActiveRecord::Schema.define do
create_table :projects, force: true do |t|
t.string :name
t.string :type
+ t.integer :firm_id
+ t.integer :mentor_id
end
create_table :randomly_named_table1, force: true do |t|
@@ -665,7 +724,10 @@ ActiveRecord::Schema.define do
create_table :ships, force: true do |t|
t.string :name
t.integer :pirate_id
+ t.belongs_to :developer
t.integer :update_only_pirate_id
+ # Conventionally named column for counter_cache
+ t.integer :treasures_count, default: 0
t.datetime :created_at
t.datetime :created_on
t.datetime :updated_at
@@ -675,7 +737,20 @@ ActiveRecord::Schema.define do
create_table :ship_parts, force: true do |t|
t.string :name
t.integer :ship_id
- t.datetime :updated_at
+ if subsecond_precision_supported?
+ t.datetime :updated_at, precision: 6
+ else
+ t.datetime :updated_at
+ end
+ end
+
+ create_table :prisoners, force: true do |t|
+ t.belongs_to :ship
+ end
+
+ create_table :shop_accounts, force: true do |t|
+ t.references :customer
+ t.references :customer_carrier
end
create_table :speedometers, force: true, id: false do |t|
@@ -736,7 +811,7 @@ ActiveRecord::Schema.define do
t.string :title, limit: 250
t.string :author_name
t.string :author_email_address
- if mysql_56?
+ if subsecond_precision_supported?
t.datetime :written_on, precision: 6
else
t.datetime :written_on
@@ -759,7 +834,11 @@ ActiveRecord::Schema.define do
t.string :parent_title
t.string :type
t.string :group
- t.timestamps null: true
+ if subsecond_precision_supported?
+ t.timestamps null: true, precision: 6
+ else
+ t.timestamps null: true
+ end
end
create_table :toys, primary_key: :toy_id, force: true do |t|
@@ -784,6 +863,11 @@ ActiveRecord::Schema.define do
t.belongs_to :ship
end
+ create_table :tuning_pegs, force: true do |t|
+ t.integer :guitar_id
+ t.float :pitch
+ end
+
create_table :tyres, force: true do |t|
t.integer :car_id
end
@@ -867,6 +951,17 @@ ActiveRecord::Schema.define do
t.string 'from'
end
+ create_table :nodes, force: true do |t|
+ t.integer :tree_id
+ t.integer :parent_id
+ t.string :name
+ t.datetime :updated_at
+ end
+ create_table :trees, force: true do |t|
+ t.string :name
+ t.datetime :updated_at
+ end
+
create_table :hotels, force: true do |t|
end
create_table :departments, force: true do |t|
@@ -913,6 +1008,10 @@ ActiveRecord::Schema.define do
t.string :token
t.string :auth_token
end
+
+ create_table :test_with_keyword_column_name, force: true do |t|
+ t.string :desc
+ end
end
Course.connection.create_table :courses, force: true do |t|
@@ -923,3 +1022,12 @@ end
College.connection.create_table :colleges, force: true do |t|
t.column :name, :string, null: false
end
+
+Professor.connection.create_table :professors, force: true do |t|
+ t.column :name, :string, null: false
+end
+
+Professor.connection.create_table :courses_professors, id: false, force: true do |t|
+ t.references :course
+ t.references :professor
+end
diff --git a/activerecord/test/support/connection.rb b/activerecord/test/support/connection.rb
index d11fd9cfc1..c5334e8596 100644
--- a/activerecord/test/support/connection.rb
+++ b/activerecord/test/support/connection.rb
@@ -1,6 +1,7 @@
require 'active_support/logger'
require 'models/college'
require 'models/course'
+require 'models/professor'
module ARTest
def self.connection_name