diff options
author | Willian Gustavo Veiga <beberveiga@gmail.com> | 2018-10-02 12:55:36 -0300 |
---|---|---|
committer | Willian Gustavo Veiga <beberveiga@gmail.com> | 2018-10-02 12:57:37 -0300 |
commit | 2d4df1349efdf8dd2c8cc4503fd5a871b0066500 (patch) | |
tree | 9f71a297cf1bb30f1d59039997a60f97357d0782 /activerecord/test | |
parent | 00c50c2b5966fa1d719c8a58564811c672a0e8c6 (diff) | |
parent | cf608ee34dd833b0357ef4eefa692db33242d2aa (diff) | |
download | rails-2d4df1349efdf8dd2c8cc4503fd5a871b0066500.tar.gz rails-2d4df1349efdf8dd2c8cc4503fd5a871b0066500.tar.bz2 rails-2d4df1349efdf8dd2c8cc4503fd5a871b0066500.zip |
Merge branch 'master' into feature/reselect-method
Diffstat (limited to 'activerecord/test')
95 files changed, 1609 insertions, 614 deletions
diff --git a/activerecord/test/cases/adapter_test.rb b/activerecord/test/cases/adapter_test.rb index 59b99351d1..64c2b51f83 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 ## @@ -225,7 +227,7 @@ module ActiveRecord post = Post.create!(title: "foo", body: "bar") expected = @connection.select_all("SELECT * FROM posts WHERE id = #{post.id}") result = @connection.select_all("SELECT * FROM posts WHERE id = #{Arel::Nodes::BindParam.new(nil).to_sql}", nil, [[nil, post.id]]) - assert_equal expected.to_hash, result.to_hash + assert_equal expected.to_a, result.to_a end def test_insert_update_delete_with_legacy_binds @@ -288,7 +290,7 @@ module ActiveRecord def test_log_invalid_encoding error = assert_raises RuntimeError do @connection.send :log, "SELECT 'ы' FROM DUAL" do - raise "ы".dup.force_encoding(Encoding::ASCII_8BIT) + raise (+"ы").force_encoding(Encoding::ASCII_8BIT) end end @@ -299,6 +301,34 @@ 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 @@ -417,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 6fc9df5083..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 diff --git a/activerecord/test/cases/adapters/mysql2/case_sensitivity_test.rb b/activerecord/test/cases/adapters/mysql2/case_sensitivity_test.rb index aa870349be..c32475c683 100644 --- a/activerecord/test/cases/adapters/mysql2/case_sensitivity_test.rb +++ b/activerecord/test/cases/adapters/mysql2/case_sensitivity_test.rb @@ -9,8 +9,8 @@ class Mysql2CaseSensitivityTest < ActiveRecord::Mysql2TestCase repair_validations(CollationTest) def test_columns_include_collation_different_from_table - assert_equal "utf8_bin", CollationTest.columns_hash["string_cs_column"].collation - assert_equal "utf8_general_ci", CollationTest.columns_hash["string_ci_column"].collation + assert_equal "utf8mb4_bin", CollationTest.columns_hash["string_cs_column"].collation + assert_equal "utf8mb4_general_ci", CollationTest.columns_hash["string_ci_column"].collation end def test_case_sensitive diff --git a/activerecord/test/cases/adapters/mysql2/charset_collation_test.rb b/activerecord/test/cases/adapters/mysql2/charset_collation_test.rb index d0c57de65d..0bdbefdfb9 100644 --- a/activerecord/test/cases/adapters/mysql2/charset_collation_test.rb +++ b/activerecord/test/cases/adapters/mysql2/charset_collation_test.rb @@ -32,20 +32,20 @@ class Mysql2CharsetCollationTest < ActiveRecord::Mysql2TestCase end test "add column with charset and collation" do - @connection.add_column :charset_collations, :title, :string, charset: "utf8", collation: "utf8_bin" + @connection.add_column :charset_collations, :title, :string, charset: "utf8mb4", collation: "utf8mb4_bin" column = @connection.columns(:charset_collations).find { |c| c.name == "title" } assert_equal :string, column.type - assert_equal "utf8_bin", column.collation + assert_equal "utf8mb4_bin", column.collation end test "change column with charset and collation" do - @connection.add_column :charset_collations, :description, :string, charset: "utf8", collation: "utf8_unicode_ci" - @connection.change_column :charset_collations, :description, :text, charset: "utf8", collation: "utf8_general_ci" + @connection.add_column :charset_collations, :description, :string, charset: "utf8mb4", collation: "utf8mb4_unicode_ci" + @connection.change_column :charset_collations, :description, :text, charset: "utf8mb4", collation: "utf8mb4_general_ci" column = @connection.columns(:charset_collations).find { |c| c.name == "description" } assert_equal :text, column.type - assert_equal "utf8_general_ci", column.collation + assert_equal "utf8mb4_general_ci", column.collation end test "schema dump includes collation" do 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/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/bytea_test.rb b/activerecord/test/cases/adapters/postgresql/bytea_test.rb index 64bb6906cd..3988c2adca 100644 --- a/activerecord/test/cases/adapters/postgresql/bytea_test.rb +++ b/activerecord/test/cases/adapters/postgresql/bytea_test.rb @@ -49,7 +49,7 @@ class PostgresqlByteaTest < ActiveRecord::PostgreSQLTestCase end def test_type_cast_binary_value - data = "\u001F\x8B".dup.force_encoding("BINARY") + data = (+"\u001F\x8B").force_encoding("BINARY") assert_equal(data, @type.deserialize(data)) end diff --git a/activerecord/test/cases/adapters/postgresql/connection_test.rb b/activerecord/test/cases/adapters/postgresql/connection_test.rb index 54b0dde7dc..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 diff --git a/activerecord/test/cases/adapters/postgresql/money_test.rb b/activerecord/test/cases/adapters/postgresql/money_test.rb index 61e75e772d..75e5aaed53 100644 --- a/activerecord/test/cases/adapters/postgresql/money_test.rb +++ b/activerecord/test/cases/adapters/postgresql/money_test.rb @@ -52,10 +52,10 @@ class PostgresqlMoneyTest < ActiveRecord::PostgreSQLTestCase def test_money_type_cast type = PostgresqlMoney.type_for_attribute("wealth") - assert_equal(12345678.12, type.cast("$12,345,678.12".dup)) - assert_equal(12345678.12, type.cast("$12.345.678,12".dup)) - assert_equal(-1.15, type.cast("-$1.15".dup)) - assert_equal(-2.25, type.cast("($2.25)".dup)) + assert_equal(12345678.12, type.cast(+"$12,345,678.12")) + assert_equal(12345678.12, type.cast(+"$12.345.678,12")) + assert_equal(-1.15, type.cast(+"-$1.15")) + assert_equal(-2.25, type.cast(+"($2.25)")) end def test_schema_dumping @@ -65,7 +65,7 @@ class PostgresqlMoneyTest < ActiveRecord::PostgreSQLTestCase end def test_create_and_update_money - money = PostgresqlMoney.create(wealth: "987.65".dup) + money = PostgresqlMoney.create(wealth: +"987.65") assert_equal 987.65, money.wealth new_value = BigDecimal("123.45") diff --git a/activerecord/test/cases/adapters/postgresql/referential_integrity_test.rb b/activerecord/test/cases/adapters/postgresql/referential_integrity_test.rb index 0bcc214c24..ba477c63f4 100644 --- a/activerecord/test/cases/adapters/postgresql/referential_integrity_test.rb +++ b/activerecord/test/cases/adapters/postgresql/referential_integrity_test.rb @@ -101,7 +101,7 @@ class PostgreSQLReferentialIntegrityTest < ActiveRecord::PostgreSQLTestCase @connection.extend ProgrammerMistake assert_raises ArgumentError do - @connection.disable_referential_integrity {} + @connection.disable_referential_integrity { } 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 d1d4d545a3..89052019f8 100644 --- a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb +++ b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb @@ -87,7 +87,7 @@ module ActiveRecord def test_connection_no_db assert_raises(ArgumentError) do - Base.sqlite3_connection {} + Base.sqlite3_connection { } end end @@ -167,7 +167,7 @@ module ActiveRecord data binary ) eosql - str = "\x80".dup.force_encoding("ASCII-8BIT") + str = (+"\x80").force_encoding("ASCII-8BIT") binary = DualEncoding.new name: "いただきます!", data: str binary.save! assert_equal str, binary.data @@ -176,7 +176,7 @@ module ActiveRecord end def test_type_cast_should_not_mutate_encoding - name = "hello".dup.force_encoding(Encoding::ASCII_8BIT) + name = (+"hello").force_encoding(Encoding::ASCII_8BIT) Owner.create(name: name) assert_equal Encoding::ASCII_8BIT, name.encoding ensure @@ -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") diff --git a/activerecord/test/cases/arel/collectors/sql_string_test.rb b/activerecord/test/cases/arel/collectors/sql_string_test.rb index 380573494d..443c7eb54b 100644 --- a/activerecord/test/cases/arel/collectors/sql_string_test.rb +++ b/activerecord/test/cases/arel/collectors/sql_string_test.rb @@ -19,27 +19,21 @@ module Arel collect(node).value end - def ast_with_binds(bv) + def ast_with_binds table = Table.new(:users) manager = Arel::SelectManager.new table - manager.where(table[:age].eq(bv)) - manager.where(table[:name].eq(bv)) + manager.where(table[:age].eq(Nodes::BindParam.new("hello"))) + manager.where(table[:name].eq(Nodes::BindParam.new("world"))) manager.ast end def test_compile - bv = Nodes::BindParam.new(1) - collector = collect ast_with_binds bv - - sql = collector.compile ["hello", "world"] + sql = compile(ast_with_binds) assert_equal 'SELECT FROM "users" WHERE "users"."age" = ? AND "users"."name" = ?', sql end def test_returned_sql_uses_utf8_encoding - bv = Nodes::BindParam.new(1) - collector = collect ast_with_binds bv - - sql = collector.compile ["hello", "world"] + sql = compile(ast_with_binds) assert_equal sql.encoding, Encoding::UTF_8 end end diff --git a/activerecord/test/cases/arel/delete_manager_test.rb b/activerecord/test/cases/arel/delete_manager_test.rb index 17a5271039..0bad02f4d2 100644 --- a/activerecord/test/cases/arel/delete_manager_test.rb +++ b/activerecord/test/cases/arel/delete_manager_test.rb @@ -15,6 +15,7 @@ module Arel dm = Arel::DeleteManager.new dm.take 10 dm.from table + dm.key = table[:id] assert_match(/LIMIT 10/, dm.to_sql) end diff --git a/activerecord/test/cases/arel/select_manager_test.rb b/activerecord/test/cases/arel/select_manager_test.rb index c17487ae88..5220950905 100644 --- a/activerecord/test/cases/arel/select_manager_test.rb +++ b/activerecord/test/cases/arel/select_manager_test.rb @@ -131,7 +131,7 @@ module Arel right = table.alias mgr = table.from mgr.join(right).on("omg") - mgr.to_sql.must_be_like %{ SELECT FROM "users" INNER JOIN "users" "users_2" ON omg } + mgr.to_sql.must_be_like %{ SELECT FROM "users" INNER JOIN "users" "users_2" ON omg } end it "converts to sqlliterals with multiple items" do @@ -139,7 +139,7 @@ module Arel right = table.alias mgr = table.from mgr.join(right).on("omg", "123") - mgr.to_sql.must_be_like %{ SELECT FROM "users" INNER JOIN "users" "users_2" ON omg AND 123 } + mgr.to_sql.must_be_like %{ SELECT FROM "users" INNER JOIN "users" "users_2" ON omg AND 123 } end end end diff --git a/activerecord/test/cases/arel/visitors/depth_first_test.rb b/activerecord/test/cases/arel/visitors/depth_first_test.rb index 3baccc7316..f94ad521d7 100644 --- a/activerecord/test/cases/arel/visitors/depth_first_test.rb +++ b/activerecord/test/cases/arel/visitors/depth_first_test.rb @@ -33,7 +33,6 @@ module Arel Arel::Nodes::Ordering, Arel::Nodes::StringJoin, Arel::Nodes::UnqualifiedColumn, - Arel::Nodes::Top, Arel::Nodes::Limit, Arel::Nodes::Else, ].each do |klass| diff --git a/activerecord/test/cases/arel/visitors/dot_test.rb b/activerecord/test/cases/arel/visitors/dot_test.rb index 98f3bab620..6b3c132f83 100644 --- a/activerecord/test/cases/arel/visitors/dot_test.rb +++ b/activerecord/test/cases/arel/visitors/dot_test.rb @@ -37,7 +37,6 @@ module Arel Arel::Nodes::Offset, Arel::Nodes::Ordering, Arel::Nodes::UnqualifiedColumn, - Arel::Nodes::Top, Arel::Nodes::Limit, ].each do |klass| define_method("test_#{klass.name.gsub('::', '_')}") do diff --git a/activerecord/test/cases/arel/visitors/to_sql_test.rb b/activerecord/test/cases/arel/visitors/to_sql_test.rb index e8ac50bfa3..b6426a211e 100644 --- a/activerecord/test/cases/arel/visitors/to_sql_test.rb +++ b/activerecord/test/cases/arel/visitors/to_sql_test.rb @@ -221,7 +221,7 @@ module Arel sql.must_be_like "foo AS bar" end - it "should visit_Bignum" do + it "should visit_Integer" do compile 8787878092 end diff --git a/activerecord/test/cases/associations/belongs_to_associations_test.rb b/activerecord/test/cases/associations/belongs_to_associations_test.rb index 0cc4ed7127..1fca1be181 100644 --- a/activerecord/test/cases/associations/belongs_to_associations_test.rb +++ b/activerecord/test/cases/associations/belongs_to_associations_test.rb @@ -452,15 +452,39 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase end def test_belongs_to_counter_with_assigning_nil - post = Post.find(1) - comment = Comment.find(1) + topic = Topic.create!(title: "debate") + reply = Reply.create!(title: "blah!", content: "world around!", topic: topic) - assert_equal post.id, comment.post_id - assert_equal 2, Post.find(post.id).comments.size + assert_equal topic.id, reply.parent_id + assert_equal 1, topic.reload.replies.size - comment.post = nil + reply.topic = nil + reply.reload - assert_equal 1, Post.find(post.id).comments.size + assert_equal topic.id, reply.parent_id + assert_equal 1, topic.reload.replies.size + + reply.topic = nil + reply.save! + + assert_equal 0, topic.reload.replies.size + end + + def test_belongs_to_counter_with_assigning_new_object + topic = Topic.create!(title: "debate") + reply = Reply.create!(title: "blah!", content: "world around!", topic: topic) + + assert_equal topic.id, reply.parent_id + assert_equal 1, topic.reload.replies_count + + topic2 = reply.build_topic(title: "debate2") + reply.save! + + assert_not_equal topic.id, reply.parent_id + assert_equal topic2.id, reply.parent_id + + assert_equal 0, topic.reload.replies_count + assert_equal 1, topic2.reload.replies_count end def test_belongs_to_with_primary_key_counter @@ -485,11 +509,13 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase assert_equal 0, debate2.reload.replies_count reply.topic_with_primary_key = debate2 + reply.save! assert_equal 0, debate.reload.replies_count assert_equal 1, debate2.reload.replies_count reply.topic_with_primary_key = nil + reply.save! assert_equal 0, debate.reload.replies_count assert_equal 0, debate2.reload.replies_count @@ -516,11 +542,13 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase assert_equal 1, Topic.find(topic2.id).replies.size reply1.topic = nil + reply1.save! assert_equal 0, Topic.find(topic1.id).replies.size assert_equal 0, Topic.find(topic2.id).replies.size reply1.topic = topic1 + reply1.save! assert_equal 1, Topic.find(topic1.id).replies.size assert_equal 0, Topic.find(topic2.id).replies.size @@ -550,7 +578,11 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase def test_belongs_to_counter_after_save topic = Topic.create!(title: "monday night") - topic.replies.create!(title: "re: monday night", content: "football") + + assert_queries(2) do + topic.replies.create!(title: "re: monday night", content: "football") + end + assert_equal 1, Topic.find(topic.id)[:replies_count] topic.save! @@ -584,8 +616,10 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase debate.touch(time: time) debate2.touch(time: time) - reply.parent_title = "debate" - reply.save! + assert_queries(3) do + reply.parent_title = "debate" + reply.save! + end assert_operator debate.reload.updated_at, :>, time assert_operator debate2.reload.updated_at, :>, time @@ -593,7 +627,10 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase debate.touch(time: time) debate2.touch(time: time) - reply.topic_with_primary_key = debate2 + assert_queries(3) do + reply.topic_with_primary_key = debate2 + reply.save! + end assert_operator debate.reload.updated_at, :>, time assert_operator debate2.reload.updated_at, :>, time @@ -772,6 +809,7 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase reply = Reply.create(title: "re: zoom", content: "speedy quick!") reply.topic = topic + reply.save! assert_equal 1, topic.reload[:replies_count] assert_equal 1, topic.replies.size @@ -827,6 +865,7 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase silly = SillyReply.create(title: "gaga", content: "boo-boo") silly.reply = reply + silly.save! assert_equal 1, reply.reload[:replies_count] assert_equal 1, reply.replies.size diff --git a/activerecord/test/cases/associations/cascaded_eager_loading_test.rb b/activerecord/test/cases/associations/cascaded_eager_loading_test.rb index e717621928..ba2104eb26 100644 --- a/activerecord/test/cases/associations/cascaded_eager_loading_test.rb +++ b/activerecord/test/cases/associations/cascaded_eager_loading_test.rb @@ -117,9 +117,8 @@ class CascadedEagerLoadingTest < ActiveRecord::TestCase end def test_eager_association_loading_with_has_many_sti_and_subclasses - silly = SillyReply.new(title: "gaga", content: "boo-boo", parent_id: 1) - silly.parent_id = 1 - assert silly.save + reply = Reply.new(title: "gaga", content: "boo-boo", parent_id: 1) + assert reply.save topics = Topic.all.merge!(includes: :replies, order: ["topics.id", "replies_topics.id"]).to_a assert_no_queries do diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb index 5b8d4722af..79b3b4a6ad 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, @@ -1571,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 @@ -1604,6 +1618,32 @@ class EagerAssociationTest < ActiveRecord::TestCase end end + # Associations::Preloader#preloaders_on works with hash-like objects + test "preloading works with an object that responds to :to_hash" do + CustomHash = Class.new(Hash) + + assert_nothing_raised do + Post.preload(CustomHash.new(comments: [{ author: :essays }])).first + end + end + + # Associations::Preloader#preloaders_on works with string-like objects + test "preloading works with an object that responds to :to_str" do + CustomString = Class.new(String) + + assert_nothing_raised do + Post.preload(CustomString.new("comments")).first + end + end + + # Associations::Preloader#preloaders_on does not work with ranges + test "preloading fails when Range is passed" do + exception = assert_raises(ArgumentError) do + Post.preload(1..10).first + end + assert_equal("1..10 was not recognized for preload", exception.message) + end + private def find_all_ordered(klass, include = nil) klass.order("#{klass.table_name}.#{klass.primary_key}").includes(include).to_a diff --git a/activerecord/test/cases/associations/extension_test.rb b/activerecord/test/cases/associations/extension_test.rb index 5eacb5a3d8..aef8f31112 100644 --- a/activerecord/test/cases/associations/extension_test.rb +++ b/activerecord/test/cases/associations/extension_test.rb @@ -89,6 +89,6 @@ class AssociationsExtensionsTest < ActiveRecord::TestCase private def extend!(model) - ActiveRecord::Associations::Builder::HasMany.define_extensions(model, :association_name) {} + ActiveRecord::Associations::Builder::HasMany.define_extensions(model, :association_name) { } end end 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 f414fbf64b..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 @@ -781,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 0ca902385a..0b44515e00 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 @@ -1177,6 +1197,38 @@ class HasManyAssociationsTest < ActiveRecord::TestCase assert_equal 2, topic.reload.replies.size end + def test_counter_cache_updates_in_memory_after_update_with_inverse_of_disabled + topic = Topic.create!(title: "Zoom-zoom-zoom") + + assert_equal 0, topic.replies_count + + reply1 = Reply.create!(title: "re: zoom", content: "speedy quick!") + reply2 = Reply.create!(title: "re: zoom 2", content: "OMG lol!") + + assert_queries(4) do + topic.replies << [reply1, reply2] + end + + assert_equal 2, topic.replies_count + assert_equal 2, topic.reload.replies_count + end + + def test_counter_cache_updates_in_memory_after_update_with_inverse_of_enabled + category = Category.create!(name: "Counter Cache") + + assert_nil category.categorizations_count + + categorization1 = Categorization.create! + categorization2 = Categorization.create! + + assert_queries(4) do + category.categorizations << [categorization1, categorization2] + end + + assert_equal 2, category.categorizations_count + assert_equal 2, category.reload.categorizations_count + end + def test_pushing_association_updates_counter_cache topic = Topic.order("id ASC").first reply = Reply.create! @@ -2134,21 +2186,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 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 d5573b6d02..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 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/associations_test.rb b/activerecord/test/cases/associations_test.rb index 739eb02e0c..081da95df7 100644 --- a/activerecord/test/cases/associations_test.rb +++ b/activerecord/test/cases/associations_test.rb @@ -80,7 +80,7 @@ class AssociationsTest < ActiveRecord::TestCase def test_force_reload firm = Firm.new("name" => "A New Firm, Inc") firm.save - firm.clients.each {} # forcing to load all clients + firm.clients.each { } # forcing to load all clients assert firm.clients.empty?, "New firm shouldn't have client objects" assert_equal 0, firm.clients.size, "New firm should have 0 clients" diff --git a/activerecord/test/cases/attributes_test.rb b/activerecord/test/cases/attributes_test.rb index 3bc56694be..2632aec7ab 100644 --- a/activerecord/test/cases/attributes_test.rb +++ b/activerecord/test/cases/attributes_test.rb @@ -148,6 +148,20 @@ module ActiveRecord assert_equal 2, klass.new.counter end + test "procs for default values are evaluated even after column_defaults is called" do + klass = Class.new(OverloadedType) do + @@counter = 0 + attribute :counter, :integer, default: -> { @@counter += 1 } + end + + assert_equal 1, klass.new.counter + + # column_defaults will increment the counter since the proc is called + klass.column_defaults + + assert_equal 3, klass.new.counter + end + test "procs are memoized before type casting" do klass = Class.new(OverloadedType) do @@counter = 0 diff --git a/activerecord/test/cases/autosave_association_test.rb b/activerecord/test/cases/autosave_association_test.rb index ade1f4b44d..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" @@ -558,6 +559,13 @@ 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 @@ -568,7 +576,13 @@ class TestDefaultAutosaveAssociationOnAHasManyAssociation < ActiveRecord::TestCa ]) assert_not reply.save - assert_not_empty reply.errors + 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 @@ -1774,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 d216fe16fa..09e5517449 100644 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -282,11 +282,13 @@ class BasicsTest < ActiveRecord::TestCase end def test_initialize_with_invalid_attribute - Topic.new("title" => "test", - "last_read(1i)" => "2005", "last_read(2i)" => "2", "last_read(3i)" => "31") - rescue ActiveRecord::MultiparameterAssignmentErrors => ex + ex = assert_raise(ActiveRecord::MultiparameterAssignmentErrors) do + Topic.new("title" => "test", + "written_on(4i)" => "16", "written_on(5i)" => "24", "written_on(6i)" => "00") + end + assert_equal(1, ex.errors.size) - assert_equal("last_read", ex.errors[0].attribute) + assert_equal("written_on", ex.errors[0].attribute) end def test_create_after_initialize_without_block @@ -853,8 +855,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 @@ -866,7 +867,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/batches_test.rb b/activerecord/test/cases/batches_test.rb index c8163901c6..d21218a997 100644 --- a/activerecord/test/cases/batches_test.rb +++ b/activerecord/test/cases/batches_test.rb @@ -24,7 +24,7 @@ class EachTest < ActiveRecord::TestCase def test_each_should_not_return_query_chain_and_execute_only_one_query assert_queries(1) do - result = Post.find_each(batch_size: 100000) {} + result = Post.find_each(batch_size: 100000) { } assert_nil result end end @@ -155,7 +155,7 @@ class EachTest < ActiveRecord::TestCase end def test_find_in_batches_should_not_use_records_after_yielding_them_in_case_original_array_is_modified - not_a_post = "not a post".dup + not_a_post = +"not a post" def not_a_post.id; end not_a_post.stub(:id, -> { raise StandardError.new("not_a_post had #id called on it") }) do assert_nothing_raised do @@ -183,7 +183,7 @@ class EachTest < ActiveRecord::TestCase def test_find_in_batches_should_error_on_ignore_the_order assert_raise(ArgumentError) do - PostWithDefaultScope.find_in_batches(error_on_ignore: true) {} + PostWithDefaultScope.find_in_batches(error_on_ignore: true) { } end end @@ -192,7 +192,7 @@ class EachTest < ActiveRecord::TestCase prev = ActiveRecord::Base.error_on_ignored_order ActiveRecord::Base.error_on_ignored_order = true assert_nothing_raised do - PostWithDefaultScope.find_in_batches(error_on_ignore: false) {} + PostWithDefaultScope.find_in_batches(error_on_ignore: false) { } end ensure # Set back to default @@ -204,7 +204,7 @@ class EachTest < ActiveRecord::TestCase prev = ActiveRecord::Base.error_on_ignored_order ActiveRecord::Base.error_on_ignored_order = true assert_raise(ArgumentError) do - PostWithDefaultScope.find_in_batches() {} + PostWithDefaultScope.find_in_batches() { } end ensure # Set back to default @@ -213,7 +213,7 @@ class EachTest < ActiveRecord::TestCase def test_find_in_batches_should_not_error_by_default assert_nothing_raised do - PostWithDefaultScope.find_in_batches() {} + PostWithDefaultScope.find_in_batches() { } end end @@ -228,7 +228,7 @@ class EachTest < ActiveRecord::TestCase def test_find_in_batches_should_not_modify_passed_options assert_nothing_raised do - Post.find_in_batches({ batch_size: 42, start: 1 }.freeze) {} + Post.find_in_batches({ batch_size: 42, start: 1 }.freeze) { } end end @@ -420,7 +420,7 @@ class EachTest < ActiveRecord::TestCase end def test_in_batches_should_not_use_records_after_yielding_them_in_case_original_array_is_modified - not_a_post = "not a post".dup + not_a_post = +"not a post" def not_a_post.id raise StandardError.new("not_a_post had #id called on it") end @@ -446,7 +446,7 @@ class EachTest < ActiveRecord::TestCase def test_in_batches_should_not_modify_passed_options assert_nothing_raised do - Post.in_batches({ of: 42, start: 1 }.freeze) {} + Post.in_batches({ of: 42, start: 1 }.freeze) { } end end @@ -597,15 +597,15 @@ class EachTest < ActiveRecord::TestCase table: table_alias, predicate_builder: predicate_builder ) - posts.find_each {} + posts.find_each { } end end test ".find_each bypasses the query cache for its own queries" do Post.cache do assert_queries(2) do - Post.find_each {} - Post.find_each {} + Post.find_each { } + Post.find_each { } end end end @@ -624,8 +624,8 @@ class EachTest < ActiveRecord::TestCase test ".find_in_batches bypasses the query cache for its own queries" do Post.cache do assert_queries(2) do - Post.find_in_batches {} - Post.find_in_batches {} + Post.find_in_batches { } + Post.find_in_batches { } end end end @@ -644,8 +644,8 @@ class EachTest < ActiveRecord::TestCase test ".in_batches bypasses the query cache for its own queries" do Post.cache do assert_queries(2) do - Post.in_batches {} - Post.in_batches {} + Post.in_batches { } + Post.in_batches { } end end end diff --git a/activerecord/test/cases/binary_test.rb b/activerecord/test/cases/binary_test.rb index d5376ece69..58abdb47f7 100644 --- a/activerecord/test/cases/binary_test.rb +++ b/activerecord/test/cases/binary_test.rb @@ -12,7 +12,7 @@ unless current_adapter?(:DB2Adapter) FIXTURES = %w(flowers.jpg example.log test.txt) def test_mixed_encoding - str = "\x80".dup + str = +"\x80" str.force_encoding("ASCII-8BIT") binary = Binary.new name: "いただきます!", data: str 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/callbacks_test.rb b/activerecord/test/cases/callbacks_test.rb index 253c3099d6..0ea3fb86a6 100644 --- a/activerecord/test/cases/callbacks_test.rb +++ b/activerecord/test/cases/callbacks_test.rb @@ -480,7 +480,7 @@ class CallbacksTest < ActiveRecord::TestCase def test_before_save_doesnt_allow_on_option exception = assert_raises ArgumentError do Class.new(ActiveRecord::Base) do - before_save(on: :create) {} + before_save(on: :create) { } end end assert_equal "Unknown key: :on. Valid keys are: :if, :unless, :prepend", exception.message @@ -489,7 +489,7 @@ class CallbacksTest < ActiveRecord::TestCase def test_around_save_doesnt_allow_on_option exception = assert_raises ArgumentError do Class.new(ActiveRecord::Base) do - around_save(on: :create) {} + around_save(on: :create) { } end end assert_equal "Unknown key: :on. Valid keys are: :if, :unless, :prepend", exception.message @@ -498,7 +498,7 @@ class CallbacksTest < ActiveRecord::TestCase def test_after_save_doesnt_allow_on_option exception = assert_raises ArgumentError do Class.new(ActiveRecord::Base) do - after_save(on: :create) {} + after_save(on: :create) { } end end assert_equal "Unknown key: :on. Valid keys are: :if, :unless, :prepend", exception.message diff --git a/activerecord/test/cases/connection_adapters/connection_handler_test.rb b/activerecord/test/cases/connection_adapters/connection_handler_test.rb index b8e623f17b..5e3447efde 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 @@ -148,6 +151,30 @@ module ActiveRecord ActiveRecord::Base.configurations = @prev_configs end + def test_symbolized_configurations_assignment + @prev_configs = ActiveRecord::Base.configurations + config = { + development: { + primary: { + adapter: "sqlite3", + database: "db/development.sqlite3", + }, + }, + test: { + primary: { + adapter: "sqlite3", + database: "db/test.sqlite3", + }, + }, + } + ActiveRecord::Base.configurations = config + ActiveRecord::Base.configurations.configs_for.each do |db_config| + assert_instance_of ActiveRecord::DatabaseConfigurations::HashConfig, db_config + end + ensure + ActiveRecord::Base.configurations = @prev_configs + end + def test_retrieve_connection assert @handler.retrieve_connection(@spec_name) 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_management_test.rb b/activerecord/test/cases/connection_management_test.rb index 0941ee3309..b9b5cc0e28 100644 --- a/activerecord/test/cases/connection_management_test.rb +++ b/activerecord/test/cases/connection_management_test.rb @@ -106,7 +106,7 @@ module ActiveRecord def middleware(app) lambda do |env| a, b, c = executor.wrap { app.call(env) } - [a, b, Rack::BodyProxy.new(c) {}] + [a, b, Rack::BodyProxy.new(c) { }] end end end diff --git a/activerecord/test/cases/connection_pool_test.rb b/activerecord/test/cases/connection_pool_test.rb index 9ac03629c3..633d56e479 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) @@ -578,7 +661,7 @@ module ActiveRecord end stuck_thread = Thread.new do - pool.with_connection {} + pool.with_connection { } end # wait for stuck_thread to get in queue 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/core_test.rb b/activerecord/test/cases/core_test.rb index 6e7ae2efb4..f7fbf3ee8a 100644 --- a/activerecord/test/cases/core_test.rb +++ b/activerecord/test/cases/core_test.rb @@ -36,7 +36,7 @@ class CoreTest < ActiveRecord::TestCase def test_pretty_print_new topic = Topic.new - actual = "".dup + actual = +"" PP.pp(topic, StringIO.new(actual)) expected = <<~PRETTY #<Topic:0xXXXXXX @@ -65,7 +65,7 @@ class CoreTest < ActiveRecord::TestCase def test_pretty_print_persisted topic = topics(:first) - actual = "".dup + actual = +"" PP.pp(topic, StringIO.new(actual)) expected = <<~PRETTY #<Topic:0x\\w+ @@ -93,7 +93,7 @@ class CoreTest < ActiveRecord::TestCase def test_pretty_print_uninitialized topic = Topic.allocate - actual = "".dup + actual = +"" PP.pp(topic, StringIO.new(actual)) expected = "#<Topic:XXXXXX not initialized>\n" assert actual.start_with?(expected.split("XXXXXX").first) @@ -106,7 +106,7 @@ class CoreTest < ActiveRecord::TestCase "inspecting topic" end end - actual = "".dup + actual = +"" PP.pp(subtopic.new, StringIO.new(actual)) assert_equal "inspecting topic\n", actual 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_respond_to_test.rb b/activerecord/test/cases/finder_respond_to_test.rb index 59af4e6961..e0acd30c22 100644 --- a/activerecord/test/cases/finder_respond_to_test.rb +++ b/activerecord/test/cases/finder_respond_to_test.rb @@ -12,7 +12,7 @@ class FinderRespondToTest < ActiveRecord::TestCase end def test_should_preserve_normal_respond_to_behaviour_and_respond_to_newly_added_method - class << Topic; self; end.send(:define_method, :method_added_for_finder_respond_to_test) {} + class << Topic; self; end.send(:define_method, :method_added_for_finder_respond_to_test) { } assert_respond_to Topic, :method_added_for_finder_respond_to_test ensure class << Topic; self; end.send(:remove_method, :method_added_for_finder_respond_to_test) diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb index e73c88dd5d..355fb4517f 100644 --- a/activerecord/test/cases/finder_test.rb +++ b/activerecord/test/cases/finder_test.rb @@ -371,7 +371,10 @@ class FinderTest < ActiveRecord::TestCase end def test_find_an_empty_array - assert_equal [], Topic.find([]) + empty_array = [] + result = Topic.find(empty_array) + assert_equal [], result + assert_not_same empty_array, result end def test_find_doesnt_have_implicit_ordering diff --git a/activerecord/test/cases/fixtures_test.rb b/activerecord/test/cases/fixtures_test.rb index c65523d8c1..82ca15b415 100644 --- a/activerecord/test/cases/fixtures_test.rb +++ b/activerecord/test/cases/fixtures_test.rb @@ -974,7 +974,7 @@ class TransactionalFixturesOnConnectionNotification < ActiveRecord::TestCase connection_id: connection.object_id } - message_bus.instrument("!connection.active_record", payload) {} + message_bus.instrument("!connection.active_record", payload) { } end end end @@ -1344,3 +1344,19 @@ class SameNameDifferentDatabaseFixturesTest < ActiveRecord::TestCase assert_kind_of OtherDog, other_dogs(:lassie) end end + +class NilFixturePathTest < ActiveRecord::TestCase + test "raises an error when all fixtures loaded" do + error = assert_raises(StandardError) do + TestCase = Class.new(ActiveRecord::TestCase) + TestCase.class_eval do + self.fixture_path = nil + fixtures :all + end + end + assert_equal <<~MSG.squish, error.message + No fixture path found. + Please set `NilFixturePathTest::TestCase.fixture_path`. + MSG + 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/invertible_migration_test.rb b/activerecord/test/cases/invertible_migration_test.rb index 363beb4780..6cf17ac15d 100644 --- a/activerecord/test/cases/invertible_migration_test.rb +++ b/activerecord/test/cases/invertible_migration_test.rb @@ -22,6 +22,14 @@ module ActiveRecord end end + class InvertibleTransactionMigration < InvertibleMigration + def change + transaction do + super + end + end + end + class InvertibleRevertMigration < SilentMigration def change revert do @@ -271,6 +279,14 @@ module ActiveRecord assert_not revert.connection.table_exists?("horses") end + def test_migrate_revert_transaction + migration = InvertibleTransactionMigration.new + migration.migrate :up + assert migration.connection.table_exists?("horses") + migration.migrate :down + assert_not migration.connection.table_exists?("horses") + end + def test_migrate_revert_change_column_default migration1 = ChangeColumnDefault1.new migration1.migrate(:up) 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/log_subscriber_test.rb b/activerecord/test/cases/log_subscriber_test.rb index f0126fdb0d..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 diff --git a/activerecord/test/cases/migration/change_table_test.rb b/activerecord/test/cases/migration/change_table_test.rb index 034bf32165..c108d372d1 100644 --- a/activerecord/test/cases/migration/change_table_test.rb +++ b/activerecord/test/cases/migration/change_table_test.rb @@ -164,6 +164,14 @@ module ActiveRecord end end + def test_column_creates_column_with_index + with_change_table do |t| + @connection.expect :add_column, nil, [:delete_me, :bar, :integer, {}] + @connection.expect :add_index, nil, [:delete_me, :bar, {}] + t.column :bar, :integer, index: true + end + end + def test_index_creates_index with_change_table do |t| @connection.expect :add_index, nil, [:delete_me, :bar, {}] diff --git a/activerecord/test/cases/migration/command_recorder_test.rb b/activerecord/test/cases/migration/command_recorder_test.rb index 3a11bb081b..01f8628fc5 100644 --- a/activerecord/test/cases/migration/command_recorder_test.rb +++ b/activerecord/test/cases/migration/command_recorder_test.rb @@ -117,13 +117,13 @@ module ActiveRecord end def test_invert_create_table_with_options_and_block - block = Proc.new {} + block = Proc.new { } drop_table = @recorder.inverse_of :create_table, [:people_reminders, id: false], &block assert_equal [:drop_table, [:people_reminders, id: false], block], drop_table end def test_invert_drop_table - block = Proc.new {} + block = Proc.new { } create_table = @recorder.inverse_of :drop_table, [:people_reminders, id: false], &block assert_equal [:create_table, [:people_reminders, id: false], block], create_table end @@ -145,7 +145,7 @@ module ActiveRecord end def test_invert_drop_join_table - block = Proc.new {} + block = Proc.new { } create_join_table = @recorder.inverse_of :drop_join_table, [:musics, :artists, table_name: :catalog], &block assert_equal [:create_join_table, [:musics, :artists, table_name: :catalog], block], create_join_table end @@ -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"] @@ -347,6 +360,16 @@ module ActiveRecord @recorder.inverse_of :remove_foreign_key, [:dogs] end end + + def test_invert_transaction_with_irreversible_inside_is_irreversible + assert_raises(ActiveRecord::IrreversibleMigration) do + @recorder.revert do + @recorder.transaction do + @recorder.execute "some sql" + end + end + end + end end end end diff --git a/activerecord/test/cases/migration/compatibility_test.rb b/activerecord/test/cases/migration/compatibility_test.rb index 69a50674af..017ee7951e 100644 --- a/activerecord/test/cases/migration/compatibility_test.rb +++ b/activerecord/test/cases/migration/compatibility_test.rb @@ -127,6 +127,20 @@ module ActiveRecord assert_match(/LegacyMigration < ActiveRecord::Migration\[4\.2\]/, e.message) end + def test_legacy_migrations_not_raise_exception_on_reverting_transaction + migration = Class.new(ActiveRecord::Migration[5.2]) { + def change + transaction do + execute "select 1" + end + end + }.new + + assert_nothing_raised do + migration.migrate(:down) + end + end + if current_adapter?(:PostgreSQLAdapter) class Testing < ActiveRecord::Base end 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/pending_migrations_test.rb b/activerecord/test/cases/migration/pending_migrations_test.rb index dedb5ea502..119bfd372a 100644 --- a/activerecord/test/cases/migration/pending_migrations_test.rb +++ b/activerecord/test/cases/migration/pending_migrations_test.rb @@ -25,7 +25,7 @@ module ActiveRecord ActiveRecord::Base.connection.drop_table "schema_migrations", if_exists: true assert_raises ActiveRecord::PendingMigrationError do - CheckPending.new(Proc.new {}).call({}) + CheckPending.new(Proc.new { }).call({}) end end @@ -34,7 +34,7 @@ module ActiveRecord migrator = Base.connection.migration_context capture(:stdout) { migrator.migrate } - assert_nil CheckPending.new(Proc.new {}).call({}) + assert_nil CheckPending.new(Proc.new { }).call({}) end end end diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb index d1292dc53d..5d060c8899 100644 --- a/activerecord/test/cases/migration_test.rb +++ b/activerecord/test/cases/migration_test.rb @@ -87,7 +87,6 @@ class MigrationTest < ActiveRecord::TestCase def test_migrator_versions migrations_path = MIGRATIONS_ROOT + "/valid" - old_path = ActiveRecord::Migrator.migrations_paths migrator = ActiveRecord::MigrationContext.new(migrations_path) migrator.up @@ -100,24 +99,18 @@ class MigrationTest < ActiveRecord::TestCase ActiveRecord::SchemaMigration.create!(version: 3) assert_equal true, migrator.needs_migration? - ensure - ActiveRecord::MigrationContext.new(old_path) end def test_migration_detection_without_schema_migration_table ActiveRecord::Base.connection.drop_table "schema_migrations", if_exists: true migrations_path = MIGRATIONS_ROOT + "/valid" - old_path = ActiveRecord::Migrator.migrations_paths migrator = ActiveRecord::MigrationContext.new(migrations_path) assert_equal true, migrator.needs_migration? - ensure - ActiveRecord::MigrationContext.new(old_path) end def test_any_migrations - old_path = ActiveRecord::Migrator.migrations_paths migrator = ActiveRecord::MigrationContext.new(MIGRATIONS_ROOT + "/valid") assert_predicate migrator, :any_migrations? @@ -125,8 +118,6 @@ class MigrationTest < ActiveRecord::TestCase migrator_empty = ActiveRecord::MigrationContext.new(MIGRATIONS_ROOT + "/empty") assert_not_predicate migrator_empty, :any_migrations? - ensure - ActiveRecord::MigrationContext.new(old_path) end def test_migration_version @@ -393,7 +384,6 @@ class MigrationTest < ActiveRecord::TestCase def test_internal_metadata_stores_environment current_env = ActiveRecord::ConnectionHandling::DEFAULT_ENV.call migrations_path = MIGRATIONS_ROOT + "/valid" - old_path = ActiveRecord::Migrator.migrations_paths migrator = ActiveRecord::MigrationContext.new(migrations_path) migrator.up @@ -410,7 +400,6 @@ class MigrationTest < ActiveRecord::TestCase migrator.up assert_equal new_env, ActiveRecord::InternalMetadata[:environment] ensure - migrator = ActiveRecord::MigrationContext.new(old_path) ENV["RAILS_ENV"] = original_rails_env ENV["RACK_ENV"] = original_rack_env migrator.up @@ -422,16 +411,12 @@ class MigrationTest < ActiveRecord::TestCase current_env = ActiveRecord::ConnectionHandling::DEFAULT_ENV.call migrations_path = MIGRATIONS_ROOT + "/valid" - old_path = ActiveRecord::Migrator.migrations_paths current_env = ActiveRecord::ConnectionHandling::DEFAULT_ENV.call migrator = ActiveRecord::MigrationContext.new(migrations_path) migrator.up assert_equal current_env, ActiveRecord::InternalMetadata[:environment] assert_equal "bar", ActiveRecord::InternalMetadata[:foo] - ensure - migrator = ActiveRecord::MigrationContext.new(old_path) - migrator.up end def test_proper_table_name_on_migration @@ -793,12 +778,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 +799,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 @@ -1150,7 +1144,7 @@ class CopyMigrationsTest < ActiveRecord::TestCase def test_check_pending_with_stdlib_logger old, ActiveRecord::Base.logger = ActiveRecord::Base.logger, ::Logger.new($stdout) quietly do - assert_nothing_raised { ActiveRecord::Migration::CheckPending.new(Proc.new {}).call({}) } + assert_nothing_raised { ActiveRecord::Migration::CheckPending.new(Proc.new { }).call({}) } end ensure ActiveRecord::Base.logger = old diff --git a/activerecord/test/cases/migrator_test.rb b/activerecord/test/cases/migrator_test.rb index 873455cf67..30e199f1c5 100644 --- a/activerecord/test/cases/migrator_test.rb +++ b/activerecord/test/cases/migrator_test.rb @@ -100,7 +100,6 @@ class MigratorTest < ActiveRecord::TestCase def test_finds_migrations_in_subdirectories migrations = ActiveRecord::MigrationContext.new(MIGRATIONS_ROOT + "/valid_with_subdirectories").migrations - [[1, "ValidPeopleHaveLastNames"], [2, "WeNeedReminders"], [3, "InnocentJointable"]].each_with_index do |pair, i| assert_equal migrations[i].version, pair.first assert_equal migrations[i].name, pair.last diff --git a/activerecord/test/cases/nested_attributes_test.rb b/activerecord/test/cases/nested_attributes_test.rb index aa519fd332..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 diff --git a/activerecord/test/cases/persistence_test.rb b/activerecord/test/cases/persistence_test.rb index 7348a22dd3..8073cabae6 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 @@ -230,16 +127,9 @@ class PersistenceTest < ActiveRecord::TestCase assert_operator previously_written_on, :<, topic.written_on 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" - end + def test_increment_with_no_arg + topic = topics(:first) + assert_raises(ArgumentError) { topic.increment! } end def test_destroy_many @@ -638,32 +528,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 @@ -1185,21 +1049,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/query_cache_test.rb b/activerecord/test/cases/query_cache_test.rb index 69be091869..3eb4e04cb7 100644 --- a/activerecord/test/cases/query_cache_test.rb +++ b/activerecord/test/cases/query_cache_test.rb @@ -55,16 +55,6 @@ class QueryCacheTest < ActiveRecord::TestCase assert_cache :off end - private def with_temporary_connection_pool - old_pool = ActiveRecord::Base.connection_handler.retrieve_connection_pool(ActiveRecord::Base.connection_specification_name) - new_pool = ActiveRecord::ConnectionAdapters::ConnectionPool.new ActiveRecord::Base.connection_pool.spec - ActiveRecord::Base.connection_handler.send(:owner_to_pool)["primary"] = new_pool - - yield - ensure - ActiveRecord::Base.connection_handler.send(:owner_to_pool)["primary"] = old_pool - end - def test_query_cache_across_threads with_temporary_connection_pool do begin @@ -495,6 +485,17 @@ class QueryCacheTest < ActiveRecord::TestCase end private + + def with_temporary_connection_pool + old_pool = ActiveRecord::Base.connection_handler.retrieve_connection_pool(ActiveRecord::Base.connection_specification_name) + new_pool = ActiveRecord::ConnectionAdapters::ConnectionPool.new ActiveRecord::Base.connection_pool.spec + ActiveRecord::Base.connection_handler.send(:owner_to_pool)["primary"] = new_pool + + yield + ensure + ActiveRecord::Base.connection_handler.send(:owner_to_pool)["primary"] = old_pool + end + def middleware(&app) executor = Class.new(ActiveSupport::Executor) ActiveRecord::QueryCache.install_executor_hooks executor 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..224e4f39a8 100644 --- a/activerecord/test/cases/relation/merging_test.rb +++ b/activerecord/test/cases/relation/merging_test.rb @@ -121,6 +121,20 @@ class RelationMergingTest < ActiveRecord::TestCase relation = relation.merge(Post.from("posts")) assert_not_empty relation.from_clause end + + def test_merging_with_from_clause_on_different_class + assert Comment.joins(:post).merge(Post.from("posts")).first + 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/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/relations_test.rb b/activerecord/test/cases/relations_test.rb index 5412ab5def..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,13 +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 - 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_do_not_double_quote_string_id van = Minivan.last assert van @@ -862,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) @@ -985,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 @@ -1405,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 @@ -1486,114 +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 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 - 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 68fcafb682..825aee2423 100644 --- a/activerecord/test/cases/result_test.rb +++ b/activerecord/test/cases/result_test.rb @@ -21,12 +21,22 @@ module ActiveRecord assert_equal 3, result.length end - test "to_hash returns row_hashes" do + test "to_a returns row_hashes" do assert_equal [ { "col_1" => "row 1 col 1", "col_2" => "row 1 col 2" }, { "col_1" => "row 2 col 1", "col_2" => "row 2 col 2" }, { "col_1" => "row 3 col 1", "col_2" => "row 3 col 2" }, - ], result.to_hash + ], result.to_a + end + + test "to_hash (deprecated) returns row_hashes" do + assert_deprecated do + assert_equal [ + { "col_1" => "row 1 col 1", "col_2" => "row 1 col 2" }, + { "col_1" => "row 2 col 1", "col_2" => "row 2 col 2" }, + { "col_1" => "row 3 col 1", "col_2" => "row 3 col 2" }, + ], result.to_hash + end end test "first returns first row as a hash" do diff --git a/activerecord/test/cases/schema_dumper_test.rb b/activerecord/test/cases/schema_dumper_test.rb index 75b68b521c..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 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/named_scoping_test.rb b/activerecord/test/cases/scoping/named_scoping_test.rb index 4214f347fb..f707951a16 100644 --- a/activerecord/test/cases/scoping/named_scoping_test.rb +++ b/activerecord/test/cases/scoping/named_scoping_test.rb @@ -489,7 +489,7 @@ class NamedScopingTest < ActiveRecord::TestCase [:public_method, :protected_method, :private_method].each do |reserved_method| assert Topic.respond_to?(reserved_method, true) assert_called(ActiveRecord::Base.logger, :warn) do - silence_warnings { Topic.scope(reserved_method, -> {}) } + silence_warnings { Topic.scope(reserved_method, -> { }) } end end end diff --git a/activerecord/test/cases/scoping/relation_scoping_test.rb b/activerecord/test/cases/scoping/relation_scoping_test.rb index 544adc9b39..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 diff --git a/activerecord/test/cases/serialized_attribute_test.rb b/activerecord/test/cases/serialized_attribute_test.rb index 4cd4515c3b..1192b30b14 100644 --- a/activerecord/test/cases/serialized_attribute_test.rb +++ b/activerecord/test/cases/serialized_attribute_test.rb @@ -322,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/suppressor_test.rb b/activerecord/test/cases/suppressor_test.rb index b68f0033d9..9be5356901 100644 --- a/activerecord/test/cases/suppressor_test.rb +++ b/activerecord/test/cases/suppressor_test.rb @@ -66,7 +66,7 @@ class SuppressorTest < ActiveRecord::TestCase def test_suppresses_when_nested_multiple_times assert_no_difference -> { Notification.count } do Notification.suppress do - Notification.suppress {} + Notification.suppress { } Notification.create Notification.create! Notification.new.save diff --git a/activerecord/test/cases/tasks/database_tasks_test.rb b/activerecord/test/cases/tasks/database_tasks_test.rb index a6efe3fa5e..d674bd562f 100644 --- a/activerecord/test/cases/tasks/database_tasks_test.rb +++ b/activerecord/test/cases/tasks/database_tasks_test.rb @@ -46,35 +46,46 @@ 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 @@ -82,10 +93,14 @@ module ActiveRecord def test_raises_an_error_if_no_migrations_have_been_made ActiveRecord::InternalMetadata.stub(:table_exists?, false) do - ActiveRecord::MigrationContext.any_instance.stubs(:current_version).returns(1) - - assert_raise(ActiveRecord::NoEnvironmentInSchemaError) do - ActiveRecord::Tasks::DatabaseTasks.check_protected_environments! + 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 @@ -213,18 +228,17 @@ module ActiveRecord end private - def with_stubbed_configurations_establish_connection - ActiveRecord::Base.stub(:configurations, @configurations) do - # 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 + 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 @@ -233,7 +247,7 @@ module ActiveRecord @configurations = { "development" => { "database" => "dev-db" }, "test" => { "database" => "test-db" }, - "production" => { "url" => "prod-db-url" } + "production" => { "url" => "abstract://prod-db-host/prod-db" } } end @@ -256,7 +270,7 @@ module ActiveRecord assert_called_with( ActiveRecord::Tasks::DatabaseTasks, :create, - ["url" => "prod-db-url"], + ["adapter" => "abstract", "database" => "prod-db", "host" => "prod-db-host"], ) do ActiveRecord::Tasks::DatabaseTasks.create_current( ActiveSupport::StringInquirer.new("production") @@ -315,13 +329,15 @@ module ActiveRecord end private - def with_stubbed_configurations_establish_connection - ActiveRecord::Base.stub(:configurations, @configurations) do - ActiveRecord::Base.stub(:establish_connection, nil) do - yield - end + old_configurations = ActiveRecord::Base.configurations + ActiveRecord::Base.configurations = @configurations + + ActiveRecord::Base.connection_handler.stub(:establish_connection, nil) do + yield end + ensure + ActiveRecord::Base.configurations = old_configurations end end @@ -330,7 +346,7 @@ 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" } } } end @@ -357,8 +373,8 @@ module ActiveRecord ActiveRecord::Tasks::DatabaseTasks, :create, [ - ["url" => "prod-db-url"], - ["url" => "secondary-prod-db-url"] + ["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( @@ -426,13 +442,15 @@ module ActiveRecord end private - def with_stubbed_configurations_establish_connection - ActiveRecord::Base.stub(:configurations, @configurations) do - ActiveRecord::Base.stub(:establish_connection, nil) do - yield - end + old_configurations = ActiveRecord::Base.configurations + ActiveRecord::Base.configurations = @configurations + + ActiveRecord::Base.connection_handler.stub(:establish_connection, nil) do + yield end + ensure + ActiveRecord::Base.configurations = old_configurations end end @@ -465,7 +483,7 @@ module ActiveRecord def test_ignores_configurations_without_databases @configurations[:development]["database"] = nil - ActiveRecord::Base.stub(:configurations, @configurations) do + with_stubbed_configurations do assert_not_called(ActiveRecord::Tasks::DatabaseTasks, :drop) do ActiveRecord::Tasks::DatabaseTasks.drop_all end @@ -475,7 +493,7 @@ module ActiveRecord def test_ignores_remote_databases @configurations[:development]["host"] = "my.server.tld" - ActiveRecord::Base.stub(:configurations, @configurations) do + with_stubbed_configurations do assert_not_called(ActiveRecord::Tasks::DatabaseTasks, :drop) do ActiveRecord::Tasks::DatabaseTasks.drop_all end @@ -485,7 +503,7 @@ module ActiveRecord def test_warning_for_remote_databases @configurations[:development]["host"] = "my.server.tld" - ActiveRecord::Base.stub(:configurations, @configurations) 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.", @@ -496,7 +514,7 @@ module ActiveRecord def test_drops_configurations_with_local_ip @configurations[:development]["host"] = "127.0.0.1" - ActiveRecord::Base.stub(:configurations, @configurations) do + with_stubbed_configurations do assert_called(ActiveRecord::Tasks::DatabaseTasks, :drop) do ActiveRecord::Tasks::DatabaseTasks.drop_all end @@ -506,7 +524,7 @@ module ActiveRecord def test_drops_configurations_with_local_host @configurations[:development]["host"] = "localhost" - ActiveRecord::Base.stub(:configurations, @configurations) do + with_stubbed_configurations do assert_called(ActiveRecord::Tasks::DatabaseTasks, :drop) do ActiveRecord::Tasks::DatabaseTasks.drop_all end @@ -516,12 +534,22 @@ module ActiveRecord def test_drops_configurations_with_blank_hosts @configurations[:development]["host"] = nil - ActiveRecord::Base.stub(:configurations, @configurations) do + 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 @@ -529,12 +557,12 @@ module ActiveRecord @configurations = { "development" => { "database" => "dev-db" }, "test" => { "database" => "test-db" }, - "production" => { "url" => "prod-db-url" } + "production" => { "url" => "abstract://prod-db-host/prod-db" } } end def test_drops_current_environment_database - ActiveRecord::Base.stub(:configurations, @configurations) do + with_stubbed_configurations do assert_called_with(ActiveRecord::Tasks::DatabaseTasks, :drop, ["database" => "test-db"]) do ActiveRecord::Tasks::DatabaseTasks.drop_current( @@ -545,9 +573,9 @@ module ActiveRecord end def test_drops_current_environment_database_with_url - ActiveRecord::Base.stub(:configurations, @configurations) do + with_stubbed_configurations do assert_called_with(ActiveRecord::Tasks::DatabaseTasks, :drop, - ["url" => "prod-db-url"]) do + ["adapter" => "abstract", "database" => "prod-db", "host" => "prod-db-host"]) do ActiveRecord::Tasks::DatabaseTasks.drop_current( ActiveSupport::StringInquirer.new("production") ) @@ -556,7 +584,7 @@ module ActiveRecord end def test_drops_test_and_development_databases_when_env_was_not_specified - ActiveRecord::Base.stub(:configurations, @configurations) do + with_stubbed_configurations do assert_called_with( ActiveRecord::Tasks::DatabaseTasks, :drop, @@ -576,7 +604,7 @@ module ActiveRecord old_env = ENV["RAILS_ENV"] ENV["RAILS_ENV"] = "development" - ActiveRecord::Base.stub(:configurations, @configurations) do + with_stubbed_configurations do assert_called_with( ActiveRecord::Tasks::DatabaseTasks, :drop, @@ -593,6 +621,16 @@ module ActiveRecord 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 @@ -600,12 +638,12 @@ 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" } } } end def test_drops_current_environment_database - ActiveRecord::Base.stub(:configurations, @configurations) do + with_stubbed_configurations do assert_called_with( ActiveRecord::Tasks::DatabaseTasks, :drop, @@ -622,13 +660,13 @@ module ActiveRecord end def test_drops_current_environment_database_with_url - ActiveRecord::Base.stub(:configurations, @configurations) do + with_stubbed_configurations do assert_called_with( ActiveRecord::Tasks::DatabaseTasks, :drop, [ - ["url" => "prod-db-url"], - ["url" => "secondary-prod-db-url"] + ["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( @@ -639,7 +677,7 @@ module ActiveRecord end def test_drops_test_and_development_databases_when_env_was_not_specified - ActiveRecord::Base.stub(:configurations, @configurations) do + with_stubbed_configurations do assert_called_with( ActiveRecord::Tasks::DatabaseTasks, :drop, @@ -661,7 +699,7 @@ module ActiveRecord old_env = ENV["RAILS_ENV"] ENV["RAILS_ENV"] = "development" - ActiveRecord::Base.stub(:configurations, @configurations) do + with_stubbed_configurations do assert_called_with( ActiveRecord::Tasks::DatabaseTasks, :drop, @@ -680,6 +718,16 @@ module ActiveRecord 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? @@ -833,37 +881,44 @@ module ActiveRecord 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.stub(:configurations, configurations) do - 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 + + ActiveRecord::Base.configurations = configurations + + 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.stub(:configurations, configurations) do - assert_called_with( - ActiveRecord::Tasks::DatabaseTasks, - :purge, - ["database" => "my-db"] - ) do - ActiveRecord::Tasks::DatabaseTasks.purge_all - end + ActiveRecord::Base.configurations = configurations + + 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 diff --git a/activerecord/test/cases/tasks/mysql_rake_test.rb b/activerecord/test/cases/tasks/mysql_rake_test.rb index eeb4222d97..4d6dff68f9 100644 --- a/activerecord/test/cases/tasks/mysql_rake_test.rb +++ b/activerecord/test/cases/tasks/mysql_rake_test.rb @@ -152,10 +152,14 @@ if current_adapter?(:Mysql2Adapter) end def test_establishes_connection_to_mysql_database - with_stubbed_connection_establish_connection do - 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 @@ -196,10 +200,14 @@ if current_adapter?(:Mysql2Adapter) end def test_establishes_connection_to_the_appropriate_database - with_stubbed_connection_establish_connection do - 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 diff --git a/activerecord/test/cases/tasks/postgresql_rake_test.rb b/activerecord/test/cases/tasks/postgresql_rake_test.rb index 00005e7a0d..065ba7734c 100644 --- a/activerecord/test/cases/tasks/postgresql_rake_test.rb +++ b/activerecord/test/cases/tasks/postgresql_rake_test.rb @@ -112,7 +112,7 @@ if current_adapter?(:PostgreSQLAdapter) 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 database for #{@configuration.inspect}", $stderr.string + assert_match "Couldn't create '#{@configuration['database']}' database. Please check your configuration.", $stderr.string end end end @@ -166,12 +166,17 @@ if current_adapter?(:PostgreSQLAdapter) def test_establishes_connection_to_postgresql_database ActiveRecord::Base.stub(:connection, @connection) do - ActiveRecord::Base.expects(:establish_connection).with( - "adapter" => "postgresql", - "database" => "postgres", - "schema_search_path" => "public" - ) - ActiveRecord::Tasks::DatabaseTasks.drop @configuration + assert_called_with( + ActiveRecord::Base, + :establish_connection, + [ + "adapter" => "postgresql", + "database" => "postgres", + "schema_search_path" => "public" + ] + ) do + ActiveRecord::Tasks::DatabaseTasks.drop @configuration + end end end @@ -491,7 +496,7 @@ if current_adapter?(:PostgreSQLAdapter) assert_called_with( Kernel, :system, - ["psql", "-v", "ON_ERROR_STOP=1", "-q", "-f", filename, @configuration["database"]], + ["psql", "-v", "ON_ERROR_STOP=1", "-q", "-X", "-f", filename, @configuration["database"]], returns: true ) do ActiveRecord::Tasks::DatabaseTasks.structure_load(@configuration, filename) @@ -500,7 +505,7 @@ if current_adapter?(:PostgreSQLAdapter) def test_structure_load_with_extra_flags filename = "awesome-file.sql" - expected_command = ["psql", "-v", "ON_ERROR_STOP=1", "-q", "-f", filename, "--noop", @configuration["database"]] + expected_command = ["psql", "-v", "ON_ERROR_STOP=1", "-q", "-X", "-f", filename, "--noop", @configuration["database"]] assert_called_with(Kernel, :system, expected_command, returns: true) do with_structure_load_flags(["--noop"]) do @@ -514,7 +519,7 @@ if current_adapter?(:PostgreSQLAdapter) assert_called_with( Kernel, :system, - ["psql", "-v", "ON_ERROR_STOP=1", "-q", "-f", filename, @configuration["database"]], + ["psql", "-v", "ON_ERROR_STOP=1", "-q", "-X", "-f", filename, @configuration["database"]], returns: true ) do ActiveRecord::Tasks::DatabaseTasks.structure_load(@configuration, filename) diff --git a/activerecord/test/cases/tasks/sqlite_rake_test.rb b/activerecord/test/cases/tasks/sqlite_rake_test.rb index 7eb062b456..c1092b97c1 100644 --- a/activerecord/test/cases/tasks/sqlite_rake_test.rb +++ b/activerecord/test/cases/tasks/sqlite_rake_test.rb @@ -47,9 +47,9 @@ if current_adapter?(:SQLite3Adapter) def test_db_create_with_file_does_nothing File.stub(:exist?, true) do - ActiveRecord::Base.expects(:establish_connection).never - - ActiveRecord::Tasks::DatabaseTasks.create @configuration, "/rails/root" + assert_not_called(ActiveRecord::Base, :establish_connection) do + ActiveRecord::Tasks::DatabaseTasks.create @configuration, "/rails/root" + end end end @@ -62,7 +62,7 @@ if current_adapter?(:SQLite3Adapter) def test_db_create_with_error_prints_message ActiveRecord::Base.stub(:establish_connection, proc { raise Exception }) do assert_raises(Exception) { ActiveRecord::Tasks::DatabaseTasks.create @configuration, "/rails/root" } - assert_match "Couldn't create database for #{@configuration.inspect}", $stderr.string + assert_match "Couldn't create '#{@configuration['database']}' database. Please check your configuration.", $stderr.string end end end 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_isolation_test.rb b/activerecord/test/cases/transaction_isolation_test.rb index eaafd13360..2932969412 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 @@ -90,7 +90,7 @@ else test "setting isolation when joining a transaction raises an error" do Tag.transaction do assert_raises(ActiveRecord::TransactionIsolationError) do - Tag.transaction(isolation: :serializable) {} + Tag.transaction(isolation: :serializable) { } end end end @@ -98,7 +98,7 @@ else test "setting isolation when starting a nested transaction raises error" do Tag.transaction do assert_raises(ActiveRecord::TransactionIsolationError) do - Tag.transaction(requires_new: true, isolation: :serializable) {} + Tag.transaction(requires_new: true, isolation: :serializable) { } end end end diff --git a/activerecord/test/cases/transactions_test.rb b/activerecord/test/cases/transactions_test.rb index 46463ac414..50740054f7 100644 --- a/activerecord/test/cases/transactions_test.rb +++ b/activerecord/test/cases/transactions_test.rb @@ -575,7 +575,7 @@ class TransactionTest < ActiveRecord::TestCase 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 @@ -930,7 +930,7 @@ class TransactionTest < ActiveRecord::TestCase klass = Class.new(ActiveRecord::Base) do self.table_name = "transaction_without_primary_keys" - after_commit {} # necessary to trigger the has_transactional_callbacks branch + after_commit { } # necessary to trigger the has_transactional_callbacks branch end assert_no_difference(-> { klass.count }) do @@ -943,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/type/type_map_test.rb b/activerecord/test/cases/type/type_map_test.rb index f3699c11a2..1ce515a90c 100644 --- a/activerecord/test/cases/type/type_map_test.rb +++ b/activerecord/test/cases/type/type_map_test.rb @@ -32,7 +32,7 @@ module ActiveRecord end def test_fuzzy_lookup - string = String.new + string = +"" mapping = TypeMap.new mapping.register_type(/varchar/i, string) @@ -41,7 +41,7 @@ module ActiveRecord end def test_aliasing_types - string = String.new + string = +"" mapping = TypeMap.new mapping.register_type(/string/i, string) @@ -73,7 +73,7 @@ module ActiveRecord end def test_register_proc - string = String.new + string = +"" binary = Binary.new mapping = TypeMap.new 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/models/author.rb b/activerecord/test/models/author.rb index 75932c7eb6..8b5a2fa0c8 100644 --- a/activerecord/test/models/author.rb +++ b/activerecord/test/models/author.rb @@ -81,7 +81,7 @@ class Author < ActiveRecord::Base after_add: [:log_after_adding, Proc.new { |o, r| o.post_log << "after_adding_proc#{r.id || '<new>'}" }] has_many :unchangeable_posts, class_name: "Post", before_add: :raise_exception, after_add: :log_after_adding - has_many :categorizations, -> {} + has_many :categorizations, -> { } has_many :categories, through: :categorizations has_many :named_categories, through: :categorizations 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 d4d5275b78..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" @@ -204,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/post.rb b/activerecord/test/models/post.rb index 640cdb33b4..528585fb75 100644 --- a/activerecord/test/models/post.rb +++ b/activerecord/test/models/post.rb @@ -324,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 0ea110f4f8..0807bcf875 100644 --- a/activerecord/test/models/reply.rb +++ b/activerecord/test/models/reply.rb @@ -9,6 +9,10 @@ class Reply < Topic has_many :silly_unique_replies, dependent: :destroy, foreign_key: "parent_id" end +class SillyReply < Topic + belongs_to :reply, foreign_key: "parent_id", counter_cache: :replies_count +end + class UniqueReply < Reply belongs_to :topic, foreign_key: "parent_id", counter_cache: true validates_uniqueness_of :content, scope: "parent_id" @@ -54,10 +58,6 @@ class WrongReply < Reply end end -class SillyReply < Reply - belongs_to :reply, foreign_key: "parent_id", counter_cache: :replies_count -end - module Web class Reply < Web::Topic belongs_to :topic, foreign_key: "parent_id", counter_cache: true, class_name: "Web::Topic" diff --git a/activerecord/test/models/topic.rb b/activerecord/test/models/topic.rb index 72699046f9..4aad6a4498 100644 --- a/activerecord/test/models/topic.rb +++ b/activerecord/test/models/topic.rb @@ -24,7 +24,7 @@ class Topic < ActiveRecord::Base end scope "approved_as_string", -> { where(approved: true) } - scope :anonymous_extension, -> {} do + scope :anonymous_extension, -> { } do def one 1 end diff --git a/activerecord/test/schema/mysql2_specific_schema.rb b/activerecord/test/schema/mysql2_specific_schema.rb index 8371ba9528..499280cb0c 100644 --- a/activerecord/test/schema/mysql2_specific_schema.rb +++ b/activerecord/test/schema/mysql2_specific_schema.rb @@ -14,6 +14,13 @@ ActiveRecord::Schema.define do end end + 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| t.binary :var_binary, limit: 255 t.binary :var_binary_large, limit: 4095 @@ -29,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 @@ -39,8 +46,8 @@ ActiveRecord::Schema.define do end create_table :collation_tests, id: false, force: true do |t| - t.string :string_cs_column, limit: 1, collation: "utf8_bin" - t.string :string_ci_column, limit: 1, collation: "utf8_general_ci" + t.string :string_cs_column, limit: 1, collation: "utf8mb4_bin" + t.string :string_ci_column, limit: 1, collation: "utf8mb4_general_ci" t.binary :binary_column, limit: 1 end diff --git a/activerecord/test/schema/schema.rb b/activerecord/test/schema/schema.rb index 266e55f682..2aaf393009 100644 --- a/activerecord/test/schema/schema.rb +++ b/activerecord/test/schema/schema.rb @@ -160,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| @@ -946,7 +947,7 @@ ActiveRecord::Schema.define do end [:circles, :squares, :triangles, :non_poly_ones, :non_poly_twos].each do |t| - create_table(t, force: true) {} + create_table(t, force: true) { } end create_table :men, 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 |