aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/test
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord/test')
-rw-r--r--activerecord/test/cases/adapter_test.rb75
-rw-r--r--activerecord/test/cases/adapters/mysql2/active_schema_test.rb20
-rw-r--r--activerecord/test/cases/adapters/mysql2/connection_test.rb6
-rw-r--r--activerecord/test/cases/adapters/mysql2/datetime_precision_quoting_test.rb7
-rw-r--r--activerecord/test/cases/adapters/mysql2/virtual_column_test.rb4
-rw-r--r--activerecord/test/cases/adapters/postgresql/active_schema_test.rb2
-rw-r--r--activerecord/test/cases/adapters/postgresql/connection_test.rb5
-rw-r--r--activerecord/test/cases/adapters/postgresql/money_test.rb5
-rw-r--r--activerecord/test/cases/adapters/postgresql/partitions_test.rb22
-rw-r--r--activerecord/test/cases/adapters/sqlite3/quoting_test.rb26
-rw-r--r--activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb69
-rw-r--r--activerecord/test/cases/arel/attributes/math_test.rb20
-rw-r--r--activerecord/test/cases/arel/insert_manager_test.rb2
-rw-r--r--activerecord/test/cases/arel/select_manager_test.rb13
-rw-r--r--activerecord/test/cases/arel/visitors/postgres_test.rb12
-rw-r--r--activerecord/test/cases/arel/visitors/to_sql_test.rb2
-rw-r--r--activerecord/test/cases/associations/belongs_to_associations_test.rb42
-rw-r--r--activerecord/test/cases/associations/eager_test.rb82
-rw-r--r--activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb20
-rw-r--r--activerecord/test/cases/associations/has_many_associations_test.rb67
-rw-r--r--activerecord/test/cases/associations/has_many_through_associations_test.rb25
-rw-r--r--activerecord/test/cases/associations/has_one_associations_test.rb3
-rw-r--r--activerecord/test/cases/associations/inverse_associations_test.rb12
-rw-r--r--activerecord/test/cases/attribute_methods_test.rb13
-rw-r--r--activerecord/test/cases/autosave_association_test.rb48
-rw-r--r--activerecord/test/cases/base_test.rb51
-rw-r--r--activerecord/test/cases/bind_parameter_test.rb6
-rw-r--r--activerecord/test/cases/boolean_test.rb43
-rw-r--r--activerecord/test/cases/calculations_test.rb30
-rw-r--r--activerecord/test/cases/callbacks_test.rb27
-rw-r--r--activerecord/test/cases/connection_adapters/connection_handler_test.rb5
-rw-r--r--activerecord/test/cases/connection_adapters/merge_and_resolve_default_url_config_test.rb7
-rw-r--r--activerecord/test/cases/connection_pool_test.rb89
-rw-r--r--activerecord/test/cases/connection_specification/resolver_test.rb8
-rw-r--r--activerecord/test/cases/counter_cache_test.rb12
-rw-r--r--activerecord/test/cases/defaults_test.rb30
-rw-r--r--activerecord/test/cases/dirty_test.rb22
-rw-r--r--activerecord/test/cases/explain_subscriber_test.rb2
-rw-r--r--activerecord/test/cases/filter_attributes_test.rb84
-rw-r--r--activerecord/test/cases/finder_test.rb19
-rw-r--r--activerecord/test/cases/fixtures_test.rb169
-rw-r--r--activerecord/test/cases/helper.rb2
-rw-r--r--activerecord/test/cases/inheritance_test.rb1
-rw-r--r--activerecord/test/cases/legacy_configurations_test.rb43
-rw-r--r--activerecord/test/cases/locking_test.rb24
-rw-r--r--activerecord/test/cases/log_subscriber_test.rb15
-rw-r--r--activerecord/test/cases/migration/change_schema_test.rb11
-rw-r--r--activerecord/test/cases/migration/command_recorder_test.rb13
-rw-r--r--activerecord/test/cases/migration/foreign_key_test.rb35
-rw-r--r--activerecord/test/cases/migration_test.rb13
-rw-r--r--activerecord/test/cases/nested_attributes_test.rb25
-rw-r--r--activerecord/test/cases/persistence_test.rb213
-rw-r--r--activerecord/test/cases/primary_keys_test.rb1
-rw-r--r--activerecord/test/cases/query_cache_test.rb25
-rw-r--r--activerecord/test/cases/quoting_test.rb30
-rw-r--r--activerecord/test/cases/reaper_test.rb4
-rw-r--r--activerecord/test/cases/relation/delegation_test.rb6
-rw-r--r--activerecord/test/cases/relation/delete_all_test.rb104
-rw-r--r--activerecord/test/cases/relation/merging_test.rb10
-rw-r--r--activerecord/test/cases/relation/select_test.rb2
-rw-r--r--activerecord/test/cases/relation/update_all_test.rb239
-rw-r--r--activerecord/test/cases/relation_test.rb27
-rw-r--r--activerecord/test/cases/relations_test.rb169
-rw-r--r--activerecord/test/cases/result_test.rb5
-rw-r--r--activerecord/test/cases/schema_dumper_test.rb55
-rw-r--r--activerecord/test/cases/scoping/default_scoping_test.rb16
-rw-r--r--activerecord/test/cases/scoping/relation_scoping_test.rb9
-rw-r--r--activerecord/test/cases/serialized_attribute_test.rb9
-rw-r--r--activerecord/test/cases/store_test.rb34
-rw-r--r--activerecord/test/cases/tasks/database_tasks_test.rb786
-rw-r--r--activerecord/test/cases/tasks/mysql_rake_test.rb245
-rw-r--r--activerecord/test/cases/tasks/postgresql_rake_test.rb345
-rw-r--r--activerecord/test/cases/tasks/sqlite_rake_test.rb134
-rw-r--r--activerecord/test/cases/test_case.rb2
-rw-r--r--activerecord/test/cases/transaction_callbacks_test.rb17
-rw-r--r--activerecord/test/cases/transaction_isolation_test.rb2
-rw-r--r--activerecord/test/cases/transactions_test.rb73
-rw-r--r--activerecord/test/cases/validations_test.rb18
-rw-r--r--activerecord/test/config.example.yml6
-rw-r--r--activerecord/test/fixtures/citations.yml4
-rw-r--r--activerecord/test/fixtures/memberships.yml7
-rw-r--r--activerecord/test/models/admin/user.rb3
-rw-r--r--activerecord/test/models/car.rb2
-rw-r--r--activerecord/test/models/citation.rb1
-rw-r--r--activerecord/test/models/company.rb24
-rw-r--r--activerecord/test/models/contract.rb4
-rw-r--r--activerecord/test/models/member.rb7
-rw-r--r--activerecord/test/models/pirate.rb8
-rw-r--r--activerecord/test/models/post.rb3
-rw-r--r--activerecord/test/models/price_estimate.rb8
-rw-r--r--activerecord/test/models/reply.rb4
-rw-r--r--activerecord/test/models/topic.rb10
-rw-r--r--activerecord/test/models/wheel.rb2
-rw-r--r--activerecord/test/schema/mysql2_specific_schema.rb20
-rw-r--r--activerecord/test/schema/oracle_specific_schema.rb2
-rw-r--r--activerecord/test/schema/postgresql_specific_schema.rb1
-rw-r--r--activerecord/test/schema/schema.rb6
-rw-r--r--activerecord/test/schema/sqlite_specific_schema.rb11
98 files changed, 2956 insertions, 1145 deletions
diff --git a/activerecord/test/cases/adapter_test.rb b/activerecord/test/cases/adapter_test.rb
index 79642f5871..a93e5e2b40 100644
--- a/activerecord/test/cases/adapter_test.rb
+++ b/activerecord/test/cases/adapter_test.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
require "cases/helper"
+require "support/connection_helper"
require "models/book"
require "models/post"
require "models/author"
@@ -10,6 +11,7 @@ module ActiveRecord
class AdapterTest < ActiveRecord::TestCase
def setup
@connection = ActiveRecord::Base.connection
+ @connection.materialize_transactions
end
##
@@ -299,11 +301,41 @@ module ActiveRecord
def test_supports_multi_insert_is_deprecated
assert_deprecated { @connection.supports_multi_insert? }
end
+
+ def test_column_name_length_is_deprecated
+ assert_deprecated { @connection.column_name_length }
+ end
+
+ def test_table_name_length_is_deprecated
+ assert_deprecated { @connection.table_name_length }
+ end
+
+ def test_columns_per_table_is_deprecated
+ assert_deprecated { @connection.columns_per_table }
+ end
+
+ def test_indexes_per_table_is_deprecated
+ assert_deprecated { @connection.indexes_per_table }
+ end
+
+ def test_columns_per_multicolumn_index_is_deprecated
+ assert_deprecated { @connection.columns_per_multicolumn_index }
+ end
+
+ def test_sql_query_length_is_deprecated
+ assert_deprecated { @connection.sql_query_length }
+ end
+
+ def test_joins_per_query_is_deprecated
+ assert_deprecated { @connection.joins_per_query }
+ end
end
class AdapterForeignKeyTest < ActiveRecord::TestCase
self.use_transactional_tests = false
+ fixtures :fk_test_has_pk
+
def setup
@connection = ActiveRecord::Base.connection
end
@@ -322,7 +354,7 @@ module ActiveRecord
assert_not_nil error.cause
end
- def test_foreign_key_violations_are_translated_to_specific_exception
+ def test_foreign_key_violations_on_insert_are_translated_to_specific_exception
error = assert_raises(ActiveRecord::InvalidForeignKey) do
insert_into_fk_test_has_fk
end
@@ -330,6 +362,16 @@ module ActiveRecord
assert_not_nil error.cause
end
+ def test_foreign_key_violations_on_delete_are_translated_to_specific_exception
+ insert_into_fk_test_has_fk fk_id: 1
+
+ error = assert_raises(ActiveRecord::InvalidForeignKey) do
+ @connection.execute "DELETE FROM fk_test_has_pk WHERE pk_id = 1"
+ end
+
+ assert_not_nil error.cause
+ end
+
def test_disable_referential_integrity
assert_nothing_raised do
@connection.disable_referential_integrity do
@@ -342,14 +384,13 @@ module ActiveRecord
end
private
-
- def insert_into_fk_test_has_fk
+ def insert_into_fk_test_has_fk(fk_id: 0)
# Oracle adapter uses prefetched primary key values from sequence and passes them to connection adapter insert method
if @connection.prefetch_primary_key?
id_value = @connection.next_sequence_value(@connection.default_sequence_name("fk_test_has_fk", "id"))
- @connection.execute "INSERT INTO fk_test_has_fk (id,fk_id) VALUES (#{id_value},0)"
+ @connection.execute "INSERT INTO fk_test_has_fk (id,fk_id) VALUES (#{id_value},#{fk_id})"
else
- @connection.execute "INSERT INTO fk_test_has_fk (fk_id) VALUES (0)"
+ @connection.execute "INSERT INTO fk_test_has_fk (fk_id) VALUES (#{fk_id})"
end
end
end
@@ -406,3 +447,27 @@ module ActiveRecord
end
end
end
+
+if ActiveRecord::Base.connection.supports_advisory_locks?
+ class AdvisoryLocksEnabledTest < ActiveRecord::TestCase
+ include ConnectionHelper
+
+ def test_advisory_locks_enabled?
+ assert ActiveRecord::Base.connection.advisory_locks_enabled?
+
+ run_without_connection do |orig_connection|
+ ActiveRecord::Base.establish_connection(
+ orig_connection.merge(advisory_locks: false)
+ )
+
+ assert_not ActiveRecord::Base.connection.advisory_locks_enabled?
+
+ ActiveRecord::Base.establish_connection(
+ orig_connection.merge(advisory_locks: true)
+ )
+
+ assert ActiveRecord::Base.connection.advisory_locks_enabled?
+ end
+ end
+ end
+end
diff --git a/activerecord/test/cases/adapters/mysql2/active_schema_test.rb b/activerecord/test/cases/adapters/mysql2/active_schema_test.rb
index 976c5dde58..261fee13eb 100644
--- a/activerecord/test/cases/adapters/mysql2/active_schema_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/active_schema_test.rb
@@ -106,7 +106,7 @@ class Mysql2ActiveSchemaTest < ActiveRecord::Mysql2TestCase
end
def test_create_mysql_database_with_encoding
- assert_equal "CREATE DATABASE `matt` DEFAULT CHARACTER SET `utf8`", create_database(:matt)
+ assert_equal "CREATE DATABASE `matt` DEFAULT CHARACTER SET `utf8mb4`", create_database(:matt)
assert_equal "CREATE DATABASE `aimonetti` DEFAULT CHARACTER SET `latin1`", create_database(:aimonetti, charset: "latin1")
assert_equal "CREATE DATABASE `matt_aimonetti` DEFAULT COLLATE `utf8mb4_bin`", create_database(:matt_aimonetti, collation: "utf8mb4_bin")
end
@@ -157,15 +157,19 @@ class Mysql2ActiveSchemaTest < ActiveRecord::Mysql2TestCase
end
def test_indexes_in_create
- ActiveRecord::Base.connection.stubs(:data_source_exists?).with(:temp).returns(false)
- ActiveRecord::Base.connection.stubs(:index_name_exists?).with(:index_temp_on_zip).returns(false)
+ assert_called_with(
+ ActiveRecord::Base.connection,
+ :data_source_exists?,
+ [:temp],
+ returns: false
+ ) do
+ expected = "CREATE TEMPORARY TABLE `temp` ( INDEX `index_temp_on_zip` (`zip`)) AS SELECT id, name, zip FROM a_really_complicated_query"
+ actual = ActiveRecord::Base.connection.create_table(:temp, temporary: true, as: "SELECT id, name, zip FROM a_really_complicated_query") do |t|
+ t.index :zip
+ end
- expected = "CREATE TEMPORARY TABLE `temp` ( INDEX `index_temp_on_zip` (`zip`)) AS SELECT id, name, zip FROM a_really_complicated_query"
- actual = ActiveRecord::Base.connection.create_table(:temp, temporary: true, as: "SELECT id, name, zip FROM a_really_complicated_query") do |t|
- t.index :zip
+ assert_equal expected, actual
end
-
- assert_equal expected, actual
end
private
diff --git a/activerecord/test/cases/adapters/mysql2/connection_test.rb b/activerecord/test/cases/adapters/mysql2/connection_test.rb
index 726f58d58e..3103589186 100644
--- a/activerecord/test/cases/adapters/mysql2/connection_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/connection_test.rb
@@ -104,8 +104,8 @@ class Mysql2ConnectionTest < ActiveRecord::Mysql2TestCase
end
def test_mysql_connection_collation_is_configured
- assert_equal "utf8_unicode_ci", @connection.show_variable("collation_connection")
- assert_equal "utf8_general_ci", ARUnit2Model.connection.show_variable("collation_connection")
+ assert_equal "utf8mb4_unicode_ci", @connection.show_variable("collation_connection")
+ assert_equal "utf8mb4_general_ci", ARUnit2Model.connection.show_variable("collation_connection")
end
def test_mysql_default_in_strict_mode
@@ -170,6 +170,8 @@ class Mysql2ConnectionTest < ActiveRecord::Mysql2TestCase
end
def test_logs_name_show_variable
+ ActiveRecord::Base.connection.materialize_transactions
+ @subscriber.logged.clear
@connection.show_variable "foo"
assert_equal "SCHEMA", @subscriber.logged[0][1]
end
diff --git a/activerecord/test/cases/adapters/mysql2/datetime_precision_quoting_test.rb b/activerecord/test/cases/adapters/mysql2/datetime_precision_quoting_test.rb
index fa54f39992..00a075e063 100644
--- a/activerecord/test/cases/adapters/mysql2/datetime_precision_quoting_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/datetime_precision_quoting_test.rb
@@ -45,9 +45,10 @@ class Mysql2DatetimePrecisionQuotingTest < ActiveRecord::Mysql2TestCase
end
def stub_version(full_version_string)
- @connection.stubs(:full_version).returns(full_version_string)
- @connection.remove_instance_variable(:@version) if @connection.instance_variable_defined?(:@version)
- yield
+ @connection.stub(:full_version, full_version_string) do
+ @connection.remove_instance_variable(:@version) if @connection.instance_variable_defined?(:@version)
+ yield
+ end
ensure
@connection.remove_instance_variable(:@version) if @connection.instance_variable_defined?(:@version)
end
diff --git a/activerecord/test/cases/adapters/mysql2/virtual_column_test.rb b/activerecord/test/cases/adapters/mysql2/virtual_column_test.rb
index ffde8ed4d8..8494acee3b 100644
--- a/activerecord/test/cases/adapters/mysql2/virtual_column_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/virtual_column_test.rb
@@ -18,6 +18,7 @@ if ActiveRecord::Base.connection.supports_virtual_columns?
t.string :name
t.virtual :upper_name, type: :string, as: "UPPER(`name`)"
t.virtual :name_length, type: :integer, as: "LENGTH(`name`)", stored: true
+ t.virtual :name_octet_length, type: :integer, as: "OCTET_LENGTH(`name`)", stored: true
end
VirtualColumn.create(name: "Rails")
end
@@ -55,7 +56,8 @@ if ActiveRecord::Base.connection.supports_virtual_columns?
def test_schema_dumping
output = dump_table_schema("virtual_columns")
assert_match(/t\.virtual\s+"upper_name",\s+type: :string,\s+as: "(?:UPPER|UCASE)\(`name`\)"$/i, output)
- assert_match(/t\.virtual\s+"name_length",\s+type: :integer,\s+as: "LENGTH\(`name`\)",\s+stored: true$/i, output)
+ assert_match(/t\.virtual\s+"name_length",\s+type: :integer,\s+as: "(?:octet_length|length)\(`name`\)",\s+stored: true$/i, output)
+ assert_match(/t\.virtual\s+"name_octet_length",\s+type: :integer,\s+as: "(?:octet_length|length)\(`name`\)",\s+stored: true$/i, output)
end
end
end
diff --git a/activerecord/test/cases/adapters/postgresql/active_schema_test.rb b/activerecord/test/cases/adapters/postgresql/active_schema_test.rb
index 308ad1d854..afd422881b 100644
--- a/activerecord/test/cases/adapters/postgresql/active_schema_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/active_schema_test.rb
@@ -4,6 +4,8 @@ require "cases/helper"
class PostgresqlActiveSchemaTest < ActiveRecord::PostgreSQLTestCase
def setup
+ ActiveRecord::Base.connection.materialize_transactions
+
ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.class_eval do
def execute(sql, name = nil) sql end
end
diff --git a/activerecord/test/cases/adapters/postgresql/connection_test.rb b/activerecord/test/cases/adapters/postgresql/connection_test.rb
index d1b3c434e1..70aa189893 100644
--- a/activerecord/test/cases/adapters/postgresql/connection_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/connection_test.rb
@@ -15,8 +15,9 @@ module ActiveRecord
def setup
super
@subscriber = SQLSubscriber.new
- @subscription = ActiveSupport::Notifications.subscribe("sql.active_record", @subscriber)
@connection = ActiveRecord::Base.connection
+ @connection.materialize_transactions
+ @subscription = ActiveSupport::Notifications.subscribe("sql.active_record", @subscriber)
end
def teardown
@@ -151,7 +152,7 @@ module ActiveRecord
# When prompted, restart the PostgreSQL server with the
# "-m fast" option or kill the individual connection assuming
# you know the incantation to do that.
- # To restart PostgreSQL 9.1 on OS X, installed via MacPorts, ...
+ # To restart PostgreSQL 9.1 on macOS, installed via MacPorts, ...
# sudo su postgres -c "pg_ctl restart -D /opt/local/var/db/postgresql91/defaultdb/ -m fast"
def test_reconnection_after_actual_disconnection_with_verify
original_connection_pid = @connection.query("select pg_backend_pid()")
diff --git a/activerecord/test/cases/adapters/postgresql/money_test.rb b/activerecord/test/cases/adapters/postgresql/money_test.rb
index be3590e8dd..61e75e772d 100644
--- a/activerecord/test/cases/adapters/postgresql/money_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/money_test.rb
@@ -6,7 +6,9 @@ require "support/schema_dumping_helper"
class PostgresqlMoneyTest < ActiveRecord::PostgreSQLTestCase
include SchemaDumpingHelper
- class PostgresqlMoney < ActiveRecord::Base; end
+ class PostgresqlMoney < ActiveRecord::Base
+ validates :depth, numericality: true
+ end
setup do
@connection = ActiveRecord::Base.connection
@@ -35,6 +37,7 @@ class PostgresqlMoneyTest < ActiveRecord::PostgreSQLTestCase
def test_default
assert_equal BigDecimal("150.55"), PostgresqlMoney.column_defaults["depth"]
assert_equal BigDecimal("150.55"), PostgresqlMoney.new.depth
+ assert_equal "$150.55", PostgresqlMoney.new.depth_before_type_cast
end
def test_money_values
diff --git a/activerecord/test/cases/adapters/postgresql/partitions_test.rb b/activerecord/test/cases/adapters/postgresql/partitions_test.rb
new file mode 100644
index 0000000000..0ac9ca1200
--- /dev/null
+++ b/activerecord/test/cases/adapters/postgresql/partitions_test.rb
@@ -0,0 +1,22 @@
+# frozen_string_literal: true
+
+require "cases/helper"
+
+class PostgreSQLPartitionsTest < ActiveRecord::PostgreSQLTestCase
+ def setup
+ @connection = ActiveRecord::Base.connection
+ end
+
+ def teardown
+ @connection.drop_table "partitioned_events", if_exists: true
+ end
+
+ def test_partitions_table_exists
+ skip unless ActiveRecord::Base.connection.postgresql_version >= 100000
+ @connection.create_table :partitioned_events, force: true, id: false,
+ options: "partition by range (issued_at)" do |t|
+ t.timestamp :issued_at
+ end
+ assert @connection.table_exists?("partitioned_events")
+ end
+end
diff --git a/activerecord/test/cases/adapters/sqlite3/quoting_test.rb b/activerecord/test/cases/adapters/sqlite3/quoting_test.rb
index 1c85ff5674..40b58e86bf 100644
--- a/activerecord/test/cases/adapters/sqlite3/quoting_test.rb
+++ b/activerecord/test/cases/adapters/sqlite3/quoting_test.rb
@@ -62,4 +62,30 @@ class SQLite3QuotingTest < ActiveRecord::SQLite3TestCase
assert_equal "'2000-01-01 12:30:00.999999'", @conn.quote(type.serialize(value))
end
+
+ def test_quoted_time_dst_utc
+ with_env_tz "America/New_York" do
+ with_timezone_config default: :utc do
+ t = Time.new(2000, 7, 1, 0, 0, 0, "+04:30")
+
+ expected = t.change(year: 2000, month: 1, day: 1)
+ expected = expected.getutc.to_s(:db).sub(/\A\d\d\d\d-\d\d-\d\d /, "2000-01-01 ")
+
+ assert_equal expected, @conn.quoted_time(t)
+ end
+ end
+ end
+
+ def test_quoted_time_dst_local
+ with_env_tz "America/New_York" do
+ with_timezone_config default: :local do
+ t = Time.new(2000, 7, 1, 0, 0, 0, "+04:30")
+
+ expected = t.change(year: 2000, month: 1, day: 1)
+ expected = expected.getlocal.to_s(:db).sub(/\A\d\d\d\d-\d\d-\d\d /, "2000-01-01 ")
+
+ assert_equal expected, @conn.quoted_time(t)
+ end
+ end
+ end
end
diff --git a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb
index ad155c7492..7b73621ed9 100644
--- a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb
+++ b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb
@@ -345,6 +345,42 @@ module ActiveRecord
end
end
+ if ActiveRecord::Base.connection.supports_expression_index?
+ def test_expression_index
+ with_example_table do
+ @conn.add_index "ex", "max(id, number)", name: "expression"
+ index = @conn.indexes("ex").find { |idx| idx.name == "expression" }
+ assert_equal "max(id, number)", index.columns
+ end
+ end
+
+ def test_expression_index_with_where
+ with_example_table do
+ @conn.add_index "ex", "id % 10, max(id, number)", name: "expression", where: "id > 1000"
+ index = @conn.indexes("ex").find { |idx| idx.name == "expression" }
+ assert_equal "id % 10, max(id, number)", index.columns
+ assert_equal "id > 1000", index.where
+ end
+ end
+
+ def test_complicated_expression
+ with_example_table do
+ @conn.execute "CREATE INDEX expression ON ex (id % 10, (CASE WHEN number > 0 THEN max(id, number) END))WHERE(id > 1000)"
+ index = @conn.indexes("ex").find { |idx| idx.name == "expression" }
+ assert_equal "id % 10, (CASE WHEN number > 0 THEN max(id, number) END)", index.columns
+ assert_equal "(id > 1000)", index.where
+ end
+ end
+
+ def test_not_everything_an_expression
+ with_example_table do
+ @conn.add_index "ex", "id, max(id, number)", name: "expression"
+ index = @conn.indexes("ex").find { |idx| idx.name == "expression" }
+ assert_equal "id, max(id, number)", index.columns
+ end
+ end
+ end
+
def test_primary_key
with_example_table do
assert_equal "id", @conn.primary_key("ex")
@@ -504,6 +540,39 @@ module ActiveRecord
assert_deprecated { @conn.valid_alter_table_type?(:string) }
end
+ def test_db_is_not_readonly_when_readonly_option_is_false
+ conn = Base.sqlite3_connection database: ":memory:",
+ adapter: "sqlite3",
+ readonly: false
+
+ assert_not_predicate conn.raw_connection, :readonly?
+ end
+
+ def test_db_is_not_readonly_when_readonly_option_is_unspecified
+ conn = Base.sqlite3_connection database: ":memory:",
+ adapter: "sqlite3"
+
+ assert_not_predicate conn.raw_connection, :readonly?
+ end
+
+ def test_db_is_readonly_when_readonly_option_is_true
+ conn = Base.sqlite3_connection database: ":memory:",
+ adapter: "sqlite3",
+ readonly: true
+
+ assert_predicate conn.raw_connection, :readonly?
+ end
+
+ def test_writes_are_not_permitted_to_readonly_databases
+ conn = Base.sqlite3_connection database: ":memory:",
+ adapter: "sqlite3",
+ readonly: true
+
+ assert_raises(ActiveRecord::StatementInvalid, /SQLite3::ReadOnlyException/) do
+ conn.execute("CREATE TABLE test(id integer)")
+ end
+ end
+
private
def assert_logged(logs)
diff --git a/activerecord/test/cases/arel/attributes/math_test.rb b/activerecord/test/cases/arel/attributes/math_test.rb
index f3aabe4f60..41eea217c0 100644
--- a/activerecord/test/cases/arel/attributes/math_test.rb
+++ b/activerecord/test/cases/arel/attributes/math_test.rb
@@ -6,35 +6,35 @@ module Arel
module Attributes
class MathTest < Arel::Spec
%i[* /].each do |math_operator|
- it "average should be compatiable with #{math_operator}" do
+ it "average should be compatible with #{math_operator}" do
table = Arel::Table.new :users
(table[:id].average.public_send(math_operator, 2)).to_sql.must_be_like %{
AVG("users"."id") #{math_operator} 2
}
end
- it "count should be compatiable with #{math_operator}" do
+ it "count should be compatible with #{math_operator}" do
table = Arel::Table.new :users
(table[:id].count.public_send(math_operator, 2)).to_sql.must_be_like %{
COUNT("users"."id") #{math_operator} 2
}
end
- it "maximum should be compatiable with #{math_operator}" do
+ it "maximum should be compatible with #{math_operator}" do
table = Arel::Table.new :users
(table[:id].maximum.public_send(math_operator, 2)).to_sql.must_be_like %{
MAX("users"."id") #{math_operator} 2
}
end
- it "minimum should be compatiable with #{math_operator}" do
+ it "minimum should be compatible with #{math_operator}" do
table = Arel::Table.new :users
(table[:id].minimum.public_send(math_operator, 2)).to_sql.must_be_like %{
MIN("users"."id") #{math_operator} 2
}
end
- it "attribute node should be compatiable with #{math_operator}" do
+ it "attribute node should be compatible with #{math_operator}" do
table = Arel::Table.new :users
(table[:id].public_send(math_operator, 2)).to_sql.must_be_like %{
"users"."id" #{math_operator} 2
@@ -43,35 +43,35 @@ module Arel
end
%i[+ - & | ^ << >>].each do |math_operator|
- it "average should be compatiable with #{math_operator}" do
+ it "average should be compatible with #{math_operator}" do
table = Arel::Table.new :users
(table[:id].average.public_send(math_operator, 2)).to_sql.must_be_like %{
(AVG("users"."id") #{math_operator} 2)
}
end
- it "count should be compatiable with #{math_operator}" do
+ it "count should be compatible with #{math_operator}" do
table = Arel::Table.new :users
(table[:id].count.public_send(math_operator, 2)).to_sql.must_be_like %{
(COUNT("users"."id") #{math_operator} 2)
}
end
- it "maximum should be compatiable with #{math_operator}" do
+ it "maximum should be compatible with #{math_operator}" do
table = Arel::Table.new :users
(table[:id].maximum.public_send(math_operator, 2)).to_sql.must_be_like %{
(MAX("users"."id") #{math_operator} 2)
}
end
- it "minimum should be compatiable with #{math_operator}" do
+ it "minimum should be compatible with #{math_operator}" do
table = Arel::Table.new :users
(table[:id].minimum.public_send(math_operator, 2)).to_sql.must_be_like %{
(MIN("users"."id") #{math_operator} 2)
}
end
- it "attribute node should be compatiable with #{math_operator}" do
+ it "attribute node should be compatible with #{math_operator}" do
table = Arel::Table.new :users
(table[:id].public_send(math_operator, 2)).to_sql.must_be_like %{
("users"."id" #{math_operator} 2)
diff --git a/activerecord/test/cases/arel/insert_manager_test.rb b/activerecord/test/cases/arel/insert_manager_test.rb
index ae10ccf56c..2376ad8d37 100644
--- a/activerecord/test/cases/arel/insert_manager_test.rb
+++ b/activerecord/test/cases/arel/insert_manager_test.rb
@@ -220,7 +220,6 @@ module Arel
end
describe "select" do
-
it "accepts a select query in place of a VALUES clause" do
table = Table.new :users
@@ -238,7 +237,6 @@ module Arel
INSERT INTO "users" ("id", "name") (SELECT 1, "aaron")
}
end
-
end
end
end
diff --git a/activerecord/test/cases/arel/select_manager_test.rb b/activerecord/test/cases/arel/select_manager_test.rb
index f318577b94..c17487ae88 100644
--- a/activerecord/test/cases/arel/select_manager_test.rb
+++ b/activerecord/test/cases/arel/select_manager_test.rb
@@ -244,8 +244,6 @@ module Arel
@m2 = Arel::SelectManager.new table
@m2.project Arel.star
@m2.where(table[:age].gt(99))
-
-
end
it "should union two managers" do
@@ -266,7 +264,6 @@ module Arel
( SELECT * FROM "users" WHERE "users"."age" < 18 UNION ALL SELECT * FROM "users" WHERE "users"."age" > 99 )
}
end
-
end
describe "intersect" do
@@ -279,11 +276,9 @@ module Arel
@m2 = Arel::SelectManager.new table
@m2.project Arel.star
@m2.where(table[:age].lt(99))
-
-
end
- it "should interect two managers" do
+ it "should intersect two managers" do
# FIXME should this intersect "managers" or "statements" ?
# FIXME this probably shouldn't return a node
node = @m1.intersect @m2
@@ -293,7 +288,6 @@ module Arel
( SELECT * FROM "users" WHERE "users"."age" > 18 INTERSECT SELECT * FROM "users" WHERE "users"."age" < 99 )
}
end
-
end
describe "except" do
@@ -318,7 +312,6 @@ module Arel
( SELECT * FROM "users" WHERE "users"."age" BETWEEN 18 AND 60 EXCEPT SELECT * FROM "users" WHERE "users"."age" BETWEEN 40 AND 99 )
}
end
-
end
describe "with" do
@@ -647,7 +640,6 @@ module Arel
end
describe "joins" do
-
it "returns inner join sql" do
table = Table.new :users
aliaz = table.alias
@@ -1002,7 +994,6 @@ module Arel
end
describe "update" do
-
it "creates an update statement" do
table = Table.new :users
manager = Arel::SelectManager.new
@@ -1075,7 +1066,6 @@ module Arel
UPDATE "users" SET "id" = 1 WHERE "users"."id" IN (SELECT "users"."id" FROM "users" WHERE "users"."foo" = 10 LIMIT 42)
}
end
-
end
describe "project" do
@@ -1097,7 +1087,6 @@ module Arel
manager.project "*"
manager.to_sql.must_be_like %{ SELECT * }
end
-
end
describe "projections" do
diff --git a/activerecord/test/cases/arel/visitors/postgres_test.rb b/activerecord/test/cases/arel/visitors/postgres_test.rb
index ba37afecfb..f7f2c76b6f 100644
--- a/activerecord/test/cases/arel/visitors/postgres_test.rb
+++ b/activerecord/test/cases/arel/visitors/postgres_test.rb
@@ -215,7 +215,7 @@ module Arel
}
end
- it "should know how to generate paranthesis when supplied with many Dimensions" do
+ it "should know how to generate parenthesis when supplied with many Dimensions" do
dim1 = Arel::Nodes::GroupingElement.new(@table[:name])
dim2 = Arel::Nodes::GroupingElement.new([@table[:bool], @table[:created_at]])
node = Arel::Nodes::Cube.new([dim1, dim2])
@@ -229,7 +229,7 @@ module Arel
it "should know how to visit with array arguments" do
node = Arel::Nodes::GroupingSet.new([@table[:name], @table[:bool]])
compile(node).must_be_like %{
- GROUPING SET( "users"."name", "users"."bool" )
+ GROUPING SETS( "users"."name", "users"."bool" )
}
end
@@ -237,16 +237,16 @@ module Arel
group = Arel::Nodes::GroupingElement.new([@table[:name], @table[:bool]])
node = Arel::Nodes::GroupingSet.new(group)
compile(node).must_be_like %{
- GROUPING SET( "users"."name", "users"."bool" )
+ GROUPING SETS( "users"."name", "users"."bool" )
}
end
- it "should know how to generate paranthesis when supplied with many Dimensions" do
+ it "should know how to generate parenthesis when supplied with many Dimensions" do
group1 = Arel::Nodes::GroupingElement.new(@table[:name])
group2 = Arel::Nodes::GroupingElement.new([@table[:bool], @table[:created_at]])
node = Arel::Nodes::GroupingSet.new([group1, group2])
compile(node).must_be_like %{
- GROUPING SET( ( "users"."name" ), ( "users"."bool", "users"."created_at" ) )
+ GROUPING SETS( ( "users"."name" ), ( "users"."bool", "users"."created_at" ) )
}
end
end
@@ -267,7 +267,7 @@ module Arel
}
end
- it "should know how to generate paranthesis when supplied with many Dimensions" do
+ it "should know how to generate parenthesis when supplied with many Dimensions" do
group1 = Arel::Nodes::GroupingElement.new(@table[:name])
group2 = Arel::Nodes::GroupingElement.new([@table[:bool], @table[:created_at]])
node = Arel::Nodes::RollUp.new([group1, group2])
diff --git a/activerecord/test/cases/arel/visitors/to_sql_test.rb b/activerecord/test/cases/arel/visitors/to_sql_test.rb
index ce836eded7..e8ac50bfa3 100644
--- a/activerecord/test/cases/arel/visitors/to_sql_test.rb
+++ b/activerecord/test/cases/arel/visitors/to_sql_test.rb
@@ -427,7 +427,7 @@ module Arel
compile(node).must_equal %(("products"."price" - 7))
end
- it "should handle Concatination" do
+ it "should handle Concatenation" do
table = Table.new(:users)
node = table[:name].concat(table[:name])
compile(node).must_equal %("users"."name" || "users"."name")
diff --git a/activerecord/test/cases/associations/belongs_to_associations_test.rb b/activerecord/test/cases/associations/belongs_to_associations_test.rb
index 6cbde2796a..0cc4ed7127 100644
--- a/activerecord/test/cases/associations/belongs_to_associations_test.rb
+++ b/activerecord/test/cases/associations/belongs_to_associations_test.rb
@@ -557,6 +557,48 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
assert_equal 1, Topic.find(topic.id)[:replies_count]
end
+ def test_belongs_to_counter_after_touch
+ topic = Topic.create!(title: "topic")
+
+ assert_equal 0, topic.replies_count
+ assert_equal 0, topic.after_touch_called
+
+ reply = Reply.create!(title: "blah!", content: "world around!", topic_with_primary_key: topic)
+
+ assert_equal 1, topic.replies_count
+ assert_equal 1, topic.after_touch_called
+
+ reply.destroy!
+
+ assert_equal 0, topic.replies_count
+ assert_equal 2, topic.after_touch_called
+ end
+
+ def test_belongs_to_touch_with_reassigning
+ debate = Topic.create!(title: "debate")
+ debate2 = Topic.create!(title: "debate2")
+ reply = Reply.create!(title: "blah!", content: "world around!", parent_title: "debate2")
+
+ time = 1.day.ago
+
+ debate.touch(time: time)
+ debate2.touch(time: time)
+
+ reply.parent_title = "debate"
+ reply.save!
+
+ assert_operator debate.reload.updated_at, :>, time
+ assert_operator debate2.reload.updated_at, :>, time
+
+ debate.touch(time: time)
+ debate2.touch(time: time)
+
+ reply.topic_with_primary_key = debate2
+
+ assert_operator debate.reload.updated_at, :>, time
+ assert_operator debate2.reload.updated_at, :>, time
+ end
+
def test_belongs_to_with_touch_option_on_touch
line_item = LineItem.create!
Invoice.create!(line_items: [line_item])
diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb
index f46be8734b..ca902131f4 100644
--- a/activerecord/test/cases/associations/eager_test.rb
+++ b/activerecord/test/cases/associations/eager_test.rb
@@ -18,6 +18,7 @@ require "models/job"
require "models/subscriber"
require "models/subscription"
require "models/book"
+require "models/citation"
require "models/developer"
require "models/computer"
require "models/project"
@@ -29,6 +30,18 @@ require "models/sponsor"
require "models/mentor"
require "models/contract"
+class EagerLoadingTooManyIdsTest < ActiveRecord::TestCase
+ fixtures :citations
+
+ def test_preloading_too_many_ids
+ assert_equal Citation.count, Citation.preload(:citations).to_a.size
+ end
+
+ def test_eager_loading_too_may_ids
+ assert_equal Citation.count, Citation.eager_load(:citations).offset(0).size
+ end
+end
+
class EagerAssociationTest < ActiveRecord::TestCase
fixtures :posts, :comments, :authors, :essays, :author_addresses, :categories, :categories_posts,
:companies, :accounts, :tags, :taggings, :people, :readers, :categorizations,
@@ -77,8 +90,68 @@ class EagerAssociationTest < ActiveRecord::TestCase
end
def test_loading_with_scope_including_joins
- assert_equal clubs(:boring_club), Member.preload(:general_club).find(1).general_club
- assert_equal clubs(:boring_club), Member.eager_load(:general_club).find(1).general_club
+ member = Member.first
+ assert_equal members(:groucho), member
+ assert_equal clubs(:boring_club), member.general_club
+
+ member = Member.preload(:general_club).first
+ assert_equal members(:groucho), member
+ assert_equal clubs(:boring_club), member.general_club
+
+ member = Member.eager_load(:general_club).first
+ assert_equal members(:groucho), member
+ assert_equal clubs(:boring_club), member.general_club
+ end
+
+ def test_loading_association_with_same_table_joins
+ super_memberships = [memberships(:super_membership_of_boring_club)]
+
+ member = Member.joins(:favourite_memberships).first
+ assert_equal members(:groucho), member
+ assert_equal super_memberships, member.super_memberships
+
+ member = Member.joins(:favourite_memberships).preload(:super_memberships).first
+ assert_equal members(:groucho), member
+ assert_equal super_memberships, member.super_memberships
+
+ member = Member.joins(:favourite_memberships).eager_load(:super_memberships).first
+ assert_equal members(:groucho), member
+ assert_equal super_memberships, member.super_memberships
+ end
+
+ def test_loading_association_with_intersection_joins
+ member = Member.joins(:current_membership).first
+ assert_equal members(:groucho), member
+ assert_equal clubs(:boring_club), member.club
+ assert_equal memberships(:membership_of_boring_club), member.current_membership
+
+ member = Member.joins(:current_membership).preload(:club, :current_membership).first
+ assert_equal members(:groucho), member
+ assert_equal clubs(:boring_club), member.club
+ assert_equal memberships(:membership_of_boring_club), member.current_membership
+
+ member = Member.joins(:current_membership).eager_load(:club, :current_membership).first
+ assert_equal members(:groucho), member
+ assert_equal clubs(:boring_club), member.club
+ assert_equal memberships(:membership_of_boring_club), member.current_membership
+ end
+
+ def test_loading_associations_dont_leak_instance_state
+ assertions = ->(firm) {
+ assert_equal companies(:first_firm), firm
+
+ assert_predicate firm.association(:readonly_account), :loaded?
+ assert_predicate firm.association(:accounts), :loaded?
+
+ assert_equal accounts(:signals37), firm.readonly_account
+ assert_equal [accounts(:signals37)], firm.accounts
+
+ assert_predicate firm.readonly_account, :readonly?
+ assert firm.accounts.none?(&:readonly?)
+ }
+
+ assertions.call(Firm.preload(:readonly_account, :accounts).first)
+ assertions.call(Firm.eager_load(:readonly_account, :accounts).first)
end
def test_with_ordering
@@ -1511,8 +1584,9 @@ class EagerAssociationTest < ActiveRecord::TestCase
# CollectionProxy#reader is expensive, so the preloader avoids calling it.
test "preloading has_many_through association avoids calling association.reader" do
- ActiveRecord::Associations::HasManyAssociation.any_instance.expects(:reader).never
- Author.preload(:readonly_comments).first!
+ assert_not_called_on_instance_of(ActiveRecord::Associations::HasManyAssociation, :reader) do
+ Author.preload(:readonly_comments).first!
+ end
end
test "preloading through a polymorphic association doesn't require the association to exist" do
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 5d9735d98a..482302055d 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
@@ -697,24 +697,13 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
end
def test_join_table_alias
- # FIXME: `references` has no impact on the aliases generated for the join
- # query. The fact that we pass `:developers_projects_join` to `references`
- # and that the SQL string contains `developers_projects_join` is merely a
- # coincidence.
assert_equal(
3,
- Developer.references(:developers_projects_join).merge(
- includes: { projects: :developers },
- where: "projects_developers_projects_join.joined_on IS NOT NULL"
- ).to_a.size
+ Developer.includes(projects: :developers).where.not("projects_developers_projects_join.joined_on": nil).to_a.size
)
end
def test_join_with_group
- # FIXME: `references` has no impact on the aliases generated for the join
- # query. The fact that we pass `:developers_projects_join` to `references`
- # and that the SQL string contains `developers_projects_join` is merely a
- # coincidence.
group = Developer.columns.inject([]) do |g, c|
g << "developers.#{c.name}"
g << "developers_projects_2.#{c.name}"
@@ -723,10 +712,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
assert_equal(
3,
- Developer.references(:developers_projects_join).merge(
- includes: { projects: :developers }, where: "projects_developers_projects_join.joined_on IS NOT NULL",
- group: group.join(",")
- ).to_a.size
+ Developer.includes(projects: :developers).where.not("projects_developers_projects_join.joined_on": nil).group(group.join(",")).to_a.size
)
end
@@ -795,7 +781,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
end
def test_has_many_through_polymorphic_has_manys_works
- assert_equal [10, 20].to_set, pirates(:redbeard).treasure_estimates.map(&:price).to_set
+ assert_equal ["$10.00", "$20.00"].to_set, pirates(:redbeard).treasure_estimates.map(&:price).to_set
end
def test_symbols_as_keys
diff --git a/activerecord/test/cases/associations/has_many_associations_test.rb b/activerecord/test/cases/associations/has_many_associations_test.rb
index cc8f33f142..a2f6174dc1 100644
--- a/activerecord/test/cases/associations/has_many_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_associations_test.rb
@@ -27,7 +27,6 @@ require "models/categorization"
require "models/minivan"
require "models/speedometer"
require "models/reference"
-require "models/job"
require "models/college"
require "models/student"
require "models/pirate"
@@ -114,7 +113,7 @@ end
class HasManyAssociationsTest < ActiveRecord::TestCase
fixtures :accounts, :categories, :companies, :developers, :projects,
:developers_projects, :topics, :authors, :author_addresses, :comments,
- :posts, :readers, :taggings, :cars, :jobs, :tags,
+ :posts, :readers, :taggings, :cars, :tags,
:categorizations, :zines, :interests
def setup
@@ -377,6 +376,27 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
assert_equal invoice.id, line_item.invoice_id
end
+ class SpecialAuthor < ActiveRecord::Base
+ self.table_name = "authors"
+ has_many :books, class_name: "SpecialBook", foreign_key: :author_id
+ end
+
+ class SpecialBook < ActiveRecord::Base
+ self.table_name = "books"
+
+ belongs_to :author
+ enum read_status: { unread: 0, reading: 2, read: 3, forgotten: nil }
+ end
+
+ def test_association_enum_works_properly
+ author = SpecialAuthor.create!(name: "Test")
+ book = SpecialBook.create!(read_status: "reading")
+ author.books << book
+
+ assert_equal "reading", book.read_status
+ assert_not_equal 0, SpecialAuthor.joins(:books).where(books: { read_status: "reading" }).count
+ end
+
# When creating objects on the association, we must not do it within a scope (even though it
# would be convenient), because this would cause that scope to be applied to any callbacks etc.
def test_build_and_create_should_not_happen_within_scope
@@ -2134,21 +2154,29 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
def test_defining_has_many_association_with_delete_all_dependency_lazily_evaluates_target_class
- ActiveRecord::Reflection::AssociationReflection.any_instance.expects(:class_name).never
- class_eval(<<-EOF, __FILE__, __LINE__ + 1)
- class DeleteAllModel < ActiveRecord::Base
- has_many :nonentities, :dependent => :delete_all
- end
- EOF
+ assert_not_called_on_instance_of(
+ ActiveRecord::Reflection::AssociationReflection,
+ :class_name,
+ ) do
+ class_eval(<<-EOF, __FILE__, __LINE__ + 1)
+ class DeleteAllModel < ActiveRecord::Base
+ has_many :nonentities, :dependent => :delete_all
+ end
+ EOF
+ end
end
def test_defining_has_many_association_with_nullify_dependency_lazily_evaluates_target_class
- ActiveRecord::Reflection::AssociationReflection.any_instance.expects(:class_name).never
- class_eval(<<-EOF, __FILE__, __LINE__ + 1)
- class NullifyModel < ActiveRecord::Base
- has_many :nonentities, :dependent => :nullify
- end
- EOF
+ assert_not_called_on_instance_of(
+ ActiveRecord::Reflection::AssociationReflection,
+ :class_name,
+ ) do
+ class_eval(<<-EOF, __FILE__, __LINE__ + 1)
+ class NullifyModel < ActiveRecord::Base
+ has_many :nonentities, :dependent => :nullify
+ end
+ EOF
+ end
end
def test_attributes_are_being_set_when_initialized_from_has_many_association_with_where_clause
@@ -2705,6 +2733,17 @@ class HasManyAssociationsTest < ActiveRecord::TestCase
end
end
+ def test_create_children_could_be_rolled_back_by_after_save
+ firm = Firm.create!(name: "A New Firm, Inc")
+ assert_no_difference "Client.count" do
+ client = firm.clients.create(name: "New Client") do |cli|
+ cli.rollback_on_save = true
+ assert_not cli.rollback_on_create_called
+ end
+ assert client.rollback_on_create_called
+ end
+ end
+
private
def force_signal37_to_load_all_clients_of_firm
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 0facc286da..442f4a93d4 100644
--- a/activerecord/test/cases/associations/has_many_through_associations_test.rb
+++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb
@@ -71,6 +71,15 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
club1.members.sort_by(&:id)
end
+ def test_preload_multiple_instances_of_the_same_record
+ club = Club.create!(name: "Aaron cool banana club")
+ Membership.create! club: club, member: Member.create!(name: "Aaron")
+ Membership.create! club: club, member: Member.create!(name: "Bob")
+
+ preloaded_clubs = Club.joins(:memberships).preload(:membership).to_a
+ assert_no_queries { preloaded_clubs.each(&:membership) }
+ end
+
def test_ordered_has_many_through
person_prime = Class.new(ActiveRecord::Base) do
def self.name; "Person"; end
@@ -737,6 +746,18 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
[:added, :before, "Roger"],
[:added, :after, "Roger"]
], log.last(4)
+
+ post.people_with_callbacks.build { |person| person.first_name = "Ted" }
+ assert_equal [
+ [:added, :before, "Ted"],
+ [:added, :after, "Ted"]
+ ], log.last(2)
+
+ post.people_with_callbacks.create { |person| person.first_name = "Sam" }
+ assert_equal [
+ [:added, :before, "Sam"],
+ [:added, :after, "Sam"]
+ ], log.last(2)
end
def test_dynamic_find_should_respect_association_include
@@ -1277,6 +1298,10 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase
assert_equal authors(:david), Author.joins(:comments_for_first_author).take
end
+ def test_has_many_through_with_left_joined_same_table_with_through_table
+ assert_equal [comments(:eager_other_comment1)], authors(:mary).comments.left_joins(:post)
+ end
+
def test_has_many_through_with_unscope_should_affect_to_through_scope
assert_equal [comments(:eager_other_comment1)], authors(:mary).unordered_comments
end
diff --git a/activerecord/test/cases/associations/has_one_associations_test.rb b/activerecord/test/cases/associations/has_one_associations_test.rb
index d7e898a1c0..9eea34d2b9 100644
--- a/activerecord/test/cases/associations/has_one_associations_test.rb
+++ b/activerecord/test/cases/associations/has_one_associations_test.rb
@@ -661,6 +661,8 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
self.table_name = "books"
belongs_to :author, class_name: "SpecialAuthor"
has_one :subscription, class_name: "SpecialSupscription", foreign_key: "subscriber_id"
+
+ enum status: [:proposed, :written, :published]
end
class SpecialAuthor < ActiveRecord::Base
@@ -678,6 +680,7 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
book = SpecialBook.create!(status: "published")
author.book = book
+ assert_equal "published", book.status
assert_not_equal 0, SpecialAuthor.joins(:book).where(books: { status: "published" }).count
end
diff --git a/activerecord/test/cases/associations/inverse_associations_test.rb b/activerecord/test/cases/associations/inverse_associations_test.rb
index da3a42e2b5..eb4dc73423 100644
--- a/activerecord/test/cases/associations/inverse_associations_test.rb
+++ b/activerecord/test/cases/associations/inverse_associations_test.rb
@@ -20,6 +20,8 @@ require "models/company"
require "models/project"
require "models/author"
require "models/post"
+require "models/department"
+require "models/hotel"
class AutomaticInverseFindingTests < ActiveRecord::TestCase
fixtures :ratings, :comments, :cars
@@ -724,6 +726,16 @@ class InversePolymorphicBelongsToTests < ActiveRecord::TestCase
# fails because Interest does have the correct inverse_of
assert_raise(ActiveRecord::InverseOfAssociationNotFoundError) { Face.first.polymorphic_man = Interest.first }
end
+
+ def test_favors_has_one_associations_for_inverse_of
+ inverse_name = Post.reflect_on_association(:author).inverse_of.name
+ assert_equal :post, inverse_name
+ end
+
+ def test_finds_inverse_of_for_plural_associations
+ inverse_name = Department.reflect_on_association(:hotel).inverse_of.name
+ assert_equal :departments, inverse_name
+ end
end
# NOTE - these tests might not be meaningful, ripped as they were from the parental_control plugin
diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb
index 434d32846c..0bfd46a522 100644
--- a/activerecord/test/cases/attribute_methods_test.rb
+++ b/activerecord/test/cases/attribute_methods_test.rb
@@ -163,19 +163,6 @@ class AttributeMethodsTest < ActiveRecord::TestCase
assert_equal "10", keyboard.read_attribute_before_type_cast(:key_number)
end
- # Syck calls respond_to? before actually calling initialize.
- test "respond_to? with an allocated object" do
- klass = Class.new(ActiveRecord::Base) do
- self.table_name = "topics"
- end
-
- topic = klass.allocate
- assert_not_respond_to topic, "nothingness"
- assert_not_respond_to topic, :nothingness
- assert_respond_to topic, "title"
- assert_respond_to topic, :title
- end
-
# IRB inspects the return value of MyModel.allocate.
test "allocated objects can be inspected" do
topic = Topic.allocate
diff --git a/activerecord/test/cases/autosave_association_test.rb b/activerecord/test/cases/autosave_association_test.rb
index 7355f4cd62..db3a58eba9 100644
--- a/activerecord/test/cases/autosave_association_test.rb
+++ b/activerecord/test/cases/autosave_association_test.rb
@@ -14,6 +14,7 @@ require "models/line_item"
require "models/order"
require "models/parrot"
require "models/pirate"
+require "models/project"
require "models/ship"
require "models/ship_part"
require "models/tag"
@@ -27,6 +28,7 @@ require "models/member_detail"
require "models/organization"
require "models/guitar"
require "models/tuning_peg"
+require "models/reply"
class TestAutosaveAssociationsInGeneral < ActiveRecord::TestCase
def test_autosave_validation
@@ -557,6 +559,34 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociation < ActiveRecord::TestCa
assert_equal no_of_clients + 1, Client.count
end
+ def test_parent_should_save_children_record_with_foreign_key_validation_set_in_before_save_callback
+ company = NewlyContractedCompany.new(name: "test")
+
+ assert company.save
+ assert_not_empty company.reload.new_contracts
+ end
+
+ def test_parent_should_not_get_saved_with_duplicate_children_records
+ assert_no_difference "Reply.count" do
+ assert_no_difference "SillyUniqueReply.count" do
+ reply = Reply.new
+ reply.silly_unique_replies.build([
+ { content: "Best content" },
+ { content: "Best content" }
+ ])
+
+ assert_not reply.save
+ assert_equal ["is invalid"], reply.errors[:silly_unique_replies]
+ assert_empty reply.silly_unique_replies.first.errors
+
+ assert_equal(
+ ["has already been taken"],
+ reply.silly_unique_replies.last.errors[:content]
+ )
+ end
+ end
+ end
+
def test_invalid_build
new_client = companies(:first_firm).clients_of_firm.build
assert_not_predicate new_client, :persisted?
@@ -1758,3 +1788,21 @@ class TestAutosaveAssociationOnAHasManyAssociationWithInverse < ActiveRecord::Te
assert_equal 1, comment.post_comments_count
end
end
+
+class TestAutosaveAssociationOnAHasManyAssociationDefinedInSubclassWithAcceptsNestedAttributes < ActiveRecord::TestCase
+ def test_should_update_children_when_asssociation_redefined_in_subclass
+ agency = Agency.create!(name: "Agency")
+ valid_project = Project.create!(firm: agency, name: "Initial")
+ agency.update!(
+ "projects_attributes" => {
+ "0" => {
+ "name" => "Updated",
+ "id" => valid_project.id
+ }
+ }
+ )
+ valid_project.reload
+
+ assert_equal "Updated", valid_project.name
+ end
+end
diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb
index fcfab074a2..f6311f9256 100644
--- a/activerecord/test/cases/base_test.rb
+++ b/activerecord/test/cases/base_test.rb
@@ -14,7 +14,6 @@ require "models/computer"
require "models/project"
require "models/default"
require "models/auto_id"
-require "models/boolean"
require "models/column_name"
require "models/subscriber"
require "models/comment"
@@ -716,48 +715,6 @@ class BasicsTest < ActiveRecord::TestCase
assert_equal expected_attributes, category.attributes
end
- def test_boolean
- b_nil = Boolean.create("value" => nil)
- nil_id = b_nil.id
- b_false = Boolean.create("value" => false)
- false_id = b_false.id
- b_true = Boolean.create("value" => true)
- true_id = b_true.id
-
- b_nil = Boolean.find(nil_id)
- assert_nil b_nil.value
- b_false = Boolean.find(false_id)
- assert_not_predicate b_false, :value?
- b_true = Boolean.find(true_id)
- assert_predicate b_true, :value?
- end
-
- def test_boolean_without_questionmark
- b_true = Boolean.create("value" => true)
- true_id = b_true.id
-
- subclass = Class.new(Boolean).find true_id
- superclass = Boolean.find true_id
-
- assert_equal superclass.read_attribute(:has_fun), subclass.read_attribute(:has_fun)
- end
-
- def test_boolean_cast_from_string
- b_blank = Boolean.create("value" => "")
- blank_id = b_blank.id
- b_false = Boolean.create("value" => "0")
- false_id = b_false.id
- b_true = Boolean.create("value" => "1")
- true_id = b_true.id
-
- b_blank = Boolean.find(blank_id)
- assert_nil b_blank.value
- b_false = Boolean.find(false_id)
- assert_not_predicate b_false, :value?
- b_true = Boolean.find(true_id)
- assert_predicate b_true, :value?
- end
-
def test_new_record_returns_boolean
assert_equal false, Topic.new.persisted?
assert_equal true, Topic.find(1).persisted?
@@ -896,8 +853,7 @@ class BasicsTest < ActiveRecord::TestCase
assert_equal company, Company.find(company.id)
end
- # TODO: extend defaults tests to other databases!
- if current_adapter?(:PostgreSQLAdapter)
+ if current_adapter?(:PostgreSQLAdapter, :Mysql2Adapter, :SQLite3Adapter)
def test_default
with_timezone_config default: :local do
default = Default.new
@@ -909,7 +865,10 @@ class BasicsTest < ActiveRecord::TestCase
# char types
assert_equal "Y", default.char1
assert_equal "a varchar field", default.char2
- assert_equal "a text field", default.char3
+ # Mysql text type can't have default value
+ unless current_adapter?(:Mysql2Adapter)
+ assert_equal "a text field", default.char3
+ end
end
end
end
diff --git a/activerecord/test/cases/bind_parameter_test.rb b/activerecord/test/cases/bind_parameter_test.rb
index 91cc49385c..9c1f7aaef2 100644
--- a/activerecord/test/cases/bind_parameter_test.rb
+++ b/activerecord/test/cases/bind_parameter_test.rb
@@ -34,6 +34,12 @@ if ActiveRecord::Base.connection.prepared_statements
ActiveSupport::Notifications.unsubscribe(@subscription)
end
+ def test_too_many_binds
+ bind_params_length = @connection.send(:bind_params_length)
+ topics = Topic.where(id: (1 .. bind_params_length + 1).to_a)
+ assert_equal Topic.count, topics.count
+ end
+
def test_bind_from_join_in_subquery
subquery = Author.joins(:thinking_posts).where(name: "David")
scope = Author.from(subquery, "authors").where(id: 1)
diff --git a/activerecord/test/cases/boolean_test.rb b/activerecord/test/cases/boolean_test.rb
new file mode 100644
index 0000000000..ab9f974e2c
--- /dev/null
+++ b/activerecord/test/cases/boolean_test.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+require "cases/helper"
+require "models/boolean"
+
+class BooleanTest < ActiveRecord::TestCase
+ def test_boolean
+ b_nil = Boolean.create!(value: nil)
+ b_false = Boolean.create!(value: false)
+ b_true = Boolean.create!(value: true)
+
+ assert_nil Boolean.find(b_nil.id).value
+ assert_not_predicate Boolean.find(b_false.id), :value?
+ assert_predicate Boolean.find(b_true.id), :value?
+ end
+
+ def test_boolean_without_questionmark
+ b_true = Boolean.create!(value: true)
+
+ subclass = Class.new(Boolean).find(b_true.id)
+ superclass = Boolean.find(b_true.id)
+
+ assert_equal superclass.read_attribute(:has_fun), subclass.read_attribute(:has_fun)
+ end
+
+ def test_boolean_cast_from_string
+ b_blank = Boolean.create!(value: "")
+ b_false = Boolean.create!(value: "0")
+ b_true = Boolean.create!(value: "1")
+
+ assert_nil Boolean.find(b_blank.id).value
+ assert_not_predicate Boolean.find(b_false.id), :value?
+ assert_predicate Boolean.find(b_true.id), :value?
+ end
+
+ def test_find_by_boolean_string
+ b_false = Boolean.create!(value: "false")
+ b_true = Boolean.create!(value: "true")
+
+ assert_equal b_false, Boolean.find_by(value: "false")
+ assert_equal b_true, Boolean.find_by(value: "true")
+ end
+end
diff --git a/activerecord/test/cases/calculations_test.rb b/activerecord/test/cases/calculations_test.rb
index 080d2a54bc..5c9ed42173 100644
--- a/activerecord/test/cases/calculations_test.rb
+++ b/activerecord/test/cases/calculations_test.rb
@@ -642,6 +642,18 @@ class CalculationsTest < ActiveRecord::TestCase
assert_equal [ topic.written_on ], relation.pluck(:written_on)
end
+ def test_pluck_with_type_cast_does_not_corrupt_the_query_cache
+ topic = topics(:first)
+ relation = Topic.where(id: topic.id)
+ assert_queries 1 do
+ Topic.cache do
+ kind = relation.select(:written_on).load.first.read_attribute_before_type_cast(:written_on).class
+ relation.pluck(:written_on)
+ assert_kind_of kind, relation.select(:written_on).load.first.read_attribute_before_type_cast(:written_on)
+ end
+ end
+ end
+
def test_pluck_and_distinct
assert_equal [50, 53, 55, 60], Account.order(:credit_limit).distinct.pluck(:credit_limit)
end
@@ -705,6 +717,24 @@ class CalculationsTest < ActiveRecord::TestCase
assert_equal [], Topic.includes(:replies).order(:id).offset(5).pluck(:id)
end
+ def test_group_by_with_limit
+ expected = { "Post" => 8, "SpecialPost" => 1 }
+ actual = Post.includes(:comments).group(:type).order(:type).limit(2).count("comments.id")
+ assert_equal expected, actual
+ end
+
+ def test_group_by_with_offset
+ expected = { "SpecialPost" => 1, "StiPost" => 2 }
+ actual = Post.includes(:comments).group(:type).order(:type).offset(1).count("comments.id")
+ assert_equal expected, actual
+ end
+
+ def test_group_by_with_limit_and_offset
+ expected = { "SpecialPost" => 1 }
+ actual = Post.includes(:comments).group(:type).order(:type).offset(1).limit(1).count("comments.id")
+ assert_equal expected, actual
+ end
+
def test_pluck_not_auto_table_name_prefix_if_column_included
Company.create!(name: "test", contracts: [Contract.new(developer_id: 7)])
ids = Company.includes(:contracts).pluck(:developer_id)
diff --git a/activerecord/test/cases/callbacks_test.rb b/activerecord/test/cases/callbacks_test.rb
index b9ba51c730..253c3099d6 100644
--- a/activerecord/test/cases/callbacks_test.rb
+++ b/activerecord/test/cases/callbacks_test.rb
@@ -476,4 +476,31 @@ class CallbacksTest < ActiveRecord::TestCase
child.save
assert child.after_save_called
end
+
+ def test_before_save_doesnt_allow_on_option
+ exception = assert_raises ArgumentError do
+ Class.new(ActiveRecord::Base) do
+ before_save(on: :create) {}
+ end
+ end
+ assert_equal "Unknown key: :on. Valid keys are: :if, :unless, :prepend", exception.message
+ end
+
+ def test_around_save_doesnt_allow_on_option
+ exception = assert_raises ArgumentError do
+ Class.new(ActiveRecord::Base) do
+ around_save(on: :create) {}
+ end
+ end
+ assert_equal "Unknown key: :on. Valid keys are: :if, :unless, :prepend", exception.message
+ end
+
+ def test_after_save_doesnt_allow_on_option
+ exception = assert_raises ArgumentError do
+ Class.new(ActiveRecord::Base) do
+ after_save(on: :create) {}
+ end
+ end
+ assert_equal "Unknown key: :on. Valid keys are: :if, :unless, :prepend", exception.message
+ end
end
diff --git a/activerecord/test/cases/connection_adapters/connection_handler_test.rb b/activerecord/test/cases/connection_adapters/connection_handler_test.rb
index b8e623f17b..8c204a2692 100644
--- a/activerecord/test/cases/connection_adapters/connection_handler_test.rb
+++ b/activerecord/test/cases/connection_adapters/connection_handler_test.rb
@@ -28,13 +28,16 @@ module ActiveRecord
end
def test_establish_connection_uses_spec_name
+ old_config = ActiveRecord::Base.configurations
config = { "readonly" => { "adapter" => "sqlite3" } }
- resolver = ConnectionAdapters::ConnectionSpecification::Resolver.new(config)
+ ActiveRecord::Base.configurations = config
+ resolver = ConnectionAdapters::ConnectionSpecification::Resolver.new(ActiveRecord::Base.configurations)
spec = resolver.spec(:readonly)
@handler.establish_connection(spec.to_hash)
assert_not_nil @handler.retrieve_connection_pool("readonly")
ensure
+ ActiveRecord::Base.configurations = old_config
@handler.remove_connection("readonly")
end
diff --git a/activerecord/test/cases/connection_adapters/merge_and_resolve_default_url_config_test.rb b/activerecord/test/cases/connection_adapters/merge_and_resolve_default_url_config_test.rb
index 1b64324cc4..06c1c51724 100644
--- a/activerecord/test/cases/connection_adapters/merge_and_resolve_default_url_config_test.rb
+++ b/activerecord/test/cases/connection_adapters/merge_and_resolve_default_url_config_test.rb
@@ -18,11 +18,14 @@ module ActiveRecord
end
def resolve_config(config)
- ActiveRecord::ConnectionHandling::MergeAndResolveDefaultUrlConfig.new(config).resolve
+ configs = ActiveRecord::DatabaseConfigurations.new(config)
+ configs.to_h
end
def resolve_spec(spec, config)
- ConnectionSpecification::Resolver.new(resolve_config(config)).resolve(spec)
+ configs = ActiveRecord::DatabaseConfigurations.new(config)
+ resolver = ConnectionAdapters::ConnectionSpecification::Resolver.new(configs)
+ resolver.resolve(spec, spec)
end
def test_resolver_with_database_uri_and_current_env_symbol_key
diff --git a/activerecord/test/cases/connection_pool_test.rb b/activerecord/test/cases/connection_pool_test.rb
index 9ac03629c3..06869eeab0 100644
--- a/activerecord/test/cases/connection_pool_test.rb
+++ b/activerecord/test/cases/connection_pool_test.rb
@@ -91,7 +91,9 @@ module ActiveRecord
end
def test_full_pool_exception
+ @pool.checkout_timeout = 0.001 # no need to delay test suite by waiting the whole full default timeout
@pool.size.times { assert @pool.checkout }
+
assert_raises(ConnectionTimeoutError) do
@pool.checkout
end
@@ -109,6 +111,44 @@ module ActiveRecord
assert_equal connection, t.join.value
end
+ def test_full_pool_blocking_shares_load_interlock
+ @pool.instance_variable_set(:@size, 1)
+
+ load_interlock_latch = Concurrent::CountDownLatch.new
+ connection_latch = Concurrent::CountDownLatch.new
+
+ able_to_get_connection = false
+ able_to_load = false
+
+ thread_with_load_interlock = Thread.new do
+ ActiveSupport::Dependencies.interlock.running do
+ load_interlock_latch.count_down
+ connection_latch.wait
+
+ @pool.with_connection do
+ able_to_get_connection = true
+ end
+ end
+ end
+
+ thread_with_last_connection = Thread.new do
+ @pool.with_connection do
+ connection_latch.count_down
+ load_interlock_latch.wait
+
+ ActiveSupport::Dependencies.interlock.loading do
+ able_to_load = true
+ end
+ end
+ end
+
+ thread_with_load_interlock.join
+ thread_with_last_connection.join
+
+ assert able_to_get_connection
+ assert able_to_load
+ end
+
def test_removing_releases_latch
cs = @pool.size.times.map { @pool.checkout }
t = Thread.new { @pool.checkout }
@@ -156,6 +196,48 @@ module ActiveRecord
@pool.connections.each { |conn| conn.close if conn.in_use? }
end
+ def test_idle_timeout_configuration
+ @pool.disconnect!
+ spec = ActiveRecord::Base.connection_pool.spec
+ spec.config.merge!(idle_timeout: "0.02")
+ @pool = ConnectionPool.new(spec)
+ idle_conn = @pool.checkout
+ @pool.checkin(idle_conn)
+
+ idle_conn.instance_variable_set(
+ :@idle_since,
+ Concurrent.monotonic_time - 0.01
+ )
+
+ @pool.flush
+ assert_equal 1, @pool.connections.length
+
+ idle_conn.instance_variable_set(
+ :@idle_since,
+ Concurrent.monotonic_time - 0.02
+ )
+
+ @pool.flush
+ assert_equal 0, @pool.connections.length
+ end
+
+ def test_disable_flush
+ @pool.disconnect!
+ spec = ActiveRecord::Base.connection_pool.spec
+ spec.config.merge!(idle_timeout: -5)
+ @pool = ConnectionPool.new(spec)
+ idle_conn = @pool.checkout
+ @pool.checkin(idle_conn)
+
+ idle_conn.instance_variable_set(
+ :@idle_since,
+ Concurrent.monotonic_time - 1
+ )
+
+ @pool.flush
+ assert_equal 1, @pool.connections.length
+ end
+
def test_flush
idle_conn = @pool.checkout
recent_conn = @pool.checkout
@@ -166,9 +248,10 @@ module ActiveRecord
assert_equal 3, @pool.connections.length
- def idle_conn.seconds_idle
- 1000
- end
+ idle_conn.instance_variable_set(
+ :@idle_since,
+ Concurrent.monotonic_time - 1000
+ )
@pool.flush(30)
diff --git a/activerecord/test/cases/connection_specification/resolver_test.rb b/activerecord/test/cases/connection_specification/resolver_test.rb
index 5b80f16a44..72be14f507 100644
--- a/activerecord/test/cases/connection_specification/resolver_test.rb
+++ b/activerecord/test/cases/connection_specification/resolver_test.rb
@@ -7,11 +7,15 @@ module ActiveRecord
class ConnectionSpecification
class ResolverTest < ActiveRecord::TestCase
def resolve(spec, config = {})
- Resolver.new(config).resolve(spec)
+ configs = ActiveRecord::DatabaseConfigurations.new(config)
+ resolver = ConnectionAdapters::ConnectionSpecification::Resolver.new(configs)
+ resolver.resolve(spec, spec)
end
def spec(spec, config = {})
- Resolver.new(config).spec(spec)
+ configs = ActiveRecord::DatabaseConfigurations.new(config)
+ resolver = ConnectionAdapters::ConnectionSpecification::Resolver.new(configs)
+ resolver.spec(spec)
end
def test_url_invalid_adapter
diff --git a/activerecord/test/cases/counter_cache_test.rb b/activerecord/test/cases/counter_cache_test.rb
index e0948f90ac..99d286dc52 100644
--- a/activerecord/test/cases/counter_cache_test.rb
+++ b/activerecord/test/cases/counter_cache_test.rb
@@ -280,38 +280,38 @@ class CounterCacheTest < ActiveRecord::TestCase
end
test "update counters with touch: :written_on" do
- assert_touching @topic, :written_on do
+ assert_touching @topic, :updated_at, :written_on do
Topic.update_counters(@topic.id, replies_count: -1, touch: :written_on)
end
end
test "update multiple counters with touch: :written_on" do
- assert_touching @topic, :written_on do
+ assert_touching @topic, :updated_at, :written_on do
Topic.update_counters(@topic.id, replies_count: 2, unique_replies_count: 2, touch: :written_on)
end
end
test "reset counters with touch: :written_on" do
- assert_touching @topic, :written_on do
+ assert_touching @topic, :updated_at, :written_on do
Topic.reset_counters(@topic.id, :replies, touch: :written_on)
end
end
test "reset multiple counters with touch: :written_on" do
- assert_touching @topic, :written_on do
+ assert_touching @topic, :updated_at, :written_on do
Topic.update_counters(@topic.id, replies_count: 1, unique_replies_count: 1)
Topic.reset_counters(@topic.id, :replies, :unique_replies, touch: :written_on)
end
end
test "increment counters with touch: :written_on" do
- assert_touching @topic, :written_on do
+ assert_touching @topic, :updated_at, :written_on do
Topic.increment_counter(:replies_count, @topic.id, touch: :written_on)
end
end
test "decrement counters with touch: :written_on" do
- assert_touching @topic, :written_on do
+ assert_touching @topic, :updated_at, :written_on do
Topic.decrement_counter(:replies_count, @topic.id, touch: :written_on)
end
end
diff --git a/activerecord/test/cases/defaults_test.rb b/activerecord/test/cases/defaults_test.rb
index 1c96aaabe2..0f957d41cf 100644
--- a/activerecord/test/cases/defaults_test.rb
+++ b/activerecord/test/cases/defaults_test.rb
@@ -106,21 +106,31 @@ if current_adapter?(:Mysql2Adapter)
class MysqlDefaultExpressionTest < ActiveRecord::TestCase
include SchemaDumpingHelper
- if ActiveRecord::Base.connection.version >= "5.6.0"
+ if subsecond_precision_supported?
test "schema dump datetime includes default expression" do
output = dump_table_schema("datetime_defaults")
- assert_match %r/t\.datetime\s+"modified_datetime",\s+default: -> { "CURRENT_TIMESTAMP" }/, output
+ assert_match %r/t\.datetime\s+"modified_datetime",\s+default: -> { "CURRENT_TIMESTAMP(?:\(\))?" }/i, output
end
- end
- test "schema dump timestamp includes default expression" do
- output = dump_table_schema("timestamp_defaults")
- assert_match %r/t\.timestamp\s+"modified_timestamp",\s+default: -> { "CURRENT_TIMESTAMP" }/, output
- end
+ test "schema dump datetime includes precise default expression" do
+ output = dump_table_schema("datetime_defaults")
+ assert_match %r/t\.datetime\s+"precise_datetime",.+default: -> { "CURRENT_TIMESTAMP\(6\)" }/i, output
+ end
- test "schema dump timestamp without default expression" do
- output = dump_table_schema("timestamp_defaults")
- assert_match %r/t\.timestamp\s+"nullable_timestamp"$/, output
+ test "schema dump timestamp includes default expression" do
+ output = dump_table_schema("timestamp_defaults")
+ assert_match %r/t\.timestamp\s+"modified_timestamp",\s+default: -> { "CURRENT_TIMESTAMP(?:\(\))?" }/i, output
+ end
+
+ test "schema dump timestamp includes precise default expression" do
+ output = dump_table_schema("timestamp_defaults")
+ assert_match %r/t\.timestamp\s+"precise_timestamp",.+default: -> { "CURRENT_TIMESTAMP\(6\)" }/i, output
+ end
+
+ test "schema dump timestamp without default expression" do
+ output = dump_table_schema("timestamp_defaults")
+ assert_match %r/t\.timestamp\s+"nullable_timestamp"$/, output
+ end
end
end
diff --git a/activerecord/test/cases/dirty_test.rb b/activerecord/test/cases/dirty_test.rb
index 83cc2aa319..b1ebd20d6b 100644
--- a/activerecord/test/cases/dirty_test.rb
+++ b/activerecord/test/cases/dirty_test.rb
@@ -567,8 +567,6 @@ class DirtyTest < ActiveRecord::TestCase
assert_not_nil pirate.previous_changes["updated_on"][1]
assert_not pirate.previous_changes.key?("parrot_id")
assert_not pirate.previous_changes.key?("created_on")
- ensure
- travel_back
end
class Testings < ActiveRecord::Base; end
@@ -879,6 +877,26 @@ class DirtyTest < ActiveRecord::TestCase
raise "changed? should be false" if changed?
raise "has_changes_to_save? should be false" if has_changes_to_save?
raise "saved_changes? should be true" unless saved_changes?
+ raise "id_in_database should not be nil" if id_in_database.nil?
+ end
+ end
+
+ person = klass.create!(first_name: "Sean")
+ assert_not_predicate person, :changed?
+ end
+
+ test "changed? in around callbacks after yield returns false" do
+ klass = Class.new(ActiveRecord::Base) do
+ self.table_name = "people"
+
+ around_create :check_around
+
+ def check_around
+ yield
+ raise "changed? should be false" if changed?
+ raise "has_changes_to_save? should be false" if has_changes_to_save?
+ raise "saved_changes? should be true" unless saved_changes?
+ raise "id_in_database should not be nil" if id_in_database.nil?
end
end
diff --git a/activerecord/test/cases/explain_subscriber_test.rb b/activerecord/test/cases/explain_subscriber_test.rb
index 82cc891970..79a0630193 100644
--- a/activerecord/test/cases/explain_subscriber_test.rb
+++ b/activerecord/test/cases/explain_subscriber_test.rb
@@ -40,7 +40,7 @@ if ActiveRecord::Base.connection.supports_explain?
assert_equal binds, queries[0][1]
end
- def test_collects_nothing_if_the_statement_is_not_whitelisted
+ def test_collects_nothing_if_the_statement_is_not_explainable
SUBSCRIBER.finish(nil, nil, name: "SQL", sql: "SHOW max_identifier_length")
assert_empty queries
end
diff --git a/activerecord/test/cases/filter_attributes_test.rb b/activerecord/test/cases/filter_attributes_test.rb
new file mode 100644
index 0000000000..af5badd87d
--- /dev/null
+++ b/activerecord/test/cases/filter_attributes_test.rb
@@ -0,0 +1,84 @@
+# frozen_string_literal: true
+
+require "cases/helper"
+require "models/admin"
+require "models/admin/user"
+require "models/admin/account"
+require "pp"
+
+class FilterAttributesTest < ActiveRecord::TestCase
+ fixtures :"admin/users", :"admin/accounts"
+
+ setup do
+ @previous_filter_attributes = ActiveRecord::Base.filter_attributes
+ ActiveRecord::Base.filter_attributes = [:name]
+ end
+
+ teardown do
+ ActiveRecord::Base.filter_attributes = @previous_filter_attributes
+ end
+
+ test "filter_attributes" do
+ Admin::User.all.each do |user|
+ assert_includes user.inspect, "name: [FILTERED]"
+ assert_equal 1, user.inspect.scan("[FILTERED]").length
+ end
+
+ Admin::Account.all.each do |account|
+ assert_includes account.inspect, "name: [FILTERED]"
+ assert_equal 1, account.inspect.scan("[FILTERED]").length
+ end
+ end
+
+ test "filter_attributes could be overwritten by models" do
+ Admin::Account.all.each do |account|
+ assert_includes account.inspect, "name: [FILTERED]"
+ assert_equal 1, account.inspect.scan("[FILTERED]").length
+ end
+
+ begin
+ previous_account_filter_attributes = Admin::Account.filter_attributes
+ Admin::Account.filter_attributes = []
+
+ # Above changes should not impact other models
+ Admin::User.all.each do |user|
+ assert_includes user.inspect, "name: [FILTERED]"
+ assert_equal 1, user.inspect.scan("[FILTERED]").length
+ end
+
+ Admin::Account.all.each do |account|
+ assert_not_includes account.inspect, "name: [FILTERED]"
+ assert_equal 0, account.inspect.scan("[FILTERED]").length
+ end
+ ensure
+ Admin::Account.filter_attributes = previous_account_filter_attributes
+ end
+ end
+
+ test "filter_attributes should not filter nil value" do
+ account = Admin::Account.new
+
+ assert_includes account.inspect, "name: nil"
+ assert_not_includes account.inspect, "name: [FILTERED]"
+ assert_equal 0, account.inspect.scan("[FILTERED]").length
+ end
+
+ test "filter_attributes on pretty_print" do
+ user = admin_users(:david)
+ actual = "".dup
+ PP.pp(user, StringIO.new(actual))
+
+ assert_includes actual, "name: [FILTERED]"
+ assert_equal 1, actual.scan("[FILTERED]").length
+ end
+
+ test "filter_attributes on pretty_print should not filter nil value" do
+ user = Admin::User.new
+ actual = "".dup
+ PP.pp(user, StringIO.new(actual))
+
+ assert_includes actual, "name: nil"
+ assert_not_includes actual, "name: [FILTERED]"
+ assert_equal 0, actual.scan("[FILTERED]").length
+ end
+end
diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb
index 8324b26ad3..e73c88dd5d 100644
--- a/activerecord/test/cases/finder_test.rb
+++ b/activerecord/test/cases/finder_test.rb
@@ -355,6 +355,12 @@ class FinderTest < ActiveRecord::TestCase
end
def test_find_on_relation_with_large_number
+ assert_raises(ActiveRecord::RecordNotFound) do
+ Topic.where("1=1").find(9999999999999999999999999999999)
+ end
+ end
+
+ def test_find_by_on_relation_with_large_number
assert_nil Topic.where("1=1").find_by(id: 9999999999999999999999999999999)
end
@@ -457,6 +463,7 @@ class FinderTest < ActiveRecord::TestCase
expected = topics(:first)
expected.touch # PostgreSQL changes the default order if no order clause is used
assert_equal expected, Topic.first
+ assert_equal expected, Topic.limit(5).first
end
def test_model_class_responds_to_first_bang
@@ -479,6 +486,7 @@ class FinderTest < ActiveRecord::TestCase
expected = topics(:second)
expected.touch # PostgreSQL changes the default order if no order clause is used
assert_equal expected, Topic.second
+ assert_equal expected, Topic.limit(5).second
end
def test_model_class_responds_to_second_bang
@@ -501,6 +509,7 @@ class FinderTest < ActiveRecord::TestCase
expected = topics(:third)
expected.touch # PostgreSQL changes the default order if no order clause is used
assert_equal expected, Topic.third
+ assert_equal expected, Topic.limit(5).third
end
def test_model_class_responds_to_third_bang
@@ -523,6 +532,7 @@ class FinderTest < ActiveRecord::TestCase
expected = topics(:fourth)
expected.touch # PostgreSQL changes the default order if no order clause is used
assert_equal expected, Topic.fourth
+ assert_equal expected, Topic.limit(5).fourth
end
def test_model_class_responds_to_fourth_bang
@@ -545,6 +555,7 @@ class FinderTest < ActiveRecord::TestCase
expected = topics(:fifth)
expected.touch # PostgreSQL changes the default order if no order clause is used
assert_equal expected, Topic.fifth
+ assert_equal expected, Topic.limit(5).fifth
end
def test_model_class_responds_to_fifth_bang
@@ -707,6 +718,14 @@ class FinderTest < ActiveRecord::TestCase
assert_equal comments.limit(2).to_a.first(3), comments.limit(2).first(3)
end
+ def test_first_have_determined_order_by_default
+ expected = [companies(:second_client), companies(:another_client)]
+ clients = Client.where(name: expected.map(&:name))
+
+ assert_equal expected, clients.first(2)
+ assert_equal expected, clients.limit(5).first(2)
+ end
+
def test_take_and_first_and_last_with_integer_should_return_an_array
assert_kind_of Array, Topic.take(5)
assert_kind_of Array, Topic.first(5)
diff --git a/activerecord/test/cases/fixtures_test.rb b/activerecord/test/cases/fixtures_test.rb
index ee88bd8144..c65523d8c1 100644
--- a/activerecord/test/cases/fixtures_test.rb
+++ b/activerecord/test/cases/fixtures_test.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
require "cases/helper"
+require "support/connection_helper"
require "models/admin"
require "models/admin/account"
require "models/admin/randomly_named_c1"
@@ -32,6 +33,8 @@ require "models/treasure"
require "tempfile"
class FixturesTest < ActiveRecord::TestCase
+ include ConnectionHelper
+
self.use_instantiated_fixtures = true
self.use_transactional_tests = false
@@ -114,9 +117,96 @@ class FixturesTest < ActiveRecord::TestCase
end
end
end
+
+ def test_bulk_insert_with_a_multi_statement_query_in_a_nested_transaction
+ fixtures = {
+ "traffic_lights" => [
+ { "location" => "US", "state" => ["NY"], "long_state" => ["a"] },
+ ]
+ }
+
+ assert_difference "TrafficLight.count" do
+ ActiveRecord::Base.transaction do
+ conn = ActiveRecord::Base.connection
+ assert_equal 1, conn.open_transactions
+ conn.insert_fixtures_set(fixtures)
+ assert_equal 1, conn.open_transactions
+ end
+ end
+ end
end
if current_adapter?(:Mysql2Adapter)
+ def test_bulk_insert_with_multi_statements_enabled
+ run_without_connection do |orig_connection|
+ ActiveRecord::Base.establish_connection(
+ orig_connection.merge(flags: %w[MULTI_STATEMENTS])
+ )
+
+ fixtures = {
+ "traffic_lights" => [
+ { "location" => "US", "state" => ["NY"], "long_state" => ["a"] },
+ ]
+ }
+
+ ActiveRecord::Base.connection.stub(:supports_set_server_option?, false) do
+ assert_nothing_raised do
+ conn = ActiveRecord::Base.connection
+ conn.execute("SELECT 1; SELECT 2;")
+ conn.raw_connection.abandon_results!
+ end
+
+ assert_difference "TrafficLight.count" do
+ ActiveRecord::Base.transaction do
+ conn = ActiveRecord::Base.connection
+ assert_equal 1, conn.open_transactions
+ conn.insert_fixtures_set(fixtures)
+ assert_equal 1, conn.open_transactions
+ end
+ end
+
+ assert_nothing_raised do
+ conn = ActiveRecord::Base.connection
+ conn.execute("SELECT 1; SELECT 2;")
+ conn.raw_connection.abandon_results!
+ end
+ end
+ end
+ end
+
+ def test_bulk_insert_with_multi_statements_disabled
+ run_without_connection do |orig_connection|
+ ActiveRecord::Base.establish_connection(
+ orig_connection.merge(flags: [])
+ )
+
+ fixtures = {
+ "traffic_lights" => [
+ { "location" => "US", "state" => ["NY"], "long_state" => ["a"] },
+ ]
+ }
+
+ ActiveRecord::Base.connection.stub(:supports_set_server_option?, false) do
+ assert_raises(ActiveRecord::StatementInvalid) do
+ conn = ActiveRecord::Base.connection
+ conn.execute("SELECT 1; SELECT 2;")
+ conn.raw_connection.abandon_results!
+ end
+
+ assert_difference "TrafficLight.count" do
+ conn = ActiveRecord::Base.connection
+ conn.insert_fixtures_set(fixtures)
+ end
+
+ assert_raises(ActiveRecord::StatementInvalid) do
+ conn = ActiveRecord::Base.connection
+ conn.execute("SELECT 1; SELECT 2;")
+ conn.raw_connection.abandon_results!
+ end
+ end
+ end
+ end
+
def test_insert_fixtures_set_raises_an_error_when_max_allowed_packet_is_smaller_than_fixtures_set_size
conn = ActiveRecord::Base.connection
mysql_margin = 2
@@ -128,10 +218,10 @@ class FixturesTest < ActiveRecord::TestCase
]
}
- conn.stubs(:max_allowed_packet).returns(packet_size - mysql_margin)
-
- error = assert_raises(ActiveRecord::ActiveRecordError) { conn.insert_fixtures_set(fixtures) }
- assert_match(/Fixtures set is too large #{packet_size}\./, error.message)
+ conn.stub(:max_allowed_packet, packet_size - mysql_margin) do
+ error = assert_raises(ActiveRecord::ActiveRecordError) { conn.insert_fixtures_set(fixtures) }
+ assert_match(/Fixtures set is too large #{packet_size}\./, error.message)
+ end
end
def test_insert_fixture_set_when_max_allowed_packet_is_bigger_than_fixtures_set_size
@@ -143,10 +233,10 @@ class FixturesTest < ActiveRecord::TestCase
]
}
- conn.stubs(:max_allowed_packet).returns(packet_size)
-
- assert_difference "TrafficLight.count" do
- conn.insert_fixtures_set(fixtures)
+ conn.stub(:max_allowed_packet, packet_size) do
+ assert_difference "TrafficLight.count" do
+ conn.insert_fixtures_set(fixtures)
+ end
end
end
@@ -164,12 +254,13 @@ class FixturesTest < ActiveRecord::TestCase
]
}
- conn.stubs(:max_allowed_packet).returns(packet_size)
+ conn.stub(:max_allowed_packet, packet_size) do
+ conn.insert_fixtures_set(fixtures)
- conn.insert_fixtures_set(fixtures)
- assert_equal 2, subscriber.events.size
- assert_operator subscriber.events.first.bytesize, :<, packet_size
- assert_operator subscriber.events.second.bytesize, :<, packet_size
+ assert_equal 2, subscriber.events.size
+ assert_operator subscriber.events.first.bytesize, :<, packet_size
+ assert_operator subscriber.events.second.bytesize, :<, packet_size
+ end
ensure
ActiveSupport::Notifications.unsubscribe(subscription)
end
@@ -188,10 +279,10 @@ class FixturesTest < ActiveRecord::TestCase
]
}
- conn.stubs(:max_allowed_packet).returns(packet_size)
-
- assert_difference ["TrafficLight.count", "Comment.count"], +1 do
- conn.insert_fixtures_set(fixtures)
+ conn.stub(:max_allowed_packet, packet_size) do
+ assert_difference ["TrafficLight.count", "Comment.count"], +1 do
+ conn.insert_fixtures_set(fixtures)
+ end
end
assert_equal 1, subscriber.events.size
ensure
@@ -833,44 +924,58 @@ class TransactionalFixturesOnConnectionNotification < ActiveRecord::TestCase
self.use_instantiated_fixtures = false
def test_transaction_created_on_connection_notification
- connection = stub(transaction_open?: false)
- connection.expects(:begin_transaction).with(joinable: false)
- pool = connection.stubs(:pool).returns(ActiveRecord::ConnectionAdapters::ConnectionPool.new(ActiveRecord::Base.connection_pool.spec))
- pool.stubs(:lock_thread=).with(false)
- fire_connection_notification(connection)
+ connection = Class.new do
+ attr_accessor :pool
+
+ def transaction_open?; end
+ def begin_transaction(*args); end
+ def rollback_transaction(*args); end
+ end.new
+
+ connection.pool = Class.new do
+ def lock_thread=(lock_thread); end
+ end.new
+
+ assert_called_with(connection, :begin_transaction, [joinable: false]) do
+ fire_connection_notification(connection)
+ end
end
def test_notification_established_transactions_are_rolled_back
- # Mocha is not thread-safe so define our own stub to test
connection = Class.new do
attr_accessor :rollback_transaction_called
attr_accessor :pool
+
def transaction_open?; true; end
def begin_transaction(*args); end
def rollback_transaction(*args)
@rollback_transaction_called = true
end
end.new
+
connection.pool = Class.new do
- def lock_thread=(lock_thread); false; end
+ def lock_thread=(lock_thread); end
end.new
+
fire_connection_notification(connection)
teardown_fixtures
+
assert(connection.rollback_transaction_called, "Expected <mock connection>#rollback_transaction to be called but was not")
end
private
def fire_connection_notification(connection)
- ActiveRecord::Base.connection_handler.stubs(:retrieve_connection).with("book").returns(connection)
- message_bus = ActiveSupport::Notifications.instrumenter
- payload = {
- spec_name: "book",
- config: nil,
- connection_id: connection.object_id
- }
+ assert_called_with(ActiveRecord::Base.connection_handler, :retrieve_connection, ["book"], returns: connection) do
+ message_bus = ActiveSupport::Notifications.instrumenter
+ payload = {
+ spec_name: "book",
+ config: nil,
+ connection_id: connection.object_id
+ }
- message_bus.instrument("!connection.active_record", payload) {}
+ message_bus.instrument("!connection.active_record", payload) {}
+ end
end
end
diff --git a/activerecord/test/cases/helper.rb b/activerecord/test/cases/helper.rb
index 66f11fe5bd..68be685e4b 100644
--- a/activerecord/test/cases/helper.rb
+++ b/activerecord/test/cases/helper.rb
@@ -183,5 +183,3 @@ module InTimeZone
ActiveRecord::Base.time_zone_aware_attributes = old_tz
end
end
-
-require "mocha/minitest" # FIXME: stop using mocha
diff --git a/activerecord/test/cases/inheritance_test.rb b/activerecord/test/cases/inheritance_test.rb
index 4a0ad0442a..3d3189900f 100644
--- a/activerecord/test/cases/inheritance_test.rb
+++ b/activerecord/test/cases/inheritance_test.rb
@@ -91,7 +91,6 @@ class InheritanceTest < ActiveRecord::TestCase
end
ActiveSupport::Dependencies.stub(:safe_constantize, proc { raise e }) do
-
exception = assert_raises NameError do
Company.send :compute_type, "InvalidModel"
end
diff --git a/activerecord/test/cases/legacy_configurations_test.rb b/activerecord/test/cases/legacy_configurations_test.rb
new file mode 100644
index 0000000000..c36feb5116
--- /dev/null
+++ b/activerecord/test/cases/legacy_configurations_test.rb
@@ -0,0 +1,43 @@
+# frozen_string_literal: true
+
+require "cases/helper"
+
+module ActiveRecord
+ class LegacyConfigurationsTest < ActiveRecord::TestCase
+ def test_can_turn_configurations_into_a_hash
+ assert ActiveRecord::Base.configurations.to_h.is_a?(Hash), "expected to be a hash but was not."
+ assert_equal ["arunit", "arunit2", "arunit_without_prepared_statements"].sort, ActiveRecord::Base.configurations.to_h.keys.sort
+ end
+
+ def test_each_is_deprecated
+ assert_deprecated do
+ ActiveRecord::Base.configurations.each do |db_config|
+ assert_equal "primary", db_config.spec_name
+ end
+ end
+ end
+
+ def test_first_is_deprecated
+ assert_deprecated do
+ db_config = ActiveRecord::Base.configurations.first
+ assert_equal "arunit", db_config.env_name
+ assert_equal "primary", db_config.spec_name
+ end
+ end
+
+ def test_fetch_is_deprecated
+ assert_deprecated do
+ db_config = ActiveRecord::Base.configurations.fetch("arunit").first
+ assert_equal "arunit", db_config.env_name
+ assert_equal "primary", db_config.spec_name
+ end
+ end
+
+ def test_values_are_deprecated
+ config_hashes = ActiveRecord::Base.configurations.configurations.map(&:config)
+ assert_deprecated do
+ assert_equal config_hashes, ActiveRecord::Base.configurations.values
+ end
+ end
+ end
+end
diff --git a/activerecord/test/cases/locking_test.rb b/activerecord/test/cases/locking_test.rb
index 8513edb0ab..33bd74e114 100644
--- a/activerecord/test/cases/locking_test.rb
+++ b/activerecord/test/cases/locking_test.rb
@@ -445,32 +445,38 @@ class OptimisticLockingTest < ActiveRecord::TestCase
assert_equal 0, car.wheels_count
assert_equal 0, car.lock_version
- previously_car_updated_at = car.updated_at
- travel(2.second) do
+ previously_updated_at = car.updated_at
+ previously_wheels_owned_at = car.wheels_owned_at
+ travel(1.second) do
Wheel.create!(wheelable: car)
end
assert_equal 1, car.reload.wheels_count
- assert_not_equal previously_car_updated_at, car.updated_at
assert_equal 1, car.lock_version
+ assert_operator previously_updated_at, :<, car.updated_at
+ assert_operator previously_wheels_owned_at, :<, car.wheels_owned_at
- previously_car_updated_at = car.updated_at
- travel(1.day) do
+ previously_updated_at = car.updated_at
+ previously_wheels_owned_at = car.wheels_owned_at
+ travel(2.second) do
car.wheels.first.update(size: 42)
end
assert_equal 1, car.reload.wheels_count
- assert_not_equal previously_car_updated_at, car.updated_at
assert_equal 2, car.lock_version
+ assert_operator previously_updated_at, :<, car.updated_at
+ assert_operator previously_wheels_owned_at, :<, car.wheels_owned_at
- previously_car_updated_at = car.updated_at
- travel(2.second) do
+ previously_updated_at = car.updated_at
+ previously_wheels_owned_at = car.wheels_owned_at
+ travel(3.second) do
car.wheels.first.destroy!
end
assert_equal 0, car.reload.wheels_count
- assert_not_equal previously_car_updated_at, car.updated_at
assert_equal 3, car.lock_version
+ assert_operator previously_updated_at, :<, car.updated_at
+ assert_operator previously_wheels_owned_at, :<, car.wheels_owned_at
end
def test_polymorphic_destroy_with_dependencies_and_lock_version
diff --git a/activerecord/test/cases/log_subscriber_test.rb b/activerecord/test/cases/log_subscriber_test.rb
index e2742ed33e..ae2597adc8 100644
--- a/activerecord/test/cases/log_subscriber_test.rb
+++ b/activerecord/test/cases/log_subscriber_test.rb
@@ -44,6 +44,7 @@ class LogSubscriberTest < ActiveRecord::TestCase
def setup
@old_logger = ActiveRecord::Base.logger
Developer.primary_key
+ ActiveRecord::Base.connection.materialize_transactions
super
ActiveRecord::LogSubscriber.attach_to(:active_record)
end
@@ -177,11 +178,25 @@ class LogSubscriberTest < ActiveRecord::TestCase
logger = TestDebugLogSubscriber.new
logger.sql(Event.new(0, sql: "hi mom!"))
+ assert_equal 2, @logger.logged(:debug).size
assert_match(/↳/, @logger.logged(:debug).last)
ensure
ActiveRecord::Base.verbose_query_logs = false
end
+ def test_verbose_query_with_ignored_callstack
+ ActiveRecord::Base.verbose_query_logs = true
+
+ logger = TestDebugLogSubscriber.new
+ def logger.extract_query_source_location(*); nil; end
+
+ logger.sql(Event.new(0, sql: "hi mom!"))
+ assert_equal 1, @logger.logged(:debug).size
+ assert_no_match(/↳/, @logger.logged(:debug).last)
+ ensure
+ ActiveRecord::Base.verbose_query_logs = false
+ end
+
def test_verbose_query_logs_disabled_by_default
logger = TestDebugLogSubscriber.new
logger.sql(Event.new(0, sql: "hi mom!"))
diff --git a/activerecord/test/cases/migration/change_schema_test.rb b/activerecord/test/cases/migration/change_schema_test.rb
index f4d16cb093..7777508349 100644
--- a/activerecord/test/cases/migration/change_schema_test.rb
+++ b/activerecord/test/cases/migration/change_schema_test.rb
@@ -196,6 +196,17 @@ module ActiveRecord
assert_equal "you can't redefine the primary key column 'testing_id'. To define a custom primary key, pass { id: false } to create_table.", error.message
end
+ def test_create_table_raises_when_defining_existing_column
+ error = assert_raise(ArgumentError) do
+ connection.create_table :testings do |t|
+ t.column :testing_column, :string
+ t.column :testing_column, :integer
+ end
+ end
+
+ assert_equal "you can't define an already defined column 'testing_column'.", error.message
+ end
+
def test_create_table_with_timestamps_should_create_datetime_columns
connection.create_table table_name do |t|
t.timestamps
diff --git a/activerecord/test/cases/migration/command_recorder_test.rb b/activerecord/test/cases/migration/command_recorder_test.rb
index 3a11bb081b..1a19b8dafd 100644
--- a/activerecord/test/cases/migration/command_recorder_test.rb
+++ b/activerecord/test/cases/migration/command_recorder_test.rb
@@ -329,11 +329,24 @@ module ActiveRecord
assert_equal [:add_foreign_key, [:dogs, :people, primary_key: "person_id"]], enable
end
+ def test_invert_remove_foreign_key_with_primary_key_and_to_table_in_options
+ enable = @recorder.inverse_of :remove_foreign_key, [:dogs, to_table: :people, primary_key: "uuid"]
+ assert_equal [:add_foreign_key, [:dogs, :people, primary_key: "uuid"]], 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_with_to_table_in_options
+ enable = @recorder.inverse_of :remove_foreign_key, [:dogs, to_table: :people]
+ assert_equal [:add_foreign_key, [:dogs, :people]], enable
+
+ enable = @recorder.inverse_of :remove_foreign_key, [:dogs, to_table: :people, column: :owner_id]
+ assert_equal [:add_foreign_key, [:dogs, :people, column: :owner_id]], 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"]
diff --git a/activerecord/test/cases/migration/foreign_key_test.rb b/activerecord/test/cases/migration/foreign_key_test.rb
index c471dd1106..bb233fbf74 100644
--- a/activerecord/test/cases/migration/foreign_key_test.rb
+++ b/activerecord/test/cases/migration/foreign_key_test.rb
@@ -53,16 +53,49 @@ if ActiveRecord::Base.connection.supports_foreign_keys_in_create?
end
def test_change_column_of_parent_table
- foreign_keys = ActiveRecord::Base.connection.foreign_keys("astronauts")
rocket = Rocket.create!(name: "myrocket")
rocket.astronauts << Astronaut.create!
@connection.change_column_null :rockets, :name, false
+ foreign_keys = @connection.foreign_keys("astronauts")
+ assert_equal 1, foreign_keys.size
+
+ fk = foreign_keys.first
+ assert_equal "myrocket", Rocket.first.name
+ assert_equal "astronauts", fk.from_table
+ assert_equal "rockets", fk.to_table
+ end
+
+ def test_rename_column_of_child_table
+ rocket = Rocket.create!(name: "myrocket")
+ rocket.astronauts << Astronaut.create!
+
+ @connection.rename_column :astronauts, :name, :astronaut_name
+
+ foreign_keys = @connection.foreign_keys("astronauts")
+ assert_equal 1, foreign_keys.size
+
+ fk = foreign_keys.first
+ assert_equal "myrocket", Rocket.first.name
+ assert_equal "astronauts", fk.from_table
+ assert_equal "rockets", fk.to_table
+ end
+
+ def test_rename_reference_column_of_child_table
+ rocket = Rocket.create!(name: "myrocket")
+ rocket.astronauts << Astronaut.create!
+
+ @connection.rename_column :astronauts, :rocket_id, :new_rocket_id
+
+ foreign_keys = @connection.foreign_keys("astronauts")
+ assert_equal 1, foreign_keys.size
+
fk = foreign_keys.first
assert_equal "myrocket", Rocket.first.name
assert_equal "astronauts", fk.from_table
assert_equal "rockets", fk.to_table
+ assert_equal "new_rocket_id", fk.options[:column]
end
end
end
diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb
index d1292dc53d..868bb40ab2 100644
--- a/activerecord/test/cases/migration_test.rb
+++ b/activerecord/test/cases/migration_test.rb
@@ -793,12 +793,20 @@ if ActiveRecord::Base.connection.supports_bulk_alter?
end
def test_adding_multiple_columns
- assert_queries(1) do
+ classname = ActiveRecord::Base.connection.class.name[/[^:]*$/]
+ expected_query_count = {
+ "Mysql2Adapter" => 1,
+ "PostgreSQLAdapter" => 2, # one for bulk change, one for comment
+ }.fetch(classname) {
+ raise "need an expected query count for #{classname}"
+ }
+
+ assert_queries(expected_query_count) do
with_bulk_change_table do |t|
t.column :name, :string
t.string :qualification, :experience
t.integer :age, default: 0
- t.date :birthdate
+ t.date :birthdate, comment: "This is a comment"
t.timestamps null: true
end
end
@@ -806,6 +814,7 @@ if ActiveRecord::Base.connection.supports_bulk_alter?
assert_equal 8, columns.size
[:name, :qualification, :experience].each { |s| assert_equal :string, column(s).type }
assert_equal "0", column(:age).default
+ assert_equal "This is a comment", column(:birthdate).comment
end
def test_removing_columns
diff --git a/activerecord/test/cases/nested_attributes_test.rb b/activerecord/test/cases/nested_attributes_test.rb
index 32af90caef..bb1c1ea17d 100644
--- a/activerecord/test/cases/nested_attributes_test.rb
+++ b/activerecord/test/cases/nested_attributes_test.rb
@@ -217,6 +217,18 @@ class TestNestedAttributesInGeneral < ActiveRecord::TestCase
mean_pirate.parrot_attributes = { name: "James" }
assert_equal "James", mean_pirate.parrot.name
end
+
+ def test_should_not_create_duplicates_with_create_with
+ Man.accepts_nested_attributes_for(:interests)
+
+ assert_difference("Interest.count", 1) do
+ Man.create_with(
+ interests_attributes: [{ topic: "Pirate king" }]
+ ).find_or_create_by!(
+ name: "Monkey D. Luffy"
+ )
+ end
+ end
end
class TestNestedAttributesOnAHasOneAssociation < ActiveRecord::TestCase
@@ -669,7 +681,6 @@ module NestedAttributesOnACollectionAssociationTests
def test_should_take_a_hash_with_composite_id_keys_and_assign_the_attributes_to_the_associated_models
@child_1.stub(:id, "ABC1X") do
@child_2.stub(:id, "ABC2X") do
-
@pirate.attributes = {
association_getter => [
{ id: @child_1.id, name: "Grace OMalley" },
@@ -1095,3 +1106,15 @@ class TestHasManyAutosaveAssociationWhichItselfHasAutosaveAssociations < ActiveR
assert_equal ["Ship name can't be blank"], part.errors.full_messages
end
end
+
+class TestNestedAttributesWithExtend < ActiveRecord::TestCase
+ setup do
+ Pirate.accepts_nested_attributes_for :treasures
+ end
+
+ def test_extend_affects_nested_attributes
+ pirate = Pirate.create!(catchphrase: "Don' botharrr talkin' like one, savvy?")
+ pirate.treasures_attributes = [{ id: nil }]
+ assert_equal "from extension", pirate.treasures[0].name
+ end
+end
diff --git a/activerecord/test/cases/persistence_test.rb b/activerecord/test/cases/persistence_test.rb
index 4c332e30aa..576fd21642 100644
--- a/activerecord/test/cases/persistence_test.rb
+++ b/activerecord/test/cases/persistence_test.rb
@@ -13,90 +13,15 @@ require "models/developer"
require "models/computer"
require "models/project"
require "models/minimalistic"
-require "models/warehouse_thing"
require "models/parrot"
require "models/minivan"
-require "models/owner"
require "models/person"
-require "models/pet"
require "models/ship"
-require "models/toy"
require "models/admin"
require "models/admin/user"
-require "rexml/document"
class PersistenceTest < ActiveRecord::TestCase
- fixtures :topics, :companies, :developers, :projects, :computers, :accounts, :minimalistics, "warehouse-things", :authors, :author_addresses, :categorizations, :categories, :posts, :minivans, :pets, :toys
-
- # Oracle UPDATE does not support ORDER BY
- unless current_adapter?(:OracleAdapter)
- def test_update_all_ignores_order_without_limit_from_association
- author = authors(:david)
- assert_nothing_raised do
- assert_equal author.posts_with_comments_and_categories.length, author.posts_with_comments_and_categories.update_all([ "body = ?", "bulk update!" ])
- end
- end
-
- def test_update_all_doesnt_ignore_order
- assert_equal authors(:david).id + 1, authors(:mary).id # make sure there is going to be a duplicate PK error
- test_update_with_order_succeeds = lambda do |order|
- begin
- Author.order(order).update_all("id = id + 1")
- rescue ActiveRecord::ActiveRecordError
- false
- end
- end
-
- if test_update_with_order_succeeds.call("id DESC")
- assert_not test_update_with_order_succeeds.call("id ASC") # test that this wasn't a fluke and using an incorrect order results in an exception
- else
- # test that we're failing because the current Arel's engine doesn't support UPDATE ORDER BY queries is using subselects instead
- assert_sql(/\AUPDATE .+ \(SELECT .* ORDER BY id DESC\)\z/i) do
- test_update_with_order_succeeds.call("id DESC")
- end
- end
- end
-
- def test_update_all_with_order_and_limit_updates_subset_only
- author = authors(:david)
- limited_posts = author.posts_sorted_by_id_limited
- assert_equal 1, limited_posts.size
- assert_equal 2, limited_posts.limit(2).size
- assert_equal 1, limited_posts.update_all([ "body = ?", "bulk update!" ])
- assert_equal "bulk update!", posts(:welcome).body
- assert_not_equal "bulk update!", posts(:thinking).body
- end
-
- def test_update_all_with_order_and_limit_and_offset_updates_subset_only
- author = authors(:david)
- limited_posts = author.posts_sorted_by_id_limited.offset(1)
- assert_equal 1, limited_posts.size
- assert_equal 2, limited_posts.limit(2).size
- assert_equal 1, limited_posts.update_all([ "body = ?", "bulk update!" ])
- assert_equal "bulk update!", posts(:thinking).body
- assert_not_equal "bulk update!", posts(:welcome).body
- end
-
- def test_delete_all_with_order_and_limit_deletes_subset_only
- author = authors(:david)
- limited_posts = Post.where(author: author).order(:id).limit(1)
- assert_equal 1, limited_posts.size
- assert_equal 2, limited_posts.limit(2).size
- assert_equal 1, limited_posts.delete_all
- assert_raise(ActiveRecord::RecordNotFound) { posts(:welcome) }
- assert posts(:thinking)
- end
-
- def test_delete_all_with_order_and_limit_and_offset_deletes_subset_only
- author = authors(:david)
- limited_posts = Post.where(author: author).order(:id).limit(1).offset(1)
- assert_equal 1, limited_posts.size
- assert_equal 2, limited_posts.limit(2).size
- assert_equal 1, limited_posts.delete_all
- assert_raise(ActiveRecord::RecordNotFound) { posts(:thinking) }
- assert posts(:welcome)
- end
- end
+ fixtures :topics, :companies, :developers, :accounts, :minimalistics, :authors, :author_addresses, :posts, :minivans
def test_update_many
topic_data = { 1 => { "content" => "1 updated" }, 2 => { "content" => "2 updated" } }
@@ -145,34 +70,6 @@ class PersistenceTest < ActiveRecord::TestCase
assert_equal Topic.count, Topic.delete_all
end
- def test_delete_all_with_joins_and_where_part_is_hash
- pets = Pet.joins(:toys).where(toys: { name: "Bone" })
-
- assert_equal true, pets.exists?
- assert_equal pets.count, pets.delete_all
- end
-
- def test_delete_all_with_joins_and_where_part_is_not_hash
- pets = Pet.joins(:toys).where("toys.name = ?", "Bone")
-
- assert_equal true, pets.exists?
- assert_equal pets.count, pets.delete_all
- end
-
- def test_delete_all_with_left_joins
- pets = Pet.left_joins(:toys).where(toys: { name: "Bone" })
-
- assert_equal true, pets.exists?
- assert_equal pets.count, pets.delete_all
- end
-
- def test_delete_all_with_includes
- pets = Pet.includes(:toys).where(toys: { name: "Bone" })
-
- assert_equal true, pets.exists?
- assert_equal pets.count, pets.delete_all
- end
-
def test_increment_attribute
assert_equal 50, accounts(:signals37).credit_limit
accounts(:signals37).increment! :credit_limit
@@ -206,24 +103,28 @@ class PersistenceTest < ActiveRecord::TestCase
assert_equal initial_credit + 2, a1.reload.credit_limit
end
- def test_increment_updates_timestamps
+ def test_increment_with_touch_updates_timestamps
topic = topics(:first)
- topic.update_columns(updated_at: 5.minutes.ago)
- previous_updated_at = topic.updated_at
- topic.increment!(:replies_count, touch: true)
- assert_operator previous_updated_at, :<, topic.reload.updated_at
+ assert_equal 1, topic.replies_count
+ previously_updated_at = topic.updated_at
+ travel(1.second) do
+ topic.increment!(:replies_count, touch: true)
+ end
+ assert_equal 2, topic.reload.replies_count
+ assert_operator previously_updated_at, :<, topic.updated_at
end
- def test_destroy_all
- conditions = "author_name = 'Mary'"
- topics_by_mary = Topic.all.merge!(where: conditions, order: "id").to_a
- assert_not_empty topics_by_mary
-
- assert_difference("Topic.count", -topics_by_mary.size) do
- destroyed = Topic.where(conditions).destroy_all.sort_by(&:id)
- assert_equal topics_by_mary, destroyed
- assert destroyed.all?(&:frozen?), "destroyed topics should be frozen"
+ def test_increment_with_touch_an_attribute_updates_timestamps
+ topic = topics(:first)
+ assert_equal 1, topic.replies_count
+ previously_updated_at = topic.updated_at
+ previously_written_on = topic.written_on
+ travel(1.second) do
+ topic.increment!(:replies_count, touch: :written_on)
end
+ assert_equal 2, topic.reload.replies_count
+ assert_operator previously_updated_at, :<, topic.updated_at
+ assert_operator previously_written_on, :<, topic.written_on
end
def test_destroy_many
@@ -333,12 +234,28 @@ class PersistenceTest < ActiveRecord::TestCase
assert_equal 41, accounts(:signals37, :reload).credit_limit
end
- def test_decrement_updates_timestamps
+ def test_decrement_with_touch_updates_timestamps
+ topic = topics(:first)
+ assert_equal 1, topic.replies_count
+ previously_updated_at = topic.updated_at
+ travel(1.second) do
+ topic.decrement!(:replies_count, touch: true)
+ end
+ assert_equal 0, topic.reload.replies_count
+ assert_operator previously_updated_at, :<, topic.updated_at
+ end
+
+ def test_decrement_with_touch_an_attribute_updates_timestamps
topic = topics(:first)
- topic.update_columns(updated_at: 5.minutes.ago)
- previous_updated_at = topic.updated_at
- topic.decrement!(:replies_count, touch: true)
- assert_operator previous_updated_at, :<, topic.reload.updated_at
+ assert_equal 1, topic.replies_count
+ previously_updated_at = topic.updated_at
+ previously_written_on = topic.written_on
+ travel(1.second) do
+ topic.decrement!(:replies_count, touch: :written_on)
+ end
+ assert_equal 0, topic.reload.replies_count
+ assert_operator previously_updated_at, :<, topic.updated_at
+ assert_operator previously_written_on, :<, topic.written_on
end
def test_create
@@ -606,32 +523,6 @@ class PersistenceTest < ActiveRecord::TestCase
assert_nil Topic.find(2).last_read
end
- def test_update_all_with_joins
- pets = Pet.joins(:toys).where(toys: { name: "Bone" })
-
- assert_equal true, pets.exists?
- assert_equal pets.count, pets.update_all(name: "Bob")
- end
-
- def test_update_all_with_left_joins
- pets = Pet.left_joins(:toys).where(toys: { name: "Bone" })
-
- assert_equal true, pets.exists?
- assert_equal pets.count, pets.update_all(name: "Bob")
- end
-
- def test_update_all_with_includes
- pets = Pet.includes(:toys).where(toys: { name: "Bone" })
-
- assert_equal true, pets.exists?
- assert_equal pets.count, pets.update_all(name: "Bob")
- end
-
- def test_update_all_with_non_standard_table_name
- assert_equal 1, WarehouseThing.where(id: 1).update_all(["value = ?", 0])
- assert_equal 0, WarehouseThing.find(1).value
- end
-
def test_delete_new_record
client = Client.new(name: "37signals")
client.delete
@@ -1153,21 +1044,19 @@ class PersistenceTest < ActiveRecord::TestCase
ActiveRecord::Base.connection.disable_query_cache!
end
- class SaveTest < ActiveRecord::TestCase
- def test_save_touch_false
- pet = Pet.create!(
- name: "Bob",
- created_at: 1.day.ago,
- updated_at: 1.day.ago)
+ def test_save_touch_false
+ parrot = Parrot.create!(
+ name: "Bob",
+ created_at: 1.day.ago,
+ updated_at: 1.day.ago)
- created_at = pet.created_at
- updated_at = pet.updated_at
+ created_at = parrot.created_at
+ updated_at = parrot.updated_at
- pet.name = "Barb"
- pet.save!(touch: false)
- assert_equal pet.created_at, created_at
- assert_equal pet.updated_at, updated_at
- end
+ parrot.name = "Barb"
+ parrot.save!(touch: false)
+ assert_equal parrot.created_at, created_at
+ assert_equal parrot.updated_at, updated_at
end
def test_reset_column_information_resets_children
diff --git a/activerecord/test/cases/primary_keys_test.rb b/activerecord/test/cases/primary_keys_test.rb
index 60dac91ec9..4ed7469039 100644
--- a/activerecord/test/cases/primary_keys_test.rb
+++ b/activerecord/test/cases/primary_keys_test.rb
@@ -305,6 +305,7 @@ class PrimaryKeyAnyTypeTest < ActiveRecord::TestCase
test "schema dump primary key includes type and options" do
schema = dump_table_schema "barcodes"
assert_match %r{create_table "barcodes", primary_key: "code", id: :string, limit: 42}, schema
+ assert_no_match %r{t\.index \["code"\]}, schema
end
if current_adapter?(:Mysql2Adapter) && subsecond_precision_supported?
diff --git a/activerecord/test/cases/query_cache_test.rb b/activerecord/test/cases/query_cache_test.rb
index 0f990bac9d..69be091869 100644
--- a/activerecord/test/cases/query_cache_test.rb
+++ b/activerecord/test/cases/query_cache_test.rb
@@ -13,12 +13,13 @@ class QueryCacheTest < ActiveRecord::TestCase
fixtures :tasks, :topics, :categories, :posts, :categories_posts
class ShouldNotHaveExceptionsLogger < ActiveRecord::LogSubscriber
- attr_reader :logger
+ attr_reader :logger, :events
def initialize
super
@logger = ::Logger.new File::NULL
@exception = false
+ @events = []
end
def exception?
@@ -26,6 +27,7 @@ class QueryCacheTest < ActiveRecord::TestCase
end
def sql(event)
+ @events << event
super
rescue
@exception = true
@@ -265,6 +267,26 @@ class QueryCacheTest < ActiveRecord::TestCase
end
end
+ def test_cache_notifications_can_be_overridden
+ logger = ShouldNotHaveExceptionsLogger.new
+ subscriber = ActiveSupport::Notifications.subscribe "sql.active_record", logger
+
+ connection = ActiveRecord::Base.connection.dup
+
+ def connection.cache_notification_info(sql, name, binds)
+ super.merge(neat: true)
+ end
+
+ connection.cache do
+ connection.select_all "select 1"
+ connection.select_all "select 1"
+ end
+
+ assert_equal true, logger.events.last.payload[:neat]
+ ensure
+ ActiveSupport::Notifications.unsubscribe subscriber
+ end
+
def test_cache_does_not_raise_exceptions
logger = ShouldNotHaveExceptionsLogger.new
subscriber = ActiveSupport::Notifications.subscribe "sql.active_record", logger
@@ -460,7 +482,6 @@ class QueryCacheTest < ActiveRecord::TestCase
assert_not ActiveRecord::Base.connection.query_cache_enabled
}.join
}.call({})
-
end
end
diff --git a/activerecord/test/cases/quoting_test.rb b/activerecord/test/cases/quoting_test.rb
index 92eb0c814f..723fccc8d9 100644
--- a/activerecord/test/cases/quoting_test.rb
+++ b/activerecord/test/cases/quoting_test.rb
@@ -71,8 +71,8 @@ module ActiveRecord
with_timezone_config default: :utc do
t = Time.now.change(usec: 0)
- expected = t.getutc.change(year: 2000, month: 1, day: 1)
- expected = expected.to_s(:db).sub("2000-01-01 ", "")
+ expected = t.change(year: 2000, month: 1, day: 1)
+ expected = expected.getutc.to_s(:db).slice(11..-1)
assert_equal expected, @quoter.quoted_time(t)
end
@@ -89,6 +89,32 @@ module ActiveRecord
end
end
+ def test_quoted_time_dst_utc
+ with_env_tz "America/New_York" do
+ with_timezone_config default: :utc do
+ t = Time.new(2000, 7, 1, 0, 0, 0, "+04:30")
+
+ expected = t.change(year: 2000, month: 1, day: 1)
+ expected = expected.getutc.to_s(:db).slice(11..-1)
+
+ assert_equal expected, @quoter.quoted_time(t)
+ end
+ end
+ end
+
+ def test_quoted_time_dst_local
+ with_env_tz "America/New_York" do
+ with_timezone_config default: :local do
+ t = Time.new(2000, 7, 1, 0, 0, 0, "+04:30")
+
+ expected = t.change(year: 2000, month: 1, day: 1)
+ expected = expected.getlocal.to_s(:db).slice(11..-1)
+
+ assert_equal expected, @quoter.quoted_time(t)
+ end
+ end
+ end
+
def test_quoted_time_crazy
with_timezone_config default: :asdfasdf do
t = Time.now.change(usec: 0)
diff --git a/activerecord/test/cases/reaper_test.rb b/activerecord/test/cases/reaper_test.rb
index b034fe3e3b..b630f782bc 100644
--- a/activerecord/test/cases/reaper_test.rb
+++ b/activerecord/test/cases/reaper_test.rb
@@ -61,9 +61,9 @@ module ActiveRecord
def test_reaping_frequency_configuration
spec = ActiveRecord::Base.connection_pool.spec.dup
- spec.config[:reaping_frequency] = 100
+ spec.config[:reaping_frequency] = "10.01"
pool = ConnectionPool.new spec
- assert_equal 100, pool.reaper.frequency
+ assert_equal 10.01, pool.reaper.frequency
end
def test_connection_pool_starts_reaper
diff --git a/activerecord/test/cases/relation/delegation_test.rb b/activerecord/test/cases/relation/delegation_test.rb
index 3f3d41980c..a8030c2d64 100644
--- a/activerecord/test/cases/relation/delegation_test.rb
+++ b/activerecord/test/cases/relation/delegation_test.rb
@@ -5,7 +5,7 @@ require "models/post"
require "models/comment"
module ActiveRecord
- module DelegationWhitelistTests
+ module ArrayDelegationTests
ARRAY_DELEGATES = [
:+, :-, :|, :&, :[], :shuffle,
:all?, :collect, :compact, :detect, :each, :each_cons, :each_with_index,
@@ -38,7 +38,7 @@ module ActiveRecord
end
class DelegationAssociationTest < ActiveRecord::TestCase
- include DelegationWhitelistTests
+ include ArrayDelegationTests
include DeprecatedArelDelegationTests
def target
@@ -47,7 +47,7 @@ module ActiveRecord
end
class DelegationRelationTest < ActiveRecord::TestCase
- include DelegationWhitelistTests
+ include ArrayDelegationTests
include DeprecatedArelDelegationTests
def target
diff --git a/activerecord/test/cases/relation/delete_all_test.rb b/activerecord/test/cases/relation/delete_all_test.rb
new file mode 100644
index 0000000000..446d7621ea
--- /dev/null
+++ b/activerecord/test/cases/relation/delete_all_test.rb
@@ -0,0 +1,104 @@
+# frozen_string_literal: true
+
+require "cases/helper"
+require "models/author"
+require "models/post"
+require "models/pet"
+require "models/toy"
+
+class DeleteAllTest < ActiveRecord::TestCase
+ fixtures :authors, :author_addresses, :posts, :pets, :toys
+
+ def test_destroy_all
+ davids = Author.where(name: "David")
+
+ # Force load
+ assert_equal [authors(:david)], davids.to_a
+ assert_predicate davids, :loaded?
+
+ assert_difference("Author.count", -1) do
+ destroyed = davids.destroy_all
+ assert_equal [authors(:david)], destroyed
+ assert_predicate destroyed.first, :frozen?
+ end
+
+ assert_equal [], davids.to_a
+ assert_predicate davids, :loaded?
+ end
+
+ def test_delete_all
+ davids = Author.where(name: "David")
+
+ assert_difference("Author.count", -1) { davids.delete_all }
+ assert_not_predicate davids, :loaded?
+ end
+
+ def test_delete_all_loaded
+ davids = Author.where(name: "David")
+
+ # Force load
+ assert_equal [authors(:david)], davids.to_a
+ assert_predicate davids, :loaded?
+
+ assert_difference("Author.count", -1) { davids.delete_all }
+
+ assert_equal [], davids.to_a
+ assert_predicate davids, :loaded?
+ end
+
+ def test_delete_all_with_unpermitted_relation_raises_error
+ 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 }
+ end
+
+ def test_delete_all_with_joins_and_where_part_is_hash
+ pets = Pet.joins(:toys).where(toys: { name: "Bone" })
+
+ assert_equal true, pets.exists?
+ assert_equal pets.count, pets.delete_all
+ end
+
+ def test_delete_all_with_joins_and_where_part_is_not_hash
+ pets = Pet.joins(:toys).where("toys.name = ?", "Bone")
+
+ assert_equal true, pets.exists?
+ assert_equal pets.count, pets.delete_all
+ end
+
+ def test_delete_all_with_left_joins
+ pets = Pet.left_joins(:toys).where(toys: { name: "Bone" })
+
+ assert_equal true, pets.exists?
+ assert_equal pets.count, pets.delete_all
+ end
+
+ def test_delete_all_with_includes
+ pets = Pet.includes(:toys).where(toys: { name: "Bone" })
+
+ assert_equal true, pets.exists?
+ assert_equal pets.count, pets.delete_all
+ end
+
+ unless current_adapter?(:OracleAdapter)
+ def test_delete_all_with_order_and_limit_deletes_subset_only
+ author = authors(:david)
+ limited_posts = Post.where(author: author).order(:id).limit(1)
+ assert_equal 1, limited_posts.size
+ assert_equal 2, limited_posts.limit(2).size
+ assert_equal 1, limited_posts.delete_all
+ assert_raise(ActiveRecord::RecordNotFound) { posts(:welcome) }
+ assert posts(:thinking)
+ end
+
+ def test_delete_all_with_order_and_limit_and_offset_deletes_subset_only
+ author = authors(:david)
+ limited_posts = Post.where(author: author).order(:id).limit(1).offset(1)
+ assert_equal 1, limited_posts.size
+ assert_equal 2, limited_posts.limit(2).size
+ assert_equal 1, limited_posts.delete_all
+ assert_raise(ActiveRecord::RecordNotFound) { posts(:thinking) }
+ assert posts(:welcome)
+ end
+ end
+end
diff --git a/activerecord/test/cases/relation/merging_test.rb b/activerecord/test/cases/relation/merging_test.rb
index f53ef1fe35..6e7998d15a 100644
--- a/activerecord/test/cases/relation/merging_test.rb
+++ b/activerecord/test/cases/relation/merging_test.rb
@@ -121,6 +121,16 @@ class RelationMergingTest < ActiveRecord::TestCase
relation = relation.merge(Post.from("posts"))
assert_not_empty relation.from_clause
end
+
+ def test_merging_with_order_with_binds
+ relation = Post.all.merge(Post.order([Arel.sql("title LIKE ?"), "%suffix"]))
+ assert_equal ["title LIKE '%suffix'"], relation.order_values
+ end
+
+ def test_merging_with_order_without_binds
+ relation = Post.all.merge(Post.order(Arel.sql("title LIKE '%?'")))
+ assert_equal ["title LIKE '%?'"], relation.order_values
+ end
end
class MergingDifferentRelationsTest < ActiveRecord::TestCase
diff --git a/activerecord/test/cases/relation/select_test.rb b/activerecord/test/cases/relation/select_test.rb
index 0577e6bfdb..dec8a6925d 100644
--- a/activerecord/test/cases/relation/select_test.rb
+++ b/activerecord/test/cases/relation/select_test.rb
@@ -7,7 +7,7 @@ module ActiveRecord
class SelectTest < ActiveRecord::TestCase
fixtures :posts
- def test_select_with_nil_agrument
+ def test_select_with_nil_argument
expected = Post.select(:title).to_sql
assert_equal expected, Post.select(nil).select(:title).to_sql
end
diff --git a/activerecord/test/cases/relation/update_all_test.rb b/activerecord/test/cases/relation/update_all_test.rb
new file mode 100644
index 0000000000..09c365f31b
--- /dev/null
+++ b/activerecord/test/cases/relation/update_all_test.rb
@@ -0,0 +1,239 @@
+# frozen_string_literal: true
+
+require "cases/helper"
+require "models/author"
+require "models/category"
+require "models/comment"
+require "models/computer"
+require "models/developer"
+require "models/post"
+require "models/person"
+require "models/pet"
+require "models/toy"
+require "models/topic"
+require "models/tag"
+require "models/tagging"
+require "models/warehouse_thing"
+
+class UpdateAllTest < ActiveRecord::TestCase
+ fixtures :authors, :author_addresses, :comments, :developers, :posts, :people, :pets, :toys, :tags, :taggings, "warehouse-things"
+
+ class TopicWithCallbacks < ActiveRecord::Base
+ self.table_name = :topics
+ cattr_accessor :topic_count
+ before_update { |topic| topic.author_name = "David" if topic.author_name.blank? }
+ after_update { |topic| topic.class.topic_count = topic.class.count }
+ end
+
+ def test_update_all_with_scope
+ tag = Tag.first
+ Post.tagged_with(tag.id).update_all(title: "rofl")
+ posts = Post.tagged_with(tag.id).all.to_a
+ assert_operator posts.length, :>, 0
+ posts.each { |post| assert_equal "rofl", post.title }
+ end
+
+ def test_update_all_with_non_standard_table_name
+ assert_equal 1, WarehouseThing.where(id: 1).update_all(["value = ?", 0])
+ assert_equal 0, WarehouseThing.find(1).value
+ end
+
+ def test_update_all_with_blank_argument
+ assert_raises(ArgumentError) { Comment.update_all({}) }
+ end
+
+ def test_update_all_with_joins
+ pets = Pet.joins(:toys).where(toys: { name: "Bone" })
+
+ assert_equal true, pets.exists?
+ assert_equal pets.count, pets.update_all(name: "Bob")
+ end
+
+ def test_update_all_with_left_joins
+ pets = Pet.left_joins(:toys).where(toys: { name: "Bone" })
+
+ assert_equal true, pets.exists?
+ assert_equal pets.count, pets.update_all(name: "Bob")
+ end
+
+ def test_update_all_with_includes
+ pets = Pet.includes(:toys).where(toys: { name: "Bone" })
+
+ assert_equal true, pets.exists?
+ assert_equal pets.count, pets.update_all(name: "Bob")
+ end
+
+ def test_update_all_with_joins_and_limit
+ comments = Comment.joins(:post).where("posts.id" => posts(:welcome).id).limit(1)
+ assert_equal 1, comments.update_all(post_id: posts(:thinking).id)
+ assert_equal posts(:thinking), comments(:greetings).post
+ end
+
+ def test_update_all_with_joins_and_limit_and_order
+ comments = Comment.joins(:post).where("posts.id" => posts(:welcome).id).order("comments.id").limit(1)
+ assert_equal 1, comments.update_all(post_id: posts(:thinking).id)
+ assert_equal posts(:thinking), comments(:greetings).post
+ assert_equal posts(:welcome), comments(:more_greetings).post
+ end
+
+ def test_update_all_with_joins_and_offset
+ all_comments = Comment.joins(:post).where("posts.id" => posts(:welcome).id)
+ count = all_comments.count
+ comments = all_comments.offset(1)
+
+ assert_equal count - 1, comments.update_all(post_id: posts(:thinking).id)
+ end
+
+ def test_update_all_with_joins_and_offset_and_order
+ all_comments = Comment.joins(:post).where("posts.id" => posts(:welcome).id).order("posts.id", "comments.id")
+ count = all_comments.count
+ comments = all_comments.offset(1)
+
+ assert_equal count - 1, comments.update_all(post_id: posts(:thinking).id)
+ assert_equal posts(:thinking), comments(:more_greetings).post
+ assert_equal posts(:welcome), comments(:greetings).post
+ end
+
+ def test_update_counters_with_joins
+ assert_nil pets(:parrot).integer
+
+ Pet.joins(:toys).where(toys: { name: "Bone" }).update_counters(integer: 1)
+
+ assert_equal 1, pets(:parrot).reload.integer
+ end
+
+ def test_touch_all_updates_records_timestamps
+ david = developers(:david)
+ david_previously_updated_at = david.updated_at
+ jamis = developers(:jamis)
+ jamis_previously_updated_at = jamis.updated_at
+ Developer.where(name: "David").touch_all
+
+ assert_not_equal david_previously_updated_at, david.reload.updated_at
+ assert_equal jamis_previously_updated_at, jamis.reload.updated_at
+ end
+
+ def test_touch_all_with_custom_timestamp
+ developer = developers(:david)
+ previously_created_at = developer.created_at
+ previously_updated_at = developer.updated_at
+ Developer.where(name: "David").touch_all(:created_at)
+ developer.reload
+
+ assert_not_equal previously_created_at, developer.created_at
+ assert_not_equal previously_updated_at, developer.updated_at
+ end
+
+ def test_touch_all_with_given_time
+ developer = developers(:david)
+ previously_created_at = developer.created_at
+ previously_updated_at = developer.updated_at
+ new_time = Time.utc(2015, 2, 16, 4, 54, 0)
+ Developer.where(name: "David").touch_all(:created_at, time: new_time)
+ developer.reload
+
+ assert_not_equal previously_created_at, developer.created_at
+ assert_not_equal previously_updated_at, developer.updated_at
+ assert_equal new_time, developer.created_at
+ assert_equal new_time, developer.updated_at
+ end
+
+ def test_touch_all_updates_locking_column
+ person = people(:david)
+
+ assert_difference -> { person.reload.lock_version }, +1 do
+ Person.where(first_name: "David").touch_all
+ end
+ end
+
+ def test_update_on_relation
+ topic1 = TopicWithCallbacks.create! title: "arel", author_name: nil
+ topic2 = TopicWithCallbacks.create! title: "activerecord", author_name: nil
+ topics = TopicWithCallbacks.where(id: [topic1.id, topic2.id])
+ topics.update(title: "adequaterecord")
+
+ assert_equal TopicWithCallbacks.count, TopicWithCallbacks.topic_count
+
+ assert_equal "adequaterecord", topic1.reload.title
+ assert_equal "adequaterecord", topic2.reload.title
+ # Testing that the before_update callbacks have run
+ assert_equal "David", topic1.reload.author_name
+ assert_equal "David", topic2.reload.author_name
+ end
+
+ def test_update_with_ids_on_relation
+ topic1 = TopicWithCallbacks.create!(title: "arel", author_name: nil)
+ topic2 = TopicWithCallbacks.create!(title: "activerecord", author_name: nil)
+ topics = TopicWithCallbacks.none
+ topics.update(
+ [topic1.id, topic2.id],
+ [{ title: "adequaterecord" }, { title: "adequaterecord" }]
+ )
+
+ assert_equal TopicWithCallbacks.count, TopicWithCallbacks.topic_count
+
+ assert_equal "adequaterecord", topic1.reload.title
+ assert_equal "adequaterecord", topic2.reload.title
+ # Testing that the before_update callbacks have run
+ assert_equal "David", topic1.reload.author_name
+ assert_equal "David", topic2.reload.author_name
+ end
+
+ def test_update_on_relation_passing_active_record_object_is_not_permitted
+ topic = Topic.create!(title: "Foo", author_name: nil)
+ assert_raises(ArgumentError) do
+ Topic.where(id: topic.id).update(topic, title: "Bar")
+ end
+ end
+
+ # Oracle UPDATE does not support ORDER BY
+ unless current_adapter?(:OracleAdapter)
+ def test_update_all_ignores_order_without_limit_from_association
+ author = authors(:david)
+ assert_nothing_raised do
+ assert_equal author.posts_with_comments_and_categories.length, author.posts_with_comments_and_categories.update_all([ "body = ?", "bulk update!" ])
+ end
+ end
+
+ def test_update_all_doesnt_ignore_order
+ assert_equal authors(:david).id + 1, authors(:mary).id # make sure there is going to be a duplicate PK error
+ test_update_with_order_succeeds = lambda do |order|
+ begin
+ Author.order(order).update_all("id = id + 1")
+ rescue ActiveRecord::ActiveRecordError
+ false
+ end
+ end
+
+ if test_update_with_order_succeeds.call("id DESC")
+ # test that this wasn't a fluke and using an incorrect order results in an exception
+ assert_not test_update_with_order_succeeds.call("id ASC")
+ else
+ # test that we're failing because the current Arel's engine doesn't support UPDATE ORDER BY queries is using subselects instead
+ assert_sql(/\AUPDATE .+ \(SELECT .* ORDER BY id DESC\)\z/i) do
+ test_update_with_order_succeeds.call("id DESC")
+ end
+ end
+ end
+
+ def test_update_all_with_order_and_limit_updates_subset_only
+ author = authors(:david)
+ limited_posts = author.posts_sorted_by_id_limited
+ assert_equal 1, limited_posts.size
+ assert_equal 2, limited_posts.limit(2).size
+ assert_equal 1, limited_posts.update_all([ "body = ?", "bulk update!" ])
+ assert_equal "bulk update!", posts(:welcome).body
+ assert_not_equal "bulk update!", posts(:thinking).body
+ end
+
+ def test_update_all_with_order_and_limit_and_offset_updates_subset_only
+ author = authors(:david)
+ limited_posts = author.posts_sorted_by_id_limited.offset(1)
+ assert_equal 1, limited_posts.size
+ assert_equal 2, limited_posts.limit(2).size
+ assert_equal 1, limited_posts.update_all([ "body = ?", "bulk update!" ])
+ assert_equal "bulk update!", posts(:thinking).body
+ assert_not_equal "bulk update!", posts(:welcome).body
+ end
+ end
+end
diff --git a/activerecord/test/cases/relation_test.rb b/activerecord/test/cases/relation_test.rb
index 93e2363025..fbeb617b29 100644
--- a/activerecord/test/cases/relation_test.rb
+++ b/activerecord/test/cases/relation_test.rb
@@ -5,10 +5,11 @@ require "models/post"
require "models/comment"
require "models/author"
require "models/rating"
+require "models/categorization"
module ActiveRecord
class RelationTest < ActiveRecord::TestCase
- fixtures :posts, :comments, :authors, :author_addresses, :ratings
+ fixtures :posts, :comments, :authors, :author_addresses, :ratings, :categorizations
def test_construction
relation = Relation.new(FakeKlass, table: :b)
@@ -223,6 +224,30 @@ module ActiveRecord
assert_equal manual_comments_on_post_that_have_author.size, merged_authors_with_commented_posts_relation.to_a.size
end
+ def test_relation_merging_with_merged_symbol_joins_is_aliased
+ categorizations_with_authors = Categorization.joins(:author)
+ queries = capture_sql { Post.joins(:author, :categorizations).merge(Author.select(:id)).merge(categorizations_with_authors).to_a }
+
+ nb_inner_join = queries.sum { |sql| sql.scan(/INNER\s+JOIN/i).size }
+ assert_equal 3, nb_inner_join, "Wrong amount of INNER JOIN in query"
+
+ # using `\W` as the column separator
+ assert queries.any? { |sql| %r[INNER\s+JOIN\s+#{Author.quoted_table_name}\s+\Wauthors_categorizations\W]i.match?(sql) }, "Should be aliasing the child INNER JOINs in query"
+ end
+
+ def test_relation_with_merged_joins_aliased_works
+ categorizations_with_authors = Categorization.joins(:author)
+ posts_with_joins_and_merges = Post.joins(:author, :categorizations)
+ .merge(Author.select(:id)).merge(categorizations_with_authors)
+
+ author_with_posts = Author.joins(:posts).ids
+ categorizations_with_author = Categorization.joins(:author).ids
+ posts_with_author_and_categorizations = Post.joins(:categorizations).where(author_id: author_with_posts, categorizations: { id: categorizations_with_author }).ids
+
+ assert_equal posts_with_author_and_categorizations.size, posts_with_joins_and_merges.count
+ assert_equal posts_with_author_and_categorizations.size, posts_with_joins_and_merges.to_a.size
+ end
+
def test_relation_merging_with_joins_as_join_dependency_pick_proper_parent
post = Post.create!(title: "haha", body: "huhu")
comment = post.comments.create!(body: "hu")
diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb
index 952d2dd5d9..9914a61033 100644
--- a/activerecord/test/cases/relations_test.rb
+++ b/activerecord/test/cases/relations_test.rb
@@ -9,6 +9,7 @@ require "models/comment"
require "models/author"
require "models/entrant"
require "models/developer"
+require "models/project"
require "models/person"
require "models/computer"
require "models/reply"
@@ -28,11 +29,6 @@ require "models/subscriber"
class RelationTest < ActiveRecord::TestCase
fixtures :authors, :author_addresses, :topics, :entrants, :developers, :people, :companies, :developers_projects, :accounts, :categories, :categorizations, :categories_posts, :posts, :comments, :tags, :taggings, :cars, :minivans
- class TopicWithCallbacks < ActiveRecord::Base
- self.table_name = :topics
- before_update { |topic| topic.author_name = "David" if topic.author_name.blank? }
- end
-
def test_do_not_double_quote_string_id
van = Minivan.last
assert van
@@ -860,45 +856,6 @@ class RelationTest < ActiveRecord::TestCase
assert_equal authors(:bob), authors.last
end
- def test_destroy_all
- davids = Author.where(name: "David")
-
- # Force load
- assert_equal [authors(:david)], davids.to_a
- assert_predicate davids, :loaded?
-
- assert_difference("Author.count", -1) { davids.destroy_all }
-
- assert_equal [], davids.to_a
- assert_predicate davids, :loaded?
- end
-
- def test_delete_all
- davids = Author.where(name: "David")
-
- assert_difference("Author.count", -1) { davids.delete_all }
- assert_not_predicate davids, :loaded?
- end
-
- def test_delete_all_loaded
- davids = Author.where(name: "David")
-
- # Force load
- assert_equal [authors(:david)], davids.to_a
- assert_predicate davids, :loaded?
-
- assert_difference("Author.count", -1) { davids.delete_all }
-
- assert_equal [], davids.to_a
- assert_predicate davids, :loaded?
- end
-
- def test_delete_all_with_unpermitted_relation_raises_error
- 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 }
- end
-
def test_select_with_aggregates
posts = Post.select(:title, :body)
@@ -983,14 +940,6 @@ class RelationTest < ActiveRecord::TestCase
assert_queries(1) { assert_equal 11, posts.load.size }
end
- def test_update_all_with_scope
- tag = Tag.first
- Post.tagged_with(tag.id).update_all title: "rofl"
- list = Post.tagged_with(tag.id).all.to_a
- assert_operator list.length, :>, 0
- list.each { |post| assert_equal "rofl", post.title }
- end
-
def test_count_explicit_columns
Post.update_all(comments_count: nil)
posts = Post.all
@@ -1403,6 +1352,16 @@ class RelationTest < ActiveRecord::TestCase
assert_equal "cock", hens.new.name
end
+ def test_create_with_nested_attributes
+ assert_difference("Project.count", 1) do
+ developers = Developer.where(name: "Aaron")
+ developers = developers.create_with(
+ projects_attributes: [{ name: "p1" }]
+ )
+ developers.create!
+ end
+ end
+
def test_except
relation = Post.where(author_id: 1).order("id ASC").limit(1)
assert_equal [posts(:welcome)], relation.to_a
@@ -1484,112 +1443,6 @@ class RelationTest < ActiveRecord::TestCase
assert_equal authors(:david), Author.order("id DESC , name DESC").last
end
- def test_update_all_with_blank_argument
- assert_raises(ArgumentError) { Comment.update_all({}) }
- end
-
- def test_update_all_with_joins
- comments = Comment.joins(:post).where("posts.id" => posts(:welcome).id)
- count = comments.count
-
- assert_equal count, comments.update_all(post_id: posts(:thinking).id)
- assert_equal posts(:thinking), comments(:greetings).post
- end
-
- def test_update_all_with_joins_and_limit
- comments = Comment.joins(:post).where("posts.id" => posts(:welcome).id).limit(1)
- assert_equal 1, comments.update_all(post_id: posts(:thinking).id)
- end
-
- def test_update_all_with_joins_and_limit_and_order
- comments = Comment.joins(:post).where("posts.id" => posts(:welcome).id).order("comments.id").limit(1)
- assert_equal 1, comments.update_all(post_id: posts(:thinking).id)
- assert_equal posts(:thinking), comments(:greetings).post
- assert_equal posts(:welcome), comments(:more_greetings).post
- end
-
- def test_update_all_with_joins_and_offset
- all_comments = Comment.joins(:post).where("posts.id" => posts(:welcome).id)
- count = all_comments.count
- comments = all_comments.offset(1)
-
- assert_equal count - 1, comments.update_all(post_id: posts(:thinking).id)
- end
-
- def test_update_all_with_joins_and_offset_and_order
- all_comments = Comment.joins(:post).where("posts.id" => posts(:welcome).id).order("posts.id", "comments.id")
- count = all_comments.count
- comments = all_comments.offset(1)
-
- assert_equal count - 1, comments.update_all(post_id: posts(:thinking).id)
- assert_equal posts(:thinking), comments(:more_greetings).post
- assert_equal posts(:welcome), comments(:greetings).post
- end
-
- def test_touch_all_updates_records_timestamps
- david = developers(:david)
- david_previously_updated_at = david.updated_at
- jamis = developers(:jamis)
- jamis_previously_updated_at = jamis.updated_at
- Developer.where(name: "David").touch_all
-
- assert_not_equal david_previously_updated_at, david.reload.updated_at
- assert_equal jamis_previously_updated_at, jamis.reload.updated_at
- end
-
- def test_touch_all_with_custom_timestamp
- developer = developers(:david)
- previously_created_at = developer.created_at
- previously_updated_at = developer.updated_at
- Developer.where(name: "David").touch_all(:created_at)
- developer = developer.reload
-
- assert_not_equal previously_created_at, developer.created_at
- assert_not_equal previously_updated_at, developer.updated_at
- end
-
- def test_touch_all_with_given_time
- developer = developers(:david)
- previously_created_at = developer.created_at
- previously_updated_at = developer.updated_at
- new_time = Time.utc(2015, 2, 16, 4, 54, 0)
- Developer.where(name: "David").touch_all(:created_at, time: new_time)
- developer = developer.reload
-
- assert_not_equal previously_created_at, developer.created_at
- assert_not_equal previously_updated_at, developer.updated_at
- assert_equal new_time, developer.created_at
- assert_equal new_time, developer.updated_at
- end
-
- def test_touch_all_updates_locking_column
- person = people(:david)
-
- assert_difference -> { person.reload.lock_version }, +1 do
- Person.where(first_name: "David").touch_all
- end
- end
-
- def test_update_on_relation
- topic1 = TopicWithCallbacks.create! title: "arel", author_name: nil
- topic2 = TopicWithCallbacks.create! title: "activerecord", author_name: nil
- topics = TopicWithCallbacks.where(id: [topic1.id, topic2.id])
- topics.update(title: "adequaterecord")
-
- assert_equal "adequaterecord", topic1.reload.title
- assert_equal "adequaterecord", topic2.reload.title
- # Testing that the before_update callbacks have run
- assert_equal "David", topic1.reload.author_name
- assert_equal "David", topic2.reload.author_name
- end
-
- def test_update_on_relation_passing_active_record_object_is_not_permitted
- topic = Topic.create!(title: "Foo", author_name: nil)
- assert_raises(ArgumentError) do
- Topic.where(id: topic.id).update(topic, title: "Bar")
- end
- end
-
def test_distinct
tag1 = Tag.create(name: "Foo")
tag2 = Tag.create(name: "Foo")
diff --git a/activerecord/test/cases/result_test.rb b/activerecord/test/cases/result_test.rb
index db52c108ac..68fcafb682 100644
--- a/activerecord/test/cases/result_test.rb
+++ b/activerecord/test/cases/result_test.rb
@@ -12,6 +12,11 @@ module ActiveRecord
])
end
+ test "includes_column?" do
+ assert result.includes_column?("col_1")
+ assert_not result.includes_column?("foo")
+ end
+
test "length" do
assert_equal 3, result.length
end
diff --git a/activerecord/test/cases/schema_dumper_test.rb b/activerecord/test/cases/schema_dumper_test.rb
index 15f4cea1a6..db13f20a39 100644
--- a/activerecord/test/cases/schema_dumper_test.rb
+++ b/activerecord/test/cases/schema_dumper_test.rb
@@ -226,6 +226,20 @@ class SchemaDumperTest < ActiveRecord::TestCase
assert_match %r{t\.float\s+"temperature"$}, output
end
+ if ActiveRecord::Base.connection.supports_expression_index?
+ def test_schema_dump_expression_indices
+ index_definition = dump_table_schema("companies").split(/\n/).grep(/t\.index.*company_expression_index/).first.strip
+
+ if current_adapter?(:PostgreSQLAdapter)
+ assert_match %r{CASE.+lower\(\(name\)::text\)}i, index_definition
+ elsif current_adapter?(:SQLite3Adapter)
+ assert_match %r{CASE.+lower\(name\)}i, index_definition
+ else
+ assert false
+ end
+ end
+ end
+
if current_adapter?(:Mysql2Adapter)
def test_schema_dump_includes_length_for_mysql_binary_fields
output = standard_dump
@@ -278,11 +292,6 @@ class SchemaDumperTest < ActiveRecord::TestCase
assert_match %r{t\.decimal\s+"decimal_array_default",\s+default: \["1.23", "3.45"\],\s+array: true}, output
end
- def test_schema_dump_expression_indices
- index_definition = dump_table_schema("companies").split(/\n/).grep(/t\.index.*company_expression_index/).first.strip
- assert_match %r{CASE.+lower\(\(name\)::text\)}i, index_definition
- end
-
def test_schema_dump_interval_type
output = dump_table_schema "postgresql_times"
assert_match %r{t\.interval\s+"time_interval"$}, output
@@ -297,29 +306,33 @@ class SchemaDumperTest < ActiveRecord::TestCase
def test_schema_dump_includes_extensions
connection = ActiveRecord::Base.connection
- connection.stubs(:extensions).returns(["hstore"])
- output = perform_schema_dump
- assert_match "# These are extensions that must be enabled", output
- assert_match %r{enable_extension "hstore"}, output
+ connection.stub(:extensions, ["hstore"]) do
+ output = perform_schema_dump
+ assert_match "# These are extensions that must be enabled", output
+ assert_match %r{enable_extension "hstore"}, output
+ end
- connection.stubs(:extensions).returns([])
- output = perform_schema_dump
- assert_no_match "# These are extensions that must be enabled", output
- assert_no_match %r{enable_extension}, output
+ connection.stub(:extensions, []) do
+ output = perform_schema_dump
+ assert_no_match "# These are extensions that must be enabled", output
+ assert_no_match %r{enable_extension}, output
+ end
end
def test_schema_dump_includes_extensions_in_alphabetic_order
connection = ActiveRecord::Base.connection
- connection.stubs(:extensions).returns(["hstore", "uuid-ossp", "xml2"])
- output = perform_schema_dump
- enabled_extensions = output.scan(%r{enable_extension "(.+)"}).flatten
- assert_equal ["hstore", "uuid-ossp", "xml2"], enabled_extensions
+ connection.stub(:extensions, ["hstore", "uuid-ossp", "xml2"]) do
+ output = perform_schema_dump
+ enabled_extensions = output.scan(%r{enable_extension "(.+)"}).flatten
+ assert_equal ["hstore", "uuid-ossp", "xml2"], enabled_extensions
+ end
- connection.stubs(:extensions).returns(["uuid-ossp", "xml2", "hstore"])
- output = perform_schema_dump
- enabled_extensions = output.scan(%r{enable_extension "(.+)"}).flatten
- assert_equal ["hstore", "uuid-ossp", "xml2"], enabled_extensions
+ connection.stub(:extensions, ["uuid-ossp", "xml2", "hstore"]) do
+ output = perform_schema_dump
+ enabled_extensions = output.scan(%r{enable_extension "(.+)"}).flatten
+ assert_equal ["hstore", "uuid-ossp", "xml2"], enabled_extensions
+ end
end
end
diff --git a/activerecord/test/cases/scoping/default_scoping_test.rb b/activerecord/test/cases/scoping/default_scoping_test.rb
index e3a34aa50d..6281712df6 100644
--- a/activerecord/test/cases/scoping/default_scoping_test.rb
+++ b/activerecord/test/cases/scoping/default_scoping_test.rb
@@ -4,6 +4,7 @@ require "cases/helper"
require "models/post"
require "models/comment"
require "models/developer"
+require "models/project"
require "models/computer"
require "models/vehicle"
require "models/cat"
@@ -366,6 +367,21 @@ class DefaultScopingTest < ActiveRecord::TestCase
assert_equal "Jamis", jamis.name
end
+ def test_create_with_takes_precedence_over_where
+ developer = Developer.where(name: nil).create_with(name: "Aaron").new
+ assert_equal "Aaron", developer.name
+ end
+
+ def test_create_with_nested_attributes
+ assert_difference("Project.count", 1) do
+ Developer.create_with(
+ projects_attributes: [{ name: "p1" }]
+ ).scoping do
+ Developer.create!(name: "Aaron")
+ end
+ end
+ end
+
# FIXME: I don't know if this is *desired* behavior, but it is *today's*
# behavior.
def test_create_with_empty_hash_will_not_reset
diff --git a/activerecord/test/cases/scoping/relation_scoping_test.rb b/activerecord/test/cases/scoping/relation_scoping_test.rb
index f18f1ed981..b4f4379e5e 100644
--- a/activerecord/test/cases/scoping/relation_scoping_test.rb
+++ b/activerecord/test/cases/scoping/relation_scoping_test.rb
@@ -236,8 +236,8 @@ class RelationScopingTest < ActiveRecord::TestCase
SpecialComment.unscoped.created
end
- assert_nil Comment.current_scope
- assert_nil SpecialComment.current_scope
+ assert_nil Comment.send(:current_scope)
+ assert_nil SpecialComment.send(:current_scope)
end
def test_scoping_respects_current_class
@@ -254,6 +254,11 @@ class RelationScopingTest < ActiveRecord::TestCase
end
end
+ def test_scoping_works_in_the_scope_block
+ expected = SpecialPostWithDefaultScope.unscoped.to_a
+ assert_equal expected, SpecialPostWithDefaultScope.unscoped_all
+ end
+
def test_circular_joins_with_scoping_does_not_crash
posts = Post.joins(comments: :post).scoping do
Post.first(10)
diff --git a/activerecord/test/cases/serialized_attribute_test.rb b/activerecord/test/cases/serialized_attribute_test.rb
index 7764a37273..1192b30b14 100644
--- a/activerecord/test/cases/serialized_attribute_test.rb
+++ b/activerecord/test/cases/serialized_attribute_test.rb
@@ -159,6 +159,13 @@ class SerializedAttributeTest < ActiveRecord::TestCase
assert_equal(settings, Topic.find(topic.id).content)
end
+ def test_where_by_serialized_attribute_with_array
+ settings = [ "color" => "green" ]
+ Topic.serialize(:content, Array)
+ topic = Topic.create!(content: settings)
+ assert_equal topic, Topic.where(content: settings).take
+ end
+
def test_where_by_serialized_attribute_with_hash
settings = { "color" => "green" }
Topic.serialize(:content, Hash)
@@ -315,7 +322,7 @@ class SerializedAttributeTest < ActiveRecord::TestCase
topic = Topic.create!(content: {})
topic2 = Topic.create!(content: nil)
- assert_equal [topic, topic2], Topic.where(content: nil)
+ assert_equal [topic, topic2], Topic.where(content: nil).sort_by(&:id)
end
def test_nil_is_always_persisted_as_null
diff --git a/activerecord/test/cases/store_test.rb b/activerecord/test/cases/store_test.rb
index 3bd480cfbd..4457cfbd37 100644
--- a/activerecord/test/cases/store_test.rb
+++ b/activerecord/test/cases/store_test.rb
@@ -214,4 +214,38 @@ class StoreTest < ActiveRecord::TestCase
second_dump = YAML.dump(loaded)
assert_equal @john, YAML.load(second_dump)
end
+
+ test "read store attributes through accessors with default suffix" do
+ @john.configs[:two_factor_auth] = true
+ assert_equal true, @john.two_factor_auth_configs
+ end
+
+ test "write store attributes through accessors with default suffix" do
+ @john.two_factor_auth_configs = false
+ assert_equal false, @john.configs[:two_factor_auth]
+ end
+
+ test "read store attributes through accessors with custom suffix" do
+ @john.configs[:login_retry] = 3
+ assert_equal 3, @john.login_retry_config
+ end
+
+ test "write store attributes through accessors with custom suffix" do
+ @john.login_retry_config = 5
+ assert_equal 5, @john.configs[:login_retry]
+ end
+
+ test "read accessor without pre/suffix in the same store as other pre/suffixed accessors still works" do
+ @john.configs[:secret_question] = "What is your high school?"
+ assert_equal "What is your high school?", @john.secret_question
+ end
+
+ test "write accessor without pre/suffix in the same store as other pre/suffixed accessors still works" do
+ @john.secret_question = "What was the Rails version when you first worked on it?"
+ assert_equal "What was the Rails version when you first worked on it?", @john.configs[:secret_question]
+ end
+
+ test "prefix/suffix do not affect stored attributes" do
+ assert_equal [:secret_question, :two_factor_auth, :login_retry], Admin::User.stored_attributes[:configs]
+ end
end
diff --git a/activerecord/test/cases/tasks/database_tasks_test.rb b/activerecord/test/cases/tasks/database_tasks_test.rb
index 60c7cb1bb4..d674bd562f 100644
--- a/activerecord/test/cases/tasks/database_tasks_test.rb
+++ b/activerecord/test/cases/tasks/database_tasks_test.rb
@@ -6,10 +6,18 @@ require "active_record/tasks/database_tasks"
module ActiveRecord
module DatabaseTasksSetupper
def setup
- @mysql_tasks, @postgresql_tasks, @sqlite_tasks = stub, stub, stub
- ActiveRecord::Tasks::MySQLDatabaseTasks.stubs(:new).returns @mysql_tasks
- ActiveRecord::Tasks::PostgreSQLDatabaseTasks.stubs(:new).returns @postgresql_tasks
- ActiveRecord::Tasks::SQLiteDatabaseTasks.stubs(:new).returns @sqlite_tasks
+ @mysql_tasks, @postgresql_tasks, @sqlite_tasks = Array.new(
+ 3,
+ Class.new do
+ def create; end
+ def drop; end
+ def purge; end
+ def charset; end
+ def collation; end
+ def structure_dump(*); end
+ def structure_load(*); end
+ end.new
+ )
$stdout, @original_stdout = StringIO.new, $stdout
$stderr, @original_stderr = StringIO.new, $stderr
@@ -18,6 +26,16 @@ module ActiveRecord
def teardown
$stdout, $stderr = @original_stdout, @original_stderr
end
+
+ def with_stubbed_new
+ ActiveRecord::Tasks::MySQLDatabaseTasks.stub(:new, @mysql_tasks) do
+ ActiveRecord::Tasks::PostgreSQLDatabaseTasks.stub(:new, @postgresql_tasks) do
+ ActiveRecord::Tasks::SQLiteDatabaseTasks.stub(:new, @sqlite_tasks) do
+ yield
+ end
+ end
+ end
+ end
end
ADAPTERS_TASKS = {
@@ -28,45 +46,62 @@ module ActiveRecord
class DatabaseTasksUtilsTask < ActiveRecord::TestCase
def test_raises_an_error_when_called_with_protected_environment
- ActiveRecord::MigrationContext.any_instance.stubs(:current_version).returns(1)
-
protected_environments = ActiveRecord::Base.protected_environments
current_env = ActiveRecord::Base.connection.migration_context.current_environment
- assert_not_includes protected_environments, current_env
- # Assert no error
- ActiveRecord::Tasks::DatabaseTasks.check_protected_environments!
- ActiveRecord::Base.protected_environments = [current_env]
- assert_raise(ActiveRecord::ProtectedEnvironmentError) do
+ assert_called_on_instance_of(
+ ActiveRecord::MigrationContext,
+ :current_version,
+ times: 6,
+ returns: 1
+ ) do
+ assert_not_includes protected_environments, current_env
+ # Assert no error
ActiveRecord::Tasks::DatabaseTasks.check_protected_environments!
+
+ ActiveRecord::Base.protected_environments = [current_env]
+
+ assert_raise(ActiveRecord::ProtectedEnvironmentError) do
+ ActiveRecord::Tasks::DatabaseTasks.check_protected_environments!
+ end
end
ensure
ActiveRecord::Base.protected_environments = protected_environments
end
def test_raises_an_error_when_called_with_protected_environment_which_name_is_a_symbol
- ActiveRecord::MigrationContext.any_instance.stubs(:current_version).returns(1)
-
protected_environments = ActiveRecord::Base.protected_environments
current_env = ActiveRecord::Base.connection.migration_context.current_environment
- assert_not_includes protected_environments, current_env
- # Assert no error
- ActiveRecord::Tasks::DatabaseTasks.check_protected_environments!
-
- ActiveRecord::Base.protected_environments = [current_env.to_sym]
- assert_raise(ActiveRecord::ProtectedEnvironmentError) do
+ assert_called_on_instance_of(
+ ActiveRecord::MigrationContext,
+ :current_version,
+ times: 6,
+ returns: 1
+ ) do
+ assert_not_includes protected_environments, current_env
+ # Assert no error
ActiveRecord::Tasks::DatabaseTasks.check_protected_environments!
+
+ ActiveRecord::Base.protected_environments = [current_env.to_sym]
+ assert_raise(ActiveRecord::ProtectedEnvironmentError) do
+ ActiveRecord::Tasks::DatabaseTasks.check_protected_environments!
+ end
end
ensure
ActiveRecord::Base.protected_environments = protected_environments
end
def test_raises_an_error_if_no_migrations_have_been_made
- ActiveRecord::InternalMetadata.stubs(:table_exists?).returns(false)
- ActiveRecord::MigrationContext.any_instance.stubs(:current_version).returns(1)
-
- assert_raise(ActiveRecord::NoEnvironmentInSchemaError) do
- ActiveRecord::Tasks::DatabaseTasks.check_protected_environments!
+ ActiveRecord::InternalMetadata.stub(:table_exists?, false) do
+ assert_called_on_instance_of(
+ ActiveRecord::MigrationContext,
+ :current_version,
+ returns: 1
+ ) do
+ assert_raise(ActiveRecord::NoEnvironmentInSchemaError) do
+ ActiveRecord::Tasks::DatabaseTasks.check_protected_environments!
+ end
+ end
end
end
end
@@ -79,11 +114,11 @@ module ActiveRecord
end
instance = klazz.new
- klazz.stubs(:new).returns instance
-
- assert_called_with(instance, :structure_dump, ["awesome-file.sql", nil]) do
- ActiveRecord::Tasks::DatabaseTasks.register_task(/foo/, klazz)
- ActiveRecord::Tasks::DatabaseTasks.structure_dump({ "adapter" => :foo }, "awesome-file.sql")
+ klazz.stub(:new, instance) do
+ assert_called_with(instance, :structure_dump, ["awesome-file.sql", nil]) do
+ ActiveRecord::Tasks::DatabaseTasks.register_task(/foo/, klazz)
+ ActiveRecord::Tasks::DatabaseTasks.structure_dump({ "adapter" => :foo }, "awesome-file.sql")
+ end
end
end
@@ -99,8 +134,11 @@ module ActiveRecord
ADAPTERS_TASKS.each do |k, v|
define_method("test_#{k}_create") do
- eval("@#{v}").expects(:create)
- ActiveRecord::Tasks::DatabaseTasks.create "adapter" => k
+ with_stubbed_new do
+ assert_called(eval("@#{v}"), :create) do
+ ActiveRecord::Tasks::DatabaseTasks.create "adapter" => k
+ end
+ end
end
end
end
@@ -120,59 +158,88 @@ module ActiveRecord
def setup
@configurations = { "development" => { "database" => "my-db" } }
- ActiveRecord::Base.stubs(:configurations).returns(@configurations)
- # To refrain from connecting to a newly created empty DB in sqlite3_mem tests
- ActiveRecord::Base.connection_handler.stubs(:establish_connection)
+ $stdout, @original_stdout = StringIO.new, $stdout
+ $stderr, @original_stderr = StringIO.new, $stderr
+ end
+
+ def teardown
+ $stdout, $stderr = @original_stdout, @original_stderr
end
def test_ignores_configurations_without_databases
- @configurations["development"].merge!("database" => nil)
+ @configurations["development"]["database"] = nil
- assert_not_called(ActiveRecord::Tasks::DatabaseTasks, :create) do
- ActiveRecord::Tasks::DatabaseTasks.create_all
+ with_stubbed_configurations_establish_connection do
+ assert_not_called(ActiveRecord::Tasks::DatabaseTasks, :create) do
+ ActiveRecord::Tasks::DatabaseTasks.create_all
+ end
end
end
def test_ignores_remote_databases
- @configurations["development"].merge!("host" => "my.server.tld")
- $stderr.stubs(:puts).returns(nil)
+ @configurations["development"]["host"] = "my.server.tld"
- assert_not_called(ActiveRecord::Tasks::DatabaseTasks, :create) do
- ActiveRecord::Tasks::DatabaseTasks.create_all
+ with_stubbed_configurations_establish_connection do
+ assert_not_called(ActiveRecord::Tasks::DatabaseTasks, :create) do
+ ActiveRecord::Tasks::DatabaseTasks.create_all
+ end
end
end
def test_warning_for_remote_databases
- @configurations["development"].merge!("host" => "my.server.tld")
+ @configurations["development"]["host"] = "my.server.tld"
- assert_called_with($stderr, :puts, ["This task only modifies local databases. my-db is on a remote host."]) do
+ with_stubbed_configurations_establish_connection do
ActiveRecord::Tasks::DatabaseTasks.create_all
+
+ assert_match "This task only modifies local databases. my-db is on a remote host.",
+ $stderr.string
end
end
def test_creates_configurations_with_local_ip
- @configurations["development"].merge!("host" => "127.0.0.1")
+ @configurations["development"]["host"] = "127.0.0.1"
- assert_called(ActiveRecord::Tasks::DatabaseTasks, :create) do
- ActiveRecord::Tasks::DatabaseTasks.create_all
+ with_stubbed_configurations_establish_connection do
+ assert_called(ActiveRecord::Tasks::DatabaseTasks, :create) do
+ ActiveRecord::Tasks::DatabaseTasks.create_all
+ end
end
end
def test_creates_configurations_with_local_host
- @configurations["development"].merge!("host" => "localhost")
+ @configurations["development"]["host"] = "localhost"
- assert_called(ActiveRecord::Tasks::DatabaseTasks, :create) do
- ActiveRecord::Tasks::DatabaseTasks.create_all
+ with_stubbed_configurations_establish_connection do
+ assert_called(ActiveRecord::Tasks::DatabaseTasks, :create) do
+ ActiveRecord::Tasks::DatabaseTasks.create_all
+ end
end
end
def test_creates_configurations_with_blank_hosts
- @configurations["development"].merge!("host" => nil)
+ @configurations["development"]["host"] = nil
- assert_called(ActiveRecord::Tasks::DatabaseTasks, :create) do
- ActiveRecord::Tasks::DatabaseTasks.create_all
+ with_stubbed_configurations_establish_connection do
+ assert_called(ActiveRecord::Tasks::DatabaseTasks, :create) do
+ ActiveRecord::Tasks::DatabaseTasks.create_all
+ end
end
end
+
+ private
+ def with_stubbed_configurations_establish_connection
+ old_configurations = ActiveRecord::Base.configurations
+ ActiveRecord::Base.configurations = @configurations
+
+ # To refrain from connecting to a newly created empty DB in
+ # sqlite3_mem tests
+ ActiveRecord::Base.connection_handler.stub(:establish_connection, nil) do
+ yield
+ end
+ ensure
+ ActiveRecord::Base.configurations = old_configurations
+ end
end
class DatabaseTasksCreateCurrentTest < ActiveRecord::TestCase
@@ -180,72 +247,98 @@ module ActiveRecord
@configurations = {
"development" => { "database" => "dev-db" },
"test" => { "database" => "test-db" },
- "production" => { "url" => "prod-db-url" }
+ "production" => { "url" => "abstract://prod-db-host/prod-db" }
}
-
- ActiveRecord::Base.stubs(:configurations).returns(@configurations)
- ActiveRecord::Base.stubs(:establish_connection).returns(true)
end
def test_creates_current_environment_database
- assert_called_with(
- ActiveRecord::Tasks::DatabaseTasks,
- :create,
- ["database" => "test-db"],
- ) do
- ActiveRecord::Tasks::DatabaseTasks.create_current(
- ActiveSupport::StringInquirer.new("test")
- )
+ with_stubbed_configurations_establish_connection do
+ assert_called_with(
+ ActiveRecord::Tasks::DatabaseTasks,
+ :create,
+ ["database" => "test-db"],
+ ) do
+ ActiveRecord::Tasks::DatabaseTasks.create_current(
+ ActiveSupport::StringInquirer.new("test")
+ )
+ end
end
end
def test_creates_current_environment_database_with_url
- assert_called_with(
- ActiveRecord::Tasks::DatabaseTasks,
- :create,
- ["url" => "prod-db-url"],
- ) do
- ActiveRecord::Tasks::DatabaseTasks.create_current(
- ActiveSupport::StringInquirer.new("production")
- )
+ with_stubbed_configurations_establish_connection do
+ assert_called_with(
+ ActiveRecord::Tasks::DatabaseTasks,
+ :create,
+ ["adapter" => "abstract", "database" => "prod-db", "host" => "prod-db-host"],
+ ) do
+ ActiveRecord::Tasks::DatabaseTasks.create_current(
+ ActiveSupport::StringInquirer.new("production")
+ )
+ end
end
end
def test_creates_test_and_development_databases_when_env_was_not_specified
- ActiveRecord::Tasks::DatabaseTasks.expects(:create).
- with("database" => "dev-db")
- ActiveRecord::Tasks::DatabaseTasks.expects(:create).
- with("database" => "test-db")
-
- ActiveRecord::Tasks::DatabaseTasks.create_current(
- ActiveSupport::StringInquirer.new("development")
- )
+ with_stubbed_configurations_establish_connection do
+ assert_called_with(
+ ActiveRecord::Tasks::DatabaseTasks,
+ :create,
+ [
+ ["database" => "dev-db"],
+ ["database" => "test-db"]
+ ],
+ ) do
+ ActiveRecord::Tasks::DatabaseTasks.create_current(
+ ActiveSupport::StringInquirer.new("development")
+ )
+ end
+ end
end
def test_creates_test_and_development_databases_when_rails_env_is_development
old_env = ENV["RAILS_ENV"]
ENV["RAILS_ENV"] = "development"
- ActiveRecord::Tasks::DatabaseTasks.expects(:create).
- with("database" => "dev-db")
- ActiveRecord::Tasks::DatabaseTasks.expects(:create).
- with("database" => "test-db")
- ActiveRecord::Tasks::DatabaseTasks.create_current(
- ActiveSupport::StringInquirer.new("development")
- )
+ with_stubbed_configurations_establish_connection do
+ assert_called_with(
+ ActiveRecord::Tasks::DatabaseTasks,
+ :create,
+ [
+ ["database" => "dev-db"],
+ ["database" => "test-db"]
+ ],
+ ) do
+ ActiveRecord::Tasks::DatabaseTasks.create_current(
+ ActiveSupport::StringInquirer.new("development")
+ )
+ end
+ end
ensure
ENV["RAILS_ENV"] = old_env
end
def test_establishes_connection_for_the_given_environments
- ActiveRecord::Tasks::DatabaseTasks.stubs(:create).returns true
+ ActiveRecord::Tasks::DatabaseTasks.stub(:create, nil) do
+ assert_called_with(ActiveRecord::Base, :establish_connection, [:development]) do
+ ActiveRecord::Tasks::DatabaseTasks.create_current(
+ ActiveSupport::StringInquirer.new("development")
+ )
+ end
+ end
+ end
- ActiveRecord::Base.expects(:establish_connection).with(:development)
+ private
+ def with_stubbed_configurations_establish_connection
+ old_configurations = ActiveRecord::Base.configurations
+ ActiveRecord::Base.configurations = @configurations
- ActiveRecord::Tasks::DatabaseTasks.create_current(
- ActiveSupport::StringInquirer.new("development")
- )
- end
+ ActiveRecord::Base.connection_handler.stub(:establish_connection, nil) do
+ yield
+ end
+ ensure
+ ActiveRecord::Base.configurations = old_configurations
+ end
end
class DatabaseTasksCreateCurrentThreeTierTest < ActiveRecord::TestCase
@@ -253,80 +346,112 @@ module ActiveRecord
@configurations = {
"development" => { "primary" => { "database" => "dev-db" }, "secondary" => { "database" => "secondary-dev-db" } },
"test" => { "primary" => { "database" => "test-db" }, "secondary" => { "database" => "secondary-test-db" } },
- "production" => { "primary" => { "url" => "prod-db-url" }, "secondary" => { "url" => "secondary-prod-db-url" } }
+ "production" => { "primary" => { "url" => "abstract://prod-db-host/prod-db" }, "secondary" => { "url" => "abstract://secondary-prod-db-host/secondary-prod-db" } }
}
-
- ActiveRecord::Base.stubs(:configurations).returns(@configurations)
- ActiveRecord::Base.stubs(:establish_connection).returns(true)
end
def test_creates_current_environment_database
- ActiveRecord::Tasks::DatabaseTasks.expects(:create).
- with("database" => "test-db")
-
- ActiveRecord::Tasks::DatabaseTasks.expects(:create).
- with("database" => "secondary-test-db")
-
- ActiveRecord::Tasks::DatabaseTasks.create_current(
- ActiveSupport::StringInquirer.new("test")
- )
+ with_stubbed_configurations_establish_connection do
+ assert_called_with(
+ ActiveRecord::Tasks::DatabaseTasks,
+ :create,
+ [
+ ["database" => "test-db"],
+ ["database" => "secondary-test-db"]
+ ]
+ ) do
+ ActiveRecord::Tasks::DatabaseTasks.create_current(
+ ActiveSupport::StringInquirer.new("test")
+ )
+ end
+ end
end
def test_creates_current_environment_database_with_url
- ActiveRecord::Tasks::DatabaseTasks.expects(:create).
- with("url" => "prod-db-url")
-
- ActiveRecord::Tasks::DatabaseTasks.expects(:create).
- with("url" => "secondary-prod-db-url")
-
- ActiveRecord::Tasks::DatabaseTasks.create_current(
- ActiveSupport::StringInquirer.new("production")
- )
+ with_stubbed_configurations_establish_connection do
+ assert_called_with(
+ ActiveRecord::Tasks::DatabaseTasks,
+ :create,
+ [
+ ["adapter" => "abstract", "database" => "prod-db", "host" => "prod-db-host"],
+ ["adapter" => "abstract", "database" => "secondary-prod-db", "host" => "secondary-prod-db-host"]
+ ]
+ ) do
+ ActiveRecord::Tasks::DatabaseTasks.create_current(
+ ActiveSupport::StringInquirer.new("production")
+ )
+ end
+ end
end
def test_creates_test_and_development_databases_when_env_was_not_specified
- ActiveRecord::Tasks::DatabaseTasks.expects(:create).
- with("database" => "dev-db")
- ActiveRecord::Tasks::DatabaseTasks.expects(:create).
- with("database" => "secondary-dev-db")
- ActiveRecord::Tasks::DatabaseTasks.expects(:create).
- with("database" => "test-db")
- ActiveRecord::Tasks::DatabaseTasks.expects(:create).
- with("database" => "secondary-test-db")
-
- ActiveRecord::Tasks::DatabaseTasks.create_current(
- ActiveSupport::StringInquirer.new("development")
- )
+ with_stubbed_configurations_establish_connection do
+ assert_called_with(
+ ActiveRecord::Tasks::DatabaseTasks,
+ :create,
+ [
+ ["database" => "dev-db"],
+ ["database" => "secondary-dev-db"],
+ ["database" => "test-db"],
+ ["database" => "secondary-test-db"]
+ ]
+ ) do
+ ActiveRecord::Tasks::DatabaseTasks.create_current(
+ ActiveSupport::StringInquirer.new("development")
+ )
+ end
+ end
end
def test_creates_test_and_development_databases_when_rails_env_is_development
old_env = ENV["RAILS_ENV"]
ENV["RAILS_ENV"] = "development"
- ActiveRecord::Tasks::DatabaseTasks.expects(:create).
- with("database" => "dev-db")
- ActiveRecord::Tasks::DatabaseTasks.expects(:create).
- with("database" => "secondary-dev-db")
- ActiveRecord::Tasks::DatabaseTasks.expects(:create).
- with("database" => "test-db")
- ActiveRecord::Tasks::DatabaseTasks.expects(:create).
- with("database" => "secondary-test-db")
-
- ActiveRecord::Tasks::DatabaseTasks.create_current(
- ActiveSupport::StringInquirer.new("development")
- )
+
+ with_stubbed_configurations_establish_connection do
+ assert_called_with(
+ ActiveRecord::Tasks::DatabaseTasks,
+ :create,
+ [
+ ["database" => "dev-db"],
+ ["database" => "secondary-dev-db"],
+ ["database" => "test-db"],
+ ["database" => "secondary-test-db"]
+ ]
+ ) do
+ ActiveRecord::Tasks::DatabaseTasks.create_current(
+ ActiveSupport::StringInquirer.new("development")
+ )
+ end
+ end
ensure
ENV["RAILS_ENV"] = old_env
end
def test_establishes_connection_for_the_given_environments_config
- ActiveRecord::Tasks::DatabaseTasks.stubs(:create).returns true
+ ActiveRecord::Tasks::DatabaseTasks.stub(:create, nil) do
+ assert_called_with(
+ ActiveRecord::Base,
+ :establish_connection,
+ [:development]
+ ) do
+ ActiveRecord::Tasks::DatabaseTasks.create_current(
+ ActiveSupport::StringInquirer.new("development")
+ )
+ end
+ end
+ end
- ActiveRecord::Base.expects(:establish_connection).with(:development)
+ private
+ def with_stubbed_configurations_establish_connection
+ old_configurations = ActiveRecord::Base.configurations
+ ActiveRecord::Base.configurations = @configurations
- ActiveRecord::Tasks::DatabaseTasks.create_current(
- ActiveSupport::StringInquirer.new("development")
- )
- end
+ ActiveRecord::Base.connection_handler.stub(:establish_connection, nil) do
+ yield
+ end
+ ensure
+ ActiveRecord::Base.configurations = old_configurations
+ end
end
class DatabaseTasksDropTest < ActiveRecord::TestCase
@@ -334,8 +459,11 @@ module ActiveRecord
ADAPTERS_TASKS.each do |k, v|
define_method("test_#{k}_drop") do
- eval("@#{v}").expects(:drop)
- ActiveRecord::Tasks::DatabaseTasks.drop "adapter" => k
+ with_stubbed_new do
+ assert_called(eval("@#{v}"), :drop) do
+ ActiveRecord::Tasks::DatabaseTasks.drop "adapter" => k
+ end
+ end
end
end
end
@@ -344,61 +472,84 @@ module ActiveRecord
def setup
@configurations = { development: { "database" => "my-db" } }
- ActiveRecord::Base.stubs(:configurations).returns(@configurations)
+ $stdout, @original_stdout = StringIO.new, $stdout
+ $stderr, @original_stderr = StringIO.new, $stderr
+ end
+
+ def teardown
+ $stdout, $stderr = @original_stdout, @original_stderr
end
def test_ignores_configurations_without_databases
- @configurations[:development].merge!("database" => nil)
+ @configurations[:development]["database"] = nil
- assert_not_called(ActiveRecord::Tasks::DatabaseTasks, :drop) do
- ActiveRecord::Tasks::DatabaseTasks.drop_all
+ with_stubbed_configurations do
+ assert_not_called(ActiveRecord::Tasks::DatabaseTasks, :drop) do
+ ActiveRecord::Tasks::DatabaseTasks.drop_all
+ end
end
end
def test_ignores_remote_databases
- @configurations[:development].merge!("host" => "my.server.tld")
- $stderr.stubs(:puts).returns(nil)
+ @configurations[:development]["host"] = "my.server.tld"
- assert_not_called(ActiveRecord::Tasks::DatabaseTasks, :drop) do
- ActiveRecord::Tasks::DatabaseTasks.drop_all
+ with_stubbed_configurations do
+ assert_not_called(ActiveRecord::Tasks::DatabaseTasks, :drop) do
+ ActiveRecord::Tasks::DatabaseTasks.drop_all
+ end
end
end
def test_warning_for_remote_databases
- @configurations[:development].merge!("host" => "my.server.tld")
+ @configurations[:development]["host"] = "my.server.tld"
- assert_called_with(
- $stderr,
- :puts,
- ["This task only modifies local databases. my-db is on a remote host."],
- ) do
+ with_stubbed_configurations do
ActiveRecord::Tasks::DatabaseTasks.drop_all
+
+ assert_match "This task only modifies local databases. my-db is on a remote host.",
+ $stderr.string
end
end
def test_drops_configurations_with_local_ip
- @configurations[:development].merge!("host" => "127.0.0.1")
+ @configurations[:development]["host"] = "127.0.0.1"
- assert_called(ActiveRecord::Tasks::DatabaseTasks, :drop) do
- ActiveRecord::Tasks::DatabaseTasks.drop_all
+ with_stubbed_configurations do
+ assert_called(ActiveRecord::Tasks::DatabaseTasks, :drop) do
+ ActiveRecord::Tasks::DatabaseTasks.drop_all
+ end
end
end
def test_drops_configurations_with_local_host
- @configurations[:development].merge!("host" => "localhost")
+ @configurations[:development]["host"] = "localhost"
- assert_called(ActiveRecord::Tasks::DatabaseTasks, :drop) do
- ActiveRecord::Tasks::DatabaseTasks.drop_all
+ with_stubbed_configurations do
+ assert_called(ActiveRecord::Tasks::DatabaseTasks, :drop) do
+ ActiveRecord::Tasks::DatabaseTasks.drop_all
+ end
end
end
def test_drops_configurations_with_blank_hosts
- @configurations[:development].merge!("host" => nil)
+ @configurations[:development]["host"] = nil
- assert_called(ActiveRecord::Tasks::DatabaseTasks, :drop) do
- ActiveRecord::Tasks::DatabaseTasks.drop_all
+ with_stubbed_configurations do
+ assert_called(ActiveRecord::Tasks::DatabaseTasks, :drop) do
+ ActiveRecord::Tasks::DatabaseTasks.drop_all
+ end
end
end
+
+ private
+ def with_stubbed_configurations
+ old_configurations = ActiveRecord::Base.configurations
+ ActiveRecord::Base.configurations = @configurations
+
+ yield
+ ensure
+ ActiveRecord::Base.configurations = old_configurations
+ end
end
class DatabaseTasksDropCurrentTest < ActiveRecord::TestCase
@@ -406,55 +557,80 @@ module ActiveRecord
@configurations = {
"development" => { "database" => "dev-db" },
"test" => { "database" => "test-db" },
- "production" => { "url" => "prod-db-url" }
+ "production" => { "url" => "abstract://prod-db-host/prod-db" }
}
-
- ActiveRecord::Base.stubs(:configurations).returns(@configurations)
end
def test_drops_current_environment_database
- ActiveRecord::Tasks::DatabaseTasks.expects(:drop).
- with("database" => "test-db")
-
- ActiveRecord::Tasks::DatabaseTasks.drop_current(
- ActiveSupport::StringInquirer.new("test")
- )
+ with_stubbed_configurations do
+ assert_called_with(ActiveRecord::Tasks::DatabaseTasks, :drop,
+ ["database" => "test-db"]) do
+ ActiveRecord::Tasks::DatabaseTasks.drop_current(
+ ActiveSupport::StringInquirer.new("test")
+ )
+ end
+ end
end
def test_drops_current_environment_database_with_url
- ActiveRecord::Tasks::DatabaseTasks.expects(:drop).
- with("url" => "prod-db-url")
-
- ActiveRecord::Tasks::DatabaseTasks.drop_current(
- ActiveSupport::StringInquirer.new("production")
- )
+ with_stubbed_configurations do
+ assert_called_with(ActiveRecord::Tasks::DatabaseTasks, :drop,
+ ["adapter" => "abstract", "database" => "prod-db", "host" => "prod-db-host"]) do
+ ActiveRecord::Tasks::DatabaseTasks.drop_current(
+ ActiveSupport::StringInquirer.new("production")
+ )
+ end
+ end
end
def test_drops_test_and_development_databases_when_env_was_not_specified
- ActiveRecord::Tasks::DatabaseTasks.expects(:drop).
- with("database" => "dev-db")
- ActiveRecord::Tasks::DatabaseTasks.expects(:drop).
- with("database" => "test-db")
-
- ActiveRecord::Tasks::DatabaseTasks.drop_current(
- ActiveSupport::StringInquirer.new("development")
- )
+ with_stubbed_configurations do
+ assert_called_with(
+ ActiveRecord::Tasks::DatabaseTasks,
+ :drop,
+ [
+ ["database" => "dev-db"],
+ ["database" => "test-db"]
+ ]
+ ) do
+ ActiveRecord::Tasks::DatabaseTasks.drop_current(
+ ActiveSupport::StringInquirer.new("development")
+ )
+ end
+ end
end
def test_drops_testand_development_databases_when_rails_env_is_development
old_env = ENV["RAILS_ENV"]
ENV["RAILS_ENV"] = "development"
- ActiveRecord::Tasks::DatabaseTasks.expects(:drop).
- with("database" => "dev-db")
- ActiveRecord::Tasks::DatabaseTasks.expects(:drop).
- with("database" => "test-db")
- ActiveRecord::Tasks::DatabaseTasks.drop_current(
- ActiveSupport::StringInquirer.new("development")
- )
+ with_stubbed_configurations do
+ assert_called_with(
+ ActiveRecord::Tasks::DatabaseTasks,
+ :drop,
+ [
+ ["database" => "dev-db"],
+ ["database" => "test-db"]
+ ]
+ ) do
+ ActiveRecord::Tasks::DatabaseTasks.drop_current(
+ ActiveSupport::StringInquirer.new("development")
+ )
+ end
+ end
ensure
ENV["RAILS_ENV"] = old_env
end
+
+ private
+ def with_stubbed_configurations
+ old_configurations = ActiveRecord::Base.configurations
+ ActiveRecord::Base.configurations = @configurations
+
+ yield
+ ensure
+ ActiveRecord::Base.configurations = old_configurations
+ end
end
class DatabaseTasksDropCurrentThreeTierTest < ActiveRecord::TestCase
@@ -462,69 +638,96 @@ module ActiveRecord
@configurations = {
"development" => { "primary" => { "database" => "dev-db" }, "secondary" => { "database" => "secondary-dev-db" } },
"test" => { "primary" => { "database" => "test-db" }, "secondary" => { "database" => "secondary-test-db" } },
- "production" => { "primary" => { "url" => "prod-db-url" }, "secondary" => { "url" => "secondary-prod-db-url" } }
+ "production" => { "primary" => { "url" => "abstract://prod-db-host/prod-db" }, "secondary" => { "url" => "abstract://secondary-prod-db-host/secondary-prod-db" } }
}
-
- ActiveRecord::Base.stubs(:configurations).returns(@configurations)
end
def test_drops_current_environment_database
- ActiveRecord::Tasks::DatabaseTasks.expects(:drop).
- with("database" => "test-db")
-
- ActiveRecord::Tasks::DatabaseTasks.expects(:drop).
- with("database" => "secondary-test-db")
-
- ActiveRecord::Tasks::DatabaseTasks.drop_current(
- ActiveSupport::StringInquirer.new("test")
- )
+ with_stubbed_configurations do
+ assert_called_with(
+ ActiveRecord::Tasks::DatabaseTasks,
+ :drop,
+ [
+ ["database" => "test-db"],
+ ["database" => "secondary-test-db"]
+ ]
+ ) do
+ ActiveRecord::Tasks::DatabaseTasks.drop_current(
+ ActiveSupport::StringInquirer.new("test")
+ )
+ end
+ end
end
def test_drops_current_environment_database_with_url
- ActiveRecord::Tasks::DatabaseTasks.expects(:drop).
- with("url" => "prod-db-url")
-
- ActiveRecord::Tasks::DatabaseTasks.expects(:drop).
- with("url" => "secondary-prod-db-url")
-
- ActiveRecord::Tasks::DatabaseTasks.drop_current(
- ActiveSupport::StringInquirer.new("production")
- )
+ with_stubbed_configurations do
+ assert_called_with(
+ ActiveRecord::Tasks::DatabaseTasks,
+ :drop,
+ [
+ ["adapter" => "abstract", "database" => "prod-db", "host" => "prod-db-host"],
+ ["adapter" => "abstract", "database" => "secondary-prod-db", "host" => "secondary-prod-db-host"]
+ ]
+ ) do
+ ActiveRecord::Tasks::DatabaseTasks.drop_current(
+ ActiveSupport::StringInquirer.new("production")
+ )
+ end
+ end
end
def test_drops_test_and_development_databases_when_env_was_not_specified
- ActiveRecord::Tasks::DatabaseTasks.expects(:drop).
- with("database" => "dev-db")
- ActiveRecord::Tasks::DatabaseTasks.expects(:drop).
- with("database" => "secondary-dev-db")
- ActiveRecord::Tasks::DatabaseTasks.expects(:drop).
- with("database" => "test-db")
- ActiveRecord::Tasks::DatabaseTasks.expects(:drop).
- with("database" => "secondary-test-db")
-
- ActiveRecord::Tasks::DatabaseTasks.drop_current(
- ActiveSupport::StringInquirer.new("development")
- )
+ with_stubbed_configurations do
+ assert_called_with(
+ ActiveRecord::Tasks::DatabaseTasks,
+ :drop,
+ [
+ ["database" => "dev-db"],
+ ["database" => "secondary-dev-db"],
+ ["database" => "test-db"],
+ ["database" => "secondary-test-db"]
+ ]
+ ) do
+ ActiveRecord::Tasks::DatabaseTasks.drop_current(
+ ActiveSupport::StringInquirer.new("development")
+ )
+ end
+ end
end
def test_drops_testand_development_databases_when_rails_env_is_development
old_env = ENV["RAILS_ENV"]
ENV["RAILS_ENV"] = "development"
- ActiveRecord::Tasks::DatabaseTasks.expects(:drop).
- with("database" => "dev-db")
- ActiveRecord::Tasks::DatabaseTasks.expects(:drop).
- with("database" => "secondary-dev-db")
- ActiveRecord::Tasks::DatabaseTasks.expects(:drop).
- with("database" => "test-db")
- ActiveRecord::Tasks::DatabaseTasks.expects(:drop).
- with("database" => "secondary-test-db")
-
- ActiveRecord::Tasks::DatabaseTasks.drop_current(
- ActiveSupport::StringInquirer.new("development")
- )
+
+ with_stubbed_configurations do
+ assert_called_with(
+ ActiveRecord::Tasks::DatabaseTasks,
+ :drop,
+ [
+ ["database" => "dev-db"],
+ ["database" => "secondary-dev-db"],
+ ["database" => "test-db"],
+ ["database" => "secondary-test-db"]
+ ]
+ ) do
+ ActiveRecord::Tasks::DatabaseTasks.drop_current(
+ ActiveSupport::StringInquirer.new("development")
+ )
+ end
+ end
ensure
ENV["RAILS_ENV"] = old_env
end
+
+ private
+ def with_stubbed_configurations
+ old_configurations = ActiveRecord::Base.configurations
+ ActiveRecord::Base.configurations = @configurations
+
+ yield
+ ensure
+ ActiveRecord::Base.configurations = old_configurations
+ end
end
if current_adapter?(:SQLite3Adapter) && !in_memory_db?
@@ -649,10 +852,10 @@ module ActiveRecord
end
def test_migrate_raise_error_on_failed_check_target_version
- ActiveRecord::Tasks::DatabaseTasks.stubs(:check_target_version).raises("foo")
-
- e = assert_raise(RuntimeError) { ActiveRecord::Tasks::DatabaseTasks.migrate }
- assert_equal "foo", e.message
+ ActiveRecord::Tasks::DatabaseTasks.stub(:check_target_version, -> { raise "foo" }) do
+ e = assert_raise(RuntimeError) { ActiveRecord::Tasks::DatabaseTasks.migrate }
+ assert_equal "foo", e.message
+ end
end
def test_migrate_clears_schema_cache_afterward
@@ -667,39 +870,55 @@ module ActiveRecord
ADAPTERS_TASKS.each do |k, v|
define_method("test_#{k}_purge") do
- eval("@#{v}").expects(:purge)
- ActiveRecord::Tasks::DatabaseTasks.purge "adapter" => k
+ with_stubbed_new do
+ assert_called(eval("@#{v}"), :purge) do
+ ActiveRecord::Tasks::DatabaseTasks.purge "adapter" => k
+ end
+ end
end
end
end
class DatabaseTasksPurgeCurrentTest < ActiveRecord::TestCase
def test_purges_current_environment_database
+ old_configurations = ActiveRecord::Base.configurations
configurations = {
"development" => { "database" => "dev-db" },
"test" => { "database" => "test-db" },
"production" => { "database" => "prod-db" }
}
- ActiveRecord::Base.stubs(:configurations).returns(configurations)
- ActiveRecord::Tasks::DatabaseTasks.expects(:purge).
- with("database" => "prod-db")
+ ActiveRecord::Base.configurations = configurations
- assert_called_with(ActiveRecord::Base, :establish_connection, [:production]) do
- ActiveRecord::Tasks::DatabaseTasks.purge_current("production")
+ assert_called_with(
+ ActiveRecord::Tasks::DatabaseTasks,
+ :purge,
+ ["database" => "prod-db"]
+ ) do
+ assert_called_with(ActiveRecord::Base, :establish_connection, [:production]) do
+ ActiveRecord::Tasks::DatabaseTasks.purge_current("production")
+ end
end
+ ensure
+ ActiveRecord::Base.configurations = old_configurations
end
end
class DatabaseTasksPurgeAllTest < ActiveRecord::TestCase
def test_purge_all_local_configurations
+ old_configurations = ActiveRecord::Base.configurations
configurations = { development: { "database" => "my-db" } }
- ActiveRecord::Base.stubs(:configurations).returns(configurations)
-
- ActiveRecord::Tasks::DatabaseTasks.expects(:purge).
- with("database" => "my-db")
+ ActiveRecord::Base.configurations = configurations
- ActiveRecord::Tasks::DatabaseTasks.purge_all
+ assert_called_with(
+ ActiveRecord::Tasks::DatabaseTasks,
+ :purge,
+ ["database" => "my-db"]
+ ) do
+ ActiveRecord::Tasks::DatabaseTasks.purge_all
+ end
+ ensure
+ ActiveRecord::Base.configurations = old_configurations
end
end
@@ -708,8 +927,11 @@ module ActiveRecord
ADAPTERS_TASKS.each do |k, v|
define_method("test_#{k}_charset") do
- eval("@#{v}").expects(:charset)
- ActiveRecord::Tasks::DatabaseTasks.charset "adapter" => k
+ with_stubbed_new do
+ assert_called(eval("@#{v}"), :charset) do
+ ActiveRecord::Tasks::DatabaseTasks.charset "adapter" => k
+ end
+ end
end
end
end
@@ -719,8 +941,11 @@ module ActiveRecord
ADAPTERS_TASKS.each do |k, v|
define_method("test_#{k}_collation") do
- eval("@#{v}").expects(:collation)
- ActiveRecord::Tasks::DatabaseTasks.collation "adapter" => k
+ with_stubbed_new do
+ assert_called(eval("@#{v}"), :collation) do
+ ActiveRecord::Tasks::DatabaseTasks.collation "adapter" => k
+ end
+ end
end
end
end
@@ -832,8 +1057,14 @@ module ActiveRecord
ADAPTERS_TASKS.each do |k, v|
define_method("test_#{k}_structure_dump") do
- eval("@#{v}").expects(:structure_dump).with("awesome-file.sql", nil)
- ActiveRecord::Tasks::DatabaseTasks.structure_dump({ "adapter" => k }, "awesome-file.sql")
+ with_stubbed_new do
+ assert_called_with(
+ eval("@#{v}"), :structure_dump,
+ ["awesome-file.sql", nil]
+ ) do
+ ActiveRecord::Tasks::DatabaseTasks.structure_dump({ "adapter" => k }, "awesome-file.sql")
+ end
+ end
end
end
end
@@ -843,8 +1074,15 @@ module ActiveRecord
ADAPTERS_TASKS.each do |k, v|
define_method("test_#{k}_structure_load") do
- eval("@#{v}").expects(:structure_load).with("awesome-file.sql", nil)
- ActiveRecord::Tasks::DatabaseTasks.structure_load({ "adapter" => k }, "awesome-file.sql")
+ with_stubbed_new do
+ assert_called_with(
+ eval("@#{v}"),
+ :structure_load,
+ ["awesome-file.sql", nil]
+ ) do
+ ActiveRecord::Tasks::DatabaseTasks.structure_load({ "adapter" => k }, "awesome-file.sql")
+ end
+ end
end
end
end
@@ -859,16 +1097,18 @@ module ActiveRecord
class DatabaseTasksCheckSchemaFileDefaultsTest < ActiveRecord::TestCase
def test_check_schema_file_defaults
- ActiveRecord::Tasks::DatabaseTasks.stubs(:db_dir).returns("/tmp")
- assert_equal "/tmp/schema.rb", ActiveRecord::Tasks::DatabaseTasks.schema_file
+ ActiveRecord::Tasks::DatabaseTasks.stub(:db_dir, "/tmp") do
+ assert_equal "/tmp/schema.rb", ActiveRecord::Tasks::DatabaseTasks.schema_file
+ end
end
end
class DatabaseTasksCheckSchemaFileSpecifiedFormatsTest < ActiveRecord::TestCase
{ ruby: "schema.rb", sql: "structure.sql" }.each_pair do |fmt, filename|
define_method("test_check_schema_file_for_#{fmt}_format") do
- ActiveRecord::Tasks::DatabaseTasks.stubs(:db_dir).returns("/tmp")
- assert_equal "/tmp/#{filename}", ActiveRecord::Tasks::DatabaseTasks.schema_file(fmt)
+ ActiveRecord::Tasks::DatabaseTasks.stub(:db_dir, "/tmp") do
+ assert_equal "/tmp/#{filename}", ActiveRecord::Tasks::DatabaseTasks.schema_file(fmt)
+ end
end
end
end
diff --git a/activerecord/test/cases/tasks/mysql_rake_test.rb b/activerecord/test/cases/tasks/mysql_rake_test.rb
index 6cddfaefeb..4d6dff68f9 100644
--- a/activerecord/test/cases/tasks/mysql_rake_test.rb
+++ b/activerecord/test/cases/tasks/mysql_rake_test.rb
@@ -7,15 +7,11 @@ if current_adapter?(:Mysql2Adapter)
module ActiveRecord
class MysqlDBCreateTest < ActiveRecord::TestCase
def setup
- @connection = stub(create_database: true)
+ @connection = Class.new { def create_database(*); end }.new
@configuration = {
"adapter" => "mysql2",
"database" => "my-app-db"
}
-
- ActiveRecord::Base.stubs(:connection).returns(@connection)
- ActiveRecord::Base.stubs(:establish_connection).returns(true)
-
$stdout, @original_stdout = StringIO.new, $stdout
$stderr, @original_stderr = StringIO.new, $stderr
end
@@ -25,59 +21,97 @@ if current_adapter?(:Mysql2Adapter)
end
def test_establishes_connection_without_database
- ActiveRecord::Base.expects(:establish_connection).
- with("adapter" => "mysql2", "database" => nil)
-
- ActiveRecord::Tasks::DatabaseTasks.create @configuration
+ ActiveRecord::Base.stub(:connection, @connection) do
+ assert_called_with(
+ ActiveRecord::Base,
+ :establish_connection,
+ [
+ [ "adapter" => "mysql2", "database" => nil ],
+ [ "adapter" => "mysql2", "database" => "my-app-db" ],
+ ]
+ ) do
+ ActiveRecord::Tasks::DatabaseTasks.create @configuration
+ end
+ end
end
def test_creates_database_with_no_default_options
- @connection.expects(:create_database).
- with("my-app-db", {})
-
- ActiveRecord::Tasks::DatabaseTasks.create @configuration
+ with_stubbed_connection_establish_connection do
+ assert_called_with(@connection, :create_database, ["my-app-db", {}]) do
+ ActiveRecord::Tasks::DatabaseTasks.create @configuration
+ end
+ end
end
def test_creates_database_with_given_encoding
- @connection.expects(:create_database).
- with("my-app-db", charset: "latin1")
-
- ActiveRecord::Tasks::DatabaseTasks.create @configuration.merge("encoding" => "latin1")
+ with_stubbed_connection_establish_connection do
+ assert_called_with(@connection, :create_database, ["my-app-db", charset: "latin1"]) do
+ ActiveRecord::Tasks::DatabaseTasks.create @configuration.merge("encoding" => "latin1")
+ end
+ end
end
def test_creates_database_with_given_collation
- @connection.expects(:create_database).
- with("my-app-db", collation: "latin1_swedish_ci")
-
- ActiveRecord::Tasks::DatabaseTasks.create @configuration.merge("collation" => "latin1_swedish_ci")
+ with_stubbed_connection_establish_connection do
+ assert_called_with(
+ @connection,
+ :create_database,
+ ["my-app-db", collation: "latin1_swedish_ci"]
+ ) do
+ ActiveRecord::Tasks::DatabaseTasks.create @configuration.merge("collation" => "latin1_swedish_ci")
+ end
+ end
end
def test_establishes_connection_to_database
- ActiveRecord::Base.expects(:establish_connection).with(@configuration)
-
- ActiveRecord::Tasks::DatabaseTasks.create @configuration
+ ActiveRecord::Base.stub(:connection, @connection) do
+ assert_called_with(
+ ActiveRecord::Base,
+ :establish_connection,
+ [
+ ["adapter" => "mysql2", "database" => nil],
+ [@configuration]
+ ]
+ ) do
+ ActiveRecord::Tasks::DatabaseTasks.create @configuration
+ end
+ end
end
def test_when_database_created_successfully_outputs_info_to_stdout
- ActiveRecord::Tasks::DatabaseTasks.create @configuration
+ with_stubbed_connection_establish_connection do
+ ActiveRecord::Tasks::DatabaseTasks.create @configuration
- assert_equal "Created database 'my-app-db'\n", $stdout.string
+ assert_equal "Created database 'my-app-db'\n", $stdout.string
+ end
end
def test_create_when_database_exists_outputs_info_to_stderr
- ActiveRecord::Base.connection.stubs(:create_database).raises(
- ActiveRecord::Tasks::DatabaseAlreadyExists
- )
+ with_stubbed_connection_establish_connection do
+ ActiveRecord::Base.connection.stub(
+ :create_database,
+ proc { raise ActiveRecord::Tasks::DatabaseAlreadyExists }
+ ) do
+ ActiveRecord::Tasks::DatabaseTasks.create @configuration
+
+ assert_equal "Database 'my-app-db' already exists\n", $stderr.string
+ end
+ end
+ end
- ActiveRecord::Tasks::DatabaseTasks.create @configuration
+ private
- assert_equal "Database 'my-app-db' already exists\n", $stderr.string
- end
+ def with_stubbed_connection_establish_connection
+ ActiveRecord::Base.stub(:establish_connection, nil) do
+ ActiveRecord::Base.stub(:connection, @connection) do
+ yield
+ end
+ end
+ end
end
class MysqlDBCreateWithInvalidPermissionsTest < ActiveRecord::TestCase
def setup
- @connection = stub("Connection", create_database: true)
@error = Mysql2::Error.new("Invalid permissions")
@configuration = {
"adapter" => "mysql2",
@@ -85,10 +119,6 @@ if current_adapter?(:Mysql2Adapter)
"username" => "pat",
"password" => "wossname"
}
-
- ActiveRecord::Base.stubs(:connection).returns(@connection)
- ActiveRecord::Base.stubs(:establish_connection).raises(@error)
-
$stdout, @original_stdout = StringIO.new, $stdout
$stderr, @original_stderr = StringIO.new, $stderr
end
@@ -98,23 +128,21 @@ if current_adapter?(:Mysql2Adapter)
end
def test_raises_error
- assert_raises(Mysql2::Error) do
- ActiveRecord::Tasks::DatabaseTasks.create @configuration
+ ActiveRecord::Base.stub(:establish_connection, -> * { raise @error }) do
+ assert_raises(Mysql2::Error, "Invalid permissions") do
+ ActiveRecord::Tasks::DatabaseTasks.create @configuration
+ end
end
end
end
class MySQLDBDropTest < ActiveRecord::TestCase
def setup
- @connection = stub(drop_database: true)
+ @connection = Class.new { def drop_database(name); end }.new
@configuration = {
"adapter" => "mysql2",
"database" => "my-app-db"
}
-
- ActiveRecord::Base.stubs(:connection).returns(@connection)
- ActiveRecord::Base.stubs(:establish_connection).returns(true)
-
$stdout, @original_stdout = StringIO.new, $stdout
$stderr, @original_stderr = StringIO.new, $stderr
end
@@ -124,91 +152,130 @@ if current_adapter?(:Mysql2Adapter)
end
def test_establishes_connection_to_mysql_database
- ActiveRecord::Base.expects(:establish_connection).with @configuration
-
- ActiveRecord::Tasks::DatabaseTasks.drop @configuration
+ ActiveRecord::Base.stub(:connection, @connection) do
+ assert_called_with(
+ ActiveRecord::Base,
+ :establish_connection,
+ [@configuration]
+ ) do
+ ActiveRecord::Tasks::DatabaseTasks.drop @configuration
+ end
+ end
end
def test_drops_database
- @connection.expects(:drop_database).with("my-app-db")
-
- ActiveRecord::Tasks::DatabaseTasks.drop @configuration
+ with_stubbed_connection_establish_connection do
+ assert_called_with(@connection, :drop_database, ["my-app-db"]) do
+ ActiveRecord::Tasks::DatabaseTasks.drop @configuration
+ end
+ end
end
def test_when_database_dropped_successfully_outputs_info_to_stdout
- ActiveRecord::Tasks::DatabaseTasks.drop @configuration
+ with_stubbed_connection_establish_connection do
+ ActiveRecord::Tasks::DatabaseTasks.drop @configuration
- assert_equal "Dropped database 'my-app-db'\n", $stdout.string
+ assert_equal "Dropped database 'my-app-db'\n", $stdout.string
+ end
end
+
+ private
+
+ def with_stubbed_connection_establish_connection
+ ActiveRecord::Base.stub(:establish_connection, nil) do
+ ActiveRecord::Base.stub(:connection, @connection) do
+ yield
+ end
+ end
+ end
end
class MySQLPurgeTest < ActiveRecord::TestCase
def setup
- @connection = stub(recreate_database: true)
+ @connection = Class.new { def recreate_database(*); end }.new
@configuration = {
"adapter" => "mysql2",
"database" => "test-db"
}
-
- ActiveRecord::Base.stubs(:connection).returns(@connection)
- ActiveRecord::Base.stubs(:establish_connection).returns(true)
end
def test_establishes_connection_to_the_appropriate_database
- ActiveRecord::Base.expects(:establish_connection).with(@configuration)
-
- ActiveRecord::Tasks::DatabaseTasks.purge @configuration
+ ActiveRecord::Base.stub(:connection, @connection) do
+ assert_called_with(
+ ActiveRecord::Base,
+ :establish_connection,
+ [@configuration]
+ ) do
+ ActiveRecord::Tasks::DatabaseTasks.purge @configuration
+ end
+ end
end
def test_recreates_database_with_no_default_options
- @connection.expects(:recreate_database).
- with("test-db", {})
-
- ActiveRecord::Tasks::DatabaseTasks.purge @configuration
+ with_stubbed_connection_establish_connection do
+ assert_called_with(@connection, :recreate_database, ["test-db", {}]) do
+ ActiveRecord::Tasks::DatabaseTasks.purge @configuration
+ end
+ end
end
def test_recreates_database_with_the_given_options
- @connection.expects(:recreate_database).
- with("test-db", charset: "latin", collation: "latin1_swedish_ci")
-
- ActiveRecord::Tasks::DatabaseTasks.purge @configuration.merge(
- "encoding" => "latin", "collation" => "latin1_swedish_ci")
+ with_stubbed_connection_establish_connection do
+ assert_called_with(
+ @connection,
+ :recreate_database,
+ ["test-db", charset: "latin", collation: "latin1_swedish_ci"]
+ ) do
+ ActiveRecord::Tasks::DatabaseTasks.purge @configuration.merge(
+ "encoding" => "latin", "collation" => "latin1_swedish_ci")
+ end
+ end
end
+
+ private
+
+ def with_stubbed_connection_establish_connection
+ ActiveRecord::Base.stub(:establish_connection, nil) do
+ ActiveRecord::Base.stub(:connection, @connection) do
+ yield
+ end
+ end
+ end
end
class MysqlDBCharsetTest < ActiveRecord::TestCase
def setup
- @connection = stub(create_database: true)
+ @connection = Class.new { def charset; end }.new
@configuration = {
"adapter" => "mysql2",
"database" => "my-app-db"
}
-
- ActiveRecord::Base.stubs(:connection).returns(@connection)
- ActiveRecord::Base.stubs(:establish_connection).returns(true)
end
def test_db_retrieves_charset
- @connection.expects(:charset)
- ActiveRecord::Tasks::DatabaseTasks.charset @configuration
+ ActiveRecord::Base.stub(:connection, @connection) do
+ assert_called(@connection, :charset) do
+ ActiveRecord::Tasks::DatabaseTasks.charset @configuration
+ end
+ end
end
end
class MysqlDBCollationTest < ActiveRecord::TestCase
def setup
- @connection = stub(create_database: true)
+ @connection = Class.new { def collation; end }.new
@configuration = {
"adapter" => "mysql2",
"database" => "my-app-db"
}
-
- ActiveRecord::Base.stubs(:connection).returns(@connection)
- ActiveRecord::Base.stubs(:establish_connection).returns(true)
end
def test_db_retrieves_collation
- @connection.expects(:collation)
- ActiveRecord::Tasks::DatabaseTasks.collation @configuration
+ ActiveRecord::Base.stub(:connection, @connection) do
+ assert_called_with(@connection, :collation) do
+ ActiveRecord::Tasks::DatabaseTasks.collation @configuration
+ end
+ end
end
end
@@ -245,15 +312,15 @@ if current_adapter?(:Mysql2Adapter)
def test_structure_dump_with_ignore_tables
filename = "awesome-file.sql"
- ActiveRecord::SchemaDumper.expects(:ignore_tables).returns(["foo", "bar"])
-
- assert_called_with(
- Kernel,
- :system,
- ["mysqldump", "--result-file", filename, "--no-data", "--routines", "--skip-comments", "--ignore-table=test-db.foo", "--ignore-table=test-db.bar", "test-db"],
- returns: true
- ) do
- ActiveRecord::Tasks::DatabaseTasks.structure_dump(@configuration, filename)
+ ActiveRecord::SchemaDumper.stub(:ignore_tables, ["foo", "bar"]) do
+ assert_called_with(
+ Kernel,
+ :system,
+ ["mysqldump", "--result-file", filename, "--no-data", "--routines", "--skip-comments", "--ignore-table=test-db.foo", "--ignore-table=test-db.bar", "test-db"],
+ returns: true
+ ) do
+ ActiveRecord::Tasks::DatabaseTasks.structure_dump(@configuration, filename)
+ end
end
end
diff --git a/activerecord/test/cases/tasks/postgresql_rake_test.rb b/activerecord/test/cases/tasks/postgresql_rake_test.rb
index a1a3700f07..8c6e8d79f3 100644
--- a/activerecord/test/cases/tasks/postgresql_rake_test.rb
+++ b/activerecord/test/cases/tasks/postgresql_rake_test.rb
@@ -7,15 +7,11 @@ if current_adapter?(:PostgreSQLAdapter)
module ActiveRecord
class PostgreSQLDBCreateTest < ActiveRecord::TestCase
def setup
- @connection = stub(create_database: true)
+ @connection = Class.new { def create_database(*); end }.new
@configuration = {
"adapter" => "postgresql",
"database" => "my-app-db"
}
-
- ActiveRecord::Base.stubs(:connection).returns(@connection)
- ActiveRecord::Base.stubs(:establish_connection).returns(true)
-
$stdout, @original_stdout = StringIO.new, $stdout
$stderr, @original_stderr = StringIO.new, $stderr
end
@@ -25,82 +21,141 @@ if current_adapter?(:PostgreSQLAdapter)
end
def test_establishes_connection_to_postgresql_database
- ActiveRecord::Base.expects(:establish_connection).with(
- "adapter" => "postgresql",
- "database" => "postgres",
- "schema_search_path" => "public"
- )
-
- ActiveRecord::Tasks::DatabaseTasks.create @configuration
+ ActiveRecord::Base.stub(:connection, @connection) do
+ assert_called_with(
+ ActiveRecord::Base,
+ :establish_connection,
+ [
+ [
+ "adapter" => "postgresql",
+ "database" => "postgres",
+ "schema_search_path" => "public"
+ ],
+ [
+ "adapter" => "postgresql",
+ "database" => "my-app-db"
+ ]
+ ]
+ ) do
+ ActiveRecord::Tasks::DatabaseTasks.create @configuration
+ end
+ end
end
def test_creates_database_with_default_encoding
- @connection.expects(:create_database).
- with("my-app-db", @configuration.merge("encoding" => "utf8"))
-
- ActiveRecord::Tasks::DatabaseTasks.create @configuration
+ with_stubbed_connection_establish_connection do
+ assert_called_with(
+ @connection,
+ :create_database,
+ ["my-app-db", @configuration.merge("encoding" => "utf8")]
+ ) do
+ ActiveRecord::Tasks::DatabaseTasks.create @configuration
+ end
+ end
end
def test_creates_database_with_given_encoding
- @connection.expects(:create_database).
- with("my-app-db", @configuration.merge("encoding" => "latin"))
-
- ActiveRecord::Tasks::DatabaseTasks.create @configuration.
- merge("encoding" => "latin")
+ with_stubbed_connection_establish_connection do
+ assert_called_with(
+ @connection,
+ :create_database,
+ ["my-app-db", @configuration.merge("encoding" => "latin")]
+ ) do
+ ActiveRecord::Tasks::DatabaseTasks.create @configuration.
+ merge("encoding" => "latin")
+ end
+ end
end
def test_creates_database_with_given_collation_and_ctype
- @connection.expects(:create_database).
- with("my-app-db", @configuration.merge("encoding" => "utf8", "collation" => "ja_JP.UTF8", "ctype" => "ja_JP.UTF8"))
-
- ActiveRecord::Tasks::DatabaseTasks.create @configuration.
- merge("collation" => "ja_JP.UTF8", "ctype" => "ja_JP.UTF8")
+ with_stubbed_connection_establish_connection do
+ assert_called_with(
+ @connection,
+ :create_database,
+ [
+ "my-app-db",
+ @configuration.merge(
+ "encoding" => "utf8",
+ "collation" => "ja_JP.UTF8",
+ "ctype" => "ja_JP.UTF8"
+ )
+ ]
+ ) do
+ ActiveRecord::Tasks::DatabaseTasks.create @configuration.
+ merge("collation" => "ja_JP.UTF8", "ctype" => "ja_JP.UTF8")
+ end
+ end
end
def test_establishes_connection_to_new_database
- ActiveRecord::Base.expects(:establish_connection).with(@configuration)
-
- ActiveRecord::Tasks::DatabaseTasks.create @configuration
+ ActiveRecord::Base.stub(:connection, @connection) do
+ assert_called_with(
+ ActiveRecord::Base,
+ :establish_connection,
+ [
+ [
+ "adapter" => "postgresql",
+ "database" => "postgres",
+ "schema_search_path" => "public"
+ ],
+ [
+ @configuration
+ ]
+ ]
+ ) do
+ ActiveRecord::Tasks::DatabaseTasks.create @configuration
+ end
+ end
end
def test_db_create_with_error_prints_message
- ActiveRecord::Base.stubs(:establish_connection).raises(Exception)
-
- $stderr.stubs(:puts).returns(true)
- $stderr.expects(:puts).
- with("Couldn't create database for #{@configuration.inspect}")
-
- assert_raises(Exception) { ActiveRecord::Tasks::DatabaseTasks.create @configuration }
+ ActiveRecord::Base.stub(:connection, @connection) do
+ ActiveRecord::Base.stub(:establish_connection, -> * { raise Exception }) do
+ assert_raises(Exception) { ActiveRecord::Tasks::DatabaseTasks.create @configuration }
+ assert_match "Couldn't create '#{@configuration['database']}' database. Please check your configuration.", $stderr.string
+ end
+ end
end
def test_when_database_created_successfully_outputs_info_to_stdout
- ActiveRecord::Tasks::DatabaseTasks.create @configuration
+ with_stubbed_connection_establish_connection do
+ ActiveRecord::Tasks::DatabaseTasks.create @configuration
- assert_equal "Created database 'my-app-db'\n", $stdout.string
+ assert_equal "Created database 'my-app-db'\n", $stdout.string
+ end
end
def test_create_when_database_exists_outputs_info_to_stderr
- ActiveRecord::Base.connection.stubs(:create_database).raises(
- ActiveRecord::Tasks::DatabaseAlreadyExists
- )
-
- ActiveRecord::Tasks::DatabaseTasks.create @configuration
+ with_stubbed_connection_establish_connection do
+ ActiveRecord::Base.connection.stub(
+ :create_database,
+ proc { raise ActiveRecord::Tasks::DatabaseAlreadyExists }
+ ) do
+ ActiveRecord::Tasks::DatabaseTasks.create @configuration
- assert_equal "Database 'my-app-db' already exists\n", $stderr.string
+ assert_equal "Database 'my-app-db' already exists\n", $stderr.string
+ end
+ end
end
+
+ private
+
+ def with_stubbed_connection_establish_connection
+ ActiveRecord::Base.stub(:connection, @connection) do
+ ActiveRecord::Base.stub(:establish_connection, nil) do
+ yield
+ end
+ end
+ end
end
class PostgreSQLDBDropTest < ActiveRecord::TestCase
def setup
- @connection = stub(drop_database: true)
+ @connection = Class.new { def drop_database(*); end }.new
@configuration = {
"adapter" => "postgresql",
"database" => "my-app-db"
}
-
- ActiveRecord::Base.stubs(:connection).returns(@connection)
- ActiveRecord::Base.stubs(:establish_connection).returns(true)
-
$stdout, @original_stdout = StringIO.new, $stdout
$stderr, @original_stderr = StringIO.new, $stderr
end
@@ -110,125 +165,197 @@ if current_adapter?(:PostgreSQLAdapter)
end
def test_establishes_connection_to_postgresql_database
- ActiveRecord::Base.expects(:establish_connection).with(
- "adapter" => "postgresql",
- "database" => "postgres",
- "schema_search_path" => "public"
- )
-
- ActiveRecord::Tasks::DatabaseTasks.drop @configuration
+ ActiveRecord::Base.stub(:connection, @connection) do
+ assert_called_with(
+ ActiveRecord::Base,
+ :establish_connection,
+ [
+ "adapter" => "postgresql",
+ "database" => "postgres",
+ "schema_search_path" => "public"
+ ]
+ ) do
+ ActiveRecord::Tasks::DatabaseTasks.drop @configuration
+ end
+ end
end
def test_drops_database
- @connection.expects(:drop_database).with("my-app-db")
-
- ActiveRecord::Tasks::DatabaseTasks.drop @configuration
+ with_stubbed_connection_establish_connection do
+ assert_called_with(
+ @connection,
+ :drop_database,
+ ["my-app-db"]
+ ) do
+ ActiveRecord::Tasks::DatabaseTasks.drop @configuration
+ end
+ end
end
def test_when_database_dropped_successfully_outputs_info_to_stdout
- ActiveRecord::Tasks::DatabaseTasks.drop @configuration
+ with_stubbed_connection_establish_connection do
+ ActiveRecord::Tasks::DatabaseTasks.drop @configuration
- assert_equal "Dropped database 'my-app-db'\n", $stdout.string
+ assert_equal "Dropped database 'my-app-db'\n", $stdout.string
+ end
end
+
+ private
+
+ def with_stubbed_connection_establish_connection
+ ActiveRecord::Base.stub(:connection, @connection) do
+ ActiveRecord::Base.stub(:establish_connection, nil) do
+ yield
+ end
+ end
+ end
end
class PostgreSQLPurgeTest < ActiveRecord::TestCase
def setup
- @connection = stub(create_database: true, drop_database: true)
+ @connection = Class.new do
+ def create_database(*); end
+ def drop_database(*); end
+ end.new
@configuration = {
"adapter" => "postgresql",
"database" => "my-app-db"
}
-
- ActiveRecord::Base.stubs(:connection).returns(@connection)
- ActiveRecord::Base.stubs(:clear_active_connections!).returns(true)
- ActiveRecord::Base.stubs(:establish_connection).returns(true)
end
def test_clears_active_connections
- ActiveRecord::Base.expects(:clear_active_connections!)
-
- ActiveRecord::Tasks::DatabaseTasks.purge @configuration
+ with_stubbed_connection do
+ ActiveRecord::Base.stub(:establish_connection, nil) do
+ assert_called(ActiveRecord::Base, :clear_active_connections!) do
+ ActiveRecord::Tasks::DatabaseTasks.purge @configuration
+ end
+ end
+ end
end
def test_establishes_connection_to_postgresql_database
- ActiveRecord::Base.expects(:establish_connection).with(
- "adapter" => "postgresql",
- "database" => "postgres",
- "schema_search_path" => "public"
- )
-
- ActiveRecord::Tasks::DatabaseTasks.purge @configuration
+ with_stubbed_connection do
+ assert_called_with(
+ ActiveRecord::Base,
+ :establish_connection,
+ [
+ [
+ "adapter" => "postgresql",
+ "database" => "postgres",
+ "schema_search_path" => "public"
+ ],
+ [
+ "adapter" => "postgresql",
+ "database" => "my-app-db"
+ ]
+ ]
+ ) do
+ ActiveRecord::Tasks::DatabaseTasks.purge @configuration
+ end
+ end
end
def test_drops_database
- @connection.expects(:drop_database).with("my-app-db")
-
- ActiveRecord::Tasks::DatabaseTasks.purge @configuration
+ with_stubbed_connection do
+ ActiveRecord::Base.stub(:establish_connection, nil) do
+ assert_called_with(@connection, :drop_database, ["my-app-db"]) do
+ ActiveRecord::Tasks::DatabaseTasks.purge @configuration
+ end
+ end
+ end
end
def test_creates_database
- @connection.expects(:create_database).
- with("my-app-db", @configuration.merge("encoding" => "utf8"))
-
- ActiveRecord::Tasks::DatabaseTasks.purge @configuration
+ with_stubbed_connection do
+ ActiveRecord::Base.stub(:establish_connection, nil) do
+ assert_called_with(
+ @connection,
+ :create_database,
+ ["my-app-db", @configuration.merge("encoding" => "utf8")]
+ ) do
+ ActiveRecord::Tasks::DatabaseTasks.purge @configuration
+ end
+ end
+ end
end
def test_establishes_connection
- ActiveRecord::Base.expects(:establish_connection).with(@configuration)
-
- ActiveRecord::Tasks::DatabaseTasks.purge @configuration
+ with_stubbed_connection do
+ assert_called_with(
+ ActiveRecord::Base,
+ :establish_connection,
+ [
+ [
+ "adapter" => "postgresql",
+ "database" => "postgres",
+ "schema_search_path" => "public"
+ ],
+ [
+ @configuration
+ ]
+ ]
+ ) do
+ ActiveRecord::Tasks::DatabaseTasks.purge @configuration
+ end
+ end
end
+
+ private
+
+ def with_stubbed_connection
+ ActiveRecord::Base.stub(:connection, @connection) do
+ yield
+ end
+ end
end
class PostgreSQLDBCharsetTest < ActiveRecord::TestCase
def setup
- @connection = stub(create_database: true)
+ @connection = Class.new do
+ def create_database(*); end
+ def encoding; end
+ end.new
@configuration = {
"adapter" => "postgresql",
"database" => "my-app-db"
}
-
- ActiveRecord::Base.stubs(:connection).returns(@connection)
- ActiveRecord::Base.stubs(:establish_connection).returns(true)
end
def test_db_retrieves_charset
- @connection.expects(:encoding)
- ActiveRecord::Tasks::DatabaseTasks.charset @configuration
+ ActiveRecord::Base.stub(:connection, @connection) do
+ assert_called(@connection, :encoding) do
+ ActiveRecord::Tasks::DatabaseTasks.charset @configuration
+ end
+ end
end
end
class PostgreSQLDBCollationTest < ActiveRecord::TestCase
def setup
- @connection = stub(create_database: true)
+ @connection = Class.new { def collation; end }.new
@configuration = {
"adapter" => "postgresql",
"database" => "my-app-db"
}
-
- ActiveRecord::Base.stubs(:connection).returns(@connection)
- ActiveRecord::Base.stubs(:establish_connection).returns(true)
end
def test_db_retrieves_collation
- @connection.expects(:collation)
- ActiveRecord::Tasks::DatabaseTasks.collation @configuration
+ ActiveRecord::Base.stub(:connection, @connection) do
+ assert_called(@connection, :collation) do
+ ActiveRecord::Tasks::DatabaseTasks.collation @configuration
+ end
+ end
end
end
class PostgreSQLStructureDumpTest < ActiveRecord::TestCase
def setup
- @connection = stub(schema_search_path: nil, structure_dump: true)
@configuration = {
"adapter" => "postgresql",
"database" => "my-app-db"
}
@filename = "/tmp/awesome-file.sql"
FileUtils.touch(@filename)
-
- ActiveRecord::Base.stubs(:connection).returns(@connection)
- ActiveRecord::Base.stubs(:establish_connection).returns(true)
end
def teardown
@@ -247,12 +374,12 @@ if current_adapter?(:PostgreSQLAdapter)
end
def test_structure_dump_header_comments_removed
- Kernel.stubs(:system).returns(true)
- File.write(@filename, "-- header comment\n\n-- more header comment\n statement \n-- lower comment\n")
-
- ActiveRecord::Tasks::DatabaseTasks.structure_dump(@configuration, @filename)
+ Kernel.stub(:system, true) do
+ File.write(@filename, "-- header comment\n\n-- more header comment\n statement \n-- lower comment\n")
+ ActiveRecord::Tasks::DatabaseTasks.structure_dump(@configuration, @filename)
- assert_equal [" statement \n", "-- lower comment\n"], File.readlines(@filename).first(2)
+ assert_equal [" statement \n", "-- lower comment\n"], File.readlines(@filename).first(2)
+ end
end
def test_structure_dump_with_extra_flags
@@ -358,14 +485,10 @@ if current_adapter?(:PostgreSQLAdapter)
class PostgreSQLStructureLoadTest < ActiveRecord::TestCase
def setup
- @connection = stub
@configuration = {
"adapter" => "postgresql",
"database" => "my-app-db"
}
-
- ActiveRecord::Base.stubs(:connection).returns(@connection)
- ActiveRecord::Base.stubs(:establish_connection).returns(true)
end
def test_structure_load
diff --git a/activerecord/test/cases/tasks/sqlite_rake_test.rb b/activerecord/test/cases/tasks/sqlite_rake_test.rb
index d368a7a6ee..c1092b97c1 100644
--- a/activerecord/test/cases/tasks/sqlite_rake_test.rb
+++ b/activerecord/test/cases/tasks/sqlite_rake_test.rb
@@ -9,16 +9,10 @@ if current_adapter?(:SQLite3Adapter)
class SqliteDBCreateTest < ActiveRecord::TestCase
def setup
@database = "db_create.sqlite3"
- @connection = stub :connection
@configuration = {
"adapter" => "sqlite3",
"database" => @database
}
-
- File.stubs(:exist?).returns(false)
- ActiveRecord::Base.stubs(:connection).returns(@connection)
- ActiveRecord::Base.stubs(:establish_connection).returns(true)
-
$stdout, @original_stdout = StringIO.new, $stdout
$stderr, @original_stderr = StringIO.new, $stderr
end
@@ -28,63 +22,62 @@ if current_adapter?(:SQLite3Adapter)
end
def test_db_checks_database_exists
- File.expects(:exist?).with(@database).returns(false)
-
- ActiveRecord::Tasks::DatabaseTasks.create @configuration, "/rails/root"
+ ActiveRecord::Base.stub(:establish_connection, nil) do
+ assert_called_with(File, :exist?, [@database], returns: false) do
+ ActiveRecord::Tasks::DatabaseTasks.create @configuration, "/rails/root"
+ end
+ end
end
def test_when_db_created_successfully_outputs_info_to_stdout
- ActiveRecord::Tasks::DatabaseTasks.create @configuration, "/rails/root"
+ ActiveRecord::Base.stub(:establish_connection, nil) do
+ ActiveRecord::Tasks::DatabaseTasks.create @configuration, "/rails/root"
- assert_equal "Created database '#{@database}'\n", $stdout.string
+ assert_equal "Created database '#{@database}'\n", $stdout.string
+ end
end
def test_db_create_when_file_exists
- File.stubs(:exist?).returns(true)
-
- ActiveRecord::Tasks::DatabaseTasks.create @configuration, "/rails/root"
+ File.stub(:exist?, true) do
+ ActiveRecord::Tasks::DatabaseTasks.create @configuration, "/rails/root"
- assert_equal "Database '#{@database}' already exists\n", $stderr.string
+ assert_equal "Database '#{@database}' already exists\n", $stderr.string
+ end
end
def test_db_create_with_file_does_nothing
- File.stubs(:exist?).returns(true)
- $stderr.stubs(:puts).returns(nil)
-
- ActiveRecord::Base.expects(:establish_connection).never
-
- ActiveRecord::Tasks::DatabaseTasks.create @configuration, "/rails/root"
+ File.stub(:exist?, true) do
+ assert_not_called(ActiveRecord::Base, :establish_connection) do
+ ActiveRecord::Tasks::DatabaseTasks.create @configuration, "/rails/root"
+ end
+ end
end
def test_db_create_establishes_a_connection
- ActiveRecord::Base.expects(:establish_connection).with(@configuration)
-
- ActiveRecord::Tasks::DatabaseTasks.create @configuration, "/rails/root"
+ assert_called_with(ActiveRecord::Base, :establish_connection, [@configuration]) do
+ ActiveRecord::Tasks::DatabaseTasks.create @configuration, "/rails/root"
+ end
end
def test_db_create_with_error_prints_message
- ActiveRecord::Base.stubs(:establish_connection).raises(Exception)
-
- $stderr.stubs(:puts).returns(true)
- $stderr.expects(:puts).
- with("Couldn't create database for #{@configuration.inspect}")
-
- assert_raises(Exception) { ActiveRecord::Tasks::DatabaseTasks.create @configuration, "/rails/root" }
+ ActiveRecord::Base.stub(:establish_connection, proc { raise Exception }) do
+ assert_raises(Exception) { ActiveRecord::Tasks::DatabaseTasks.create @configuration, "/rails/root" }
+ assert_match "Couldn't create '#{@configuration['database']}' database. Please check your configuration.", $stderr.string
+ end
end
end
class SqliteDBDropTest < ActiveRecord::TestCase
def setup
@database = "db_create.sqlite3"
- @path = stub(to_s: "/absolute/path", absolute?: true)
@configuration = {
"adapter" => "sqlite3",
"database" => @database
}
-
- Pathname.stubs(:new).returns(@path)
- File.stubs(:join).returns("/former/relative/path")
- FileUtils.stubs(:rm).returns(true)
+ @path = Class.new do
+ def to_s; "/absolute/path" end
+ def absolute?; true end
+ end.new
$stdout, @original_stdout = StringIO.new, $stdout
$stderr, @original_stderr = StringIO.new, $stderr
@@ -95,77 +88,76 @@ if current_adapter?(:SQLite3Adapter)
end
def test_creates_path_from_database
- Pathname.expects(:new).with(@database).returns(@path)
-
- ActiveRecord::Tasks::DatabaseTasks.drop @configuration, "/rails/root"
+ assert_called_with(Pathname, :new, [@database], returns: @path) do
+ ActiveRecord::Tasks::DatabaseTasks.drop @configuration, "/rails/root"
+ end
end
def test_removes_file_with_absolute_path
- File.stubs(:exist?).returns(true)
- @path.stubs(:absolute?).returns(true)
-
- FileUtils.expects(:rm).with("/absolute/path")
-
- ActiveRecord::Tasks::DatabaseTasks.drop @configuration, "/rails/root"
+ Pathname.stub(:new, @path) do
+ assert_called_with(FileUtils, :rm, ["/absolute/path"]) do
+ ActiveRecord::Tasks::DatabaseTasks.drop @configuration, "/rails/root"
+ end
+ end
end
def test_generates_absolute_path_with_given_root
- @path.stubs(:absolute?).returns(false)
-
- File.expects(:join).with("/rails/root", @path).
- returns("/former/relative/path")
-
- ActiveRecord::Tasks::DatabaseTasks.drop @configuration, "/rails/root"
+ Pathname.stub(:new, @path) do
+ @path.stub(:absolute?, false) do
+ assert_called_with(File, :join, ["/rails/root", @path],
+ returns: "/former/relative/path"
+ ) do
+ ActiveRecord::Tasks::DatabaseTasks.drop @configuration, "/rails/root"
+ end
+ end
+ end
end
def test_removes_file_with_relative_path
- File.stubs(:exist?).returns(true)
- @path.stubs(:absolute?).returns(false)
-
- FileUtils.expects(:rm).with("/former/relative/path")
-
- ActiveRecord::Tasks::DatabaseTasks.drop @configuration, "/rails/root"
+ File.stub(:join, "/former/relative/path") do
+ @path.stub(:absolute?, false) do
+ assert_called_with(FileUtils, :rm, ["/former/relative/path"]) do
+ ActiveRecord::Tasks::DatabaseTasks.drop @configuration, "/rails/root"
+ end
+ end
+ end
end
def test_when_db_dropped_successfully_outputs_info_to_stdout
- ActiveRecord::Tasks::DatabaseTasks.drop @configuration, "/rails/root"
+ FileUtils.stub(:rm, nil) do
+ ActiveRecord::Tasks::DatabaseTasks.drop @configuration, "/rails/root"
- assert_equal "Dropped database '#{@database}'\n", $stdout.string
+ assert_equal "Dropped database '#{@database}'\n", $stdout.string
+ end
end
end
class SqliteDBCharsetTest < ActiveRecord::TestCase
def setup
@database = "db_create.sqlite3"
- @connection = stub :connection
+ @connection = Class.new { def encoding; end }.new
@configuration = {
"adapter" => "sqlite3",
"database" => @database
}
-
- File.stubs(:exist?).returns(false)
- ActiveRecord::Base.stubs(:connection).returns(@connection)
- ActiveRecord::Base.stubs(:establish_connection).returns(true)
end
def test_db_retrieves_charset
- @connection.expects(:encoding)
- ActiveRecord::Tasks::DatabaseTasks.charset @configuration, "/rails/root"
+ ActiveRecord::Base.stub(:connection, @connection) do
+ assert_called(@connection, :encoding) do
+ ActiveRecord::Tasks::DatabaseTasks.charset @configuration, "/rails/root"
+ end
+ end
end
end
class SqliteDBCollationTest < ActiveRecord::TestCase
def setup
@database = "db_create.sqlite3"
- @connection = stub :connection
@configuration = {
"adapter" => "sqlite3",
"database" => @database
}
-
- File.stubs(:exist?).returns(false)
- ActiveRecord::Base.stubs(:connection).returns(@connection)
- ActiveRecord::Base.stubs(:establish_connection).returns(true)
end
def test_db_retrieves_collation
diff --git a/activerecord/test/cases/test_case.rb b/activerecord/test/cases/test_case.rb
index 409b07e56c..40947767f3 100644
--- a/activerecord/test/cases/test_case.rb
+++ b/activerecord/test/cases/test_case.rb
@@ -31,6 +31,7 @@ module ActiveRecord
end
def capture_sql
+ ActiveRecord::Base.connection.materialize_transactions
SQLCounter.clear_log
yield
SQLCounter.log_all.dup
@@ -48,6 +49,7 @@ module ActiveRecord
def assert_queries(num = 1, options = {})
ignore_none = options.fetch(:ignore_none) { num == :any }
+ ActiveRecord::Base.connection.materialize_transactions
SQLCounter.clear_log
x = yield
the_log = ignore_none ? SQLCounter.log_all : SQLCounter.log
diff --git a/activerecord/test/cases/transaction_callbacks_test.rb b/activerecord/test/cases/transaction_callbacks_test.rb
index 05941c75ac..c0be45eee7 100644
--- a/activerecord/test/cases/transaction_callbacks_test.rb
+++ b/activerecord/test/cases/transaction_callbacks_test.rb
@@ -139,6 +139,23 @@ class TransactionCallbacksTest < ActiveRecord::TestCase
assert_equal [], reply.history
end
+ def test_only_call_after_commit_on_destroy_after_transaction_commits_for_destroyed_new_record
+ new_record = TopicWithCallbacks.new(title: "New topic", written_on: Date.today)
+ add_transaction_execution_blocks new_record
+
+ new_record.destroy
+ assert_equal [:commit_on_destroy], new_record.history
+ end
+
+ def test_save_in_after_create_commit_wont_invoke_extra_after_create_commit
+ new_record = TopicWithCallbacks.new(title: "New topic", written_on: Date.today)
+ add_transaction_execution_blocks new_record
+ new_record.after_commit_block(:create) { |r| r.save! }
+
+ new_record.save!
+ assert_equal [:commit_on_create, :commit_on_update], new_record.history
+ end
+
def test_only_call_after_commit_on_create_and_doesnt_leaky
r = ReplyWithCallbacks.new(content: "foo")
r.save_on_after_create = true
diff --git a/activerecord/test/cases/transaction_isolation_test.rb b/activerecord/test/cases/transaction_isolation_test.rb
index eaafd13360..9663955f1f 100644
--- a/activerecord/test/cases/transaction_isolation_test.rb
+++ b/activerecord/test/cases/transaction_isolation_test.rb
@@ -11,7 +11,7 @@ unless ActiveRecord::Base.connection.supports_transaction_isolation?
test "setting the isolation level raises an error" do
assert_raises(ActiveRecord::TransactionIsolationError) do
- Tag.transaction(isolation: :serializable) {}
+ Tag.transaction(isolation: :serializable) { Tag.connection.materialize_transactions }
end
end
end
diff --git a/activerecord/test/cases/transactions_test.rb b/activerecord/test/cases/transactions_test.rb
index 5b685ca564..b13cf88c00 100644
--- a/activerecord/test/cases/transactions_test.rb
+++ b/activerecord/test/cases/transactions_test.rb
@@ -573,10 +573,9 @@ class TransactionTest < ActiveRecord::TestCase
assert_called(Topic.connection, :begin_db_transaction) do
Topic.connection.stub(:commit_db_transaction, -> { raise("OH NOES") }) do
assert_called(Topic.connection, :rollback_db_transaction) do
-
e = assert_raise RuntimeError do
Topic.transaction do
- # do nothing
+ Topic.connection.materialize_transactions
end
end
assert_equal "OH NOES", e.message
@@ -944,6 +943,76 @@ class TransactionTest < ActiveRecord::TestCase
connection.drop_table "transaction_without_primary_keys", if_exists: true
end
+ def test_empty_transaction_is_not_materialized
+ assert_no_queries do
+ Topic.transaction {}
+ end
+ end
+
+ def test_unprepared_statement_materializes_transaction
+ assert_sql(/BEGIN/i, /COMMIT/i) do
+ Topic.transaction { Topic.where("1=1").first }
+ end
+ end
+
+ if ActiveRecord::Base.connection.prepared_statements
+ def test_prepared_statement_materializes_transaction
+ Topic.first
+
+ assert_sql(/BEGIN/i, /COMMIT/i) do
+ Topic.transaction { Topic.first }
+ end
+ end
+ end
+
+ def test_savepoint_does_not_materialize_transaction
+ assert_no_queries do
+ Topic.transaction do
+ Topic.transaction(requires_new: true) {}
+ end
+ end
+ end
+
+ def test_raising_does_not_materialize_transaction
+ assert_raise(RuntimeError) do
+ assert_no_queries do
+ Topic.transaction { raise }
+ end
+ end
+ end
+
+ def test_accessing_raw_connection_materializes_transaction
+ assert_sql(/BEGIN/i, /COMMIT/i) do
+ Topic.transaction { Topic.connection.raw_connection }
+ end
+ end
+
+ def test_accessing_raw_connection_disables_lazy_transactions
+ Topic.connection.raw_connection
+
+ assert_sql(/BEGIN/i, /COMMIT/i) do
+ Topic.transaction {}
+ end
+ end
+
+ def test_checking_in_connection_reenables_lazy_transactions
+ connection = Topic.connection_pool.checkout
+ connection.raw_connection
+ Topic.connection_pool.checkin connection
+
+ assert_no_queries do
+ connection.transaction {}
+ end
+ end
+
+ def test_transactions_can_be_manually_materialized
+ assert_sql(/BEGIN/i, /COMMIT/i) do
+ Topic.transaction do
+ Topic.connection.materialize_transactions
+ end
+ end
+ end
+
private
%w(validation save destroy).each do |filter|
diff --git a/activerecord/test/cases/validations_test.rb b/activerecord/test/cases/validations_test.rb
index a33877f43a..66763c727f 100644
--- a/activerecord/test/cases/validations_test.rb
+++ b/activerecord/test/cases/validations_test.rb
@@ -3,11 +3,11 @@
require "cases/helper"
require "models/topic"
require "models/reply"
-require "models/person"
require "models/developer"
require "models/computer"
require "models/parrot"
require "models/company"
+require "models/price_estimate"
class ValidationsTest < ActiveRecord::TestCase
fixtures :topics, :developers
@@ -183,6 +183,22 @@ class ValidationsTest < ActiveRecord::TestCase
assert_not_predicate klass.new(wibble: BigDecimal("97.179")), :valid?
end
+ def test_numericality_validator_wont_be_affected_by_custom_getter
+ price_estimate = PriceEstimate.new(price: 50)
+
+ assert_equal "$50.00", price_estimate.price
+ assert_equal 50, price_estimate.price_before_type_cast
+ assert_equal 50, price_estimate.read_attribute(:price)
+
+ assert_predicate price_estimate, :price_came_from_user?
+ assert_predicate price_estimate, :valid?
+
+ price_estimate.save!
+
+ assert_not_predicate price_estimate, :price_came_from_user?
+ assert_predicate price_estimate, :valid?
+ end
+
def test_acceptance_validator_doesnt_require_db_connection
klass = Class.new(ActiveRecord::Base) do
self.table_name = "posts"
diff --git a/activerecord/test/config.example.yml b/activerecord/test/config.example.yml
index 4bcb2aeea6..be337ddcd8 100644
--- a/activerecord/test/config.example.yml
+++ b/activerecord/test/config.example.yml
@@ -54,11 +54,11 @@ connections:
mysql2:
arunit:
username: rails
- encoding: utf8
- collation: utf8_unicode_ci
+ encoding: utf8mb4
+ collation: utf8mb4_unicode_ci
arunit2:
username: rails
- encoding: utf8
+ encoding: utf8mb4
oracle:
arunit:
diff --git a/activerecord/test/fixtures/citations.yml b/activerecord/test/fixtures/citations.yml
new file mode 100644
index 0000000000..d31cb8efa1
--- /dev/null
+++ b/activerecord/test/fixtures/citations.yml
@@ -0,0 +1,4 @@
+<% 65536.times do |i| %>
+fixture_no_<%= i %>:
+ id: <%= i %>
+<% end %>
diff --git a/activerecord/test/fixtures/memberships.yml b/activerecord/test/fixtures/memberships.yml
index a5d52bd438..f7ca227533 100644
--- a/activerecord/test/fixtures/memberships.yml
+++ b/activerecord/test/fixtures/memberships.yml
@@ -26,6 +26,13 @@ blarpy_winkup_crazy_club:
favourite: false
type: CurrentMembership
+super_membership_of_boring_club:
+ joined_on: <%= 3.weeks.ago.to_s(:db) %>
+ club: boring_club
+ member_id: 1
+ favourite: false
+ type: SuperMembership
+
selected_membership_of_boring_club:
joined_on: <%= 3.weeks.ago.to_s(:db) %>
club: boring_club
diff --git a/activerecord/test/models/admin/user.rb b/activerecord/test/models/admin/user.rb
index 3f55364510..691f9f11be 100644
--- a/activerecord/test/models/admin/user.rb
+++ b/activerecord/test/models/admin/user.rb
@@ -22,6 +22,9 @@ class Admin::User < ActiveRecord::Base
store :parent, accessors: [:birthday, :name], prefix: true
store :spouse, accessors: [:birthday], prefix: :partner
store_accessor :spouse, :name, prefix: :partner
+ store :configs, accessors: [ :secret_question ]
+ store :configs, accessors: [ :two_factor_auth ], suffix: true
+ store_accessor :configs, :login_retry, suffix: :config
store :preferences, accessors: [ :remember_login ]
store :json_data, accessors: [ :height, :weight ], coder: Coder.new
store :json_data_empty, accessors: [ :is_a_good_guy ], coder: Coder.new
diff --git a/activerecord/test/models/car.rb b/activerecord/test/models/car.rb
index 3d6a7a96c2..8614926626 100644
--- a/activerecord/test/models/car.rb
+++ b/activerecord/test/models/car.rb
@@ -20,6 +20,8 @@ class Car < ActiveRecord::Base
scope :incl_engines, -> { includes(:engines) }
scope :order_using_new_style, -> { order("name asc") }
+
+ attribute :wheels_owned_at, :datetime, default: -> { Time.now }
end
class CoolCar < Car
diff --git a/activerecord/test/models/citation.rb b/activerecord/test/models/citation.rb
index 3d786f27eb..cee3d18173 100644
--- a/activerecord/test/models/citation.rb
+++ b/activerecord/test/models/citation.rb
@@ -2,4 +2,5 @@
class Citation < ActiveRecord::Base
belongs_to :reference_of, class_name: "Book", foreign_key: :book2_id
+ has_many :citations
end
diff --git a/activerecord/test/models/company.rb b/activerecord/test/models/company.rb
index 6219f57fa1..838f515aad 100644
--- a/activerecord/test/models/company.rb
+++ b/activerecord/test/models/company.rb
@@ -122,6 +122,12 @@ class RestrictedWithErrorFirm < Company
has_many :companies, -> { order("id") }, foreign_key: "client_of", dependent: :restrict_with_error
end
+class Agency < Firm
+ has_many :projects, foreign_key: :firm_id
+
+ accepts_nested_attributes_for :projects
+end
+
class Client < Company
belongs_to :firm, foreign_key: "client_of"
belongs_to :firm_with_basic_id, class_name: "Firm", foreign_key: "firm_id"
@@ -150,6 +156,16 @@ class Client < Company
throw :abort if throw_on_save
end
+ attr_accessor :rollback_on_save
+ after_save do
+ raise ActiveRecord::Rollback if rollback_on_save
+ end
+
+ attr_accessor :rollback_on_create_called
+ after_rollback(on: :create) do |client|
+ client.rollback_on_create_called = true
+ end
+
class RaisedOnDestroy < RuntimeError; end
attr_accessor :raise_on_destroy
before_destroy do
@@ -194,4 +210,12 @@ end
class VerySpecialClient < SpecialClient
end
+class NewlyContractedCompany < Company
+ has_many :new_contracts, foreign_key: "company_id"
+
+ before_save do
+ self.new_contracts << NewContract.new
+ end
+end
+
require "models/account"
diff --git a/activerecord/test/models/contract.rb b/activerecord/test/models/contract.rb
index f273badd85..3f663375c4 100644
--- a/activerecord/test/models/contract.rb
+++ b/activerecord/test/models/contract.rb
@@ -20,3 +20,7 @@ class Contract < ActiveRecord::Base
@bye_count += 1
end
end
+
+class NewContract < Contract
+ validates :company_id, presence: true
+end
diff --git a/activerecord/test/models/member.rb b/activerecord/test/models/member.rb
index 4315ba1941..6e33ac0a6d 100644
--- a/activerecord/test/models/member.rb
+++ b/activerecord/test/models/member.rb
@@ -26,13 +26,14 @@ class Member < ActiveRecord::Base
has_one :club_category, through: :club, source: :category
has_one :general_club, -> { general }, through: :current_membership, source: :club
- has_many :current_memberships, -> { where favourite: true }
- has_many :clubs, through: :current_memberships
+ has_many :super_memberships
+ has_many :favourite_memberships, -> { where(favourite: true) }, class_name: "Membership"
+ has_many :clubs, through: :favourite_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
+ has_one :club_through_many, through: :favourite_memberships, source: :club
belongs_to :admittable, polymorphic: true
has_one :premium_club, through: :admittable
diff --git a/activerecord/test/models/pirate.rb b/activerecord/test/models/pirate.rb
index c8617d1cfe..fd5083e597 100644
--- a/activerecord/test/models/pirate.rb
+++ b/activerecord/test/models/pirate.rb
@@ -17,7 +17,13 @@ class Pirate < ActiveRecord::Base
after_remove: proc { |p, pa| p.ship_log << "after_removing_proc_parrot_#{pa.id}" }
has_and_belongs_to_many :autosaved_parrots, class_name: "Parrot", autosave: true
- has_many :treasures, as: :looter
+ module PostTreasuresExtension
+ def build(attributes = {})
+ super({ name: "from extension" }.merge(attributes))
+ end
+ end
+
+ has_many :treasures, as: :looter, extend: PostTreasuresExtension
has_many :treasure_estimates, through: :treasures, source: :price_estimates
has_one :ship
diff --git a/activerecord/test/models/post.rb b/activerecord/test/models/post.rb
index 54eb5e6783..528585fb75 100644
--- a/activerecord/test/models/post.rb
+++ b/activerecord/test/models/post.rb
@@ -253,6 +253,7 @@ class SpecialPostWithDefaultScope < ActiveRecord::Base
self.inheritance_column = :disabled
self.table_name = "posts"
default_scope { where(id: [1, 5, 6]) }
+ scope :unscoped_all, -> { unscoped { all } }
end
class PostThatLoadsCommentsInAnAfterSaveHook < ActiveRecord::Base
@@ -323,7 +324,7 @@ class FakeKlass
table[name]
end
- def enforce_raw_sql_whitelist(*args)
+ def disallow_raw_sql!(*args)
# noop
end
diff --git a/activerecord/test/models/price_estimate.rb b/activerecord/test/models/price_estimate.rb
index f1f88d8d8d..669d0991f7 100644
--- a/activerecord/test/models/price_estimate.rb
+++ b/activerecord/test/models/price_estimate.rb
@@ -1,6 +1,14 @@
# frozen_string_literal: true
class PriceEstimate < ActiveRecord::Base
+ include ActiveSupport::NumberHelper
+
belongs_to :estimate_of, polymorphic: true
belongs_to :thing, polymorphic: true
+
+ validates_numericality_of :price
+
+ def price
+ number_to_currency super
+ end
end
diff --git a/activerecord/test/models/reply.rb b/activerecord/test/models/reply.rb
index bc829ec67f..0ea110f4f8 100644
--- a/activerecord/test/models/reply.rb
+++ b/activerecord/test/models/reply.rb
@@ -4,8 +4,9 @@ require "models/topic"
class Reply < Topic
belongs_to :topic, foreign_key: "parent_id", counter_cache: true
- belongs_to :topic_with_primary_key, class_name: "Topic", primary_key: "title", foreign_key: "parent_title", counter_cache: "replies_count"
+ belongs_to :topic_with_primary_key, class_name: "Topic", primary_key: "title", foreign_key: "parent_title", counter_cache: "replies_count", touch: true
has_many :replies, class_name: "SillyReply", dependent: :destroy, foreign_key: "parent_id"
+ has_many :silly_unique_replies, dependent: :destroy, foreign_key: "parent_id"
end
class UniqueReply < Reply
@@ -14,6 +15,7 @@ class UniqueReply < Reply
end
class SillyUniqueReply < UniqueReply
+ validates :content, uniqueness: true
end
class WrongReply < Reply
diff --git a/activerecord/test/models/topic.rb b/activerecord/test/models/topic.rb
index 2e386d7669..72699046f9 100644
--- a/activerecord/test/models/topic.rb
+++ b/activerecord/test/models/topic.rb
@@ -81,6 +81,16 @@ class Topic < ActiveRecord::Base
self.class.after_initialize_called = true
end
+ attr_accessor :after_touch_called
+
+ after_initialize do
+ self.after_touch_called = 0
+ end
+
+ after_touch do
+ self.after_touch_called += 1
+ end
+
def approved=(val)
@custom_approved = val
write_attribute(:approved, val)
diff --git a/activerecord/test/models/wheel.rb b/activerecord/test/models/wheel.rb
index 8db57d181e..22fc74995f 100644
--- a/activerecord/test/models/wheel.rb
+++ b/activerecord/test/models/wheel.rb
@@ -1,5 +1,5 @@
# frozen_string_literal: true
class Wheel < ActiveRecord::Base
- belongs_to :wheelable, polymorphic: true, counter_cache: true, touch: true
+ belongs_to :wheelable, polymorphic: true, counter_cache: true, touch: :wheels_owned_at
end
diff --git a/activerecord/test/schema/mysql2_specific_schema.rb b/activerecord/test/schema/mysql2_specific_schema.rb
index e634e9e6b1..c384297658 100644
--- a/activerecord/test/schema/mysql2_specific_schema.rb
+++ b/activerecord/test/schema/mysql2_specific_schema.rb
@@ -1,16 +1,24 @@
# frozen_string_literal: true
ActiveRecord::Schema.define do
-
- if ActiveRecord::Base.connection.version >= "5.6.0"
+ if subsecond_precision_supported?
create_table :datetime_defaults, force: true do |t|
t.datetime :modified_datetime, default: -> { "CURRENT_TIMESTAMP" }
+ t.datetime :precise_datetime, precision: 6, default: -> { "CURRENT_TIMESTAMP(6)" }
+ end
+
+ create_table :timestamp_defaults, force: true do |t|
+ t.timestamp :nullable_timestamp
+ t.timestamp :modified_timestamp, default: -> { "CURRENT_TIMESTAMP" }
+ t.timestamp :precise_timestamp, precision: 6, default: -> { "CURRENT_TIMESTAMP(6)" }
end
end
- create_table :timestamp_defaults, force: true do |t|
- t.timestamp :nullable_timestamp
- t.timestamp :modified_timestamp, default: -> { "CURRENT_TIMESTAMP" }
+ create_table :defaults, force: true do |t|
+ t.date :fixed_date, default: "2004-01-01"
+ t.datetime :fixed_time, default: "2004-01-01 00:00:00"
+ t.column :char1, "char(1)", default: "Y"
+ t.string :char2, limit: 50, default: "a varchar field"
end
create_table :binary_fields, force: true do |t|
@@ -28,7 +36,7 @@ ActiveRecord::Schema.define do
t.index :var_binary
end
- create_table :key_tests, force: true, options: "ENGINE=MyISAM" do |t|
+ create_table :key_tests, force: true do |t|
t.string :awesome
t.string :pizza
t.string :snacks
diff --git a/activerecord/test/schema/oracle_specific_schema.rb b/activerecord/test/schema/oracle_specific_schema.rb
index e236571caa..bc1e45ca80 100644
--- a/activerecord/test/schema/oracle_specific_schema.rb
+++ b/activerecord/test/schema/oracle_specific_schema.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
ActiveRecord::Schema.define do
-
execute "drop table test_oracle_defaults" rescue nil
execute "drop sequence test_oracle_defaults_seq" rescue nil
execute "drop sequence companies_nonstd_seq" rescue nil
@@ -38,5 +37,4 @@ create sequence test_oracle_defaults_seq minvalue 10000
)
SQL
execute "create sequence defaults_seq minvalue 10000"
-
end
diff --git a/activerecord/test/schema/postgresql_specific_schema.rb b/activerecord/test/schema/postgresql_specific_schema.rb
index f15178d695..975824ed51 100644
--- a/activerecord/test/schema/postgresql_specific_schema.rb
+++ b/activerecord/test/schema/postgresql_specific_schema.rb
@@ -1,7 +1,6 @@
# frozen_string_literal: true
ActiveRecord::Schema.define do
-
enable_extension!("uuid-ossp", ActiveRecord::Base.connection)
enable_extension!("pgcrypto", ActiveRecord::Base.connection) if ActiveRecord::Base.connection.supports_pgcrypto_uuid?
diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb
index 92ad25ef76..27c7348f5c 100644
--- a/activerecord/test/schema/schema.rb
+++ b/activerecord/test/schema/schema.rb
@@ -23,6 +23,7 @@ ActiveRecord::Schema.define do
t.string :settings, null: true, limit: 1024
t.string :parent, null: true, limit: 1024
t.string :spouse, null: true, limit: 1024
+ t.string :configs, null: true, limit: 1024
# MySQL does not allow default values for blobs. Fake it out with a
# big varchar below.
t.string :preferences, null: true, default: "", limit: 1024
@@ -35,6 +36,7 @@ ActiveRecord::Schema.define do
create_table :aircraft, force: true do |t|
t.string :name
t.integer :wheels_count, default: 0, null: false
+ t.datetime :wheels_owned_at
end
create_table :articles, force: true do |t|
@@ -125,7 +127,8 @@ ActiveRecord::Schema.define do
create_table :cars, force: true do |t|
t.string :name
t.integer :engines_count
- t.integer :wheels_count, default: 0
+ t.integer :wheels_count, default: 0, null: false
+ t.datetime :wheels_owned_at
t.column :lock_version, :integer, null: false, default: 0
t.timestamps null: false
end
@@ -157,6 +160,7 @@ ActiveRecord::Schema.define do
create_table :citations, force: true do |t|
t.column :book1_id, :integer
t.column :book2_id, :integer
+ t.references :citation
end
create_table :clubs, force: true do |t|
diff --git a/activerecord/test/schema/sqlite_specific_schema.rb b/activerecord/test/schema/sqlite_specific_schema.rb
new file mode 100644
index 0000000000..18192292e4
--- /dev/null
+++ b/activerecord/test/schema/sqlite_specific_schema.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+ActiveRecord::Schema.define do
+ create_table :defaults, force: true do |t|
+ t.date :fixed_date, default: "2004-01-01"
+ t.datetime :fixed_time, default: "2004-01-01 00:00:00"
+ t.column :char1, "char(1)", default: "Y"
+ t.string :char2, limit: 50, default: "a varchar field"
+ t.text :char3, limit: 50, default: "a text field"
+ end
+end