aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/test
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord/test')
-rw-r--r--activerecord/test/cases/adapters/mysql/active_schema_test.rb2
-rw-r--r--activerecord/test/cases/adapters/mysql/case_sensitivity_test.rb2
-rw-r--r--activerecord/test/cases/adapters/mysql/charset_collation_test.rb2
-rw-r--r--activerecord/test/cases/adapters/mysql/connection_test.rb11
-rw-r--r--activerecord/test/cases/adapters/mysql/consistency_test.rb2
-rw-r--r--activerecord/test/cases/adapters/mysql/enum_test.rb2
-rw-r--r--activerecord/test/cases/adapters/mysql/mysql_adapter_test.rb2
-rw-r--r--activerecord/test/cases/adapters/mysql/quoting_test.rb38
-rw-r--r--activerecord/test/cases/adapters/mysql/reserved_word_test.rb40
-rw-r--r--activerecord/test/cases/adapters/mysql/schema_test.rb2
-rw-r--r--activerecord/test/cases/adapters/mysql/sp_test.rb4
-rw-r--r--activerecord/test/cases/adapters/mysql/sql_types_test.rb2
-rw-r--r--activerecord/test/cases/adapters/mysql/statement_pool_test.rb28
-rw-r--r--activerecord/test/cases/adapters/mysql/table_options_test.rb2
-rw-r--r--activerecord/test/cases/adapters/mysql/unsigned_type_test.rb2
-rw-r--r--activerecord/test/cases/adapters/mysql2/active_schema_test.rb2
-rw-r--r--activerecord/test/cases/adapters/mysql2/bind_parameter_test.rb2
-rw-r--r--activerecord/test/cases/adapters/mysql2/boolean_test.rb2
-rw-r--r--activerecord/test/cases/adapters/mysql2/case_sensitivity_test.rb2
-rw-r--r--activerecord/test/cases/adapters/mysql2/charset_collation_test.rb2
-rw-r--r--activerecord/test/cases/adapters/mysql2/connection_test.rb11
-rw-r--r--activerecord/test/cases/adapters/mysql2/enum_test.rb2
-rw-r--r--activerecord/test/cases/adapters/mysql2/explain_test.rb2
-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/reserved_word_test.rb40
-rw-r--r--activerecord/test/cases/adapters/mysql2/schema_migrations_test.rb81
-rw-r--r--activerecord/test/cases/adapters/mysql2/schema_test.rb2
-rw-r--r--activerecord/test/cases/adapters/mysql2/sql_types_test.rb2
-rw-r--r--activerecord/test/cases/adapters/mysql2/table_options_test.rb2
-rw-r--r--activerecord/test/cases/adapters/mysql2/unsigned_type_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/active_schema_test.rb20
-rw-r--r--activerecord/test/cases/adapters/postgresql/array_test.rb8
-rw-r--r--activerecord/test/cases/adapters/postgresql/bit_string_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/bytea_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/change_schema_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/cidr_test.rb4
-rw-r--r--activerecord/test/cases/adapters/postgresql/citext_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/collation_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/composite_test.rb4
-rw-r--r--activerecord/test/cases/adapters/postgresql/connection_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/datatype_test.rb4
-rw-r--r--activerecord/test/cases/adapters/postgresql/domain_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/enum_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/explain_test.rb30
-rw-r--r--activerecord/test/cases/adapters/postgresql/extension_migration_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/full_text_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/geometric_test.rb123
-rw-r--r--activerecord/test/cases/adapters/postgresql/hstore_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/infinity_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/integer_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/json_test.rb4
-rw-r--r--activerecord/test/cases/adapters/postgresql/ltree_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/money_test.rb4
-rw-r--r--activerecord/test/cases/adapters/postgresql/network_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/numbers_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/quoting_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/range_test.rb4
-rw-r--r--activerecord/test/cases/adapters/postgresql/referential_integrity_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/rename_table_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/schema_authorization_test.rb4
-rw-r--r--activerecord/test/cases/adapters/postgresql/schema_test.rb100
-rw-r--r--activerecord/test/cases/adapters/postgresql/serial_test.rb4
-rw-r--r--activerecord/test/cases/adapters/postgresql/statement_pool_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/timestamp_test.rb4
-rw-r--r--activerecord/test/cases/adapters/postgresql/type_lookup_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/utils_test.rb5
-rw-r--r--activerecord/test/cases/adapters/postgresql/uuid_test.rb8
-rw-r--r--activerecord/test/cases/adapters/postgresql/view_test.rb63
-rw-r--r--activerecord/test/cases/adapters/postgresql/xml_test.rb2
-rw-r--r--activerecord/test/cases/adapters/sqlite3/collation_test.rb53
-rw-r--r--activerecord/test/cases/adapters/sqlite3/copy_table_test.rb2
-rw-r--r--activerecord/test/cases/adapters/sqlite3/explain_test.rb2
-rw-r--r--activerecord/test/cases/adapters/sqlite3/quoting_test.rb2
-rw-r--r--activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb23
-rw-r--r--activerecord/test/cases/adapters/sqlite3/sqlite3_create_folder_test.rb2
-rw-r--r--activerecord/test/cases/adapters/sqlite3/statement_pool_test.rb3
-rw-r--r--activerecord/test/cases/associations/belongs_to_associations_test.rb76
-rw-r--r--activerecord/test/cases/associations/eager_test.rb68
-rw-r--r--activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb90
-rw-r--r--activerecord/test/cases/associations/has_many_associations_test.rb131
-rw-r--r--activerecord/test/cases/associations/has_many_through_associations_test.rb107
-rw-r--r--activerecord/test/cases/associations/has_one_associations_test.rb36
-rw-r--r--activerecord/test/cases/associations/has_one_through_associations_test.rb40
-rw-r--r--activerecord/test/cases/associations/join_model_test.rb40
-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.rb6
-rw-r--r--activerecord/test/cases/attribute_test.rb1
-rw-r--r--activerecord/test/cases/attributes_test.rb22
-rw-r--r--activerecord/test/cases/autosave_association_test.rb39
-rw-r--r--activerecord/test/cases/base_test.rb52
-rw-r--r--activerecord/test/cases/batches_test.rb257
-rw-r--r--activerecord/test/cases/calculations_test.rb40
-rw-r--r--activerecord/test/cases/collection_cache_key_test.rb70
-rw-r--r--activerecord/test/cases/connection_adapters/adapter_leasing_test.rb6
-rw-r--r--activerecord/test/cases/connection_adapters/connection_handler_test.rb46
-rw-r--r--activerecord/test/cases/connection_adapters/type_lookup_test.rb6
-rw-r--r--activerecord/test/cases/connection_management_test.rb23
-rw-r--r--activerecord/test/cases/connection_pool_test.rb180
-rw-r--r--activerecord/test/cases/counter_cache_test.rb13
-rw-r--r--activerecord/test/cases/dirty_test.rb42
-rw-r--r--activerecord/test/cases/enum_test.rb136
-rw-r--r--activerecord/test/cases/errors_test.rb16
-rw-r--r--activerecord/test/cases/explain_subscriber_test.rb5
-rw-r--r--activerecord/test/cases/fixtures_test.rb44
-rw-r--r--activerecord/test/cases/helper.rb5
-rw-r--r--activerecord/test/cases/inheritance_test.rb18
-rw-r--r--activerecord/test/cases/invertible_migration_test.rb81
-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_schema_test.rb2
-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.rb46
-rw-r--r--activerecord/test/cases/migration/foreign_key_test.rb31
-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_test.rb8
-rw-r--r--activerecord/test/cases/nested_attributes_test.rb45
-rw-r--r--activerecord/test/cases/persistence_test.rb30
-rw-r--r--activerecord/test/cases/primary_keys_test.rb14
-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/delegation_test.rb2
-rw-r--r--activerecord/test/cases/relation/mutation_test.rb17
-rw-r--r--activerecord/test/cases/relation_test.rb24
-rw-r--r--activerecord/test/cases/relations_test.rb70
-rw-r--r--activerecord/test/cases/reload_models_test.rb2
-rw-r--r--activerecord/test/cases/schema_dumper_test.rb7
-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/serialized_attribute_test.rb21
-rw-r--r--activerecord/test/cases/suppressor_test.rb33
-rw-r--r--activerecord/test/cases/tasks/database_tasks_test.rb4
-rw-r--r--activerecord/test/cases/tasks/mysql_rake_test.rb15
-rw-r--r--activerecord/test/cases/tasks/postgresql_rake_test.rb12
-rw-r--r--activerecord/test/cases/test_case.rb26
-rw-r--r--activerecord/test/cases/touch_later_test.rb25
-rw-r--r--activerecord/test/cases/transactions_test.rb11
-rw-r--r--activerecord/test/cases/type/decimal_test.rb5
-rw-r--r--activerecord/test/cases/type/integer_test.rb2
-rw-r--r--activerecord/test/cases/validations/absence_validation_test.rb75
-rw-r--r--activerecord/test/cases/validations/i18n_validation_test.rb10
-rw-r--r--activerecord/test/cases/validations/uniqueness_validation_test.rb42
-rw-r--r--activerecord/test/cases/validations_test.rb7
-rw-r--r--activerecord/test/cases/view_test.rb68
-rw-r--r--activerecord/test/cases/xml_serialization_test.rb447
-rw-r--r--activerecord/test/fixtures/books.yml20
-rw-r--r--activerecord/test/fixtures/doubloons.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/trees.yml3
-rw-r--r--activerecord/test/models/aircraft.rb1
-rw-r--r--activerecord/test/models/book.rb4
-rw-r--r--activerecord/test/models/carrier.rb2
-rw-r--r--activerecord/test/models/categorization.rb2
-rw-r--r--activerecord/test/models/company.rb3
-rw-r--r--activerecord/test/models/customer_carrier.rb14
-rw-r--r--activerecord/test/models/developer.rb1
-rw-r--r--activerecord/test/models/doubloon.rb12
-rw-r--r--activerecord/test/models/face.rb2
-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/node.rb5
-rw-r--r--activerecord/test/models/post.rb24
-rw-r--r--activerecord/test/models/professor.rb5
-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/vehicle.rb7
-rw-r--r--activerecord/test/schema/postgresql_specific_schema.rb1
-rw-r--r--activerecord/test/schema/schema.rb49
-rw-r--r--activerecord/test/support/connection.rb1
180 files changed, 3207 insertions, 1197 deletions
diff --git a/activerecord/test/cases/adapters/mysql/active_schema_test.rb b/activerecord/test/cases/adapters/mysql/active_schema_test.rb
index 57eb5d0e18..f0fd95ac16 100644
--- a/activerecord/test/cases/adapters/mysql/active_schema_test.rb
+++ b/activerecord/test/cases/adapters/mysql/active_schema_test.rb
@@ -1,7 +1,7 @@
require "cases/helper"
require 'support/connection_helper'
-class ActiveSchemaTest < ActiveRecord::TestCase
+class MysqlActiveSchemaTest < ActiveRecord::MysqlTestCase
include ConnectionHelper
def setup
diff --git a/activerecord/test/cases/adapters/mysql/case_sensitivity_test.rb b/activerecord/test/cases/adapters/mysql/case_sensitivity_test.rb
index 345122b1ad..98d44315dd 100644
--- a/activerecord/test/cases/adapters/mysql/case_sensitivity_test.rb
+++ b/activerecord/test/cases/adapters/mysql/case_sensitivity_test.rb
@@ -1,6 +1,6 @@
require "cases/helper"
-class MysqlCaseSensitivityTest < ActiveRecord::TestCase
+class MysqlCaseSensitivityTest < ActiveRecord::MysqlTestCase
class CollationTest < ActiveRecord::Base
end
diff --git a/activerecord/test/cases/adapters/mysql/charset_collation_test.rb b/activerecord/test/cases/adapters/mysql/charset_collation_test.rb
index c8dd49d00a..f2117a97e6 100644
--- a/activerecord/test/cases/adapters/mysql/charset_collation_test.rb
+++ b/activerecord/test/cases/adapters/mysql/charset_collation_test.rb
@@ -1,7 +1,7 @@
require "cases/helper"
require 'support/schema_dumping_helper'
-class CharsetCollationTest < ActiveRecord::TestCase
+class MysqlCharsetCollationTest < ActiveRecord::MysqlTestCase
include SchemaDumpingHelper
self.use_transactional_tests = false
diff --git a/activerecord/test/cases/adapters/mysql/connection_test.rb b/activerecord/test/cases/adapters/mysql/connection_test.rb
index 4762ef43b5..ddbc007b87 100644
--- a/activerecord/test/cases/adapters/mysql/connection_test.rb
+++ b/activerecord/test/cases/adapters/mysql/connection_test.rb
@@ -2,7 +2,7 @@ require "cases/helper"
require 'support/connection_helper'
require 'support/ddl_helper'
-class MysqlConnectionTest < ActiveRecord::TestCase
+class MysqlConnectionTest < ActiveRecord::MysqlTestCase
include ConnectionHelper
include DdlHelper
@@ -145,6 +145,15 @@ class MysqlConnectionTest < ActiveRecord::TestCase
end
end
+ def test_mysql_strict_mode_specified_default
+ run_without_connection do |orig_connection|
+ ActiveRecord::Base.establish_connection(orig_connection.merge({strict: :default}))
+ global_sql_mode = ActiveRecord::Base.connection.exec_query "SELECT @@GLOBAL.sql_mode"
+ session_sql_mode = ActiveRecord::Base.connection.exec_query "SELECT @@SESSION.sql_mode"
+ assert_equal global_sql_mode.rows, session_sql_mode.rows
+ end
+ end
+
def test_mysql_set_session_variable
run_without_connection do |orig_connection|
ActiveRecord::Base.establish_connection(orig_connection.deep_merge({:variables => {:default_week_format => 3}}))
diff --git a/activerecord/test/cases/adapters/mysql/consistency_test.rb b/activerecord/test/cases/adapters/mysql/consistency_test.rb
index ae190b728d..743f6436e4 100644
--- a/activerecord/test/cases/adapters/mysql/consistency_test.rb
+++ b/activerecord/test/cases/adapters/mysql/consistency_test.rb
@@ -1,6 +1,6 @@
require "cases/helper"
-class MysqlConsistencyTest < ActiveRecord::TestCase
+class MysqlConsistencyTest < ActiveRecord::MysqlTestCase
self.use_transactional_tests = false
class Consistency < ActiveRecord::Base
diff --git a/activerecord/test/cases/adapters/mysql/enum_test.rb b/activerecord/test/cases/adapters/mysql/enum_test.rb
index f4e7a3ef0a..ef8ee0a6e3 100644
--- a/activerecord/test/cases/adapters/mysql/enum_test.rb
+++ b/activerecord/test/cases/adapters/mysql/enum_test.rb
@@ -1,6 +1,6 @@
require "cases/helper"
-class MysqlEnumTest < ActiveRecord::TestCase
+class MysqlEnumTest < ActiveRecord::MysqlTestCase
class EnumTest < ActiveRecord::Base
end
diff --git a/activerecord/test/cases/adapters/mysql/mysql_adapter_test.rb b/activerecord/test/cases/adapters/mysql/mysql_adapter_test.rb
index 48ceef365e..b804cb45b9 100644
--- a/activerecord/test/cases/adapters/mysql/mysql_adapter_test.rb
+++ b/activerecord/test/cases/adapters/mysql/mysql_adapter_test.rb
@@ -4,7 +4,7 @@ require 'support/ddl_helper'
module ActiveRecord
module ConnectionAdapters
- class MysqlAdapterTest < ActiveRecord::TestCase
+ class MysqlAdapterTest < ActiveRecord::MysqlTestCase
include DdlHelper
def setup
diff --git a/activerecord/test/cases/adapters/mysql/quoting_test.rb b/activerecord/test/cases/adapters/mysql/quoting_test.rb
index a2206153e9..2024aa36ab 100644
--- a/activerecord/test/cases/adapters/mysql/quoting_test.rb
+++ b/activerecord/test/cases/adapters/mysql/quoting_test.rb
@@ -1,21 +1,29 @@
require "cases/helper"
-module ActiveRecord
- module ConnectionAdapters
- class MysqlAdapter
- class QuotingTest < ActiveRecord::TestCase
- def setup
- @conn = ActiveRecord::Base.connection
- end
+class MysqlQuotingTest < ActiveRecord::MysqlTestCase
+ def setup
+ @conn = ActiveRecord::Base.connection
+ end
+
+ def test_type_cast_true
+ assert_equal 1, @conn.type_cast(true)
+ end
- def test_type_cast_true
- assert_equal 1, @conn.type_cast(true)
- end
+ 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_type_cast_false
- assert_equal 0, @conn.type_cast(false)
- end
- end
- 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/reserved_word_test.rb b/activerecord/test/cases/adapters/mysql/reserved_word_test.rb
index ec1c394f40..4ea1d9ad36 100644
--- a/activerecord/test/cases/adapters/mysql/reserved_word_test.rb
+++ b/activerecord/test/cases/adapters/mysql/reserved_word_test.rb
@@ -1,29 +1,29 @@
require "cases/helper"
-class Group < ActiveRecord::Base
- Group.table_name = 'group'
- belongs_to :select
- has_one :values
-end
+# a suite of tests to ensure the ConnectionAdapters#MysqlAdapter can handle tables with
+# reserved word names (ie: group, order, values, etc...)
+class MysqlReservedWordTest < ActiveRecord::MysqlTestCase
+ class Group < ActiveRecord::Base
+ Group.table_name = 'group'
+ belongs_to :select
+ has_one :values
+ end
-class Select < ActiveRecord::Base
- Select.table_name = 'select'
- has_many :groups
-end
+ class Select < ActiveRecord::Base
+ Select.table_name = 'select'
+ has_many :groups
+ end
-class Values < ActiveRecord::Base
- Values.table_name = 'values'
-end
+ class Values < ActiveRecord::Base
+ Values.table_name = 'values'
+ end
-class Distinct < ActiveRecord::Base
- Distinct.table_name = 'distinct'
- has_and_belongs_to_many :selects
- has_many :values, :through => :groups
-end
+ class Distinct < ActiveRecord::Base
+ Distinct.table_name = 'distinct'
+ has_and_belongs_to_many :selects
+ has_many :values, :through => :groups
+ end
-# a suite of tests to ensure the ConnectionAdapters#MysqlAdapter can handle tables with
-# reserved word names (ie: group, order, values, etc...)
-class MysqlReservedWordTest < ActiveRecord::TestCase
def setup
@connection = ActiveRecord::Base.connection
diff --git a/activerecord/test/cases/adapters/mysql/schema_test.rb b/activerecord/test/cases/adapters/mysql/schema_test.rb
index b7f9c2ce84..2e18f609fd 100644
--- a/activerecord/test/cases/adapters/mysql/schema_test.rb
+++ b/activerecord/test/cases/adapters/mysql/schema_test.rb
@@ -4,7 +4,7 @@ require 'models/comment'
module ActiveRecord
module ConnectionAdapters
- class MysqlSchemaTest < ActiveRecord::TestCase
+ class MysqlSchemaTest < ActiveRecord::MysqlTestCase
fixtures :posts
def setup
diff --git a/activerecord/test/cases/adapters/mysql/sp_test.rb b/activerecord/test/cases/adapters/mysql/sp_test.rb
index 3ca2917ca4..a3d5110032 100644
--- a/activerecord/test/cases/adapters/mysql/sp_test.rb
+++ b/activerecord/test/cases/adapters/mysql/sp_test.rb
@@ -1,11 +1,11 @@
require "cases/helper"
require 'models/topic'
-class StoredProcedureTest < ActiveRecord::TestCase
+class StoredProcedureTest < ActiveRecord::MysqlTestCase
fixtures :topics
# Test that MySQL allows multiple results for stored procedures
- if Mysql.const_defined?(:CLIENT_MULTI_RESULTS)
+ 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
diff --git a/activerecord/test/cases/adapters/mysql/sql_types_test.rb b/activerecord/test/cases/adapters/mysql/sql_types_test.rb
index 1ddb1b91c9..25b28de7f0 100644
--- a/activerecord/test/cases/adapters/mysql/sql_types_test.rb
+++ b/activerecord/test/cases/adapters/mysql/sql_types_test.rb
@@ -1,6 +1,6 @@
require "cases/helper"
-class SqlTypesTest < ActiveRecord::TestCase
+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)
diff --git a/activerecord/test/cases/adapters/mysql/statement_pool_test.rb b/activerecord/test/cases/adapters/mysql/statement_pool_test.rb
index 209a0cf464..6be36566de 100644
--- a/activerecord/test/cases/adapters/mysql/statement_pool_test.rb
+++ b/activerecord/test/cases/adapters/mysql/statement_pool_test.rb
@@ -1,23 +1,19 @@
require 'cases/helper'
-module ActiveRecord::ConnectionAdapters
- class MysqlAdapter
- class StatementPoolTest < ActiveRecord::TestCase
- if Process.respond_to?(:fork)
- def test_cache_is_per_pid
- cache = StatementPool.new nil, 10
- cache['foo'] = 'bar'
- assert_equal 'bar', cache['foo']
+class MysqlStatementPoolTest < ActiveRecord::MysqlTestCase
+ if Process.respond_to?(:fork)
+ def test_cache_is_per_pid
+ cache = ActiveRecord::ConnectionAdapters::MysqlAdapter::StatementPool.new nil, 10
+ cache['foo'] = 'bar'
+ assert_equal 'bar', cache['foo']
- pid = fork {
- lookup = cache['foo'];
- exit!(!lookup)
- }
+ pid = fork {
+ lookup = cache['foo'];
+ exit!(!lookup)
+ }
- Process.waitpid pid
- assert $?.success?, 'process should exit successfully'
- end
- end
+ Process.waitpid pid
+ assert $?.success?, 'process should exit successfully'
end
end
end
diff --git a/activerecord/test/cases/adapters/mysql/table_options_test.rb b/activerecord/test/cases/adapters/mysql/table_options_test.rb
index 0e5b0e8aec..99df6d6cba 100644
--- a/activerecord/test/cases/adapters/mysql/table_options_test.rb
+++ b/activerecord/test/cases/adapters/mysql/table_options_test.rb
@@ -1,7 +1,7 @@
require "cases/helper"
require 'support/schema_dumping_helper'
-class MysqlTableOptionsTest < ActiveRecord::TestCase
+class MysqlTableOptionsTest < ActiveRecord::MysqlTestCase
include SchemaDumpingHelper
def setup
diff --git a/activerecord/test/cases/adapters/mysql/unsigned_type_test.rb b/activerecord/test/cases/adapters/mysql/unsigned_type_test.rb
index e9edc53f93..ed9398a918 100644
--- a/activerecord/test/cases/adapters/mysql/unsigned_type_test.rb
+++ b/activerecord/test/cases/adapters/mysql/unsigned_type_test.rb
@@ -1,6 +1,6 @@
require "cases/helper"
-class UnsignedTypeTest < ActiveRecord::TestCase
+class MysqlUnsignedTypeTest < ActiveRecord::MysqlTestCase
self.use_transactional_tests = false
class UnsignedType < ActiveRecord::Base
diff --git a/activerecord/test/cases/adapters/mysql2/active_schema_test.rb b/activerecord/test/cases/adapters/mysql2/active_schema_test.rb
index 0ea556d4fa..6558d60aa1 100644
--- a/activerecord/test/cases/adapters/mysql2/active_schema_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/active_schema_test.rb
@@ -1,7 +1,7 @@
require "cases/helper"
require 'support/connection_helper'
-class ActiveSchemaTest < ActiveRecord::TestCase
+class Mysql2ActiveSchemaTest < ActiveRecord::Mysql2TestCase
include ConnectionHelper
def setup
diff --git a/activerecord/test/cases/adapters/mysql2/bind_parameter_test.rb b/activerecord/test/cases/adapters/mysql2/bind_parameter_test.rb
index 5e8065d80d..abdf3dbf5b 100644
--- a/activerecord/test/cases/adapters/mysql2/bind_parameter_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/bind_parameter_test.rb
@@ -4,7 +4,7 @@ require 'models/topic'
module ActiveRecord
module ConnectionAdapters
class Mysql2Adapter
- class BindParameterTest < ActiveRecord::TestCase
+ class BindParameterTest < ActiveRecord::Mysql2TestCase
fixtures :topics
def test_update_question_marks
diff --git a/activerecord/test/cases/adapters/mysql2/boolean_test.rb b/activerecord/test/cases/adapters/mysql2/boolean_test.rb
index 0d81dd6eee..8575df9e43 100644
--- a/activerecord/test/cases/adapters/mysql2/boolean_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/boolean_test.rb
@@ -1,6 +1,6 @@
require "cases/helper"
-class Mysql2BooleanTest < ActiveRecord::TestCase
+class Mysql2BooleanTest < ActiveRecord::Mysql2TestCase
self.use_transactional_tests = false
class BooleanType < ActiveRecord::Base
diff --git a/activerecord/test/cases/adapters/mysql2/case_sensitivity_test.rb b/activerecord/test/cases/adapters/mysql2/case_sensitivity_test.rb
index ccf3d84a44..963116f08a 100644
--- a/activerecord/test/cases/adapters/mysql2/case_sensitivity_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/case_sensitivity_test.rb
@@ -1,6 +1,6 @@
require "cases/helper"
-class Mysql2CaseSensitivityTest < ActiveRecord::TestCase
+class Mysql2CaseSensitivityTest < ActiveRecord::Mysql2TestCase
class CollationTest < ActiveRecord::Base
end
diff --git a/activerecord/test/cases/adapters/mysql2/charset_collation_test.rb b/activerecord/test/cases/adapters/mysql2/charset_collation_test.rb
index c8dd49d00a..4fd34def15 100644
--- a/activerecord/test/cases/adapters/mysql2/charset_collation_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/charset_collation_test.rb
@@ -1,7 +1,7 @@
require "cases/helper"
require 'support/schema_dumping_helper'
-class CharsetCollationTest < ActiveRecord::TestCase
+class Mysql2CharsetCollationTest < ActiveRecord::Mysql2TestCase
include SchemaDumpingHelper
self.use_transactional_tests = false
diff --git a/activerecord/test/cases/adapters/mysql2/connection_test.rb b/activerecord/test/cases/adapters/mysql2/connection_test.rb
index a8b39b21d4..000bcadebe 100644
--- a/activerecord/test/cases/adapters/mysql2/connection_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/connection_test.rb
@@ -1,7 +1,7 @@
require "cases/helper"
require 'support/connection_helper'
-class MysqlConnectionTest < ActiveRecord::TestCase
+class Mysql2ConnectionTest < ActiveRecord::Mysql2TestCase
include ConnectionHelper
fixtures :comments
@@ -84,6 +84,15 @@ class MysqlConnectionTest < ActiveRecord::TestCase
end
end
+ def test_mysql_strict_mode_specified_default
+ run_without_connection do |orig_connection|
+ ActiveRecord::Base.establish_connection(orig_connection.merge({strict: :default}))
+ global_sql_mode = ActiveRecord::Base.connection.exec_query "SELECT @@GLOBAL.sql_mode"
+ session_sql_mode = ActiveRecord::Base.connection.exec_query "SELECT @@SESSION.sql_mode"
+ assert_equal global_sql_mode.rows, session_sql_mode.rows
+ end
+ end
+
def test_mysql_set_session_variable
run_without_connection do |orig_connection|
ActiveRecord::Base.establish_connection(orig_connection.deep_merge({:variables => {:default_week_format => 3}}))
diff --git a/activerecord/test/cases/adapters/mysql2/enum_test.rb b/activerecord/test/cases/adapters/mysql2/enum_test.rb
index 6dd9a5ec87..bd732b5eca 100644
--- a/activerecord/test/cases/adapters/mysql2/enum_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/enum_test.rb
@@ -1,6 +1,6 @@
require "cases/helper"
-class Mysql2EnumTest < ActiveRecord::TestCase
+class Mysql2EnumTest < ActiveRecord::Mysql2TestCase
class EnumTest < ActiveRecord::Base
end
diff --git a/activerecord/test/cases/adapters/mysql2/explain_test.rb b/activerecord/test/cases/adapters/mysql2/explain_test.rb
index 2b01d941b8..4fc7414b18 100644
--- a/activerecord/test/cases/adapters/mysql2/explain_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/explain_test.rb
@@ -5,7 +5,7 @@ require 'models/computer'
module ActiveRecord
module ConnectionAdapters
class Mysql2Adapter
- class ExplainTest < ActiveRecord::TestCase
+ class ExplainTest < ActiveRecord::Mysql2TestCase
fixtures :developers
def test_explain_for_one_query
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/reserved_word_test.rb b/activerecord/test/cases/adapters/mysql2/reserved_word_test.rb
index 799e60a683..ffb4e2c5cf 100644
--- a/activerecord/test/cases/adapters/mysql2/reserved_word_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/reserved_word_test.rb
@@ -1,29 +1,29 @@
require "cases/helper"
-class Group < ActiveRecord::Base
- Group.table_name = 'group'
- belongs_to :select
- has_one :values
-end
+# a suite of tests to ensure the ConnectionAdapters#MysqlAdapter can handle tables with
+# reserved word names (ie: group, order, values, etc...)
+class Mysql2ReservedWordTest < ActiveRecord::Mysql2TestCase
+ class Group < ActiveRecord::Base
+ Group.table_name = 'group'
+ belongs_to :select
+ has_one :values
+ end
-class Select < ActiveRecord::Base
- Select.table_name = 'select'
- has_many :groups
-end
+ class Select < ActiveRecord::Base
+ Select.table_name = 'select'
+ has_many :groups
+ end
-class Values < ActiveRecord::Base
- Values.table_name = 'values'
-end
+ class Values < ActiveRecord::Base
+ Values.table_name = 'values'
+ end
-class Distinct < ActiveRecord::Base
- Distinct.table_name = 'distinct'
- has_and_belongs_to_many :selects
- has_many :values, :through => :groups
-end
+ class Distinct < ActiveRecord::Base
+ Distinct.table_name = 'distinct'
+ has_and_belongs_to_many :selects
+ has_many :values, :through => :groups
+ end
-# a suite of tests to ensure the ConnectionAdapters#MysqlAdapter can handle tables with
-# reserved word names (ie: group, order, values, etc...)
-class MysqlReservedWordTest < ActiveRecord::TestCase
def setup
@connection = ActiveRecord::Base.connection
diff --git a/activerecord/test/cases/adapters/mysql2/schema_migrations_test.rb b/activerecord/test/cases/adapters/mysql2/schema_migrations_test.rb
index 417ccf6d11..396f235e77 100644
--- a/activerecord/test/cases/adapters/mysql2/schema_migrations_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/schema_migrations_test.rb
@@ -1,47 +1,42 @@
require "cases/helper"
-module ActiveRecord
- module ConnectionAdapters
- class AbstractMysqlAdapter
- class SchemaMigrationsTest < ActiveRecord::TestCase
- def test_renaming_index_on_foreign_key
- connection.add_index "engines", "car_id"
- connection.add_foreign_key :engines, :cars, name: "fk_engines_cars"
-
- connection.rename_index("engines", "index_engines_on_car_id", "idx_renamed")
- assert_equal ["idx_renamed"], connection.indexes("engines").map(&:name)
- ensure
- connection.remove_foreign_key :engines, name: "fk_engines_cars"
- end
-
- def test_initializes_schema_migrations_for_encoding_utf8mb4
- smtn = ActiveRecord::Migrator.schema_migrations_table_name
- connection.drop_table smtn, if_exists: true
-
- database_name = connection.current_database
- database_info = connection.select_one("SELECT * FROM information_schema.schemata WHERE schema_name = '#{database_name}'")
-
- original_charset = database_info["DEFAULT_CHARACTER_SET_NAME"]
- original_collation = database_info["DEFAULT_COLLATION_NAME"]
-
- execute("ALTER DATABASE #{database_name} DEFAULT CHARACTER SET utf8mb4")
-
- connection.initialize_schema_migrations_table
-
- assert connection.column_exists?(smtn, :version, :string, limit: AbstractMysqlAdapter::MAX_INDEX_LENGTH_FOR_CHARSETS_OF_4BYTES_MAXLEN)
- ensure
- execute("ALTER DATABASE #{database_name} DEFAULT CHARACTER SET #{original_charset} COLLATE #{original_collation}")
- end
-
- private
- def connection
- @connection ||= ActiveRecord::Base.connection
- end
-
- def execute(sql)
- connection.execute(sql)
- end
- end
- end
+class SchemaMigrationsTest < ActiveRecord::Mysql2TestCase
+ def test_renaming_index_on_foreign_key
+ connection.add_index "engines", "car_id"
+ connection.add_foreign_key :engines, :cars, name: "fk_engines_cars"
+
+ connection.rename_index("engines", "index_engines_on_car_id", "idx_renamed")
+ assert_equal ["idx_renamed"], connection.indexes("engines").map(&:name)
+ ensure
+ connection.remove_foreign_key :engines, name: "fk_engines_cars"
+ end
+
+ def test_initializes_schema_migrations_for_encoding_utf8mb4
+ smtn = ActiveRecord::Migrator.schema_migrations_table_name
+ connection.drop_table smtn, if_exists: true
+
+ database_name = connection.current_database
+ database_info = connection.select_one("SELECT * FROM information_schema.schemata WHERE schema_name = '#{database_name}'")
+
+ original_charset = database_info["DEFAULT_CHARACTER_SET_NAME"]
+ original_collation = database_info["DEFAULT_COLLATION_NAME"]
+
+ execute("ALTER DATABASE #{database_name} DEFAULT CHARACTER SET utf8mb4")
+
+ connection.initialize_schema_migrations_table
+
+ limit = ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter::MAX_INDEX_LENGTH_FOR_CHARSETS_OF_4BYTES_MAXLEN
+ assert connection.column_exists?(smtn, :version, :string, limit: limit)
+ ensure
+ execute("ALTER DATABASE #{database_name} DEFAULT CHARACTER SET #{original_charset} COLLATE #{original_collation}")
+ end
+
+ private
+ def connection
+ @connection ||= ActiveRecord::Base.connection
+ end
+
+ def execute(sql)
+ connection.execute(sql)
end
end
diff --git a/activerecord/test/cases/adapters/mysql2/schema_test.rb b/activerecord/test/cases/adapters/mysql2/schema_test.rb
index 47707b7d4f..880a2123d2 100644
--- a/activerecord/test/cases/adapters/mysql2/schema_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/schema_test.rb
@@ -4,7 +4,7 @@ require 'models/comment'
module ActiveRecord
module ConnectionAdapters
- class Mysql2SchemaTest < ActiveRecord::TestCase
+ class Mysql2SchemaTest < ActiveRecord::Mysql2TestCase
fixtures :posts
def setup
diff --git a/activerecord/test/cases/adapters/mysql2/sql_types_test.rb b/activerecord/test/cases/adapters/mysql2/sql_types_test.rb
index 1ddb1b91c9..ae505d29c9 100644
--- a/activerecord/test/cases/adapters/mysql2/sql_types_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/sql_types_test.rb
@@ -1,6 +1,6 @@
require "cases/helper"
-class SqlTypesTest < ActiveRecord::TestCase
+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)
diff --git a/activerecord/test/cases/adapters/mysql2/table_options_test.rb b/activerecord/test/cases/adapters/mysql2/table_options_test.rb
index 0e5b0e8aec..af121ee7d9 100644
--- a/activerecord/test/cases/adapters/mysql2/table_options_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/table_options_test.rb
@@ -1,7 +1,7 @@
require "cases/helper"
require 'support/schema_dumping_helper'
-class MysqlTableOptionsTest < ActiveRecord::TestCase
+class Mysql2TableOptionsTest < ActiveRecord::Mysql2TestCase
include SchemaDumpingHelper
def setup
diff --git a/activerecord/test/cases/adapters/mysql2/unsigned_type_test.rb b/activerecord/test/cases/adapters/mysql2/unsigned_type_test.rb
index e9edc53f93..9e06db2519 100644
--- a/activerecord/test/cases/adapters/mysql2/unsigned_type_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/unsigned_type_test.rb
@@ -1,6 +1,6 @@
require "cases/helper"
-class UnsignedTypeTest < ActiveRecord::TestCase
+class Mysql2UnsignedTypeTest < ActiveRecord::Mysql2TestCase
self.use_transactional_tests = false
class UnsignedType < ActiveRecord::Base
diff --git a/activerecord/test/cases/adapters/postgresql/active_schema_test.rb b/activerecord/test/cases/adapters/postgresql/active_schema_test.rb
index 3808db5141..24def31e36 100644
--- a/activerecord/test/cases/adapters/postgresql/active_schema_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/active_schema_test.rb
@@ -1,6 +1,6 @@
require 'cases/helper'
-class PostgresqlActiveSchemaTest < ActiveRecord::TestCase
+class PostgresqlActiveSchemaTest < ActiveRecord::PostgreSQLTestCase
def setup
ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.class_eval do
def execute(sql, name = nil) sql end
@@ -25,7 +25,7 @@ class PostgresqlActiveSchemaTest < ActiveRecord::TestCase
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::TestCase
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/array_test.rb b/activerecord/test/cases/adapters/postgresql/array_test.rb
index 6edbd9c3a6..380a90d765 100644
--- a/activerecord/test/cases/adapters/postgresql/array_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/array_test.rb
@@ -1,10 +1,9 @@
require "cases/helper"
require 'support/schema_dumping_helper'
-class PostgresqlArrayTest < ActiveRecord::TestCase
+class PostgresqlArrayTest < ActiveRecord::PostgreSQLTestCase
include SchemaDumpingHelper
include InTimeZone
- OID = ActiveRecord::ConnectionAdapters::PostgreSQL::OID
class PgArray < ActiveRecord::Base
self.table_name = 'pg_arrays'
@@ -212,8 +211,9 @@ class PostgresqlArrayTest < ActiveRecord::TestCase
def test_quoting_non_standard_delimiters
strings = ["hello,", "world;"]
- comma_delim = OID::Array.new(ActiveRecord::Type::String.new, ',')
- semicolon_delim = OID::Array.new(ActiveRecord::Type::String.new, ';')
+ oid = ActiveRecord::ConnectionAdapters::PostgreSQL::OID
+ comma_delim = oid::Array.new(ActiveRecord::Type::String.new, ',')
+ semicolon_delim = oid::Array.new(ActiveRecord::Type::String.new, ';')
assert_equal %({"hello,",world;}), comma_delim.serialize(strings)
assert_equal %({hello,;"world;"}), semicolon_delim.serialize(strings)
diff --git a/activerecord/test/cases/adapters/postgresql/bit_string_test.rb b/activerecord/test/cases/adapters/postgresql/bit_string_test.rb
index 1a5ff4316c..6f72fa6e0f 100644
--- a/activerecord/test/cases/adapters/postgresql/bit_string_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/bit_string_test.rb
@@ -2,7 +2,7 @@ require "cases/helper"
require 'support/connection_helper'
require 'support/schema_dumping_helper'
-class PostgresqlBitStringTest < ActiveRecord::TestCase
+class PostgresqlBitStringTest < ActiveRecord::PostgreSQLTestCase
include ConnectionHelper
include SchemaDumpingHelper
diff --git a/activerecord/test/cases/adapters/postgresql/bytea_test.rb b/activerecord/test/cases/adapters/postgresql/bytea_test.rb
index 16db5ab83d..b6bb1929e6 100644
--- a/activerecord/test/cases/adapters/postgresql/bytea_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/bytea_test.rb
@@ -1,6 +1,6 @@
require "cases/helper"
-class PostgresqlByteaTest < ActiveRecord::TestCase
+class PostgresqlByteaTest < ActiveRecord::PostgreSQLTestCase
class ByteaDataType < ActiveRecord::Base
self.table_name = 'bytea_data_type'
end
diff --git a/activerecord/test/cases/adapters/postgresql/change_schema_test.rb b/activerecord/test/cases/adapters/postgresql/change_schema_test.rb
index 5a9796887c..bc12df668d 100644
--- a/activerecord/test/cases/adapters/postgresql/change_schema_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/change_schema_test.rb
@@ -2,7 +2,7 @@ require 'cases/helper'
module ActiveRecord
class Migration
- class PGChangeSchemaTest < ActiveRecord::TestCase
+ class PGChangeSchemaTest < ActiveRecord::PostgreSQLTestCase
attr_reader :connection
def setup
diff --git a/activerecord/test/cases/adapters/postgresql/cidr_test.rb b/activerecord/test/cases/adapters/postgresql/cidr_test.rb
index 6cb11d17b4..52f2a0096c 100644
--- a/activerecord/test/cases/adapters/postgresql/cidr_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/cidr_test.rb
@@ -3,8 +3,8 @@ require "ipaddr"
module ActiveRecord
module ConnectionAdapters
- class PostgreSQLAdapter
- class CidrTest < ActiveRecord::TestCase
+ class PostgreSQLAdapter < AbstractAdapter
+ class CidrTest < ActiveRecord::PostgreSQLTestCase
test "type casting IPAddr for database" do
type = OID::Cidr.new
ip = IPAddr.new("255.0.0.0/8")
diff --git a/activerecord/test/cases/adapters/postgresql/citext_test.rb b/activerecord/test/cases/adapters/postgresql/citext_test.rb
index f706847890..bd62041e79 100644
--- a/activerecord/test/cases/adapters/postgresql/citext_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/citext_test.rb
@@ -2,7 +2,7 @@ require 'cases/helper'
require 'support/schema_dumping_helper'
if ActiveRecord::Base.connection.supports_extensions?
- class PostgresqlCitextTest < ActiveRecord::TestCase
+ class PostgresqlCitextTest < ActiveRecord::PostgreSQLTestCase
include SchemaDumpingHelper
class Citext < ActiveRecord::Base
self.table_name = 'citexts'
diff --git a/activerecord/test/cases/adapters/postgresql/collation_test.rb b/activerecord/test/cases/adapters/postgresql/collation_test.rb
index 17ef5f304c..8470329c35 100644
--- a/activerecord/test/cases/adapters/postgresql/collation_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/collation_test.rb
@@ -1,7 +1,7 @@
require "cases/helper"
require 'support/schema_dumping_helper'
-class PostgresqlCollationTest < ActiveRecord::TestCase
+class PostgresqlCollationTest < ActiveRecord::PostgreSQLTestCase
include SchemaDumpingHelper
def setup
diff --git a/activerecord/test/cases/adapters/postgresql/composite_test.rb b/activerecord/test/cases/adapters/postgresql/composite_test.rb
index 16e3f90a47..1de87e5f01 100644
--- a/activerecord/test/cases/adapters/postgresql/composite_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/composite_test.rb
@@ -40,7 +40,7 @@ end
# "unknown OID 5653508: failed to recognize type of 'address'. It will be treated as String."
# To take full advantage of composite types, we suggest you register your own +OID::Type+.
# See PostgresqlCompositeWithCustomOIDTest
-class PostgresqlCompositeTest < ActiveRecord::TestCase
+class PostgresqlCompositeTest < ActiveRecord::PostgreSQLTestCase
include PostgresqlCompositeBehavior
def test_column
@@ -77,7 +77,7 @@ class PostgresqlCompositeTest < ActiveRecord::TestCase
end
end
-class PostgresqlCompositeWithCustomOIDTest < ActiveRecord::TestCase
+class PostgresqlCompositeWithCustomOIDTest < ActiveRecord::PostgreSQLTestCase
include PostgresqlCompositeBehavior
class FullAddressType < ActiveRecord::Type::Value
diff --git a/activerecord/test/cases/adapters/postgresql/connection_test.rb b/activerecord/test/cases/adapters/postgresql/connection_test.rb
index 55ad76c8c0..820d41e13b 100644
--- a/activerecord/test/cases/adapters/postgresql/connection_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/connection_test.rb
@@ -2,7 +2,7 @@ require "cases/helper"
require 'support/connection_helper'
module ActiveRecord
- class PostgresqlConnectionTest < ActiveRecord::TestCase
+ class PostgresqlConnectionTest < ActiveRecord::PostgreSQLTestCase
include ConnectionHelper
class NonExistentTable < ActiveRecord::Base
diff --git a/activerecord/test/cases/adapters/postgresql/datatype_test.rb b/activerecord/test/cases/adapters/postgresql/datatype_test.rb
index 2c14252ae4..232c25cb3b 100644
--- a/activerecord/test/cases/adapters/postgresql/datatype_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/datatype_test.rb
@@ -11,7 +11,7 @@ end
class PostgresqlLtree < ActiveRecord::Base
end
-class PostgresqlDataTypeTest < ActiveRecord::TestCase
+class PostgresqlDataTypeTest < ActiveRecord::PostgreSQLTestCase
self.use_transactional_tests = false
def setup
@@ -69,7 +69,7 @@ class PostgresqlDataTypeTest < ActiveRecord::TestCase
end
end
-class PostgresqlInternalDataTypeTest < ActiveRecord::TestCase
+class PostgresqlInternalDataTypeTest < ActiveRecord::PostgreSQLTestCase
include DdlHelper
setup do
diff --git a/activerecord/test/cases/adapters/postgresql/domain_test.rb b/activerecord/test/cases/adapters/postgresql/domain_test.rb
index 26e064c937..6102ddacd1 100644
--- a/activerecord/test/cases/adapters/postgresql/domain_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/domain_test.rb
@@ -1,7 +1,7 @@
require "cases/helper"
require 'support/connection_helper'
-class PostgresqlDomainTest < ActiveRecord::TestCase
+class PostgresqlDomainTest < ActiveRecord::PostgreSQLTestCase
include ConnectionHelper
class PostgresqlDomain < ActiveRecord::Base
diff --git a/activerecord/test/cases/adapters/postgresql/enum_test.rb b/activerecord/test/cases/adapters/postgresql/enum_test.rb
index ed084483bc..6816a6514b 100644
--- a/activerecord/test/cases/adapters/postgresql/enum_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/enum_test.rb
@@ -1,7 +1,7 @@
require "cases/helper"
require 'support/connection_helper'
-class PostgresqlEnumTest < ActiveRecord::TestCase
+class PostgresqlEnumTest < ActiveRecord::PostgreSQLTestCase
include ConnectionHelper
class PostgresqlEnum < ActiveRecord::Base
diff --git a/activerecord/test/cases/adapters/postgresql/explain_test.rb b/activerecord/test/cases/adapters/postgresql/explain_test.rb
index 6ffb4c9f33..4d0fd640aa 100644
--- a/activerecord/test/cases/adapters/postgresql/explain_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/explain_test.rb
@@ -2,25 +2,19 @@ require "cases/helper"
require 'models/developer'
require 'models/computer'
-module ActiveRecord
- module ConnectionAdapters
- class PostgreSQLAdapter
- class ExplainTest < ActiveRecord::TestCase
- fixtures :developers
+class PostgreSQLExplainTest < ActiveRecord::PostgreSQLTestCase
+ fixtures :developers
- def test_explain_for_one_query
- explain = Developer.where(:id => 1).explain
- assert_match %(EXPLAIN for: SELECT "developers".* FROM "developers" WHERE "developers"."id" = $1), explain
- assert_match %(QUERY PLAN), explain
- end
+ def test_explain_for_one_query
+ explain = Developer.where(:id => 1).explain
+ assert_match %(EXPLAIN for: SELECT "developers".* FROM "developers" WHERE "developers"."id" = $1), explain
+ assert_match %(QUERY PLAN), explain
+ end
- def test_explain_with_eager_loading
- explain = Developer.where(:id => 1).includes(:audit_logs).explain
- assert_match %(QUERY PLAN), explain
- assert_match %(EXPLAIN for: SELECT "developers".* FROM "developers" WHERE "developers"."id" = $1), explain
- assert_match %(EXPLAIN for: SELECT "audit_logs".* FROM "audit_logs" WHERE "audit_logs"."developer_id" = 1), explain
- end
- end
- end
+ def test_explain_with_eager_loading
+ explain = Developer.where(:id => 1).includes(:audit_logs).explain
+ assert_match %(QUERY PLAN), explain
+ assert_match %(EXPLAIN for: SELECT "developers".* FROM "developers" WHERE "developers"."id" = $1), explain
+ assert_match %(EXPLAIN for: SELECT "audit_logs".* FROM "audit_logs" WHERE "audit_logs"."developer_id" = 1), explain
end
end
diff --git a/activerecord/test/cases/adapters/postgresql/extension_migration_test.rb b/activerecord/test/cases/adapters/postgresql/extension_migration_test.rb
index 06d427f464..9cfc133308 100644
--- a/activerecord/test/cases/adapters/postgresql/extension_migration_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/extension_migration_test.rb
@@ -1,6 +1,6 @@
require "cases/helper"
-class PostgresqlExtensionMigrationTest < ActiveRecord::TestCase
+class PostgresqlExtensionMigrationTest < ActiveRecord::PostgreSQLTestCase
self.use_transactional_tests = false
class EnableHstore < ActiveRecord::Migration
diff --git a/activerecord/test/cases/adapters/postgresql/full_text_test.rb b/activerecord/test/cases/adapters/postgresql/full_text_test.rb
index b83063c94e..bde7513339 100644
--- a/activerecord/test/cases/adapters/postgresql/full_text_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/full_text_test.rb
@@ -1,7 +1,7 @@
require "cases/helper"
require 'support/schema_dumping_helper'
-class PostgresqlFullTextTest < ActiveRecord::TestCase
+class PostgresqlFullTextTest < ActiveRecord::PostgreSQLTestCase
include SchemaDumpingHelper
class Tsvector < ActiveRecord::Base; end
diff --git a/activerecord/test/cases/adapters/postgresql/geometric_test.rb b/activerecord/test/cases/adapters/postgresql/geometric_test.rb
index 41e9572907..0baf985654 100644
--- a/activerecord/test/cases/adapters/postgresql/geometric_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/geometric_test.rb
@@ -2,11 +2,19 @@ require "cases/helper"
require 'support/connection_helper'
require 'support/schema_dumping_helper'
-class PostgresqlPointTest < ActiveRecord::TestCase
+class PostgresqlPointTest < ActiveRecord::PostgreSQLTestCase
include ConnectionHelper
include SchemaDumpingHelper
- class PostgresqlPoint < ActiveRecord::Base; end
+ class PostgresqlPoint < ActiveRecord::Base
+ attribute :x, :rails_5_1_point
+ attribute :y, :rails_5_1_point
+ attribute :z, :rails_5_1_point
+ attribute :array_of_points, :rails_5_1_point, array: true
+ attribute :legacy_x, :legacy_point
+ attribute :legacy_y, :legacy_point
+ attribute :legacy_z, :legacy_point
+ end
def setup
@connection = ActiveRecord::Base.connection
@@ -14,11 +22,27 @@ class PostgresqlPointTest < ActiveRecord::TestCase
t.point :x
t.point :y, default: [12.2, 13.3]
t.point :z, default: "(14.4,15.5)"
+ t.point :array_of_points, array: true
+ t.point :legacy_x
+ t.point :legacy_y, default: [12.2, 13.3]
+ t.point :legacy_z, default: "(14.4,15.5)"
+ end
+ @connection.create_table('deprecated_points') do |t|
+ t.point :x
end
end
teardown do
@connection.drop_table 'postgresql_points', if_exists: true
+ @connection.drop_table 'deprecated_points', if_exists: true
+ end
+
+ class DeprecatedPoint < ActiveRecord::Base; end
+
+ def test_deprecated_legacy_type
+ assert_deprecated do
+ DeprecatedPoint.new
+ end
end
def test_column
@@ -32,11 +56,11 @@ class PostgresqlPointTest < ActiveRecord::TestCase
end
def test_default
- assert_equal [12.2, 13.3], PostgresqlPoint.column_defaults['y']
- assert_equal [12.2, 13.3], PostgresqlPoint.new.y
+ assert_equal ActiveRecord::Point.new(12.2, 13.3), PostgresqlPoint.column_defaults['y']
+ assert_equal ActiveRecord::Point.new(12.2, 13.3), PostgresqlPoint.new.y
- assert_equal [14.4, 15.5], PostgresqlPoint.column_defaults['z']
- assert_equal [14.4, 15.5], PostgresqlPoint.new.z
+ assert_equal ActiveRecord::Point.new(14.4, 15.5), PostgresqlPoint.column_defaults['z']
+ assert_equal ActiveRecord::Point.new(14.4, 15.5), PostgresqlPoint.new.z
end
def test_schema_dumping
@@ -49,27 +73,100 @@ class PostgresqlPointTest < ActiveRecord::TestCase
def test_roundtrip
PostgresqlPoint.create! x: [10, 25.2]
record = PostgresqlPoint.first
- assert_equal [10, 25.2], record.x
+ assert_equal ActiveRecord::Point.new(10, 25.2), record.x
- record.x = [1.1, 2.2]
+ record.x = ActiveRecord::Point.new(1.1, 2.2)
record.save!
assert record.reload
- assert_equal [1.1, 2.2], record.x
+ assert_equal ActiveRecord::Point.new(1.1, 2.2), record.x
end
def test_mutation
- p = PostgresqlPoint.create! x: [10, 20]
+ p = PostgresqlPoint.create! x: ActiveRecord::Point.new(10, 20)
+
+ p.x.y = 25
+ p.save!
+ p.reload
+
+ assert_equal ActiveRecord::Point.new(10.0, 25.0), p.x
+ assert_not p.changed?
+ end
+
+ def test_array_assignment
+ p = PostgresqlPoint.new(x: [1, 2])
+
+ assert_equal ActiveRecord::Point.new(1, 2), p.x
+ end
+
+ def test_string_assignment
+ p = PostgresqlPoint.new(x: "(1, 2)")
+
+ assert_equal ActiveRecord::Point.new(1, 2), p.x
+ end
+
+ def test_array_of_points_round_trip
+ expected_value = [
+ ActiveRecord::Point.new(1, 2),
+ ActiveRecord::Point.new(2, 3),
+ ActiveRecord::Point.new(3, 4),
+ ]
+ p = PostgresqlPoint.new(array_of_points: expected_value)
+
+ assert_equal expected_value, p.array_of_points
+ p.save!
+ p.reload
+ assert_equal expected_value, p.array_of_points
+ end
+
+ def test_legacy_column
+ column = PostgresqlPoint.columns_hash["legacy_x"]
+ assert_equal :point, column.type
+ assert_equal "point", column.sql_type
+ assert_not column.array?
+
+ type = PostgresqlPoint.type_for_attribute("legacy_x")
+ assert_not type.binary?
+ end
+
+ def test_legacy_default
+ assert_equal [12.2, 13.3], PostgresqlPoint.column_defaults['legacy_y']
+ assert_equal [12.2, 13.3], PostgresqlPoint.new.legacy_y
+
+ assert_equal [14.4, 15.5], PostgresqlPoint.column_defaults['legacy_z']
+ assert_equal [14.4, 15.5], PostgresqlPoint.new.legacy_z
+ end
+
+ def test_legacy_schema_dumping
+ output = dump_table_schema("postgresql_points")
+ assert_match %r{t\.point\s+"legacy_x"$}, output
+ assert_match %r{t\.point\s+"legacy_y",\s+default: \[12\.2, 13\.3\]$}, output
+ assert_match %r{t\.point\s+"legacy_z",\s+default: \[14\.4, 15\.5\]$}, output
+ end
+
+ def test_legacy_roundtrip
+ PostgresqlPoint.create! legacy_x: [10, 25.2]
+ record = PostgresqlPoint.first
+ assert_equal [10, 25.2], record.legacy_x
+
+ record.legacy_x = [1.1, 2.2]
+ record.save!
+ assert record.reload
+ assert_equal [1.1, 2.2], record.legacy_x
+ end
+
+ def test_legacy_mutation
+ p = PostgresqlPoint.create! legacy_x: [10, 20]
- p.x[1] = 25
+ p.legacy_x[1] = 25
p.save!
p.reload
- assert_equal [10.0, 25.0], p.x
+ assert_equal [10.0, 25.0], p.legacy_x
assert_not p.changed?
end
end
-class PostgresqlGeometricTest < ActiveRecord::TestCase
+class PostgresqlGeometricTest < ActiveRecord::PostgreSQLTestCase
class PostgresqlGeometric < ActiveRecord::Base; end
setup do
diff --git a/activerecord/test/cases/adapters/postgresql/hstore_test.rb b/activerecord/test/cases/adapters/postgresql/hstore_test.rb
index ad9dd311a6..6a2d501646 100644
--- a/activerecord/test/cases/adapters/postgresql/hstore_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/hstore_test.rb
@@ -2,7 +2,7 @@ require "cases/helper"
require 'support/schema_dumping_helper'
if ActiveRecord::Base.connection.supports_extensions?
- class PostgresqlHstoreTest < ActiveRecord::TestCase
+ class PostgresqlHstoreTest < ActiveRecord::PostgreSQLTestCase
include SchemaDumpingHelper
class Hstore < ActiveRecord::Base
self.table_name = 'hstores'
diff --git a/activerecord/test/cases/adapters/postgresql/infinity_test.rb b/activerecord/test/cases/adapters/postgresql/infinity_test.rb
index d9d7832094..bfda933fa4 100644
--- a/activerecord/test/cases/adapters/postgresql/infinity_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/infinity_test.rb
@@ -1,6 +1,6 @@
require "cases/helper"
-class PostgresqlInfinityTest < ActiveRecord::TestCase
+class PostgresqlInfinityTest < ActiveRecord::PostgreSQLTestCase
include InTimeZone
class PostgresqlInfinity < ActiveRecord::Base
diff --git a/activerecord/test/cases/adapters/postgresql/integer_test.rb b/activerecord/test/cases/adapters/postgresql/integer_test.rb
index 679a0fc7b3..b4e55964b9 100644
--- a/activerecord/test/cases/adapters/postgresql/integer_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/integer_test.rb
@@ -1,7 +1,7 @@
require "cases/helper"
require "active_support/core_ext/numeric/bytes"
-class PostgresqlIntegerTest < ActiveRecord::TestCase
+class PostgresqlIntegerTest < ActiveRecord::PostgreSQLTestCase
class PgInteger < ActiveRecord::Base
end
diff --git a/activerecord/test/cases/adapters/postgresql/json_test.rb b/activerecord/test/cases/adapters/postgresql/json_test.rb
index 6878516aeb..f242f32496 100644
--- a/activerecord/test/cases/adapters/postgresql/json_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/json_test.rb
@@ -188,7 +188,7 @@ module PostgresqlJSONSharedTestCases
end
end
-class PostgresqlJSONTest < ActiveRecord::TestCase
+class PostgresqlJSONTest < ActiveRecord::PostgreSQLTestCase
include PostgresqlJSONSharedTestCases
def column_type
@@ -196,7 +196,7 @@ class PostgresqlJSONTest < ActiveRecord::TestCase
end
end
-class PostgresqlJSONBTest < ActiveRecord::TestCase
+class PostgresqlJSONBTest < ActiveRecord::PostgreSQLTestCase
include PostgresqlJSONSharedTestCases
def column_type
diff --git a/activerecord/test/cases/adapters/postgresql/ltree_test.rb b/activerecord/test/cases/adapters/postgresql/ltree_test.rb
index ce0ad16557..56516c82b4 100644
--- a/activerecord/test/cases/adapters/postgresql/ltree_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/ltree_test.rb
@@ -1,7 +1,7 @@
require "cases/helper"
require 'support/schema_dumping_helper'
-class PostgresqlLtreeTest < ActiveRecord::TestCase
+class PostgresqlLtreeTest < ActiveRecord::PostgreSQLTestCase
include SchemaDumpingHelper
class Ltree < ActiveRecord::Base
self.table_name = 'ltrees'
diff --git a/activerecord/test/cases/adapters/postgresql/money_test.rb b/activerecord/test/cases/adapters/postgresql/money_test.rb
index cedd399380..c031178479 100644
--- a/activerecord/test/cases/adapters/postgresql/money_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/money_test.rb
@@ -1,7 +1,7 @@
require "cases/helper"
require 'support/schema_dumping_helper'
-class PostgresqlMoneyTest < ActiveRecord::TestCase
+class PostgresqlMoneyTest < ActiveRecord::PostgreSQLTestCase
include SchemaDumpingHelper
class PostgresqlMoney < ActiveRecord::Base; end
@@ -56,7 +56,7 @@ class PostgresqlMoneyTest < ActiveRecord::TestCase
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/network_test.rb b/activerecord/test/cases/adapters/postgresql/network_test.rb
index 033695518e..fe6ee4e2d9 100644
--- a/activerecord/test/cases/adapters/postgresql/network_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/network_test.rb
@@ -1,7 +1,7 @@
require "cases/helper"
require 'support/schema_dumping_helper'
-class PostgresqlNetworkTest < ActiveRecord::TestCase
+class PostgresqlNetworkTest < ActiveRecord::PostgreSQLTestCase
include SchemaDumpingHelper
class PostgresqlNetworkAddress < ActiveRecord::Base; end
diff --git a/activerecord/test/cases/adapters/postgresql/numbers_test.rb b/activerecord/test/cases/adapters/postgresql/numbers_test.rb
index d8e01e3b89..ba7e7dc9a3 100644
--- a/activerecord/test/cases/adapters/postgresql/numbers_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/numbers_test.rb
@@ -1,6 +1,6 @@
require "cases/helper"
-class PostgresqlNumberTest < ActiveRecord::TestCase
+class PostgresqlNumberTest < ActiveRecord::PostgreSQLTestCase
class PostgresqlNumber < ActiveRecord::Base; end
setup do
diff --git a/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb b/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb
index 9a1b889d4d..6e6850c4a9 100644
--- a/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb
@@ -4,7 +4,7 @@ require 'support/connection_helper'
module ActiveRecord
module ConnectionAdapters
- class PostgreSQLAdapterTest < ActiveRecord::TestCase
+ class PostgreSQLAdapterTest < ActiveRecord::PostgreSQLTestCase
include DdlHelper
include ConnectionHelper
diff --git a/activerecord/test/cases/adapters/postgresql/quoting_test.rb b/activerecord/test/cases/adapters/postgresql/quoting_test.rb
index e4420d9d13..5e6f4dbbb8 100644
--- a/activerecord/test/cases/adapters/postgresql/quoting_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/quoting_test.rb
@@ -4,7 +4,7 @@ require 'ipaddr'
module ActiveRecord
module ConnectionAdapters
class PostgreSQLAdapter
- class QuotingTest < ActiveRecord::TestCase
+ class QuotingTest < ActiveRecord::PostgreSQLTestCase
def setup
@conn = ActiveRecord::Base.connection
end
diff --git a/activerecord/test/cases/adapters/postgresql/range_test.rb b/activerecord/test/cases/adapters/postgresql/range_test.rb
index bbf96278b0..02b1083430 100644
--- a/activerecord/test/cases/adapters/postgresql/range_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/range_test.rb
@@ -1,12 +1,12 @@
require "cases/helper"
require 'support/connection_helper'
-if ActiveRecord::Base.connection.supports_ranges?
+if ActiveRecord::Base.connection.respond_to?(:supports_ranges?) && ActiveRecord::Base.connection.supports_ranges?
class PostgresqlRange < ActiveRecord::Base
self.table_name = "postgresql_ranges"
end
- class PostgresqlRangeTest < ActiveRecord::TestCase
+ class PostgresqlRangeTest < ActiveRecord::PostgreSQLTestCase
self.use_transactional_tests = false
include ConnectionHelper
diff --git a/activerecord/test/cases/adapters/postgresql/referential_integrity_test.rb b/activerecord/test/cases/adapters/postgresql/referential_integrity_test.rb
index 7200ed2771..c895ab9db5 100644
--- a/activerecord/test/cases/adapters/postgresql/referential_integrity_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/referential_integrity_test.rb
@@ -1,7 +1,7 @@
require 'cases/helper'
require 'support/connection_helper'
-class PostgreSQLReferentialIntegrityTest < ActiveRecord::TestCase
+class PostgreSQLReferentialIntegrityTest < ActiveRecord::PostgreSQLTestCase
self.use_transactional_tests = false
include ConnectionHelper
diff --git a/activerecord/test/cases/adapters/postgresql/rename_table_test.rb b/activerecord/test/cases/adapters/postgresql/rename_table_test.rb
index f507328868..bd64bae308 100644
--- a/activerecord/test/cases/adapters/postgresql/rename_table_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/rename_table_test.rb
@@ -1,6 +1,6 @@
require "cases/helper"
-class PostgresqlRenameTableTest < ActiveRecord::TestCase
+class PostgresqlRenameTableTest < ActiveRecord::PostgreSQLTestCase
def setup
@connection = ActiveRecord::Base.connection
@connection.create_table :before_rename, force: true
diff --git a/activerecord/test/cases/adapters/postgresql/schema_authorization_test.rb b/activerecord/test/cases/adapters/postgresql/schema_authorization_test.rb
index 359a45bbd1..a0afd922b2 100644
--- a/activerecord/test/cases/adapters/postgresql/schema_authorization_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/schema_authorization_test.rb
@@ -3,7 +3,7 @@ require "cases/helper"
class SchemaThing < ActiveRecord::Base
end
-class SchemaAuthorizationTest < ActiveRecord::TestCase
+class SchemaAuthorizationTest < ActiveRecord::PostgreSQLTestCase
self.use_transactional_tests = false
TABLE_NAME = 'schema_things'
@@ -31,7 +31,7 @@ class SchemaAuthorizationTest < ActiveRecord::TestCase
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 f925dcad97..bee612d8d3 100644
--- a/activerecord/test/cases/adapters/postgresql/schema_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/schema_test.rb
@@ -2,7 +2,17 @@ require "cases/helper"
require 'models/default'
require 'support/schema_dumping_helper'
-class SchemaTest < ActiveRecord::TestCase
+module PGSchemaHelper
+ 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
+end
+
+class SchemaTest < ActiveRecord::PostgreSQLTestCase
+ include PGSchemaHelper
self.use_transactional_tests = false
SCHEMA_NAME = 'test_schema'
@@ -84,8 +94,8 @@ class SchemaTest < ActiveRecord::TestCase
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 +131,17 @@ class SchemaTest < ActiveRecord::TestCase
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,12 +151,16 @@ class SchemaTest < ActiveRecord::TestCase
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
@@ -300,11 +321,11 @@ class SchemaTest < ActiveRecord::TestCase
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 +425,6 @@ class SchemaTest < ActiveRecord::TestCase
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)
@@ -441,7 +455,7 @@ class SchemaTest < ActiveRecord::TestCase
end
end
-class SchemaForeignKeyTest < ActiveRecord::TestCase
+class SchemaForeignKeyTest < ActiveRecord::PostgreSQLTestCase
include SchemaDumpingHelper
setup do
@@ -462,14 +476,14 @@ class SchemaForeignKeyTest < ActiveRecord::TestCase
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 < ActiveSupport::TestCase
+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 +494,14 @@ class DefaultsUsingMultipleSchemasAndDomainTest < ActiveSupport::TestCase
@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 +513,10 @@ class DefaultsUsingMultipleSchemasAndDomainTest < ActiveSupport::TestCase
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 +533,40 @@ class DefaultsUsingMultipleSchemasAndDomainTest < ActiveSupport::TestCase
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/serial_test.rb b/activerecord/test/cases/adapters/postgresql/serial_test.rb
index 458a8dae6c..7d30db247b 100644
--- a/activerecord/test/cases/adapters/postgresql/serial_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/serial_test.rb
@@ -1,7 +1,7 @@
require "cases/helper"
require 'support/schema_dumping_helper'
-class PostgresqlSerialTest < ActiveRecord::TestCase
+class PostgresqlSerialTest < ActiveRecord::PostgreSQLTestCase
include SchemaDumpingHelper
class PostgresqlSerial < ActiveRecord::Base; end
@@ -30,7 +30,7 @@ class PostgresqlSerialTest < ActiveRecord::TestCase
end
end
-class PostgresqlBigSerialTest < ActiveRecord::TestCase
+class PostgresqlBigSerialTest < ActiveRecord::PostgreSQLTestCase
include SchemaDumpingHelper
class PostgresqlBigSerial < ActiveRecord::Base; end
diff --git a/activerecord/test/cases/adapters/postgresql/statement_pool_test.rb b/activerecord/test/cases/adapters/postgresql/statement_pool_test.rb
index 1497b0abc7..5aab246c99 100644
--- a/activerecord/test/cases/adapters/postgresql/statement_pool_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/statement_pool_test.rb
@@ -13,7 +13,7 @@ module ActiveRecord
end
end
- class StatementPoolTest < ActiveRecord::TestCase
+ class StatementPoolTest < ActiveRecord::PostgreSQLTestCase
if Process.respond_to?(:fork)
def test_cache_is_per_pid
cache = StatementPool.new nil, 10
diff --git a/activerecord/test/cases/adapters/postgresql/timestamp_test.rb b/activerecord/test/cases/adapters/postgresql/timestamp_test.rb
index a639f98272..4c4866b46b 100644
--- a/activerecord/test/cases/adapters/postgresql/timestamp_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/timestamp_test.rb
@@ -2,7 +2,7 @@ require 'cases/helper'
require 'models/developer'
require 'models/topic'
-class PostgresqlTimestampTest < ActiveRecord::TestCase
+class PostgresqlTimestampTest < ActiveRecord::PostgreSQLTestCase
class PostgresqlTimestampWithZone < ActiveRecord::Base; end
self.use_transactional_tests = false
@@ -43,7 +43,7 @@ class PostgresqlTimestampTest < ActiveRecord::TestCase
end
end
-class TimestampTest < ActiveRecord::TestCase
+class PostgresqlTimestampFixtureTest < ActiveRecord::PostgreSQLTestCase
fixtures :topics
def test_group_by_date
diff --git a/activerecord/test/cases/adapters/postgresql/type_lookup_test.rb b/activerecord/test/cases/adapters/postgresql/type_lookup_test.rb
index c0907b8f21..77a99ca778 100644
--- a/activerecord/test/cases/adapters/postgresql/type_lookup_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/type_lookup_test.rb
@@ -1,6 +1,6 @@
require 'cases/helper'
-class PostgresqlTypeLookupTest < ActiveRecord::TestCase
+class PostgresqlTypeLookupTest < ActiveRecord::PostgreSQLTestCase
setup do
@connection = ActiveRecord::Base.connection
end
diff --git a/activerecord/test/cases/adapters/postgresql/utils_test.rb b/activerecord/test/cases/adapters/postgresql/utils_test.rb
index 3fdb6888d9..095c1826e5 100644
--- a/activerecord/test/cases/adapters/postgresql/utils_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/utils_test.rb
@@ -1,6 +1,7 @@
require 'cases/helper'
+require 'active_record/connection_adapters/postgresql/utils'
-class PostgreSQLUtilsTest < ActiveSupport::TestCase
+class PostgreSQLUtilsTest < ActiveRecord::PostgreSQLTestCase
Name = ActiveRecord::ConnectionAdapters::PostgreSQL::Name
include ActiveRecord::ConnectionAdapters::PostgreSQL::Utils
@@ -20,7 +21,7 @@ class PostgreSQLUtilsTest < ActiveSupport::TestCase
end
end
-class PostgreSQLNameTest < ActiveSupport::TestCase
+class PostgreSQLNameTest < ActiveRecord::PostgreSQLTestCase
Name = ActiveRecord::ConnectionAdapters::PostgreSQL::Name
test "represents itself as schema.name" do
diff --git a/activerecord/test/cases/adapters/postgresql/uuid_test.rb b/activerecord/test/cases/adapters/postgresql/uuid_test.rb
index e9379a1019..7127d69e9e 100644
--- a/activerecord/test/cases/adapters/postgresql/uuid_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/uuid_test.rb
@@ -11,7 +11,7 @@ module PostgresqlUUIDHelper
end
end
-class PostgresqlUUIDTest < ActiveRecord::TestCase
+class PostgresqlUUIDTest < ActiveRecord::PostgreSQLTestCase
include PostgresqlUUIDHelper
include SchemaDumpingHelper
@@ -135,7 +135,7 @@ class PostgresqlUUIDTest < ActiveRecord::TestCase
end
end
-class PostgresqlUUIDGenerationTest < ActiveRecord::TestCase
+class PostgresqlUUIDGenerationTest < ActiveRecord::PostgreSQLTestCase
include PostgresqlUUIDHelper
include SchemaDumpingHelper
@@ -210,7 +210,7 @@ class PostgresqlUUIDGenerationTest < ActiveRecord::TestCase
end
end
-class PostgresqlUUIDTestNilDefault < ActiveRecord::TestCase
+class PostgresqlUUIDTestNilDefault < ActiveRecord::PostgreSQLTestCase
include PostgresqlUUIDHelper
include SchemaDumpingHelper
@@ -244,7 +244,7 @@ class PostgresqlUUIDTestNilDefault < ActiveRecord::TestCase
end
end
-class PostgresqlUUIDTestInverseOf < ActiveRecord::TestCase
+class PostgresqlUUIDTestInverseOf < ActiveRecord::PostgreSQLTestCase
include PostgresqlUUIDHelper
class UuidPost < ActiveRecord::Base
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 8a8e1d3b17..0000000000
--- a/activerecord/test/cases/adapters/postgresql/view_test.rb
+++ /dev/null
@@ -1,63 +0,0 @@
-require "cases/helper"
-require "cases/view_test"
-
-class UpdateableViewTest < ActiveRecord::TestCase
- 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.supports_materialized_views?
-class MaterializedViewTest < ActiveRecord::TestCase
- 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/postgresql/xml_test.rb b/activerecord/test/cases/adapters/postgresql/xml_test.rb
index b097deb2f4..add32699fa 100644
--- a/activerecord/test/cases/adapters/postgresql/xml_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/xml_test.rb
@@ -1,7 +1,7 @@
require 'cases/helper'
require 'support/schema_dumping_helper'
-class PostgresqlXMLTest < ActiveRecord::TestCase
+class PostgresqlXMLTest < ActiveRecord::PostgreSQLTestCase
include SchemaDumpingHelper
class XmlDataType < ActiveRecord::Base
self.table_name = 'xml_data_type'
diff --git a/activerecord/test/cases/adapters/sqlite3/collation_test.rb b/activerecord/test/cases/adapters/sqlite3/collation_test.rb
new file mode 100644
index 0000000000..58a9469ce5
--- /dev/null
+++ b/activerecord/test/cases/adapters/sqlite3/collation_test.rb
@@ -0,0 +1,53 @@
+require "cases/helper"
+require 'support/schema_dumping_helper'
+
+class SQLite3CollationTest < ActiveRecord::SQLite3TestCase
+ include SchemaDumpingHelper
+
+ def setup
+ @connection = ActiveRecord::Base.connection
+ @connection.create_table :collation_table_sqlite3, force: true do |t|
+ t.string :string_nocase, collation: 'NOCASE'
+ t.text :text_rtrim, collation: 'RTRIM'
+ end
+ end
+
+ def teardown
+ @connection.drop_table :collation_table_sqlite3, if_exists: true
+ end
+
+ test "string column with collation" do
+ column = @connection.columns(:collation_table_sqlite3).find { |c| c.name == 'string_nocase' }
+ assert_equal :string, column.type
+ assert_equal 'NOCASE', column.collation
+ end
+
+ test "text column with collation" do
+ column = @connection.columns(:collation_table_sqlite3).find { |c| c.name == 'text_rtrim' }
+ assert_equal :text, column.type
+ assert_equal 'RTRIM', column.collation
+ end
+
+ test "add column with collation" do
+ @connection.add_column :collation_table_sqlite3, :title, :string, collation: 'RTRIM'
+
+ column = @connection.columns(:collation_table_sqlite3).find { |c| c.name == 'title' }
+ assert_equal :string, column.type
+ assert_equal 'RTRIM', column.collation
+ end
+
+ test "change column with collation" do
+ @connection.add_column :collation_table_sqlite3, :description, :string
+ @connection.change_column :collation_table_sqlite3, :description, :text, collation: 'RTRIM'
+
+ column = @connection.columns(:collation_table_sqlite3).find { |c| c.name == 'description' }
+ assert_equal :text, column.type
+ assert_equal 'RTRIM', column.collation
+ end
+
+ test "schema dump includes collation" do
+ output = dump_table_schema("collation_table_sqlite3")
+ assert_match %r{t.string\s+"string_nocase",\s+collation: "NOCASE"$}, output
+ assert_match %r{t.text\s+"text_rtrim",\s+collation: "RTRIM"$}, output
+ end
+end
diff --git a/activerecord/test/cases/adapters/sqlite3/copy_table_test.rb b/activerecord/test/cases/adapters/sqlite3/copy_table_test.rb
index 13b754d226..34e3b2e023 100644
--- a/activerecord/test/cases/adapters/sqlite3/copy_table_test.rb
+++ b/activerecord/test/cases/adapters/sqlite3/copy_table_test.rb
@@ -1,6 +1,6 @@
require "cases/helper"
-class CopyTableTest < ActiveRecord::TestCase
+class CopyTableTest < ActiveRecord::SQLite3TestCase
fixtures :customers
def setup
diff --git a/activerecord/test/cases/adapters/sqlite3/explain_test.rb b/activerecord/test/cases/adapters/sqlite3/explain_test.rb
index 7d66c44798..2aec322582 100644
--- a/activerecord/test/cases/adapters/sqlite3/explain_test.rb
+++ b/activerecord/test/cases/adapters/sqlite3/explain_test.rb
@@ -5,7 +5,7 @@ require 'models/computer'
module ActiveRecord
module ConnectionAdapters
class SQLite3Adapter
- class ExplainTest < ActiveRecord::TestCase
+ class ExplainTest < ActiveRecord::SQLite3TestCase
fixtures :developers
def test_explain_for_one_query
diff --git a/activerecord/test/cases/adapters/sqlite3/quoting_test.rb b/activerecord/test/cases/adapters/sqlite3/quoting_test.rb
index 243f65df98..87a892db37 100644
--- a/activerecord/test/cases/adapters/sqlite3/quoting_test.rb
+++ b/activerecord/test/cases/adapters/sqlite3/quoting_test.rb
@@ -6,7 +6,7 @@ require 'securerandom'
module ActiveRecord
module ConnectionAdapters
class SQLite3Adapter
- class QuotingTest < ActiveRecord::TestCase
+ class QuotingTest < ActiveRecord::SQLite3TestCase
def setup
@conn = Base.sqlite3_connection :database => ':memory:',
:adapter => 'sqlite3',
diff --git a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb
index 27f4ba8eb6..77d99bc116 100644
--- a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb
+++ b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb
@@ -5,7 +5,7 @@ require 'support/ddl_helper'
module ActiveRecord
module ConnectionAdapters
- class SQLite3AdapterTest < ActiveRecord::TestCase
+ class SQLite3AdapterTest < ActiveRecord::SQLite3TestCase
include DdlHelper
self.use_transactional_tests = false
@@ -421,17 +421,20 @@ module ActiveRecord
end
def test_statement_closed
- db = SQLite3::Database.new(ActiveRecord::Base.
+ db = ::SQLite3::Database.new(ActiveRecord::Base.
configurations['arunit']['database'])
- statement = SQLite3::Statement.new(db,
+ 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/sqlite3_create_folder_test.rb b/activerecord/test/cases/adapters/sqlite3/sqlite3_create_folder_test.rb
index deedf67c8e..887dcfc96c 100644
--- a/activerecord/test/cases/adapters/sqlite3/sqlite3_create_folder_test.rb
+++ b/activerecord/test/cases/adapters/sqlite3/sqlite3_create_folder_test.rb
@@ -3,7 +3,7 @@ require 'models/owner'
module ActiveRecord
module ConnectionAdapters
- class SQLite3CreateFolder < ActiveRecord::TestCase
+ class SQLite3CreateFolder < ActiveRecord::SQLite3TestCase
def test_sqlite_creates_directory
Dir.mktmpdir do |dir|
dir = Pathname.new(dir)
diff --git a/activerecord/test/cases/adapters/sqlite3/statement_pool_test.rb b/activerecord/test/cases/adapters/sqlite3/statement_pool_test.rb
index fd0044ac05..ef324183a7 100644
--- a/activerecord/test/cases/adapters/sqlite3/statement_pool_test.rb
+++ b/activerecord/test/cases/adapters/sqlite3/statement_pool_test.rb
@@ -2,7 +2,7 @@ require 'cases/helper'
module ActiveRecord::ConnectionAdapters
class SQLite3Adapter
- class StatementPoolTest < ActiveRecord::TestCase
+ class StatementPoolTest < ActiveRecord::SQLite3TestCase
if Process.respond_to?(:fork)
def test_cache_is_per_pid
@@ -22,4 +22,3 @@ module ActiveRecord::ConnectionAdapters
end
end
end
-
diff --git a/activerecord/test/cases/associations/belongs_to_associations_test.rb b/activerecord/test/cases/associations/belongs_to_associations_test.rb
index ba90c61d65..02b67f901f 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'
@@ -19,6 +18,11 @@ require 'models/invoice'
require 'models/line_item'
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,
@@ -31,6 +35,10 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
assert_equal companies(:first_firm).name, firm.name
end
+ def test_missing_attribute_error_is_raised_when_no_foreign_key_attribute
+ assert_raises(ActiveModel::MissingAttributeError) { Client.select(:id).first.firm }
+ end
+
def test_belongs_to_does_not_use_order_by
ActiveRecord::SQLCounter.clear_log
Client.find(3).firm
@@ -85,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
@@ -102,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
@@ -147,6 +155,30 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
assert_raise(ActiveRecord::AssociationTypeMismatch) { Account.find(1).firm = Project.find(1) }
end
+ def test_raises_type_mismatch_with_namespaced_class
+ assert_nil defined?(Region), "This test requires that there is no top-level Region class"
+
+ ActiveRecord::Base.connection.instance_eval do
+ create_table(:admin_regions) { |t| t.string :name }
+ add_column :admin_users, :region_id, :integer
+ end
+ Admin.const_set "RegionalUser", Class.new(Admin::User) { belongs_to(:region) }
+ Admin.const_set "Region", Class.new(ActiveRecord::Base)
+
+ e = assert_raise(ActiveRecord::AssociationTypeMismatch) {
+ Admin::RegionalUser.new(region: 'wrong value')
+ }
+ assert_match(/^Region\([^)]+\) expected, got String\([^)]+\)$/, e.message)
+ ensure
+ Admin.send :remove_const, "Region" if Admin.const_defined?("Region")
+ Admin.send :remove_const, "RegionalUser" if Admin.const_defined?("RegionalUser")
+
+ ActiveRecord::Base.connection.instance_eval do
+ remove_column :admin_users, :region_id if column_exists?(:admin_users, :region_id)
+ drop_table :admin_regions, if_exists: true
+ end
+ end
+
def test_natural_assignment
apple = Firm.create("name" => "Apple")
citibank = Account.create("credit_limit" => 10)
@@ -263,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
@@ -271,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
@@ -288,9 +322,13 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
def test_polymorphic_association_class
sponsor = Sponsor.new
assert_nil sponsor.association(:sponsorable).send(:klass)
+ 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)
+ sponsor.association(:sponsorable).reload
+ assert_nil sponsor.sponsorable
sponsor.sponsorable = Member.new :name => "Bert"
assert_equal Member, sponsor.association(:sponsorable).send(:klass)
@@ -311,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"
@@ -525,7 +579,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
@@ -537,7 +592,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
@@ -1032,6 +1088,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/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb
index 0ecf2ddfd1..ddfb856a05 100644
--- a/activerecord/test/cases/associations/eager_test.rb
+++ b/activerecord/test/cases/associations/eager_test.rb
@@ -108,53 +108,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
@@ -1167,7 +1171,7 @@ class EagerAssociationTest < ActiveRecord::TestCase
assert_no_queries { assert client.accounts.empty? }
end
- def test_preloading_has_many_through_with_uniq
+ def test_preloading_has_many_through_with_distinct
mary = Author.includes(:unique_categorized_posts).where(:id => authors(:mary).id).first
assert_equal 1, mary.unique_categorized_posts.length
assert_equal 1, mary.unique_categorized_post_ids.length
@@ -1325,6 +1329,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 aea9207bfe..d160c30375 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'
@@ -83,6 +85,16 @@ class DeveloperWithSymbolClassName < Developer
has_and_belongs_to_many :projects, class_name: :ProjectWithSymbolsForKeys
end
+class DeveloperWithExtendOption < Developer
+ module NamedExtension
+ def category
+ 'sns'
+ end
+ end
+
+ has_and_belongs_to_many :projects, extend: NamedExtension
+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
@@ -147,8 +159,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
@@ -166,9 +178,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
@@ -182,9 +194,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
@@ -193,7 +205,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
@@ -202,7 +214,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
@@ -217,7 +229,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
@@ -234,7 +246,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
assert_equal developers, new_project.developers
end
- def test_habtm_unique_order_preserved
+ def test_habtm_distinct_order_preserved
assert_equal developers(:poor_jamis, :jamis, :david), projects(:active_record).non_unique_developers
assert_equal developers(:poor_jamis, :jamis, :david), projects(:active_record).developers
end
@@ -339,7 +351,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
assert_equal 'Yet Another Testing Title', another_post.title
end
- def test_uniq_after_the_fact
+ def test_distinct_after_the_fact
dev = developers(:jamis)
dev.projects << projects(:active_record)
dev.projects << projects(:active_record)
@@ -348,13 +360,13 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
assert_equal 1, dev.projects.distinct.size
end
- def test_uniq_before_the_fact
+ def test_distinct_before_the_fact
projects(:active_record).developers << developers(:jamis)
projects(:active_record).developers << developers(:david)
assert_equal 3, projects(:active_record, :reload).developers.size
end
- def test_uniq_option_prevents_duplicate_push
+ def test_distinct_option_prevents_duplicate_push
project = projects(:active_record)
project.developers << developers(:jamis)
project.developers << developers(:david)
@@ -365,7 +377,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
assert_equal 3, project.developers.size
end
- def test_uniq_when_association_already_loaded
+ def test_distinct_when_association_already_loaded
project = projects(:active_record)
project.developers << [ developers(:jamis), developers(:david), developers(:jamis), developers(:david) ]
assert_equal 3, Project.includes(:developers).find(project.id).developers.size
@@ -381,8 +393,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
@@ -390,7 +402,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
@@ -398,7 +410,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
@@ -424,7 +436,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
@@ -440,7 +452,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
@@ -456,7 +468,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
@@ -472,11 +484,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
@@ -577,6 +589,11 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
assert_equal developers(:poor_jamis), projects(:active_record).developers.where("salary < 10000").first
end
+ def test_association_with_extend_option
+ eponine = DeveloperWithExtendOption.create(name: 'Eponine')
+ assert_equal 'sns', eponine.projects.category
+ end
+
def test_replace_with_less
david = developers(:david)
david.projects = [projects(:action_controller)]
@@ -639,7 +656,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
@@ -711,7 +728,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
@@ -779,9 +796,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
@@ -902,4 +920,20 @@ 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
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..f487065d9d 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'
@@ -704,7 +706,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 +761,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 +773,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 +905,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 +923,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 +934,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.treasures.instance_variable_get('@association').send(: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 +1079,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 +1100,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 +1114,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 +1134,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 +1170,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 +1203,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 +1252,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 +1278,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 +1286,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 +1324,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 +1335,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 +1346,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 +1359,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 +1370,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 +1447,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')
@@ -1518,7 +1559,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 +1575,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 +1629,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
@@ -2252,4 +2293,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 9734ea2217..cf730e4fe7 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
@@ -960,7 +962,7 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
assert_equal 1, category.categorizations.where(:special => true).count
end
- def test_joining_has_many_through_with_uniq
+ def test_joining_has_many_through_with_distinct
mary = Author.joins(:unique_categorized_posts).where(:id => authors(:mary).id).first
assert_equal 1, mary.unique_categorized_posts.length
assert_equal 1, mary.unique_categorized_post_ids.length
@@ -1040,14 +1042,6 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
end
end
- def test_save_should_not_raise_exception_when_join_record_has_errors
- repair_validations(Categorization) do
- Categorization.validate { |r| r.errors[:base] << 'Invalid Categorization' }
- c = Category.create(:name => 'Fishing', :authors => [Author.first])
- c.save
- end
- end
-
def test_assign_array_to_new_record_builds_join_records
c = Category.new(:name => 'Fishing', :authors => [Author.first])
assert_equal 1, c.categorizations.size
@@ -1072,11 +1066,11 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
end
end
- def test_create_bang_returns_falsy_when_join_record_has_errors
+ def test_save_returns_falsy_when_join_record_has_errors
repair_validations(Categorization) do
Categorization.validate { |r| r.errors[:base] << 'Invalid Categorization' }
c = Category.new(:name => 'Fishing', :authors => [Author.first])
- assert !c.save
+ assert_not c.save
end
end
@@ -1166,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..d46e7ad235 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)
@@ -332,7 +359,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 +635,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/join_model_test.rb b/activerecord/test/cases/associations/join_model_test.rb
index 213be50e67..f6dddaf5b4 100644
--- a/activerecord/test/cases/associations/join_model_test.rb
+++ b/activerecord/test/cases/associations/join_model_test.rb
@@ -35,12 +35,12 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
assert categories(:sti_test).authors.include?(authors(:mary))
end
- def test_has_many_uniq_through_join_model
+ def test_has_many_distinct_through_join_model
assert_equal 2, authors(:mary).categorized_posts.size
assert_equal 1, authors(:mary).unique_categorized_posts.size
end
- def test_has_many_uniq_through_count
+ def test_has_many_distinct_through_count
author = authors(:mary)
assert !authors(:mary).unique_categorized_posts.loaded?
assert_queries(1) { assert_equal 1, author.unique_categorized_posts.count }
@@ -49,7 +49,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
assert !authors(:mary).unique_categorized_posts.loaded?
end
- def test_has_many_uniq_through_find
+ def test_has_many_distinct_through_find
assert_equal 1, authors(:mary).unique_categorized_posts.to_a.size
end
@@ -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
@@ -625,7 +627,7 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase
assert_equal [comments(:does_it_hurt)], authors(:david).special_post_comments
end
- def test_uniq_has_many_through_should_retain_order
+ def test_distinct_has_many_through_should_retain_order
comment_ids = authors(:david).comments.map(&:id)
assert_equal comment_ids.sort, authors(:david).ordered_uniq_comments.map(&:id)
assert_equal comment_ids.sort.reverse, authors(:david).ordered_uniq_comments_desc.map(&:id)
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..e2608e3670 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
diff --git a/activerecord/test/cases/attribute_test.rb b/activerecord/test/cases/attribute_test.rb
index aa419c7a67..0ec368f51d 100644
--- a/activerecord/test/cases/attribute_test.rb
+++ b/activerecord/test/cases/attribute_test.rb
@@ -1,5 +1,4 @@
require 'cases/helper'
-require 'minitest/mock'
module ActiveRecord
class AttributeTest < ActiveRecord::TestCase
diff --git a/activerecord/test/cases/attributes_test.rb b/activerecord/test/cases/attributes_test.rb
index 927d7950a5..264b275181 100644
--- a/activerecord/test/cases/attributes_test.rb
+++ b/activerecord/test/cases/attributes_test.rb
@@ -125,8 +125,24 @@ module ActiveRecord
assert_equal "from user", model.wibble
end
+ test "procs for default values" do
+ klass = Class.new(OverloadedType) do
+ @@counter = 0
+ attribute :counter, :integer, default: -> { @@counter += 1 }
+ end
+
+ assert_equal 1, klass.new.counter
+ assert_equal 2, klass.new.counter
+ end
+
+ test "user provided defaults are persisted even if unchanged" do
+ model = OverloadedType.create!
+
+ assert_equal "the overloaded default", model.reload.string_with_default
+ 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
@@ -136,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
@@ -151,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..37ec3f1106 100644
--- a/activerecord/test/cases/autosave_association_test.rb
+++ b/activerecord/test/cases/autosave_association_test.rb
@@ -67,6 +67,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 +157,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 +171,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 +258,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 +272,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
@@ -456,7 +468,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 +493,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 +522,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 +531,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 +542,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 +555,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 +1154,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 4306738670..0edb8a8901 100644
--- a/activerecord/test/cases/base_test.rb
+++ b/activerecord/test/cases/base_test.rb
@@ -1,7 +1,6 @@
# -*- coding: utf-8 -*-
require "cases/helper"
-require 'active_support/concurrency/latch'
require 'models/post'
require 'models/author'
require 'models/topic'
@@ -29,6 +28,7 @@ require 'models/bird'
require 'models/car'
require 'models/bulb'
require 'rexml/document'
+require 'concurrent/atomics'
class FirstAbstractClass < ActiveRecord::Base
self.abstract_class = true
@@ -946,6 +946,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
@@ -1411,15 +1439,13 @@ class BasicsTest < ActiveRecord::TestCase
end
def test_uniq_delegates_to_scoped
- scope = stub
- Bird.stubs(:all).returns(mock(:uniq => scope))
- assert_equal scope, Bird.uniq
+ assert_deprecated do
+ assert_equal Bird.all.distinct, Bird.uniq
+ end
end
def test_distinct_delegates_to_scoped
- scope = stub
- Bird.stubs(:all).returns(mock(:distinct => scope))
- assert_equal scope, Bird.distinct
+ assert_equal Bird.all.distinct, Bird.distinct
end
def test_table_name_with_2_abstract_subclasses
@@ -1508,20 +1534,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
diff --git a/activerecord/test/cases/batches_test.rb b/activerecord/test/cases/batches_test.rb
index 0791dde1f2..9cb70ee239 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
@@ -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/calculations_test.rb b/activerecord/test/cases/calculations_test.rb
index 8fc996ee74..aa10817527 100644
--- a/activerecord/test/cases/calculations_test.rb
+++ b/activerecord/test/cases/calculations_test.rb
@@ -359,7 +359,10 @@ class CalculationsTest < ActiveRecord::TestCase
def test_count_with_distinct
assert_equal 4, Account.select(:credit_limit).distinct.count
- assert_equal 4, Account.select(:credit_limit).uniq.count
+
+ assert_deprecated do
+ assert_equal 4, Account.select(:credit_limit).uniq.count
+ end
end
def test_count_with_aliased_attribute
@@ -504,8 +507,8 @@ class CalculationsTest < ActiveRecord::TestCase
assert_equal [ topic.written_on ], relation.pluck(:written_on)
end
- def test_pluck_and_uniq
- assert_equal [50, 53, 55, 60], Account.order(:credit_limit).uniq.pluck(:credit_limit)
+ def test_pluck_and_distinct
+ assert_equal [50, 53, 55, 60], Account.order(:credit_limit).distinct.pluck(:credit_limit)
end
def test_pluck_in_relation
@@ -629,6 +632,27 @@ class CalculationsTest < ActiveRecord::TestCase
assert_equal [part.id], ShipPart.joins(:trinkets).pluck(:id)
end
+ def test_pluck_loaded_relation
+ companies = Company.order(:id).limit(3).load
+ assert_no_queries do
+ assert_equal ['37signals', 'Summit', 'Microsoft'], companies.pluck(:name)
+ end
+ end
+
+ def test_pluck_loaded_relation_multiple_columns
+ companies = Company.order(:id).limit(3).load
+ assert_no_queries do
+ assert_equal [[1, '37signals'], [2, 'Summit'], [3, 'Microsoft']], companies.pluck(:id, :name)
+ end
+ end
+
+ def test_pluck_loaded_relation_sql_fragment
+ companies = Company.order(:name).limit(3).load
+ assert_queries 1 do
+ assert_equal ['37signals', 'Apex', 'Ex Nihilo'], companies.pluck('DISTINCT name')
+ end
+ end
+
def test_grouped_calculation_with_polymorphic_relation
part = ShipPart.create!(name: "has trinket")
part.trinkets.create!
@@ -647,4 +671,14 @@ class CalculationsTest < ActiveRecord::TestCase
developer.ratings.includes(comment: :post).where(posts: { id: 1 }).count
end
end
+
+ def test_sum_uses_enumerable_version_when_block_is_given
+ block_called = false
+ relation = Client.all.load
+
+ assert_no_queries do
+ assert_equal 0, relation.sum { block_called = true; 0 }
+ end
+ assert block_called
+ 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..724234d7f4
--- /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(:nsec)
+ 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/adapter_leasing_test.rb b/activerecord/test/cases/connection_adapters/adapter_leasing_test.rb
index 662e19f35e..580568c8ac 100644
--- a/activerecord/test/cases/connection_adapters/adapter_leasing_test.rb
+++ b/activerecord/test/cases/connection_adapters/adapter_leasing_test.rb
@@ -6,7 +6,7 @@ module ActiveRecord
class Pool < ConnectionPool
def insert_connection_for_test!(c)
synchronize do
- @connections << c
+ adopt_connection(c)
@available.add c
end
end
@@ -24,7 +24,9 @@ module ActiveRecord
def test_lease_twice
assert @adapter.lease, 'should lease adapter'
- assert_not @adapter.lease, 'should not lease adapter'
+ assert_raises(ActiveRecordError) do
+ @adapter.lease
+ end
end
def test_expire_mutates_in_use
diff --git a/activerecord/test/cases/connection_adapters/connection_handler_test.rb b/activerecord/test/cases/connection_adapters/connection_handler_test.rb
index b72f8ca88c..9b1865e8bb 100644
--- a/activerecord/test/cases/connection_adapters/connection_handler_test.rb
+++ b/activerecord/test/cases/connection_adapters/connection_handler_test.rb
@@ -46,6 +46,52 @@ module ActiveRecord
def test_connection_pools
assert_equal([@pool], @handler.connection_pools)
end
+
+ if Process.respond_to?(:fork)
+ def test_connection_pool_per_pid
+ object_id = ActiveRecord::Base.connection.object_id
+
+ rd, wr = IO.pipe
+ rd.binmode
+ wr.binmode
+
+ pid = fork {
+ rd.close
+ wr.write Marshal.dump ActiveRecord::Base.connection.object_id
+ wr.close
+ exit!
+ }
+
+ wr.close
+
+ Process.waitpid pid
+ assert_not_equal object_id, Marshal.load(rd.read)
+ rd.close
+ end
+
+ def test_retrieve_connection_pool_copies_schema_cache_from_ancestor_pool
+ @pool.schema_cache = @pool.connection.schema_cache
+ @pool.schema_cache.add('posts')
+
+ rd, wr = IO.pipe
+ rd.binmode
+ wr.binmode
+
+ pid = fork {
+ rd.close
+ pool = @handler.retrieve_connection_pool(@klass)
+ wr.write Marshal.dump pool.schema_cache.size
+ wr.close
+ exit!
+ }
+
+ wr.close
+
+ Process.waitpid pid
+ assert_equal @pool.schema_cache.size, Marshal.load(rd.read)
+ rd.close
+ end
+ end
end
end
end
diff --git a/activerecord/test/cases/connection_adapters/type_lookup_test.rb b/activerecord/test/cases/connection_adapters/type_lookup_test.rb
index 05c57985a1..7566863653 100644
--- a/activerecord/test/cases/connection_adapters/type_lookup_test.rb
+++ b/activerecord/test/cases/connection_adapters/type_lookup_test.rb
@@ -81,7 +81,11 @@ module ActiveRecord
def test_bigint_limit
cast_type = @connection.type_map.lookup("bigint")
- assert_equal 8, cast_type.limit
+ if current_adapter?(:OracleAdapter)
+ assert_equal 19, cast_type.limit
+ else
+ assert_equal 8, cast_type.limit
+ end
end
def test_decimal_without_scale
diff --git a/activerecord/test/cases/connection_management_test.rb b/activerecord/test/cases/connection_management_test.rb
index bab624b78a..dff6ea0fb0 100644
--- a/activerecord/test/cases/connection_management_test.rb
+++ b/activerecord/test/cases/connection_management_test.rb
@@ -26,29 +26,6 @@ module ActiveRecord
assert ActiveRecord::Base.connection_handler.active_connections?
end
- if Process.respond_to?(:fork)
- def test_connection_pool_per_pid
- object_id = ActiveRecord::Base.connection.object_id
-
- rd, wr = IO.pipe
- rd.binmode
- wr.binmode
-
- pid = fork {
- rd.close
- wr.write Marshal.dump ActiveRecord::Base.connection.object_id
- wr.close
- exit!
- }
-
- wr.close
-
- Process.waitpid pid
- assert_not_equal object_id, Marshal.load(rd.read)
- rd.close
- end
- end
-
def test_app_delegation
manager = ConnectionManagement.new(@app)
diff --git a/activerecord/test/cases/connection_pool_test.rb b/activerecord/test/cases/connection_pool_test.rb
index f5928814a3..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
@@ -100,7 +100,7 @@ module ActiveRecord
t = Thread.new { @pool.checkout }
# make sure our thread is in the timeout section
- Thread.pass until t.status == "sleep"
+ Thread.pass until @pool.num_waiting_in_queue == 1
connection = cs.first
connection.close
@@ -112,7 +112,7 @@ module ActiveRecord
t = Thread.new { @pool.checkout }
# make sure our thread is in the timeout section
- Thread.pass until t.status == "sleep"
+ Thread.pass until @pool.num_waiting_in_queue == 1
connection = cs.first
@pool.remove connection
@@ -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
@@ -234,7 +234,7 @@ module ActiveRecord
mutex.synchronize { errors << e }
end
}
- Thread.pass until t.status == "sleep"
+ Thread.pass until @pool.num_waiting_in_queue == i
t
end
@@ -271,7 +271,7 @@ module ActiveRecord
mutex.synchronize { errors << e }
end
}
- Thread.pass until t.status == "sleep"
+ Thread.pass until @pool.num_waiting_in_queue == i
t
end
@@ -356,6 +356,170 @@ module ActiveRecord
pool.checkin connection
end
+
+ def test_concurrent_connection_establishment
+ assert_operator @pool.connections.size, :<=, 1
+
+ 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.count_down
+ all_go.wait
+ super()
+ end
+ end
+
+ connecting_threads = []
+ @pool.size.times do
+ connecting_threads << Thread.new { @pool.checkout }
+ end
+
+ begin
+ Timeout.timeout(5) do
+ # 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.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.count_down
+ connecting_threads.map(&:join)
+ end
+ end
+
+ def test_non_bang_disconnect_and_clear_reloadable_connections_throw_exception_if_threads_dont_return_their_conns
+ @pool.checkout_timeout = 0.001 # no need to delay test suite by waiting the whole full default timeout
+ [:disconnect, :clear_reloadable_connections].each do |group_action_method|
+ @pool.with_connection do |connection|
+ assert_raises(ExclusiveConnectionTimeoutError) do
+ Thread.new { @pool.send(group_action_method) }.join
+ end
+ end
+ end
+ end
+
+ def test_disconnect_and_clear_reloadable_connections_attempt_to_wait_for_threads_to_return_their_conns
+ [:disconnect, :disconnect!, :clear_reloadable_connections, :clear_reloadable_connections!].each do |group_action_method|
+ begin
+ thread = timed_join_result = nil
+ @pool.with_connection do |connection|
+ thread = Thread.new { @pool.send(group_action_method) }
+
+ # give the other `thread` some time to get stuck in `group_action_method`
+ timed_join_result = thread.join(0.3)
+ # thread.join # => `nil` means the other thread hasn't finished running and is still waiting for us to
+ # release our connection
+ assert_nil timed_join_result
+
+ # assert that since this is within default timeout our connection hasn't been forcefully taken away from us
+ assert @pool.active_connection?
+ end
+ ensure
+ thread.join if thread && !timed_join_result # clean up the other thread
+ end
+ end
+ end
+
+ def test_bang_versions_of_disconnect_and_clear_reloadable_connections_if_unable_to_aquire_all_connections_proceed_anyway
+ @pool.checkout_timeout = 0.001 # no need to delay test suite by waiting the whole full default timeout
+ [:disconnect!, :clear_reloadable_connections!].each do |group_action_method|
+ @pool.with_connection do |connection|
+ Thread.new { @pool.send(group_action_method) }.join
+ # assert connection has been forcefully taken away from us
+ assert_not @pool.active_connection?
+ end
+ end
+ end
+
+ def test_disconnect_and_clear_reloadable_connections_are_able_to_preempt_other_waiting_threads
+ 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 = 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.wait }
+ end
+
+ # wait for first_thread to get in queue
+ Thread.pass until pool.num_waiting_in_queue == 1
+
+ # create a different, later thread, that will attempt to do a "group action",
+ # but because of the group action semantics it should be able to preempt the
+ # first_thread when a connection is made available
+ second_thread = Thread.new do
+ pool.send(group_action_method)
+ second_thread_done.count_down
+ end
+
+ # wait for second_thread to get in queue
+ Thread.pass until pool.num_waiting_in_queue == 2
+
+ # return the only available connection
+ pool.checkin(conn)
+
+ # if the second_thread is not able to preempt the first_thread,
+ # they will temporarily (until either of them timeouts with ConnectionTimeoutError)
+ # deadlock and a join(2) timeout will be reached
+ failed = true unless second_thread.join(2)
+
+ #--- post test clean up start
+ 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
+ if (group_action_method == :disconnect || group_action_method == :disconnect!) && pool.num_waiting_in_queue > 0
+ pool.with_connection {} # create a new connection in case there are threads still stuck in a queue
+ end
+
+ first_thread.join
+ second_thread.join
+ #--- post test clean up end
+
+ flunk "#{group_action_method} is not able to preempt other waiting threads" if failed
+ end
+ end
+ end
+
+ def test_clear_reloadable_connections_creates_new_connections_for_waiting_threads_if_necessary
+ with_single_connection_pool do |pool|
+ conn = pool.connection # drain the only available connection
+ def conn.requires_reloading? # make sure it gets removed from the pool by clear_reloadable_connections
+ true
+ end
+
+ stuck_thread = Thread.new do
+ pool.with_connection {}
+ end
+
+ # wait for stuck_thread to get in queue
+ Thread.pass until pool.num_waiting_in_queue == 1
+
+ pool.clear_reloadable_connections
+
+ unless stuck_thread.join(2)
+ flunk 'clear_reloadable_connections must not let other connection waiting threads get stuck in queue'
+ end
+
+ assert_equal 0, pool.num_waiting_in_queue
+ end
+ end
+
+ private
+ def with_single_connection_pool
+ one_conn_spec = ActiveRecord::Base.connection_pool.spec.dup
+ one_conn_spec.config[:pool] = 1 # this is safe to do, because .dupped ConnectionSpecification also auto-dups its config
+ yield(pool = ConnectionPool.new(one_conn_spec))
+ ensure
+ pool.disconnect! if pool
+ end
end
end
end
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 3a7cc572e6..f5aaf22e13 100644
--- a/activerecord/test/cases/dirty_test.rb
+++ b/activerecord/test/cases/dirty_test.rb
@@ -623,32 +623,6 @@ class DirtyTest < ActiveRecord::TestCase
end
end
- test "defaults with type that implements `serialize`" do
- type = Class.new(ActiveRecord::Type::Value) do
- def cast(value)
- value.to_i
- end
-
- def serialize(value)
- value.to_s
- end
- end
-
- model_class = Class.new(ActiveRecord::Base) do
- self.table_name = 'numeric_data'
- attribute :foo, type.new, default: 1
- end
-
- model = model_class.new
- assert_not model.foo_changed?
-
- model = model_class.new(foo: 1)
- assert_not model.foo_changed?
-
- model = model_class.new(foo: '1')
- assert_not model.foo_changed?
- end
-
test "in place mutation detection" do
pirate = Pirate.create!(catchphrase: "arrrr")
pirate.catchphrase << " matey!"
@@ -729,6 +703,22 @@ class DirtyTest < ActiveRecord::TestCase
assert pirate.catchphrase_changed?(from: "arrrr", to: "arrrr matey!")
end
+ test "getters with side effects are allowed" do
+ klass = Class.new(Pirate) do
+ def catchphrase
+ if super.blank?
+ update_attribute(:catchphrase, "arr") # what could possibly go wrong?
+ end
+ super
+ end
+ end
+
+ pirate = klass.create!(catchphrase: "lol")
+ pirate.update_attribute(:catchphrase, nil)
+
+ assert_equal "arr", pirate.catchphrase
+ end
+
private
def with_partial_writes(klass, on = true)
old = klass.partial_writes?
diff --git a/activerecord/test/cases/enum_test.rb b/activerecord/test/cases/enum_test.rb
index eea184e530..7c930de97b 100644
--- a/activerecord/test/cases/enum_test.rb
+++ b/activerecord/test/cases/enum_test.rb
@@ -9,69 +9,83 @@ class EnumTest < ActiveRecord::TestCase
end
test "query state by predicate" do
- assert @book.proposed?
+ assert @book.published?
assert_not @book.written?
- assert_not @book.published?
+ assert_not @book.proposed?
- assert @book.unread?
+ assert @book.read?
+ assert @book.in_english?
+ assert @book.author_visibility_visible?
+ assert @book.illustrator_visibility_visible?
+ assert @book.with_medium_font_size?
end
test "query state with strings" do
- assert_equal "proposed", @book.status
- assert_equal "unread", @book.read_status
+ assert_equal "published", @book.status
+ assert_equal "read", @book.read_status
+ assert_equal "english", @book.language
+ assert_equal "visible", @book.author_visibility
+ assert_equal "visible", @book.illustrator_visibility
end
test "find via scope" do
- assert_equal @book, Book.proposed.first
- assert_equal @book, Book.unread.first
+ assert_equal @book, Book.published.first
+ assert_equal @book, Book.read.first
+ assert_equal @book, Book.in_english.first
+ assert_equal @book, Book.author_visibility_visible.first
+ assert_equal @book, Book.illustrator_visibility_visible.first
end
test "find via where with values" do
- proposed, written = Book.statuses[:proposed], Book.statuses[:written]
+ published, written = Book.statuses[:published], Book.statuses[:written]
- assert_equal @book, Book.where(status: proposed).first
- refute_equal @book, Book.where(status: written).first
- assert_equal @book, Book.where(status: [proposed]).first
- refute_equal @book, Book.where(status: [written]).first
- refute_equal @book, Book.where("status <> ?", proposed).first
+ assert_equal @book, Book.where(status: published).first
+ assert_not_equal @book, Book.where(status: written).first
+ assert_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: :proposed).first
- refute_equal @book, Book.where(status: :written).first
- assert_equal @book, Book.where(status: [:proposed]).first
- refute_equal @book, Book.where(status: [:written]).first
- refute_equal @book, Book.where.not(status: :proposed).first
+ assert_equal @book, Book.where(status: :published).first
+ assert_not_equal @book, Book.where(status: :written).first
+ assert_equal @book, Book.where(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: "proposed").first
- refute_equal @book, Book.where(status: "written").first
- assert_equal @book, Book.where(status: ["proposed"]).first
- refute_equal @book, Book.where(status: ["written"]).first
- refute_equal @book, Book.where.not(status: "proposed").first
+ assert_equal @book, Book.where(status: "published").first
+ assert_not_equal @book, Book.where(status: "written").first
+ assert_equal @book, Book.where(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
@book.written!
assert @book.written?
+ @book.in_english!
+ assert @book.in_english?
+ @book.author_visibility_visible!
+ assert @book.author_visibility_visible?
end
test "update by setter" do
@@ -96,42 +110,61 @@ class EnumTest < ActiveRecord::TestCase
test "enum changed attributes" do
old_status = @book.status
- @book.status = :published
+ old_language = @book.language
+ @book.status = :proposed
+ @book.language = :spanish
assert_equal old_status, @book.changed_attributes[:status]
+ assert_equal old_language, @book.changed_attributes[:language]
end
test "enum changes" do
old_status = @book.status
- @book.status = :published
- assert_equal [old_status, 'published'], @book.changes[:status]
+ old_language = @book.language
+ @book.status = :proposed
+ @book.language = :spanish
+ assert_equal [old_status, 'proposed'], @book.changes[:status]
+ assert_equal [old_language, 'spanish'], @book.changes[:language]
end
test "enum attribute was" do
old_status = @book.status
+ old_language = @book.language
@book.status = :published
+ @book.language = :spanish
assert_equal old_status, @book.attribute_was(:status)
+ assert_equal old_language, @book.attribute_was(:language)
end
test "enum attribute changed" do
- @book.status = :published
+ @book.status = :proposed
+ @book.language = :french
assert @book.attribute_changed?(:status)
+ assert @book.attribute_changed?(:language)
end
test "enum attribute changed to" do
- @book.status = :published
- assert @book.attribute_changed?(:status, to: 'published')
+ @book.status = :proposed
+ @book.language = :french
+ assert @book.attribute_changed?(:status, to: 'proposed')
+ assert @book.attribute_changed?(:language, to: 'french')
end
test "enum attribute changed from" do
old_status = @book.status
- @book.status = :published
+ old_language = @book.language
+ @book.status = :proposed
+ @book.language = :french
assert @book.attribute_changed?(:status, from: old_status)
+ assert @book.attribute_changed?(:language, from: old_language)
end
test "enum attribute changed from old status to new status" do
old_status = @book.status
- @book.status = :published
- assert @book.attribute_changed?(:status, from: old_status, to: 'published')
+ old_language = @book.language
+ @book.status = :proposed
+ @book.language = :french
+ assert @book.attribute_changed?(:status, from: old_status, to: 'proposed')
+ assert @book.attribute_changed?(:language, from: old_language, to: 'french')
end
test "enum didn't change" do
@@ -141,7 +174,7 @@ class EnumTest < ActiveRecord::TestCase
end
test "persist changes that are dirty" do
- @book.status = :published
+ @book.status = :proposed
assert @book.attribute_changed?(:status)
@book.status = :written
assert @book.attribute_changed?(:status)
@@ -149,7 +182,7 @@ class EnumTest < ActiveRecord::TestCase
test "reverted changes that are not dirty" do
old_status = @book.status
- @book.status = :published
+ @book.status = :proposed
assert @book.attribute_changed?(:status)
@book.status = old_status
assert_not @book.attribute_changed?(:status)
@@ -201,18 +234,22 @@ class EnumTest < ActiveRecord::TestCase
test "building new objects with enum scopes" do
assert Book.written.build.written?
assert Book.read.build.read?
+ assert Book.in_spanish.build.in_spanish?
+ assert Book.illustrator_visibility_invisible.build.illustrator_visibility_invisible?
end
test "creating new objects with enum scopes" do
assert Book.written.create.written?
assert Book.read.create.read?
+ assert Book.in_spanish.create.in_spanish?
+ assert Book.illustrator_visibility_invisible.create.illustrator_visibility_invisible?
end
test "_before_type_cast returns the enum label (required for form fields)" do
if @book.status_came_from_user?
- assert_equal "proposed", @book.status_before_type_cast
+ assert_equal "published", @book.status_before_type_cast
else
- assert_equal "proposed", @book.status
+ assert_equal "published", @book.status
end
end
@@ -355,4 +392,23 @@ class EnumTest < ActiveRecord::TestCase
book2 = klass.single.create!
assert book2.single?
end
+
+ test "query state by predicate with prefix" do
+ assert @book.author_visibility_visible?
+ assert_not @book.author_visibility_invisible?
+ assert @book.illustrator_visibility_visible?
+ assert_not @book.illustrator_visibility_invisible?
+ end
+
+ test "query state by predicate with custom prefix" do
+ assert @book.in_english?
+ 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_subscriber_test.rb b/activerecord/test/cases/explain_subscriber_test.rb
index 8de2ddb10d..2dee8a26a5 100644
--- a/activerecord/test/cases/explain_subscriber_test.rb
+++ b/activerecord/test/cases/explain_subscriber_test.rb
@@ -48,6 +48,11 @@ if ActiveRecord::Base.connection.supports_explain?
assert queries.empty?
end
+ def test_collects_cte_queries
+ SUBSCRIBER.finish(nil, nil, name: 'SQL', sql: 'with s as (values(3)) select 1 from s')
+ assert_equal 1, queries.size
+ end
+
teardown do
ActiveRecord::ExplainRegistry.reset
end
diff --git a/activerecord/test/cases/fixtures_test.rb b/activerecord/test/cases/fixtures_test.rb
index 97ba178b4d..64759160dc 100644
--- a/activerecord/test/cases/fixtures_test.rb
+++ b/activerecord/test/cases/fixtures_test.rb
@@ -11,11 +11,11 @@ require 'models/company'
require 'models/computer'
require 'models/course'
require 'models/developer'
-require 'models/computer'
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'
@@ -216,6 +216,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")
@@ -251,18 +258,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
@@ -691,7 +699,7 @@ end
class FoxyFixturesTest < ActiveRecord::TestCase
fixtures :parrots, :parrots_pirates, :pirates, :treasures, :mateys, :ships, :computers,
- :developers, :"admin/accounts", :"admin/users", :live_parrots, :dead_parrots
+ :developers, :"admin/accounts", :"admin/users", :live_parrots, :dead_parrots, :books
if ActiveRecord::Base.connection.adapter_name == 'PostgreSQL'
require 'models/uuid_parent'
@@ -841,6 +849,13 @@ class FoxyFixturesTest < ActiveRecord::TestCase
assert admin_accounts(:signals37).users.include?(admin_users(:david))
assert_equal 2, admin_accounts(:signals37).users.size
end
+
+ def test_resolves_enums
+ assert books(:awdr).published?
+ assert books(:awdr).read?
+ assert books(:rfr).proposed?
+ assert books(:ddd).published?
+ end
end
class ActiveSupportSubclassWithFixturesTest < ActiveRecord::TestCase
@@ -896,3 +911,12 @@ class FixturesWithDefaultScopeTest < ActiveRecord::TestCase
assert_equal "special", bulbs(:special).name
end
end
+
+class FixturesWithAbstractBelongsTo < ActiveRecord::TestCase
+ fixtures :pirates, :doubloons
+
+ test "creates fixtures with belongs_to associations defined in abstract base classes" do
+ assert_not_nil doubloons(:blackbeards_doubloon)
+ assert_equal pirates(:blackbeard), doubloons(:blackbeards_doubloon).pirate
+ end
+end
diff --git a/activerecord/test/cases/helper.rb b/activerecord/test/cases/helper.rb
index 12c793c408..b61a5126e0 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'
@@ -47,7 +48,8 @@ end
def mysql_56?
current_adapter?(:MysqlAdapter, :Mysql2Adapter) &&
- ActiveRecord::Base.connection.send(:version).join(".") >= "5.6.0"
+ ActiveRecord::Base.connection.send(:version) >= '5.6.0' &&
+ ActiveRecord::Base.connection.send(:version) < '5.7.0'
end
def mysql_enforcing_gtid_consistency?
@@ -140,6 +142,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/inheritance_test.rb b/activerecord/test/cases/inheritance_test.rb
index 278fa63e04..f67d85603a 100644
--- a/activerecord/test/cases/inheritance_test.rb
+++ b/activerecord/test/cases/inheritance_test.rb
@@ -213,10 +213,28 @@ class InheritanceTest < ActiveRecord::TestCase
assert_raise(ActiveRecord::SubclassNotFound) { Company.new(:type => 'Account') }
end
+ def test_new_with_unrelated_namespaced_type
+ without_store_full_sti_class do
+ e = assert_raises ActiveRecord::SubclassNotFound do
+ Namespaced::Company.new(type: 'Firm')
+ end
+
+ assert_equal "Invalid single-table inheritance type: Namespaced::Firm is not a subclass of Namespaced::Company", e.message
+ end
+ end
+
+
def test_new_with_complex_inheritance
assert_nothing_raised { Client.new(type: 'VerySpecialClient') }
end
+ def test_new_without_storing_full_sti_class
+ without_store_full_sti_class do
+ item = Company.new(type: 'SpecialCo')
+ assert_instance_of Company::SpecialCo, item
+ end
+ end
+
def test_new_with_autoload_paths
path = File.expand_path('../../models/autoloadable', __FILE__)
ActiveSupport::Dependencies.autoload_paths << path
diff --git a/activerecord/test/cases/invertible_migration_test.rb b/activerecord/test/cases/invertible_migration_test.rb
index 8144f3e5c5..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|
@@ -144,13 +173,17 @@ module ActiveRecord
end
def test_exception_on_removing_index_without_column_option
- RemoveIndexMigration1.new.migrate(:up)
- migration = RemoveIndexMigration2.new
- migration.migrate(:up)
+ index_definition = ["horses", [:name, :color]]
+ migration1 = RemoveIndexMigration1.new
+ migration1.migrate(:up)
+ assert migration1.connection.index_exists?(*index_definition)
- assert_raises(IrreversibleMigration) do
- migration.migrate(:down)
- end
+ migration2 = RemoveIndexMigration2.new
+ migration2.migrate(:up)
+ assert_not migration2.connection.index_exists?(*index_definition)
+
+ migration2.migrate(:down)
+ assert migration2.connection.index_exists?(*index_definition)
end
def test_migrate_up
@@ -219,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_schema_test.rb b/activerecord/test/cases/migration/change_schema_test.rb
index 46a62c272f..83e50048ec 100644
--- a/activerecord/test/cases/migration/change_schema_test.rb
+++ b/activerecord/test/cases/migration/change_schema_test.rb
@@ -105,7 +105,7 @@ module ActiveRecord
eight = columns.detect { |c| c.name == "eight_int" }
if current_adapter?(:OracleAdapter)
- assert_equal 'NUMBER(8)', eight.sql_type
+ assert_equal 'NUMBER(19)', eight.sql_type
elsif current_adapter?(:SQLite3Adapter)
assert_equal 'bigint', eight.sql_type
else
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 3844b1a92e..99f1dc65b0 100644
--- a/activerecord/test/cases/migration/command_recorder_test.rb
+++ b/activerecord/test/cases/migration/command_recorder_test.rb
@@ -169,6 +169,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
@@ -206,6 +216,11 @@ module ActiveRecord
end
def test_invert_remove_index
+ add = @recorder.inverse_of :remove_index, [:table, :one]
+ assert_equal [:add_index, [:table, :one]], add
+ end
+
+ def test_invert_remove_index_with_column
add = @recorder.inverse_of :remove_index, [:table, {column: [:one, :two], options: true}]
assert_equal [:add_index, [:table, [:one, :two], options: true]], add
end
@@ -281,17 +296,42 @@ module ActiveRecord
assert_equal [:remove_foreign_key, [:dogs, :people]], enable
end
+ def test_invert_remove_foreign_key
+ enable = @recorder.inverse_of :remove_foreign_key, [:dogs, :people]
+ assert_equal [:add_foreign_key, [:dogs, :people]], enable
+ end
+
def test_invert_add_foreign_key_with_column
enable = @recorder.inverse_of :add_foreign_key, [:dogs, :people, column: "owner_id"]
assert_equal [:remove_foreign_key, [:dogs, column: "owner_id"]], enable
end
+ def test_invert_remove_foreign_key_with_column
+ enable = @recorder.inverse_of :remove_foreign_key, [:dogs, :people, column: "owner_id"]
+ assert_equal [:add_foreign_key, [:dogs, :people, column: "owner_id"]], enable
+ end
+
def test_invert_add_foreign_key_with_column_and_name
enable = @recorder.inverse_of :add_foreign_key, [:dogs, :people, column: "owner_id", name: "fk"]
assert_equal [:remove_foreign_key, [:dogs, name: "fk"]], enable
end
- def test_remove_foreign_key_is_irreversible
+ def test_invert_remove_foreign_key_with_column_and_name
+ enable = @recorder.inverse_of :remove_foreign_key, [:dogs, :people, column: "owner_id", name: "fk"]
+ assert_equal [:add_foreign_key, [:dogs, :people, column: "owner_id", name: "fk"]], enable
+ end
+
+ def test_invert_remove_foreign_key_with_primary_key
+ enable = @recorder.inverse_of :remove_foreign_key, [:dogs, :people, primary_key: "person_id"]
+ assert_equal [:add_foreign_key, [:dogs, :people, primary_key: "person_id"]], enable
+ end
+
+ def test_invert_remove_foreign_key_with_on_delete_on_update
+ enable = @recorder.inverse_of :remove_foreign_key, [:dogs, :people, on_delete: :nullify, on_update: :cascade]
+ assert_equal [:add_foreign_key, [:dogs, :people, on_delete: :nullify, on_update: :cascade]], enable
+ end
+
+ def test_invert_remove_foreign_key_is_irreversible_without_to_table
assert_raises ActiveRecord::IrreversibleMigration do
@recorder.inverse_of :remove_foreign_key, [:dogs, column: "owner_id"]
end
@@ -299,6 +339,10 @@ module ActiveRecord
assert_raises ActiveRecord::IrreversibleMigration do
@recorder.inverse_of :remove_foreign_key, [:dogs, name: "fk"]
end
+
+ assert_raises ActiveRecord::IrreversibleMigration do
+ @recorder.inverse_of :remove_foreign_key, [:dogs]
+ end
end
end
end
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/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_test.rb b/activerecord/test/cases/migration_test.rb
index b2f209fe97..128a242495 100644
--- a/activerecord/test/cases/migration_test.rb
+++ b/activerecord/test/cases/migration_test.rb
@@ -115,7 +115,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 +132,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..b8a0401fe3 100644
--- a/activerecord/test/cases/nested_attributes_test.rb
+++ b/activerecord/test/cases/nested_attributes_test.rb
@@ -658,6 +658,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 +1064,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 1e93e2a05c..7f14082a9a 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'
@@ -125,7 +126,7 @@ class PersistenceTest < ActiveRecord::TestCase
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
@@ -897,6 +898,33 @@ class PersistenceTest < ActiveRecord::TestCase
assert_not post.new_record?
end
+ def test_reload_via_querycache
+ ActiveRecord::Base.connection.enable_query_cache!
+ ActiveRecord::Base.connection.clear_query_cache
+ assert ActiveRecord::Base.connection.query_cache_enabled, 'cache should be on'
+ parrot = Parrot.create(:name => 'Shane')
+
+ # populate the cache with the SELECT result
+ found_parrot = Parrot.find(parrot.id)
+ assert_equal parrot.id, found_parrot.id
+
+ # Manually update the 'name' attribute in the DB directly
+ assert_equal 1, ActiveRecord::Base.connection.query_cache.length
+ ActiveRecord::Base.uncached do
+ found_parrot.name = 'Mary'
+ found_parrot.save
+ end
+
+ # Now reload, and verify that it gets the DB version, and not the querycache version
+ found_parrot.reload
+ assert_equal 'Mary', found_parrot.name
+
+ found_parrot = Parrot.find(parrot.id)
+ assert_equal 'Mary', found_parrot.name
+ ensure
+ ActiveRecord::Base.connection.disable_query_cache!
+ end
+
class SaveTest < ActiveRecord::TestCase
self.use_transactional_tests = false
diff --git a/activerecord/test/cases/primary_keys_test.rb b/activerecord/test/cases/primary_keys_test.rb
index 83be9a75d8..0745a37ee9 100644
--- a/activerecord/test/cases/primary_keys_test.rb
+++ b/activerecord/test/cases/primary_keys_test.rb
@@ -175,6 +175,20 @@ class PrimaryKeysTest < ActiveRecord::TestCase
dashboard = Dashboard.first
assert_equal '2', dashboard.id
end
+
+ if current_adapter?(:PostgreSQLAdapter)
+ def test_serial_with_quoted_sequence_name
+ column = MixedCaseMonkey.columns_hash[MixedCaseMonkey.primary_key]
+ assert_equal "nextval('\"mixed_case_monkeys_monkeyID_seq\"'::regclass)", column.default_function
+ assert column.serial?
+ end
+
+ def test_serial_with_unquoted_sequence_name
+ column = Topic.columns_hash[Topic.primary_key]
+ assert_equal "nextval('topics_id_seq'::regclass)", column.default_function
+ assert column.serial?
+ end
+ end
end
class PrimaryKeyWithNoConnectionTest < ActiveRecord::TestCase
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/delegation_test.rb b/activerecord/test/cases/relation/delegation_test.rb
index 29c9d0e2af..989f4e1e5d 100644
--- a/activerecord/test/cases/relation/delegation_test.rb
+++ b/activerecord/test/cases/relation/delegation_test.rb
@@ -28,7 +28,7 @@ module ActiveRecord
module DelegationWhitelistBlacklistTests
ARRAY_DELEGATES = [
:+, :-, :|, :&, :[],
- :all?, :collect, :detect, :each, :each_cons, :each_with_index,
+ :all?, :collect, :compact, :detect, :each, :each_cons, :each_with_index,
:exclude?, :find_all, :flat_map, :group_by, :include?, :length,
:map, :none?, :one?, :partition, :reject, :reverse,
:sample, :second, :sort, :sort_by, :third,
diff --git a/activerecord/test/cases/relation/mutation_test.rb b/activerecord/test/cases/relation/mutation_test.rb
index 45ead08bd5..ba4d9d2503 100644
--- a/activerecord/test/cases/relation/mutation_test.rb
+++ b/activerecord/test/cases/relation/mutation_test.rb
@@ -81,7 +81,7 @@ module ActiveRecord
assert_equal [], relation.extending_values
end
- (Relation::SINGLE_VALUE_METHODS - [:lock, :reordering, :reverse_order, :create_with]).each do |method|
+ (Relation::SINGLE_VALUE_METHODS - [:lock, :reordering, :reverse_order, :create_with, :uniq]).each do |method|
test "##{method}!" do
assert relation.public_send("#{method}!", :foo).equal?(relation)
assert_equal :foo, relation.public_send("#{method}_value")
@@ -153,13 +153,22 @@ module ActiveRecord
test 'distinct!' do
relation.distinct! :foo
assert_equal :foo, relation.distinct_value
- assert_equal :foo, relation.uniq_value # deprecated access
+
+ assert_deprecated do
+ assert_equal :foo, relation.uniq_value # deprecated access
+ end
end
test 'uniq! was replaced by distinct!' do
- relation.uniq! :foo
+ assert_deprecated(/use distinct! instead/) do
+ relation.uniq! :foo
+ end
+
+ assert_deprecated(/use distinct_value instead/) do
+ assert_equal :foo, relation.uniq_value # deprecated access
+ end
+
assert_equal :foo, relation.distinct_value
- assert_equal :foo, relation.uniq_value # deprecated access
end
end
end
diff --git a/activerecord/test/cases/relation_test.rb b/activerecord/test/cases/relation_test.rb
index 9353be1ba7..37d3965022 100644
--- a/activerecord/test/cases/relation_test.rb
+++ b/activerecord/test/cases/relation_test.rb
@@ -242,6 +242,19 @@ module ActiveRecord
assert_equal false, post.respond_to?(:title), "post should not respond_to?(:body) since invoking it raises exception"
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
+ 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_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
@@ -276,5 +289,16 @@ module ActiveRecord
assert_equal "type cast from database", UpdateAllTestModel.first.body
end
+
+ private
+
+ def sqlite3_version_includes_quoting_bug?
+ if current_adapter?(:SQLite3Adapter)
+ selected_quoted_column_names = ActiveRecord::Base.connection.exec_query(
+ 'SELECT "join" FROM (SELECT id AS "join" FROM posts) subquery'
+ ).columns
+ ["join"] != selected_quoted_column_names
+ end
+ end
end
end
diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb
index b8e2041b6d..8256762f96 100644
--- a/activerecord/test/cases/relations_test.rb
+++ b/activerecord/test/cases/relations_test.rb
@@ -17,7 +17,7 @@ require 'models/tyre'
require 'models/minivan'
require 'models/aircraft'
require "models/possession"
-
+require "models/reader"
class RelationTest < ActiveRecord::TestCase
fixtures :authors, :topics, :entrants, :developers, :companies, :developers_projects, :accounts, :categories, :categorizations, :posts, :comments,
@@ -621,6 +621,51 @@ class RelationTest < ActiveRecord::TestCase
assert_equal 1, query.to_a.size
end
+ def test_preloading_with_associations_and_merges
+ post = Post.create! title: 'Uhuu', body: 'body'
+ reader = Reader.create! post_id: post.id, person_id: 1
+ comment = Comment.create! post_id: post.id, body: 'body'
+
+ assert !comment.respond_to?(:readers)
+
+ post_rel = Post.preload(:readers).joins(:readers).where(title: 'Uhuu')
+ result_comment = Comment.joins(:post).merge(post_rel).to_a.first
+ assert_equal comment, result_comment
+
+ assert_no_queries do
+ assert_equal post, result_comment.post
+ assert_equal [reader], result_comment.post.readers.to_a
+ end
+
+ post_rel = Post.includes(:readers).where(title: 'Uhuu')
+ result_comment = Comment.joins(:post).merge(post_rel).first
+ assert_equal comment, result_comment
+
+ assert_no_queries do
+ assert_equal post, result_comment.post
+ assert_equal [reader], result_comment.post.readers.to_a
+ 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 }
@@ -886,6 +931,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')
@@ -893,6 +944,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')
@@ -908,7 +965,7 @@ class RelationTest < ActiveRecord::TestCase
def test_delete_all_with_unpermitted_relation_raises_error
assert_raises(ActiveRecord::ActiveRecordError) { Author.limit(10).delete_all }
- assert_raises(ActiveRecord::ActiveRecordError) { Author.uniq.delete_all }
+ assert_raises(ActiveRecord::ActiveRecordError) { Author.distinct.delete_all }
assert_raises(ActiveRecord::ActiveRecordError) { Author.group(:name).delete_all }
assert_raises(ActiveRecord::ActiveRecordError) { Author.having('SUM(id) < 3').delete_all }
assert_raises(ActiveRecord::ActiveRecordError) { Author.offset(10).delete_all }
@@ -1493,14 +1550,17 @@ class RelationTest < ActiveRecord::TestCase
assert_equal ['Foo', 'Foo'], query.map(&:name)
assert_sql(/DISTINCT/) do
assert_equal ['Foo'], query.distinct.map(&:name)
- assert_equal ['Foo'], query.uniq.map(&:name)
+ assert_deprecated { assert_equal ['Foo'], query.uniq.map(&:name) }
end
assert_sql(/DISTINCT/) do
assert_equal ['Foo'], query.distinct(true).map(&:name)
- assert_equal ['Foo'], query.uniq(true).map(&:name)
+ assert_deprecated { assert_equal ['Foo'], query.uniq(true).map(&:name) }
end
assert_equal ['Foo', 'Foo'], query.distinct(true).distinct(false).map(&:name)
- assert_equal ['Foo', 'Foo'], query.uniq(true).uniq(false).map(&:name)
+
+ assert_deprecated do
+ assert_equal ['Foo', 'Foo'], query.uniq(true).uniq(false).map(&:name)
+ end
end
def test_doesnt_add_having_values_if_options_are_blank
diff --git a/activerecord/test/cases/reload_models_test.rb b/activerecord/test/cases/reload_models_test.rb
index 0d16a3526f..431fbf1297 100644
--- a/activerecord/test/cases/reload_models_test.rb
+++ b/activerecord/test/cases/reload_models_test.rb
@@ -3,7 +3,7 @@ require 'models/owner'
require 'models/pet'
class ReloadModelsTest < ActiveRecord::TestCase
- fixtures :pets
+ fixtures :pets, :owners
def test_has_one_with_reload
pet = Pet.find_by_name('parrot')
diff --git a/activerecord/test/cases/schema_dumper_test.rb b/activerecord/test/cases/schema_dumper_test.rb
index e6f0fe6f75..feb1c29656 100644
--- a/activerecord/test/cases/schema_dumper_test.rb
+++ b/activerecord/test/cases/schema_dumper_test.rb
@@ -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)
@@ -253,6 +253,11 @@ class SchemaDumperTest < ActiveRecord::TestCase
assert_match %r{t\.integer\s+"big_int_data_points\",\s+limit: 8,\s+array: true}, output
end
+ 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
+ end
+
if ActiveRecord::Base.connection.supports_extensions?
def test_schema_dump_includes_extensions
connection = ActiveRecord::Base.connection
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/serialized_attribute_test.rb b/activerecord/test/cases/serialized_attribute_test.rb
index 7c92453ee3..6056156698 100644
--- a/activerecord/test/cases/serialized_attribute_test.rb
+++ b/activerecord/test/cases/serialized_attribute_test.rb
@@ -274,4 +274,25 @@ class SerializedAttributeTest < ActiveRecord::TestCase
assert_equal({}, topic.content)
end
+
+ def test_values_cast_from_nil_are_persisted_as_nil
+ # This is required to fulfil the following contract, which must be universally
+ # true in Active Record:
+ #
+ # model.attribute = value
+ # assert_equal model.attribute, model.tap(&:save).reload.attribute
+ Topic.serialize(:content, Hash)
+ topic = Topic.create!(content: {})
+ topic2 = Topic.create!(content: nil)
+
+ assert_equal [topic, topic2], Topic.where(content: nil)
+ end
+
+ def test_nil_is_always_persisted_as_null
+ Topic.serialize(:content, Hash)
+
+ topic = Topic.create!(content: { foo: "bar" })
+ topic.update_attribute :content, nil
+ assert_equal [topic], Topic.where(content: nil)
+ end
end
diff --git a/activerecord/test/cases/suppressor_test.rb b/activerecord/test/cases/suppressor_test.rb
index 1c449d42fe..72c5c16555 100644
--- a/activerecord/test/cases/suppressor_test.rb
+++ b/activerecord/test/cases/suppressor_test.rb
@@ -3,7 +3,38 @@ require 'models/notification'
require 'models/user'
class SuppressorTest < ActiveRecord::TestCase
- def test_suppresses_creation_of_record_generated_by_callback
+ def test_suppresses_create
+ assert_no_difference -> { Notification.count } do
+ Notification.suppress do
+ Notification.create
+ Notification.create!
+ Notification.new.save
+ Notification.new.save!
+ end
+ end
+ end
+
+ def test_suppresses_update
+ user = User.create! token: 'asdf'
+
+ User.suppress do
+ user.update token: 'ghjkl'
+ assert_equal 'asdf', user.reload.token
+
+ user.update! token: 'zxcvbnm'
+ assert_equal 'asdf', user.reload.token
+
+ user.token = 'qwerty'
+ user.save
+ assert_equal 'asdf', user.reload.token
+
+ user.token = 'uiop'
+ user.save!
+ assert_equal 'asdf', user.reload.token
+ end
+ end
+
+ def test_suppresses_create_in_callback
assert_difference -> { User.count } do
assert_no_difference -> { Notification.count } do
Notification.suppress { UserWithNotification.create! }
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 f58535f044..d0deb4c273 100644
--- a/activerecord/test/cases/tasks/mysql_rake_test.rb
+++ b/activerecord/test/cases/tasks/mysql_rake_test.rb
@@ -265,14 +265,14 @@ module ActiveRecord
def test_structure_dump
filename = "awesome-file.sql"
- Kernel.expects(:system).with("mysqldump", "--result-file", filename, "--no-data", "test-db").returns(true)
+ Kernel.expects(:system).with("mysqldump", "--result-file", filename, "--no-data", "--routines", "test-db").returns(true)
ActiveRecord::Tasks::DatabaseTasks.structure_dump(@configuration, filename)
end
def test_warn_when_external_structure_dump_fails
filename = "awesome-file.sql"
- Kernel.expects(:system).with("mysqldump", "--result-file", filename, "--no-data", "test-db").returns(false)
+ Kernel.expects(:system).with("mysqldump", "--result-file", filename, "--no-data", "--routines", "test-db").returns(false)
warnings = capture(:stderr) do
ActiveRecord::Tasks::DatabaseTasks.structure_dump(@configuration, filename)
@@ -283,12 +283,21 @@ module ActiveRecord
def test_structure_dump_with_port_number
filename = "awesome-file.sql"
- Kernel.expects(:system).with("mysqldump", "--port", "10000", "--result-file", filename, "--no-data", "test-db").returns(true)
+ Kernel.expects(:system).with("mysqldump", "--port=10000", "--result-file", filename, "--no-data", "--routines", "test-db").returns(true)
ActiveRecord::Tasks::DatabaseTasks.structure_dump(
@configuration.merge('port' => 10000),
filename)
end
+
+ def test_structure_dump_with_ssl
+ filename = "awesome-file.sql"
+ Kernel.expects(:system).with("mysqldump", "--ssl-ca=ca.crt", "--result-file", filename, "--no-data", "--routines", "test-db").returns(true)
+
+ ActiveRecord::Tasks::DatabaseTasks.structure_dump(
+ @configuration.merge("sslca" => "ca.crt"),
+ filename)
+ end
end
class MySQLStructureLoadTest < ActiveRecord::TestCase
diff --git a/activerecord/test/cases/tasks/postgresql_rake_test.rb b/activerecord/test/cases/tasks/postgresql_rake_test.rb
index 084302cde5..184ff7fc63 100644
--- a/activerecord/test/cases/tasks/postgresql_rake_test.rb
+++ b/activerecord/test/cases/tasks/postgresql_rake_test.rb
@@ -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', '-i', '-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', '-i', '-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", '-i', '-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", '-i', '-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/test_case.rb b/activerecord/test/cases/test_case.rb
index e0b01ae8e0..7761ea5612 100644
--- a/activerecord/test/cases/test_case.rb
+++ b/activerecord/test/cases/test_case.rb
@@ -65,6 +65,30 @@ module ActiveRecord
end
end
+ class PostgreSQLTestCase < TestCase
+ def self.run(*args)
+ super if current_adapter?(:PostgreSQLAdapter)
+ end
+ end
+
+ class Mysql2TestCase < TestCase
+ def self.run(*args)
+ super if current_adapter?(:Mysql2Adapter)
+ end
+ end
+
+ class MysqlTestCase < TestCase
+ def self.run(*args)
+ super if current_adapter?(:MysqlAdapter)
+ end
+ end
+
+ class SQLite3TestCase < TestCase
+ def self.run(*args)
+ super if current_adapter?(:SQLite3Adapter)
+ end
+ end
+
class SQLCounter
class << self
attr_accessor :ignored_sql, :log, :log_all
@@ -81,7 +105,7 @@ module ActiveRecord
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 /]
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]
+ sqlite3_ignored = [/^\s*SELECT name\b.*\bFROM sqlite_master/im, /^\s*SELECT sql\b.*\bFROM sqlite_master/im]
[oracle_ignored, mysql_ignored, postgresql_ignored, sqlite3_ignored].each do |db_ignored_sql|
ignored_sql.concat db_ignored_sql
diff --git a/activerecord/test/cases/touch_later_test.rb b/activerecord/test/cases/touch_later_test.rb
index 11804ff90b..b3c42c8e42 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
@@ -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..29a6ec7522 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
diff --git a/activerecord/test/cases/type/decimal_test.rb b/activerecord/test/cases/type/decimal_test.rb
index fe49d0e79a..01ee36b892 100644
--- a/activerecord/test/cases/type/decimal_test.rb
+++ b/activerecord/test/cases/type/decimal_test.rb
@@ -25,6 +25,11 @@ module ActiveRecord
assert_equal BigDecimal("0.33"), type.cast(Rational(1, 3))
end
+ def test_type_cast_decimal_from_rational_with_precision_and_scale
+ type = Decimal.new(precision: 4, scale: 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))
diff --git a/activerecord/test/cases/type/integer_test.rb b/activerecord/test/cases/type/integer_test.rb
index 84fb05dd8e..0dcdbd0667 100644
--- a/activerecord/test/cases/type/integer_test.rb
+++ b/activerecord/test/cases/type/integer_test.rb
@@ -21,7 +21,7 @@ module ActiveRecord
type = Type::Integer.new
assert_nil type.cast([1,2])
assert_nil type.cast({1 => 2})
- assert_nil type.cast((1..2))
+ assert_nil type.cast(1..2)
end
test "casting ActiveRecord models" do
diff --git a/activerecord/test/cases/validations/absence_validation_test.rb b/activerecord/test/cases/validations/absence_validation_test.rb
new file mode 100644
index 0000000000..dd43ee358c
--- /dev/null
+++ b/activerecord/test/cases/validations/absence_validation_test.rb
@@ -0,0 +1,75 @@
+require "cases/helper"
+require 'models/face'
+require 'models/interest'
+require 'models/man'
+require 'models/topic'
+
+class AbsenceValidationTest < ActiveRecord::TestCase
+ def test_non_association
+ boy_klass = Class.new(Man) do
+ def self.name; "Boy" end
+ validates_absence_of :name
+ end
+
+ assert boy_klass.new.valid?
+ assert_not boy_klass.new(name: "Alex").valid?
+ end
+
+ def test_has_one_marked_for_destruction
+ boy_klass = Class.new(Man) do
+ def self.name; "Boy" end
+ validates_absence_of :face
+ end
+
+ boy = boy_klass.new(face: Face.new)
+ assert_not boy.valid?, "should not be valid if has_one association is present"
+ assert_equal 1, boy.errors[:face].size, "should only add one error"
+
+ boy.face.mark_for_destruction
+ assert boy.valid?, "should be valid if association is marked for destruction"
+ end
+
+ def test_has_many_marked_for_destruction
+ boy_klass = Class.new(Man) do
+ def self.name; "Boy" end
+ validates_absence_of :interests
+ end
+ boy = boy_klass.new
+ boy.interests << [i1 = Interest.new, i2 = Interest.new]
+ assert_not boy.valid?, "should not be valid if has_many association is present"
+
+ i1.mark_for_destruction
+ assert_not boy.valid?, "should not be valid if has_many association is present"
+
+ i2.mark_for_destruction
+ assert boy.valid?
+ end
+
+ def test_does_not_call_to_a_on_associations
+ boy_klass = Class.new(Man) do
+ def self.name; "Boy" end
+ validates_absence_of :face
+ end
+
+ face_with_to_a = Face.new
+ def face_with_to_a.to_a; ['(/)', '(\)']; end
+
+ assert_nothing_raised { boy_klass.new(face: face_with_to_a).valid? }
+ end
+
+ def test_does_not_validate_if_parent_record_is_validate_false
+ repair_validations(Interest) do
+ Interest.validates_absence_of(:topic)
+ interest = Interest.new(topic: Topic.new(title: "Math"))
+ interest.save!(validate: false)
+ assert interest.persisted?
+
+ man = Man.new(interest_ids: [interest.id])
+ man.save!
+
+ assert_equal man.interests.size, 1
+ assert interest.valid?
+ assert man.valid?
+ end
+ end
+end
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/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..a429d06aad 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
diff --git a/activerecord/test/cases/view_test.rb b/activerecord/test/cases/view_test.rb
index 3aed90ba36..1eb1430065 100644
--- a/activerecord/test/cases/view_test.rb
+++ b/activerecord/test/cases/view_test.rb
@@ -102,7 +102,7 @@ class ViewWithoutPrimaryKeyTest < ActiveRecord::TestCase
end
def test_attributes
- assert_equal({"name" => "Agile Web Development with Rails", "status" => 0},
+ assert_equal({"name" => "Agile Web Development with Rails", "status" => 2},
Paperback.first.attributes)
end
@@ -110,4 +110,70 @@ class ViewWithoutPrimaryKeyTest < ActiveRecord::TestCase
assert_nil Paperback.primary_key
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/books.yml b/activerecord/test/fixtures/books.yml
index abe56752c6..a304fba399 100644
--- a/activerecord/test/fixtures/books.yml
+++ b/activerecord/test/fixtures/books.yml
@@ -3,9 +3,29 @@ awdr:
id: 1
name: "Agile Web Development with Rails"
format: "paperback"
+ status: :published
+ read_status: :read
+ language: :english
+ author_visibility: :visible
+ illustrator_visibility: :visible
+ font_size: :medium
rfr:
author_id: 1
id: 2
name: "Ruby for Rails"
format: "ebook"
+ status: "proposed"
+ read_status: "reading"
+
+ddd:
+ author_id: 1
+ id: 3
+ name: "Domain-Driven Design"
+ format: "hardcover"
+ status: 2
+
+tlg:
+ author_id: 1
+ id: 4
+ name: "Thoughtleadering"
diff --git a/activerecord/test/fixtures/doubloons.yml b/activerecord/test/fixtures/doubloons.yml
new file mode 100644
index 0000000000..efd1643971
--- /dev/null
+++ b/activerecord/test/fixtures/doubloons.yml
@@ -0,0 +1,3 @@
+blackbeards_doubloon:
+ pirate: blackbeard
+ weight: 2
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/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/book.rb b/activerecord/test/models/book.rb
index 2170018068..1927191393 100644
--- a/activerecord/test/models/book.rb
+++ b/activerecord/test/models/book.rb
@@ -10,6 +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], _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 6961f8fd6f..67936e8e5d 100644
--- a/activerecord/test/models/company.rb
+++ b/activerecord/test/models/company.rb
@@ -26,6 +26,9 @@ class Company < AbstractCompany
def private_method
"I am Jack's innermost fears and aspirations"
end
+
+ class SpecialCo < Company
+ end
end
module Namespaced
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..8ac7a9df6a 100644
--- a/activerecord/test/models/developer.rb
+++ b/activerecord/test/models/developer.rb
@@ -50,6 +50,7 @@ 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
scope :jamises, -> { where(:name => 'Jamis') }
diff --git a/activerecord/test/models/doubloon.rb b/activerecord/test/models/doubloon.rb
new file mode 100644
index 0000000000..2b11d128e2
--- /dev/null
+++ b/activerecord/test/models/doubloon.rb
@@ -0,0 +1,12 @@
+class AbstractDoubloon < ActiveRecord::Base
+ # This has functionality that might be shared by multiple classes.
+
+ self.abstract_class = true
+ belongs_to :pirate
+end
+
+class Doubloon < AbstractDoubloon
+ # This uses an abstract class that defines attributes and associations.
+
+ self.table_name = 'doubloons'
+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/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/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/post.rb b/activerecord/test/models/post.rb
index 052b1c9690..81a18188d4 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
@@ -208,6 +208,22 @@ class PostWithDefaultScope < ActiveRecord::Base
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.table_name = 'posts'
default_scope { where(:id => [1, 5,6]) }
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/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/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/postgresql_specific_schema.rb b/activerecord/test/schema/postgresql_specific_schema.rb
index 872fa595b4..df0362573b 100644
--- a/activerecord/test/schema/postgresql_specific_schema.rb
+++ b/activerecord/test/schema/postgresql_specific_schema.rb
@@ -107,5 +107,6 @@ _SQL
create_table :bigint_array, force: true do |t|
t.integer :big_int_data_points, limit: 8, array: true
+ t.decimal :decimal_array_default, array: true, default: [1.23, 3.45]
end
end
diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb
index 66f8f1611d..994ece9244 100644
--- a/activerecord/test/schema/schema.rb
+++ b/activerecord/test/schema/schema.rb
@@ -37,6 +37,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|
@@ -100,6 +101,10 @@ ActiveRecord::Schema.define do
t.column :status, :integer, default: 0
t.column :read_status, :integer, default: 0
t.column :nullable_status, :integer
+ t.column :language, :integer, default: 0
+ t.column :author_visibility, :integer, default: 0
+ t.column :illustrator_visibility, :integer, default: 0
+ t.column :font_size, :integer, default: 0
end
create_table :booleans, force: true do |t|
@@ -126,6 +131,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
@@ -232,6 +239,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
@@ -266,6 +278,11 @@ ActiveRecord::Schema.define do
t.string :alias
end
+ create_table :doubloons, force: true do |t|
+ t.integer :pirate_id
+ t.integer :weight
+ end
+
create_table :edges, force: true, id: false do |t|
t.column :source_id, :integer, null: false
t.column :sink_id, :integer, null: false
@@ -656,7 +673,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
@@ -669,6 +689,15 @@ ActiveRecord::Schema.define do
t.datetime :updated_at
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|
t.string :speedometer_id
t.string :name
@@ -858,6 +887,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|
@@ -914,3 +954,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