aboutsummaryrefslogtreecommitdiffstats
path: root/activerecord/test/cases
diff options
context:
space:
mode:
Diffstat (limited to 'activerecord/test/cases')
-rw-r--r--activerecord/test/cases/adapters/mysql2/annotate_test.rb37
-rw-r--r--activerecord/test/cases/adapters/mysql2/enum_test.rb14
-rw-r--r--activerecord/test/cases/adapters/mysql2/mysql2_adapter_test.rb27
-rw-r--r--activerecord/test/cases/adapters/mysql2/set_test.rb32
-rw-r--r--activerecord/test/cases/adapters/mysql2/transaction_test.rb6
-rw-r--r--activerecord/test/cases/adapters/postgresql/annotate_test.rb37
-rw-r--r--activerecord/test/cases/adapters/postgresql/money_test.rb4
-rw-r--r--activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb18
-rw-r--r--activerecord/test/cases/adapters/sqlite3/annotate_test.rb37
-rw-r--r--activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb16
-rw-r--r--activerecord/test/cases/annotate_test.rb46
-rw-r--r--activerecord/test/cases/arel/attributes/attribute_test.rb24
-rw-r--r--activerecord/test/cases/arel/nodes/node_test.rb21
-rw-r--r--activerecord/test/cases/arel/select_manager_test.rb12
-rw-r--r--activerecord/test/cases/arel/visitors/depth_first_test.rb276
-rw-r--r--activerecord/test/cases/arel/visitors/dispatch_contamination_test.rb8
-rw-r--r--activerecord/test/cases/arel/visitors/mysql_test.rb46
-rw-r--r--activerecord/test/cases/associations/belongs_to_associations_test.rb7
-rw-r--r--activerecord/test/cases/associations/has_and_belongs_to_many_associations_test.rb11
-rw-r--r--activerecord/test/cases/associations/has_one_associations_test.rb25
-rw-r--r--activerecord/test/cases/associations/inner_join_association_test.rb47
-rw-r--r--activerecord/test/cases/autosave_association_test.rb57
-rw-r--r--activerecord/test/cases/batches_test.rb2
-rw-r--r--activerecord/test/cases/calculations_test.rb91
-rw-r--r--activerecord/test/cases/connection_adapters/connection_handler_test.rb2
-rw-r--r--activerecord/test/cases/connection_adapters/merge_and_resolve_default_url_config_test.rb134
-rw-r--r--activerecord/test/cases/connection_adapters/mysql_type_lookup_test.rb2
-rw-r--r--activerecord/test/cases/database_configurations_test.rb13
-rw-r--r--activerecord/test/cases/database_selector_test.rb34
-rw-r--r--activerecord/test/cases/dirty_test.rb6
-rw-r--r--activerecord/test/cases/enum_test.rb24
-rw-r--r--activerecord/test/cases/finder_test.rb8
-rw-r--r--activerecord/test/cases/fixtures_test.rb29
-rw-r--r--activerecord/test/cases/inheritance_test.rb4
-rw-r--r--activerecord/test/cases/insert_all_test.rb18
-rw-r--r--activerecord/test/cases/query_cache_test.rb17
-rw-r--r--activerecord/test/cases/relation_test.rb7
-rw-r--r--activerecord/test/cases/relations_test.rb6
-rw-r--r--activerecord/test/cases/tasks/mysql_rake_test.rb7
-rw-r--r--activerecord/test/cases/tasks/postgresql_rake_test.rb2
-rw-r--r--activerecord/test/cases/validations/i18n_validation_test.rb4
41 files changed, 755 insertions, 463 deletions
diff --git a/activerecord/test/cases/adapters/mysql2/annotate_test.rb b/activerecord/test/cases/adapters/mysql2/annotate_test.rb
deleted file mode 100644
index b512540073..0000000000
--- a/activerecord/test/cases/adapters/mysql2/annotate_test.rb
+++ /dev/null
@@ -1,37 +0,0 @@
-# frozen_string_literal: true
-
-require "cases/helper"
-require "models/post"
-
-class Mysql2AnnotateTest < ActiveRecord::Mysql2TestCase
- fixtures :posts
-
- def test_annotate_wraps_content_in_an_inline_comment
- assert_sql(%r{\ASELECT `posts`\.`id` FROM `posts` /\* foo \*/}) do
- posts = Post.select(:id).annotate("foo")
- assert posts.first
- end
- end
-
- def test_annotate_is_sanitized
- assert_sql(%r{\ASELECT `posts`\.`id` FROM `posts` /\* foo \*/}) do
- posts = Post.select(:id).annotate("*/foo/*")
- assert posts.first
- end
-
- assert_sql(%r{\ASELECT `posts`\.`id` FROM `posts` /\* foo \*/}) do
- posts = Post.select(:id).annotate("**//foo//**")
- assert posts.first
- end
-
- assert_sql(%r{\ASELECT `posts`\.`id` FROM `posts` /\* foo \*/ /\* bar \*/}) do
- posts = Post.select(:id).annotate("*/foo/*").annotate("*/bar")
- assert posts.first
- end
-
- assert_sql(%r{\ASELECT `posts`\.`id` FROM `posts` /\* \+ MAX_EXECUTION_TIME\(1\) \*/}) do
- posts = Post.select(:id).annotate("+ MAX_EXECUTION_TIME(1)")
- assert posts.first
- end
- end
-end
diff --git a/activerecord/test/cases/adapters/mysql2/enum_test.rb b/activerecord/test/cases/adapters/mysql2/enum_test.rb
index 832f5d61d1..1168b3677e 100644
--- a/activerecord/test/cases/adapters/mysql2/enum_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/enum_test.rb
@@ -1,11 +1,20 @@
# frozen_string_literal: true
require "cases/helper"
+require "support/schema_dumping_helper"
class Mysql2EnumTest < ActiveRecord::Mysql2TestCase
+ include SchemaDumpingHelper
+
class EnumTest < ActiveRecord::Base
end
+ def setup
+ EnumTest.connection.create_table :enum_tests, id: false, force: true do |t|
+ t.column :enum_column, "enum('text','blob','tiny','medium','long','unsigned','bigint')"
+ end
+ end
+
def test_enum_limit
column = EnumTest.columns_hash["enum_column"]
assert_equal 8, column.limit
@@ -20,4 +29,9 @@ class Mysql2EnumTest < ActiveRecord::Mysql2TestCase
column = EnumTest.columns_hash["enum_column"]
assert_not_predicate column, :bigint?
end
+
+ def test_schema_dumping
+ schema = dump_table_schema "enum_tests"
+ assert_match %r{t\.column "enum_column", "enum\('text','blob','tiny','medium','long','unsigned','bigint'\)"$}, schema
+ end
end
diff --git a/activerecord/test/cases/adapters/mysql2/mysql2_adapter_test.rb b/activerecord/test/cases/adapters/mysql2/mysql2_adapter_test.rb
index e1f7a0b7c5..cfc1823773 100644
--- a/activerecord/test/cases/adapters/mysql2/mysql2_adapter_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/mysql2_adapter_test.rb
@@ -20,6 +20,18 @@ class Mysql2AdapterTest < ActiveRecord::Mysql2TestCase
end
end
+ def test_database_exists_returns_false_if_database_does_not_exist
+ config = ActiveRecord::Base.configurations["arunit"].merge(database: "inexistent_activerecord_unittest")
+ assert_not ActiveRecord::ConnectionAdapters::Mysql2Adapter.database_exists?(config),
+ "expected database to not exist"
+ end
+
+ def test_database_exists_returns_true_when_the_database_exists
+ config = ActiveRecord::Base.configurations["arunit"]
+ assert ActiveRecord::ConnectionAdapters::Mysql2Adapter.database_exists?(config),
+ "expected database #{config[:database]} to exist"
+ end
+
def test_columns_for_distinct_zero_orders
assert_equal "posts.id",
@conn.columns_for_distinct("posts.id", [])
@@ -213,6 +225,21 @@ class Mysql2AdapterTest < ActiveRecord::Mysql2TestCase
end
end
+ def test_read_timeout_exception
+ ActiveRecord::Base.establish_connection(
+ ActiveRecord::Base.configurations[:arunit].merge("read_timeout" => 1)
+ )
+
+ error = assert_raises(ActiveRecord::AdapterTimeout) do
+ ActiveRecord::Base.connection.execute("SELECT SLEEP(2)")
+ end
+ assert_kind_of ActiveRecord::QueryAborted, error
+
+ assert_equal Mysql2::Error::TimeoutError, error.cause.class
+ ensure
+ ActiveRecord::Base.establish_connection :arunit
+ end
+
private
def with_example_table(definition = "id int auto_increment primary key, number int, data varchar(255)", &block)
super(@conn, "ex", definition, &block)
diff --git a/activerecord/test/cases/adapters/mysql2/set_test.rb b/activerecord/test/cases/adapters/mysql2/set_test.rb
new file mode 100644
index 0000000000..89107e142f
--- /dev/null
+++ b/activerecord/test/cases/adapters/mysql2/set_test.rb
@@ -0,0 +1,32 @@
+# frozen_string_literal: true
+
+require "cases/helper"
+require "support/schema_dumping_helper"
+
+class Mysql2SetTest < ActiveRecord::Mysql2TestCase
+ include SchemaDumpingHelper
+
+ class SetTest < ActiveRecord::Base
+ end
+
+ def setup
+ SetTest.connection.create_table :set_tests, id: false, force: true do |t|
+ t.column :set_column, "set('text','blob','tiny','medium','long','unsigned','bigint')"
+ end
+ end
+
+ def test_should_not_be_unsigned
+ column = SetTest.columns_hash["set_column"]
+ assert_not_predicate column, :unsigned?
+ end
+
+ def test_should_not_be_bigint
+ column = SetTest.columns_hash["set_column"]
+ assert_not_predicate column, :bigint?
+ end
+
+ def test_schema_dumping
+ schema = dump_table_schema "set_tests"
+ assert_match %r{t\.column "set_column", "set\('text','blob','tiny','medium','long','unsigned','bigint'\)"$}, schema
+ end
+end
diff --git a/activerecord/test/cases/adapters/mysql2/transaction_test.rb b/activerecord/test/cases/adapters/mysql2/transaction_test.rb
index 52e283f247..2041cc308f 100644
--- a/activerecord/test/cases/adapters/mysql2/transaction_test.rb
+++ b/activerecord/test/cases/adapters/mysql2/transaction_test.rb
@@ -92,7 +92,7 @@ module ActiveRecord
test "raises StatementTimeout when statement timeout exceeded" do
skip unless ActiveRecord::Base.connection.show_variable("max_execution_time")
- assert_raises(ActiveRecord::StatementTimeout) do
+ error = assert_raises(ActiveRecord::StatementTimeout) do
s = Sample.create!(value: 1)
latch1 = Concurrent::CountDownLatch.new
latch2 = Concurrent::CountDownLatch.new
@@ -117,10 +117,11 @@ module ActiveRecord
thread.join
end
end
+ assert_kind_of ActiveRecord::QueryAborted, error
end
test "raises QueryCanceled when canceling statement due to user request" do
- assert_raises(ActiveRecord::QueryCanceled) do
+ error = assert_raises(ActiveRecord::QueryCanceled) do
s = Sample.create!(value: 1)
latch = Concurrent::CountDownLatch.new
@@ -144,6 +145,7 @@ module ActiveRecord
thread.join
end
end
+ assert_kind_of ActiveRecord::QueryAborted, error
end
end
end
diff --git a/activerecord/test/cases/adapters/postgresql/annotate_test.rb b/activerecord/test/cases/adapters/postgresql/annotate_test.rb
deleted file mode 100644
index 42a2861511..0000000000
--- a/activerecord/test/cases/adapters/postgresql/annotate_test.rb
+++ /dev/null
@@ -1,37 +0,0 @@
-# frozen_string_literal: true
-
-require "cases/helper"
-require "models/post"
-
-class PostgresqlAnnotateTest < ActiveRecord::PostgreSQLTestCase
- fixtures :posts
-
- def test_annotate_wraps_content_in_an_inline_comment
- assert_sql(%r{\ASELECT "posts"\."id" FROM "posts" /\* foo \*/}) do
- posts = Post.select(:id).annotate("foo")
- assert posts.first
- end
- end
-
- def test_annotate_is_sanitized
- assert_sql(%r{\ASELECT "posts"\."id" FROM "posts" /\* foo \*/}) do
- posts = Post.select(:id).annotate("*/foo/*")
- assert posts.first
- end
-
- assert_sql(%r{\ASELECT "posts"\."id" FROM "posts" /\* foo \*/}) do
- posts = Post.select(:id).annotate("**//foo//**")
- assert posts.first
- end
-
- assert_sql(%r{\ASELECT "posts"\."id" FROM "posts" /\* foo \*/ /\* bar \*/}) do
- posts = Post.select(:id).annotate("*/foo/*").annotate("*/bar")
- assert posts.first
- end
-
- assert_sql(%r{\ASELECT "posts"\."id" FROM "posts" /\* \+ MAX_EXECUTION_TIME\(1\) \*/}) do
- posts = Post.select(:id).annotate("+ MAX_EXECUTION_TIME(1)")
- assert posts.first
- end
- end
-end
diff --git a/activerecord/test/cases/adapters/postgresql/money_test.rb b/activerecord/test/cases/adapters/postgresql/money_test.rb
index 1aa0348879..ff2ab22a80 100644
--- a/activerecord/test/cases/adapters/postgresql/money_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/money_test.rb
@@ -54,8 +54,12 @@ class PostgresqlMoneyTest < ActiveRecord::PostgreSQLTestCase
type = PostgresqlMoney.type_for_attribute("wealth")
assert_equal(12345678.12, type.cast(+"$12,345,678.12"))
assert_equal(12345678.12, type.cast(+"$12.345.678,12"))
+ 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)"))
+ assert_equal(-1.15, type.cast(+"-1.15"))
+ assert_equal(-2.25, type.cast(+"(2.25)"))
end
def test_schema_dumping
diff --git a/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb b/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb
index 68bc87eaf8..830c0892d3 100644
--- a/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb
+++ b/activerecord/test/cases/adapters/postgresql/postgresql_adapter_test.rb
@@ -24,6 +24,18 @@ module ActiveRecord
end
end
+ def test_database_exists_returns_false_when_the_database_does_not_exist
+ config = { database: "non_extant_database", adapter: "postgresql" }
+ assert_not ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.database_exists?(config),
+ "expected database #{config[:database]} to not exist"
+ end
+
+ def test_database_exists_returns_true_when_the_database_exists
+ config = ActiveRecord::Base.configurations["arunit"]
+ assert ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.database_exists?(config),
+ "expected database #{config[:database]} to exist"
+ end
+
def test_primary_key
with_example_table do
assert_equal "id", @connection.primary_key("ex")
@@ -241,9 +253,11 @@ module ActiveRecord
def test_expression_index
with_example_table do
- @connection.add_index "ex", "mod(id, 10), abs(number)", name: "expression"
+ expr = "mod(id, 10), abs(number)"
+ @connection.add_index "ex", expr, name: "expression"
index = @connection.indexes("ex").find { |idx| idx.name == "expression" }
- assert_equal "mod(id, 10), abs(number)", index.columns
+ assert_equal expr, index.columns
+ assert_equal true, @connection.index_exists?("ex", expr, name: "expression")
end
end
diff --git a/activerecord/test/cases/adapters/sqlite3/annotate_test.rb b/activerecord/test/cases/adapters/sqlite3/annotate_test.rb
deleted file mode 100644
index 6567a5eca3..0000000000
--- a/activerecord/test/cases/adapters/sqlite3/annotate_test.rb
+++ /dev/null
@@ -1,37 +0,0 @@
-# frozen_string_literal: true
-
-require "cases/helper"
-require "models/post"
-
-class SQLite3AnnotateTest < ActiveRecord::SQLite3TestCase
- fixtures :posts
-
- def test_annotate_wraps_content_in_an_inline_comment
- assert_sql(%r{\ASELECT "posts"\."id" FROM "posts" /\* foo \*/}) do
- posts = Post.select(:id).annotate("foo")
- assert posts.first
- end
- end
-
- def test_annotate_is_sanitized
- assert_sql(%r{\ASELECT "posts"\."id" FROM "posts" /\* foo \*/}) do
- posts = Post.select(:id).annotate("*/foo/*")
- assert posts.first
- end
-
- assert_sql(%r{\ASELECT "posts"\."id" FROM "posts" /\* foo \*/}) do
- posts = Post.select(:id).annotate("**//foo//**")
- assert posts.first
- end
-
- assert_sql(%r{\ASELECT "posts"\."id" FROM "posts" /\* foo \*/ /\* bar \*/}) do
- posts = Post.select(:id).annotate("*/foo/*").annotate("*/bar")
- assert posts.first
- end
-
- assert_sql(%r{\ASELECT "posts"\."id" FROM "posts" /\* \+ MAX_EXECUTION_TIME\(1\) \*/}) do
- posts = Post.select(:id).annotate("+ MAX_EXECUTION_TIME(1)")
- assert posts.first
- end
- end
-end
diff --git a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb
index 508f7d8945..b6d72c7bcd 100644
--- a/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb
+++ b/activerecord/test/cases/adapters/sqlite3/sqlite3_adapter_test.rb
@@ -30,6 +30,17 @@ module ActiveRecord
end
end
+ def test_database_exists_returns_false_when_the_database_does_not_exist
+ assert_not SQLite3Adapter.database_exists?(adapter: "sqlite3", database: "non_extant_db"),
+ "expected non_extant_db to not exist"
+ end
+
+ def test_database_exists_returns_true_when_databae_exists
+ config = ActiveRecord::Base.configurations["arunit"]
+ assert SQLite3Adapter.database_exists?(config),
+ "expected #{config[:database]} to exist"
+ end
+
unless in_memory_db?
def test_connect_with_url
original_connection = ActiveRecord::Base.remove_connection
@@ -53,6 +64,11 @@ module ActiveRecord
end
end
+ def test_database_exists_returns_true_for_an_in_memory_db
+ assert SQLite3Adapter.database_exists?(database: ":memory:"),
+ "Expected in memory database to exist"
+ end
+
def test_column_types
owner = Owner.create!(name: "hello".encode("ascii-8bit"))
owner.reload
diff --git a/activerecord/test/cases/annotate_test.rb b/activerecord/test/cases/annotate_test.rb
new file mode 100644
index 0000000000..4d71d28f83
--- /dev/null
+++ b/activerecord/test/cases/annotate_test.rb
@@ -0,0 +1,46 @@
+# frozen_string_literal: true
+
+require "cases/helper"
+require "models/post"
+
+class AnnotateTest < ActiveRecord::TestCase
+ fixtures :posts
+
+ def test_annotate_wraps_content_in_an_inline_comment
+ quoted_posts_id, quoted_posts = regexp_escape_table_name("posts.id"), regexp_escape_table_name("posts")
+
+ assert_sql(%r{\ASELECT #{quoted_posts_id} FROM #{quoted_posts} /\* foo \*/}i) do
+ posts = Post.select(:id).annotate("foo")
+ assert posts.first
+ end
+ end
+
+ def test_annotate_is_sanitized
+ quoted_posts_id, quoted_posts = regexp_escape_table_name("posts.id"), regexp_escape_table_name("posts")
+
+ assert_sql(%r{\ASELECT #{quoted_posts_id} FROM #{quoted_posts} /\* foo \*/}i) do
+ posts = Post.select(:id).annotate("*/foo/*")
+ assert posts.first
+ end
+
+ assert_sql(%r{\ASELECT #{quoted_posts_id} FROM #{quoted_posts} /\* foo \*/}i) do
+ posts = Post.select(:id).annotate("**//foo//**")
+ assert posts.first
+ end
+
+ assert_sql(%r{\ASELECT #{quoted_posts_id} FROM #{quoted_posts} /\* foo \*/ /\* bar \*/}i) do
+ posts = Post.select(:id).annotate("*/foo/*").annotate("*/bar")
+ assert posts.first
+ end
+
+ assert_sql(%r{\ASELECT #{quoted_posts_id} FROM #{quoted_posts} /\* \+ MAX_EXECUTION_TIME\(1\) \*/}i) do
+ posts = Post.select(:id).annotate("+ MAX_EXECUTION_TIME(1)")
+ assert posts.first
+ end
+ end
+
+ private
+ def regexp_escape_table_name(name)
+ Regexp.escape(Post.connection.quote_table_name(name))
+ end
+end
diff --git a/activerecord/test/cases/arel/attributes/attribute_test.rb b/activerecord/test/cases/arel/attributes/attribute_test.rb
index c7bd0a053b..7ebb90c6fd 100644
--- a/activerecord/test/cases/arel/attributes/attribute_test.rb
+++ b/activerecord/test/cases/arel/attributes/attribute_test.rb
@@ -638,6 +638,18 @@ module Arel
)
end
+ if Gem::Version.new("2.7.0") <= Gem::Version.new(RUBY_VERSION)
+ it "can be constructed with a range implicitly starting at Infinity" do
+ attribute = Attribute.new nil, nil
+ node = attribute.between(eval("..0")) # eval for backwards compatibility
+
+ node.must_equal Nodes::LessThanOrEqual.new(
+ attribute,
+ Nodes::Casted.new(0, attribute)
+ )
+ end
+ end
+
if Gem::Version.new("2.6.0") <= Gem::Version.new(RUBY_VERSION)
it "can be constructed with a range implicitly ending at Infinity" do
attribute = Attribute.new nil, nil
@@ -839,6 +851,18 @@ module Arel
)
end
+ if Gem::Version.new("2.7.0") <= Gem::Version.new(RUBY_VERSION)
+ it "can be constructed with a range implicitly starting at Infinity" do
+ attribute = Attribute.new nil, nil
+ node = attribute.not_between(eval("..0")) # eval for backwards compatibility
+
+ node.must_equal Nodes::GreaterThan.new(
+ attribute,
+ Nodes::Casted.new(0, attribute)
+ )
+ end
+ end
+
if Gem::Version.new("2.6.0") <= Gem::Version.new(RUBY_VERSION)
it "can be constructed with a range implicitly ending at Infinity" do
attribute = Attribute.new nil, nil
diff --git a/activerecord/test/cases/arel/nodes/node_test.rb b/activerecord/test/cases/arel/nodes/node_test.rb
index f4f07ef2c5..8a9ecd84ca 100644
--- a/activerecord/test/cases/arel/nodes/node_test.rb
+++ b/activerecord/test/cases/arel/nodes/node_test.rb
@@ -14,28 +14,9 @@ module Arel
}.grep(Class).each do |klass|
next if Nodes::SqlLiteral == klass
next if Nodes::BindParam == klass
- next if klass.name =~ /^Arel::Nodes::(?:Test|.*Test$)/
+ next if /^Arel::Nodes::(?:Test|.*Test$)/.match?(klass.name)
assert klass.ancestors.include?(Nodes::Node), klass.name
end
end
-
- def test_each
- list = []
- node = Nodes::Node.new
- node.each { |n| list << n }
- assert_equal [node], list
- end
-
- def test_generator
- list = []
- node = Nodes::Node.new
- node.each.each { |n| list << n }
- assert_equal [node], list
- end
-
- def test_enumerable
- node = Nodes::Node.new
- assert_kind_of Enumerable, node
- end
end
end
diff --git a/activerecord/test/cases/arel/select_manager_test.rb b/activerecord/test/cases/arel/select_manager_test.rb
index e6c49cd429..4512d8e8a6 100644
--- a/activerecord/test/cases/arel/select_manager_test.rb
+++ b/activerecord/test/cases/arel/select_manager_test.rb
@@ -369,16 +369,6 @@ module Arel
mgr = table.from
assert mgr.ast
end
-
- it "should allow orders to work when the ast is grepped" do
- table = Table.new :users
- mgr = table.from
- mgr.project Arel.sql "*"
- mgr.from table
- mgr.orders << Arel::Nodes::Ascending.new(Arel.sql("foo"))
- mgr.ast.grep(Arel::Nodes::OuterJoin)
- mgr.to_sql.must_be_like %{ SELECT * FROM "users" ORDER BY foo ASC }
- end
end
describe "taken" do
@@ -524,7 +514,7 @@ module Arel
assert_equal "bar", join.right
end
- it "should create join nodes with a outer join klass" do
+ it "should create join nodes with an outer join klass" do
relation = Arel::SelectManager.new
join = relation.create_join "foo", "bar", Arel::Nodes::OuterJoin
assert_kind_of Arel::Nodes::OuterJoin, join
diff --git a/activerecord/test/cases/arel/visitors/depth_first_test.rb b/activerecord/test/cases/arel/visitors/depth_first_test.rb
deleted file mode 100644
index 106be2311d..0000000000
--- a/activerecord/test/cases/arel/visitors/depth_first_test.rb
+++ /dev/null
@@ -1,276 +0,0 @@
-# frozen_string_literal: true
-
-require_relative "../helper"
-
-module Arel
- module Visitors
- class TestDepthFirst < Arel::Test
- Collector = Struct.new(:calls) do
- def call(object)
- calls << object
- end
- end
-
- def setup
- @collector = Collector.new []
- @visitor = Visitors::DepthFirst.new @collector
- end
-
- def test_raises_with_object
- assert_raises(TypeError) do
- @visitor.accept(Object.new)
- end
- end
-
-
- # unary ops
- [
- Arel::Nodes::Not,
- Arel::Nodes::Group,
- Arel::Nodes::On,
- Arel::Nodes::Grouping,
- Arel::Nodes::Offset,
- Arel::Nodes::Ordering,
- Arel::Nodes::StringJoin,
- Arel::Nodes::UnqualifiedColumn,
- Arel::Nodes::ValuesList,
- Arel::Nodes::Limit,
- Arel::Nodes::Else,
- ].each do |klass|
- define_method("test_#{klass.name.gsub('::', '_')}") do
- op = klass.new(:a)
- @visitor.accept op
- assert_equal [:a, op], @collector.calls
- end
- end
-
- # functions
- [
- Arel::Nodes::Exists,
- Arel::Nodes::Avg,
- Arel::Nodes::Min,
- Arel::Nodes::Max,
- Arel::Nodes::Sum,
- ].each do |klass|
- define_method("test_#{klass.name.gsub('::', '_')}") do
- func = klass.new(:a, "b")
- @visitor.accept func
- assert_equal [:a, "b", false, func], @collector.calls
- end
- end
-
- def test_named_function
- func = Arel::Nodes::NamedFunction.new(:a, :b, "c")
- @visitor.accept func
- assert_equal [:a, :b, false, "c", func], @collector.calls
- end
-
- def test_lock
- lock = Nodes::Lock.new true
- @visitor.accept lock
- assert_equal [lock], @collector.calls
- end
-
- def test_count
- count = Nodes::Count.new :a, :b, "c"
- @visitor.accept count
- assert_equal [:a, "c", :b, count], @collector.calls
- end
-
- def test_inner_join
- join = Nodes::InnerJoin.new :a, :b
- @visitor.accept join
- assert_equal [:a, :b, join], @collector.calls
- end
-
- def test_full_outer_join
- join = Nodes::FullOuterJoin.new :a, :b
- @visitor.accept join
- assert_equal [:a, :b, join], @collector.calls
- end
-
- def test_outer_join
- join = Nodes::OuterJoin.new :a, :b
- @visitor.accept join
- assert_equal [:a, :b, join], @collector.calls
- end
-
- def test_right_outer_join
- join = Nodes::RightOuterJoin.new :a, :b
- @visitor.accept join
- assert_equal [:a, :b, join], @collector.calls
- end
-
- def test_comment
- comment = Nodes::Comment.new ["foo"]
- @visitor.accept comment
- assert_equal ["foo", ["foo"], comment], @collector.calls
- end
-
- [
- Arel::Nodes::Assignment,
- Arel::Nodes::Between,
- Arel::Nodes::Concat,
- Arel::Nodes::DoesNotMatch,
- Arel::Nodes::Equality,
- Arel::Nodes::GreaterThan,
- Arel::Nodes::GreaterThanOrEqual,
- Arel::Nodes::In,
- Arel::Nodes::LessThan,
- Arel::Nodes::LessThanOrEqual,
- Arel::Nodes::Matches,
- Arel::Nodes::NotEqual,
- Arel::Nodes::NotIn,
- Arel::Nodes::Or,
- Arel::Nodes::TableAlias,
- Arel::Nodes::As,
- Arel::Nodes::DeleteStatement,
- Arel::Nodes::JoinSource,
- Arel::Nodes::When,
- ].each do |klass|
- define_method("test_#{klass.name.gsub('::', '_')}") do
- binary = klass.new(:a, :b)
- @visitor.accept binary
- assert_equal [:a, :b, binary], @collector.calls
- end
- end
-
- def test_Arel_Nodes_InfixOperation
- binary = Arel::Nodes::InfixOperation.new(:o, :a, :b)
- @visitor.accept binary
- assert_equal [:a, :b, binary], @collector.calls
- end
-
- # N-ary
- [
- Arel::Nodes::And,
- ].each do |klass|
- define_method("test_#{klass.name.gsub('::', '_')}") do
- binary = klass.new([:a, :b, :c])
- @visitor.accept binary
- assert_equal [:a, :b, :c, binary], @collector.calls
- end
- end
-
- [
- Arel::Attributes::Integer,
- Arel::Attributes::Float,
- Arel::Attributes::String,
- Arel::Attributes::Time,
- Arel::Attributes::Boolean,
- Arel::Attributes::Attribute
- ].each do |klass|
- define_method("test_#{klass.name.gsub('::', '_')}") do
- binary = klass.new(:a, :b)
- @visitor.accept binary
- assert_equal [:a, :b, binary], @collector.calls
- end
- end
-
- def test_table
- relation = Arel::Table.new(:users)
- @visitor.accept relation
- assert_equal ["users", relation], @collector.calls
- end
-
- def test_array
- node = Nodes::Or.new(:a, :b)
- list = [node]
- @visitor.accept list
- assert_equal [:a, :b, node, list], @collector.calls
- end
-
- def test_set
- node = Nodes::Or.new(:a, :b)
- set = Set.new([node])
- @visitor.accept set
- assert_equal [:a, :b, node, set], @collector.calls
- end
-
- def test_hash
- node = Nodes::Or.new(:a, :b)
- hash = { node => node }
- @visitor.accept hash
- assert_equal [:a, :b, node, :a, :b, node, hash], @collector.calls
- end
-
- def test_update_statement
- stmt = Nodes::UpdateStatement.new
- stmt.relation = :a
- stmt.values << :b
- stmt.wheres << :c
- stmt.orders << :d
- stmt.limit = :e
-
- @visitor.accept stmt
- assert_equal [:a, :b, stmt.values, :c, stmt.wheres, :d, stmt.orders,
- :e, stmt], @collector.calls
- end
-
- def test_select_core
- core = Nodes::SelectCore.new
- core.projections << :a
- core.froms = :b
- core.wheres << :c
- core.groups << :d
- core.windows << :e
- core.havings << :f
-
- @visitor.accept core
- assert_equal [
- :a, core.projections,
- :b, [],
- core.source,
- :c, core.wheres,
- :d, core.groups,
- :e, core.windows,
- :f, core.havings,
- core], @collector.calls
- end
-
- def test_select_statement
- ss = Nodes::SelectStatement.new
- ss.cores.replace [:a]
- ss.orders << :b
- ss.limit = :c
- ss.lock = :d
- ss.offset = :e
-
- @visitor.accept ss
- assert_equal [
- :a, ss.cores,
- :b, ss.orders,
- :c,
- :d,
- :e,
- ss], @collector.calls
- end
-
- def test_insert_statement
- stmt = Nodes::InsertStatement.new
- stmt.relation = :a
- stmt.columns << :b
- stmt.values = :c
-
- @visitor.accept stmt
- assert_equal [:a, :b, stmt.columns, :c, stmt], @collector.calls
- end
-
- def test_case
- node = Arel::Nodes::Case.new
- node.case = :a
- node.conditions << :b
- node.default = :c
-
- @visitor.accept node
- assert_equal [:a, :b, node.conditions, :c, node], @collector.calls
- end
-
- def test_node
- node = Nodes::Node.new
- @visitor.accept node
- assert_equal [node], @collector.calls
- end
- end
- end
-end
diff --git a/activerecord/test/cases/arel/visitors/dispatch_contamination_test.rb b/activerecord/test/cases/arel/visitors/dispatch_contamination_test.rb
index a07a1a050a..36f9eb49a2 100644
--- a/activerecord/test/cases/arel/visitors/dispatch_contamination_test.rb
+++ b/activerecord/test/cases/arel/visitors/dispatch_contamination_test.rb
@@ -48,7 +48,13 @@ module Arel
node = Nodes::Union.new(Nodes::True.new, Nodes::False.new)
assert_equal "( TRUE UNION FALSE )", node.to_sql
- node.first # from Nodes::Node's Enumerable mixin
+ visitor = Class.new(Visitor) {
+ def visit_Arel_Nodes_Union(o); end
+ alias :visit_Arel_Nodes_True :visit_Arel_Nodes_Union
+ alias :visit_Arel_Nodes_False :visit_Arel_Nodes_Union
+ }.new
+
+ visitor.accept(node)
assert_equal "( TRUE UNION FALSE )", node.to_sql
end
diff --git a/activerecord/test/cases/arel/visitors/mysql_test.rb b/activerecord/test/cases/arel/visitors/mysql_test.rb
index 5f37587957..05dccd126e 100644
--- a/activerecord/test/cases/arel/visitors/mysql_test.rb
+++ b/activerecord/test/cases/arel/visitors/mysql_test.rb
@@ -104,6 +104,52 @@ module Arel
sql.must_be_like %{ NOT "users"."name" <=> NULL }
end
end
+
+ describe "Nodes::Regexp" do
+ before do
+ @table = Table.new(:users)
+ @attr = @table[:id]
+ end
+
+ it "should know how to visit" do
+ node = @table[:name].matches_regexp("foo.*")
+ node.must_be_kind_of Nodes::Regexp
+ compile(node).must_be_like %{
+ "users"."name" REGEXP 'foo.*'
+ }
+ end
+
+ it "can handle subqueries" do
+ subquery = @table.project(:id).where(@table[:name].matches_regexp("foo.*"))
+ node = @attr.in subquery
+ compile(node).must_be_like %{
+ "users"."id" IN (SELECT id FROM "users" WHERE "users"."name" REGEXP 'foo.*')
+ }
+ end
+ end
+
+ describe "Nodes::NotRegexp" do
+ before do
+ @table = Table.new(:users)
+ @attr = @table[:id]
+ end
+
+ it "should know how to visit" do
+ node = @table[:name].does_not_match_regexp("foo.*")
+ node.must_be_kind_of Nodes::NotRegexp
+ compile(node).must_be_like %{
+ "users"."name" NOT REGEXP 'foo.*'
+ }
+ end
+
+ it "can handle subqueries" do
+ subquery = @table.project(:id).where(@table[:name].does_not_match_regexp("foo.*"))
+ node = @attr.in subquery
+ compile(node).must_be_like %{
+ "users"."id" IN (SELECT id FROM "users" WHERE "users"."name" NOT REGEXP 'foo.*')
+ }
+ end
+ end
end
end
end
diff --git a/activerecord/test/cases/associations/belongs_to_associations_test.rb b/activerecord/test/cases/associations/belongs_to_associations_test.rb
index 3525fa2ab8..6bd305306f 100644
--- a/activerecord/test/cases/associations/belongs_to_associations_test.rb
+++ b/activerecord/test/cases/associations/belongs_to_associations_test.rb
@@ -60,11 +60,8 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
end
def test_belongs_to_does_not_use_order_by
- ActiveRecord::SQLCounter.clear_log
- Client.find(3).firm
- ensure
- sql_log = ActiveRecord::SQLCounter.log
- assert sql_log.all? { |sql| /order by/i !~ sql }, "ORDER BY was used in the query: #{sql_log}"
+ sql_log = capture_sql { Client.find(3).firm }
+ assert sql_log.all? { |sql| !/order by/i.match?(sql) }, "ORDER BY was used in the query: #{sql_log}"
end
def test_belongs_to_with_primary_key
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 25cfa0a723..de9742b250 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
@@ -700,10 +700,17 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
assert_equal ["id"], developers(:david).projects.select(:id).first.attributes.keys
end
+ def test_join_middle_table_alias
+ assert_equal(
+ 2,
+ Project.includes(:developers_projects).where.not("developers_projects.joined_on": nil).to_a.size
+ )
+ end
+
def test_join_table_alias
assert_equal(
3,
- Developer.includes(projects: :developers).where.not("projects_developers_projects_join.joined_on": nil).to_a.size
+ Developer.includes(projects: :developers).where.not("developers_projects_projects_join.joined_on": nil).to_a.size
)
end
@@ -716,7 +723,7 @@ class HasAndBelongsToManyAssociationsTest < ActiveRecord::TestCase
assert_equal(
3,
- Developer.includes(projects: :developers).where.not("projects_developers_projects_join.joined_on": nil).group(group.join(",")).to_a.size
+ Developer.includes(projects: :developers).where.not("developers_projects_projects_join.joined_on": nil).group(group.join(",")).to_a.size
)
end
diff --git a/activerecord/test/cases/associations/has_one_associations_test.rb b/activerecord/test/cases/associations/has_one_associations_test.rb
index 3ef25c7027..6c2a09c296 100644
--- a/activerecord/test/cases/associations/has_one_associations_test.rb
+++ b/activerecord/test/cases/associations/has_one_associations_test.rb
@@ -37,11 +37,8 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
end
def test_has_one_does_not_use_order_by
- ActiveRecord::SQLCounter.clear_log
- companies(:first_firm).account
- ensure
- sql_log = ActiveRecord::SQLCounter.log
- assert sql_log.all? { |sql| /order by/i !~ sql }, "ORDER BY was used in the query: #{sql_log}"
+ sql_log = capture_sql { companies(:first_firm).account }
+ assert sql_log.all? { |sql| !/order by/i.match?(sql) }, "ORDER BY was used in the query: #{sql_log}"
end
def test_has_one_cache_nils
@@ -712,6 +709,24 @@ class HasOneAssociationsTest < ActiveRecord::TestCase
}
end
+ def test_polymorphic_has_one_with_touch_option_on_create_wont_cache_association_so_fetching_after_transaction_commit_works
+ assert_queries(4) {
+ chef = Chef.create(employable: DrinkDesignerWithPolymorphicTouchChef.new)
+ employable = chef.employable
+
+ assert_equal chef, employable.chef
+ }
+ end
+
+ def test_polymorphic_has_one_with_touch_option_on_update_will_touch_record_by_fetching_from_database_if_needed
+ DrinkDesignerWithPolymorphicTouchChef.create(chef: Chef.new)
+ designer = DrinkDesignerWithPolymorphicTouchChef.last
+
+ assert_queries(3) {
+ designer.update(name: "foo")
+ }
+ end
+
def test_has_one_with_touch_option_on_update
new_club = Club.create(name: "1000 Oaks")
new_club.create_membership
diff --git a/activerecord/test/cases/associations/inner_join_association_test.rb b/activerecord/test/cases/associations/inner_join_association_test.rb
index e0dac01f4a..166a59ec7b 100644
--- a/activerecord/test/cases/associations/inner_join_association_test.rb
+++ b/activerecord/test/cases/associations/inner_join_association_test.rb
@@ -13,7 +13,7 @@ require "models/tag"
class InnerJoinAssociationTest < ActiveRecord::TestCase
fixtures :authors, :author_addresses, :essays, :posts, :comments, :categories, :categories_posts, :categorizations,
- :taggings, :tags
+ :taggings, :tags, :people
def test_construct_finder_sql_applies_aliases_tables_on_association_conditions
result = Author.joins(:thinking_posts, :welcome_posts).to_a
@@ -36,16 +36,47 @@ class InnerJoinAssociationTest < ActiveRecord::TestCase
end
def test_construct_finder_sql_does_not_table_name_collide_with_string_joins
- sql = Person.joins(:agents).joins("JOIN people agents_people ON agents_people.primary_contact_id = people.id").to_sql
- assert_match(/agents_people_2/i, sql)
+ string_join = <<~SQL
+ JOIN people agents_people ON agents_people.primary_contact_id = agents_people_2.id AND agents_people.id > agents_people_2.id
+ SQL
+
+ expected = people(:susan)
+ assert_sql(/agents_people_2/i) do
+ assert_equal [expected], Person.joins(:agents).joins(string_join)
+ end
end
def test_construct_finder_sql_does_not_table_name_collide_with_aliased_joins
- people = Person.arel_table
- agents = people.alias("agents_people")
- constraint = agents[:primary_contact_id].eq(people[:id])
- sql = Person.joins(:agents).joins(agents.create_join(agents, agents.create_on(constraint))).to_sql
- assert_match(/agents_people_2/i, sql)
+ agents = Person.arel_table.alias("agents_people")
+ agents_2 = Person.arel_table.alias("agents_people_2")
+ constraint = agents[:primary_contact_id].eq(agents_2[:id]).and(agents[:id].gt(agents_2[:id]))
+
+ expected = people(:susan)
+ assert_sql(/agents_people_2/i) do
+ assert_equal [expected], Person.joins(:agents).joins(agents.create_join(agents, agents.create_on(constraint)))
+ end
+ end
+
+ def test_user_supplied_joins_order_should_be_preserved
+ string_join = <<~SQL
+ JOIN people agents_people_2 ON agents_people_2.primary_contact_id = people.id
+ SQL
+ agents = Person.arel_table.alias("agents_people")
+ agents_2 = Person.arel_table.alias("agents_people_2")
+ constraint = agents[:primary_contact_id].eq(agents_2[:id]).and(agents[:id].gt(agents_2[:id]))
+
+ expected = people(:susan)
+ assert_equal [expected], Person.joins(string_join).joins(agents.create_join(agents, agents.create_on(constraint)))
+ end
+
+ def test_deduplicate_joins
+ posts = Post.arel_table
+ constraint = posts[:author_id].eq(Author.arel_attribute(:id))
+
+ authors = Author.joins(posts.create_join(posts, posts.create_on(constraint)))
+ authors = authors.joins(:author_address).merge(authors.where("posts.type": "SpecialPost"))
+
+ assert_equal [authors(:david)], authors
end
def test_construct_finder_sql_ignores_empty_joins_hash
diff --git a/activerecord/test/cases/autosave_association_test.rb b/activerecord/test/cases/autosave_association_test.rb
index 7e61ac9d8b..3528ac045f 100644
--- a/activerecord/test/cases/autosave_association_test.rb
+++ b/activerecord/test/cases/autosave_association_test.rb
@@ -2,6 +2,7 @@
require "cases/helper"
require "models/author"
+require "models/book"
require "models/bird"
require "models/post"
require "models/comment"
@@ -12,12 +13,14 @@ require "models/developer"
require "models/computer"
require "models/invoice"
require "models/line_item"
+require "models/mouse"
require "models/order"
require "models/parrot"
require "models/pirate"
require "models/project"
require "models/ship"
require "models/ship_part"
+require "models/squeak"
require "models/tag"
require "models/tagging"
require "models/treasure"
@@ -385,6 +388,20 @@ class TestDefaultAutosaveAssociationOnABelongsToAssociation < ActiveRecord::Test
assert_predicate auditlog, :valid?
end
+
+ def test_validation_does_not_validate_non_dirty_association_target
+ mouse = Mouse.create!(name: "Will")
+ Squeak.create!(mouse: mouse)
+
+ mouse.name = nil
+ mouse.save! validate: false
+
+ squeak = Squeak.last
+
+ assert_equal true, squeak.valid?
+ assert_equal true, squeak.mouse.present?
+ assert_equal true, squeak.valid?
+ end
end
class TestDefaultAutosaveAssociationOnAHasManyAssociationWithAcceptsNestedAttributes < ActiveRecord::TestCase
@@ -1671,6 +1688,10 @@ class TestAutosaveAssociationValidationsOnAHasManyAssociation < ActiveRecord::Te
super
@pirate = Pirate.create(catchphrase: "Don' botharrr talkin' like one, savvy?")
@pirate.birds.create(name: "cookoo")
+
+ @author = Author.new(name: "DHH")
+ @author.published_books.build(name: "Rework", isbn: "1234")
+ @author.published_books.build(name: "Remote", isbn: "1234")
end
test "should automatically validate associations" do
@@ -1679,6 +1700,42 @@ class TestAutosaveAssociationValidationsOnAHasManyAssociation < ActiveRecord::Te
assert_not_predicate @pirate, :valid?
end
+
+ test "rollbacks whole transaction and raises ActiveRecord::RecordInvalid when associations fail to #save! due to uniqueness validation failure" do
+ author_count_before_save = Author.count
+ book_count_before_save = Book.count
+
+ assert_no_difference "Author.count" do
+ assert_no_difference "Book.count" do
+ exception = assert_raises(ActiveRecord::RecordInvalid) do
+ @author.save!
+ end
+
+ assert_equal("Validation failed: Published books is invalid", exception.message)
+ end
+ end
+
+ assert_equal(author_count_before_save, Author.count)
+ assert_equal(book_count_before_save, Book.count)
+ end
+
+ test "rollbacks whole transaction when associations fail to #save due to uniqueness validation failure" do
+ author_count_before_save = Author.count
+ book_count_before_save = Book.count
+
+ assert_no_difference "Author.count" do
+ assert_no_difference "Book.count" do
+ assert_nothing_raised do
+ result = @author.save
+
+ assert_not(result)
+ end
+ end
+ end
+
+ assert_equal(author_count_before_save, Author.count)
+ assert_equal(book_count_before_save, Book.count)
+ end
end
class TestAutosaveAssociationValidationsOnAHasOneAssociation < ActiveRecord::TestCase
diff --git a/activerecord/test/cases/batches_test.rb b/activerecord/test/cases/batches_test.rb
index cf6e280898..0d0bf39f79 100644
--- a/activerecord/test/cases/batches_test.rb
+++ b/activerecord/test/cases/batches_test.rb
@@ -146,7 +146,7 @@ class EachTest < ActiveRecord::TestCase
def test_find_in_batches_should_quote_batch_order
c = Post.connection
- assert_sql(/ORDER BY #{c.quote_table_name('posts')}\.#{c.quote_column_name('id')}/) do
+ assert_sql(/ORDER BY #{Regexp.escape(c.quote_table_name("posts.id"))}/i) do
Post.find_in_batches(batch_size: 1) do |batch|
assert_kind_of Array, batch
assert_kind_of Post, batch.first
diff --git a/activerecord/test/cases/calculations_test.rb b/activerecord/test/cases/calculations_test.rb
index 525085bb28..abce4565a4 100644
--- a/activerecord/test/cases/calculations_test.rb
+++ b/activerecord/test/cases/calculations_test.rb
@@ -139,6 +139,13 @@ class CalculationsTest < ActiveRecord::TestCase
end
end
+ def test_should_not_use_alias_for_grouped_field
+ assert_sql(/GROUP BY #{Regexp.escape(Account.connection.quote_table_name("accounts.firm_id"))}/i) do
+ c = Account.group(:firm_id).order("accounts_firm_id").sum(:credit_limit)
+ assert_equal [1, 2, 6, 9], c.keys.compact
+ end
+ end
+
def test_should_order_by_grouped_field
c = Account.group(:firm_id).order("firm_id").sum(:credit_limit)
assert_equal [1, 2, 6, 9], c.keys.compact
@@ -945,6 +952,90 @@ class CalculationsTest < ActiveRecord::TestCase
assert_equal({ "proposed" => 2, "published" => 2 }, Book.group(:status).count)
end
+ def test_select_avg_with_group_by_as_virtual_attribute_with_sql
+ rails_core = companies(:rails_core)
+
+ sql = <<~SQL
+ SELECT firm_id, AVG(credit_limit) AS avg_credit_limit
+ FROM accounts
+ WHERE firm_id = ?
+ GROUP BY firm_id
+ LIMIT 1
+ SQL
+
+ account = Account.find_by_sql([sql, rails_core]).first
+
+ # id was not selected, so it should be nil
+ # (cannot select id because it wasn't used in the GROUP BY clause)
+ assert_nil account.id
+
+ # firm_id was explicitly selected, so it should be present
+ assert_equal(rails_core, account.firm)
+
+ # avg_credit_limit should be present as a virtual attribute
+ assert_equal(52.5, account.avg_credit_limit)
+ end
+
+ def test_select_avg_with_group_by_as_virtual_attribute_with_ar
+ rails_core = companies(:rails_core)
+
+ account = Account
+ .select(:firm_id, "AVG(credit_limit) AS avg_credit_limit")
+ .where(firm: rails_core)
+ .group(:firm_id)
+ .take!
+
+ # id was not selected, so it should be nil
+ # (cannot select id because it wasn't used in the GROUP BY clause)
+ assert_nil account.id
+
+ # firm_id was explicitly selected, so it should be present
+ assert_equal(rails_core, account.firm)
+
+ # avg_credit_limit should be present as a virtual attribute
+ assert_equal(52.5, account.avg_credit_limit)
+ end
+
+ def test_select_avg_with_joins_and_group_by_as_virtual_attribute_with_sql
+ rails_core = companies(:rails_core)
+
+ sql = <<~SQL
+ SELECT companies.*, AVG(accounts.credit_limit) AS avg_credit_limit
+ FROM companies
+ INNER JOIN accounts ON companies.id = accounts.firm_id
+ WHERE companies.id = ?
+ GROUP BY companies.id
+ LIMIT 1
+ SQL
+
+ firm = DependentFirm.find_by_sql([sql, rails_core]).first
+
+ # all the DependentFirm attributes should be present
+ assert_equal rails_core, firm
+ assert_equal rails_core.name, firm.name
+
+ # avg_credit_limit should be present as a virtual attribute
+ assert_equal(52.5, firm.avg_credit_limit)
+ end
+
+ def test_select_avg_with_joins_and_group_by_as_virtual_attribute_with_ar
+ rails_core = companies(:rails_core)
+
+ firm = DependentFirm
+ .select("companies.*", "AVG(accounts.credit_limit) AS avg_credit_limit")
+ .where(id: rails_core)
+ .joins(:account)
+ .group(:id)
+ .take!
+
+ # all the DependentFirm attributes should be present
+ assert_equal rails_core, firm
+ assert_equal rails_core.name, firm.name
+
+ # avg_credit_limit should be present as a virtual attribute
+ assert_equal(52.5, firm.avg_credit_limit)
+ end
+
def test_count_with_block_and_column_name_raises_an_error
assert_raises(ArgumentError) do
Account.count(:firm_id) { true }
diff --git a/activerecord/test/cases/connection_adapters/connection_handler_test.rb b/activerecord/test/cases/connection_adapters/connection_handler_test.rb
index 27589966af..843242a897 100644
--- a/activerecord/test/cases/connection_adapters/connection_handler_test.rb
+++ b/activerecord/test/cases/connection_adapters/connection_handler_test.rb
@@ -29,7 +29,7 @@ module ActiveRecord
def test_establish_connection_uses_spec_name
old_config = ActiveRecord::Base.configurations
- config = { "readonly" => { "adapter" => "sqlite3" } }
+ config = { "readonly" => { "adapter" => "sqlite3", "pool" => "5" } }
ActiveRecord::Base.configurations = config
resolver = ConnectionAdapters::ConnectionSpecification::Resolver.new(ActiveRecord::Base.configurations)
spec = resolver.spec(:readonly)
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 515bf5df06..2ac249b478 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
@@ -28,6 +28,22 @@ module ActiveRecord
resolver.resolve(spec, spec)
end
+ def test_invalid_string_config
+ config = { "foo" => "bar" }
+
+ assert_raises ActiveRecord::DatabaseConfigurations::InvalidConfigurationError do
+ resolve_config(config)
+ end
+ end
+
+ def test_invalid_symbol_config
+ config = { "foo" => :bar }
+
+ assert_raises ActiveRecord::DatabaseConfigurations::InvalidConfigurationError do
+ resolve_config(config)
+ end
+ end
+
def test_resolver_with_database_uri_and_current_env_symbol_key
ENV["DATABASE_URL"] = "postgres://localhost/foo"
config = { "not_production" => { "adapter" => "not_postgres", "database" => "not_foo" } }
@@ -244,6 +260,25 @@ module ActiveRecord
assert_equal expected, actual
end
+ def test_no_url_sub_key_with_database_url_doesnt_trample_other_envs
+ ENV["DATABASE_URL"] = "postgres://localhost/baz"
+
+ config = { "default_env" => { "database" => "foo" }, "other_env" => { "url" => "postgres://foohost/bardb" } }
+ actual = resolve_config(config)
+ expected = { "default_env" =>
+ { "database" => "baz",
+ "adapter" => "postgresql",
+ "host" => "localhost"
+ },
+ "other_env" =>
+ { "adapter" => "postgresql",
+ "database" => "bardb",
+ "host" => "foohost"
+ }
+ }
+ assert_equal expected, actual
+ end
+
def test_merge_no_conflicts_with_database_url
ENV["DATABASE_URL"] = "postgres://localhost/foo"
@@ -273,6 +308,105 @@ module ActiveRecord
}
assert_equal expected, actual
end
+
+ def test_merge_no_conflicts_with_database_url_and_adapter
+ ENV["DATABASE_URL"] = "postgres://localhost/foo"
+
+ config = { "default_env" => { "adapter" => "postgresql", "pool" => "5" } }
+ actual = resolve_config(config)
+ expected = { "default_env" =>
+ { "adapter" => "postgresql",
+ "database" => "foo",
+ "host" => "localhost",
+ "pool" => "5"
+ }
+ }
+ assert_equal expected, actual
+ end
+
+ def test_merge_no_conflicts_with_database_url_and_numeric_pool
+ ENV["DATABASE_URL"] = "postgres://localhost/foo"
+
+ config = { "default_env" => { "pool" => 5 } }
+ actual = resolve_config(config)
+ expected = { "default_env" =>
+ { "adapter" => "postgresql",
+ "database" => "foo",
+ "host" => "localhost",
+ "pool" => 5
+ }
+ }
+
+ assert_equal expected, actual
+ end
+
+ def test_tiered_configs_with_database_url
+ ENV["DATABASE_URL"] = "postgres://localhost/foo"
+
+ config = {
+ "default_env" => {
+ "primary" => { "pool" => 5 },
+ "animals" => { "pool" => 5 }
+ }
+ }
+
+ configs = ActiveRecord::DatabaseConfigurations.new(config)
+ actual = configs.configs_for(env_name: "default_env", spec_name: "primary").config
+ expected = {
+ "adapter" => "postgresql",
+ "database" => "foo",
+ "host" => "localhost",
+ "pool" => 5
+ }
+
+ assert_equal expected, actual
+
+ configs = ActiveRecord::DatabaseConfigurations.new(config)
+ actual = configs.configs_for(env_name: "default_env", spec_name: "animals").config
+ expected = { "pool" => 5 }
+
+ assert_equal expected, actual
+ end
+
+ def test_separate_database_env_vars
+ ENV["DATABASE_URL"] = "postgres://localhost/foo"
+ ENV["PRIMARY_DATABASE_URL"] = "postgres://localhost/primary"
+ ENV["ANIMALS_DATABASE_URL"] = "postgres://localhost/animals"
+
+ config = {
+ "default_env" => {
+ "primary" => { "pool" => 5 },
+ "animals" => { "pool" => 5 }
+ }
+ }
+
+ configs = ActiveRecord::DatabaseConfigurations.new(config)
+ actual = configs.configs_for(env_name: "default_env", spec_name: "primary").config
+ assert_equal "primary", actual["database"]
+
+ configs = ActiveRecord::DatabaseConfigurations.new(config)
+ actual = configs.configs_for(env_name: "default_env", spec_name: "animals").config
+ assert_equal "animals", actual["database"]
+ ensure
+ ENV.delete("PRIMARY_DATABASE_URL")
+ ENV.delete("ANIMALS_DATABASE_URL")
+ end
+
+ def test_does_not_change_other_environments
+ ENV["DATABASE_URL"] = "postgres://localhost/foo"
+ config = { "production" => { "adapter" => "not_postgres", "database" => "not_foo", "host" => "localhost" }, "default_env" => {} }
+
+ actual = resolve_spec(:production, config)
+ assert_equal config["production"].merge("name" => "production"), actual
+
+ actual = resolve_spec(:default_env, config)
+ assert_equal({
+ "host" => "localhost",
+ "database" => "foo",
+ "adapter" => "postgresql",
+ "name" => "default_env"
+ }, actual)
+ end
end
end
end
diff --git a/activerecord/test/cases/connection_adapters/mysql_type_lookup_test.rb b/activerecord/test/cases/connection_adapters/mysql_type_lookup_test.rb
index bc823fd072..774380d7e0 100644
--- a/activerecord/test/cases/connection_adapters/mysql_type_lookup_test.rb
+++ b/activerecord/test/cases/connection_adapters/mysql_type_lookup_test.rb
@@ -40,7 +40,7 @@ if current_adapter?(:Mysql2Adapter)
end
def test_enum_type_with_value_matching_other_type
- assert_lookup_type :string, "ENUM('unicode', '8bit', 'none')"
+ assert_lookup_type :string, "ENUM('unicode', '8bit', 'none', 'time')"
end
def test_binary_types
diff --git a/activerecord/test/cases/database_configurations_test.rb b/activerecord/test/cases/database_configurations_test.rb
index ed8151f01a..714b9d75c5 100644
--- a/activerecord/test/cases/database_configurations_test.rb
+++ b/activerecord/test/cases/database_configurations_test.rb
@@ -80,17 +80,20 @@ class LegacyDatabaseConfigurationsTest < ActiveRecord::TestCase
def test_each_is_deprecated
assert_deprecated do
- ActiveRecord::Base.configurations.each do |db_config|
- assert_equal "primary", db_config.spec_name
+ all_configs = ActiveRecord::Base.configurations.values
+ ActiveRecord::Base.configurations.each do |env_name, config|
+ assert_includes ["arunit", "arunit2", "arunit_without_prepared_statements"], env_name
+ assert_includes all_configs, config
end
end
end
def test_first_is_deprecated
+ first_config = ActiveRecord::Base.configurations.configurations.map(&:config).first
assert_deprecated do
- db_config = ActiveRecord::Base.configurations.first
- assert_equal "arunit", db_config.env_name
- assert_equal "primary", db_config.spec_name
+ env_name, config = ActiveRecord::Base.configurations.first
+ assert_equal "arunit", env_name
+ assert_equal first_config, config
end
end
diff --git a/activerecord/test/cases/database_selector_test.rb b/activerecord/test/cases/database_selector_test.rb
index fd02d2acb4..340151e6db 100644
--- a/activerecord/test/cases/database_selector_test.rb
+++ b/activerecord/test/cases/database_selector_test.rb
@@ -123,6 +123,40 @@ module ActiveRecord
assert read
end
+ def test_preventing_writes_turns_off_for_primary_write
+ resolver = ActiveRecord::Middleware::DatabaseSelector::Resolver.new(@session, delay: 5.seconds)
+
+ # Session should start empty
+ assert_nil @session_store[:last_write]
+
+ called = false
+ resolver.write do
+ assert ActiveRecord::Base.connected_to?(role: :writing)
+ called = true
+ end
+ assert called
+
+ # and be populated by the last write time
+ assert @session_store[:last_write]
+
+ read = false
+ write = false
+ resolver.read do
+ assert ActiveRecord::Base.connected_to?(role: :writing)
+ assert ActiveRecord::Base.connection_handler.prevent_writes
+ read = true
+
+ resolver.write do
+ assert ActiveRecord::Base.connected_to?(role: :writing)
+ assert_not ActiveRecord::Base.connection_handler.prevent_writes
+ write = true
+ end
+ end
+
+ assert write
+ assert read
+ end
+
def test_read_from_replica_with_no_delay
resolver = ActiveRecord::Middleware::DatabaseSelector::Resolver.new(@session, delay: 0.seconds)
diff --git a/activerecord/test/cases/dirty_test.rb b/activerecord/test/cases/dirty_test.rb
index a2a501a794..38baf0509c 100644
--- a/activerecord/test/cases/dirty_test.rb
+++ b/activerecord/test/cases/dirty_test.rb
@@ -491,6 +491,7 @@ class DirtyTest < ActiveRecord::TestCase
assert_equal 4, pirate.previous_changes.size
assert_equal [nil, "arrr"], pirate.previous_changes["catchphrase"]
+ assert_nil pirate.catchphrase_previously_was
assert_equal [nil, pirate.id], pirate.previous_changes["id"]
assert_nil pirate.previous_changes["updated_on"][0]
assert_not_nil pirate.previous_changes["updated_on"][1]
@@ -507,6 +508,7 @@ class DirtyTest < ActiveRecord::TestCase
assert_equal 4, pirate.previous_changes.size
assert_equal [nil, "arrr"], pirate.previous_changes["catchphrase"]
+ assert_nil pirate.catchphrase_previously_was
assert_equal [nil, pirate.id], pirate.previous_changes["id"]
assert_includes pirate.previous_changes, "updated_on"
assert_includes pirate.previous_changes, "created_on"
@@ -525,6 +527,7 @@ class DirtyTest < ActiveRecord::TestCase
assert_equal 2, pirate.previous_changes.size
assert_equal ["arrr", "Me Maties!"], pirate.previous_changes["catchphrase"]
+ assert_equal "arrr", pirate.catchphrase_previously_was
assert_not_nil pirate.previous_changes["updated_on"][0]
assert_not_nil pirate.previous_changes["updated_on"][1]
assert_not pirate.previous_changes.key?("parrot_id")
@@ -539,6 +542,7 @@ class DirtyTest < ActiveRecord::TestCase
assert_equal 2, pirate.previous_changes.size
assert_equal ["Me Maties!", "Thar She Blows!"], pirate.previous_changes["catchphrase"]
+ assert_equal "Me Maties!", pirate.catchphrase_previously_was
assert_not_nil pirate.previous_changes["updated_on"][0]
assert_not_nil pirate.previous_changes["updated_on"][1]
assert_not pirate.previous_changes.key?("parrot_id")
@@ -551,6 +555,7 @@ class DirtyTest < ActiveRecord::TestCase
assert_equal 2, pirate.previous_changes.size
assert_equal ["Thar She Blows!", "Ahoy!"], pirate.previous_changes["catchphrase"]
+ assert_equal "Thar She Blows!", pirate.catchphrase_previously_was
assert_not_nil pirate.previous_changes["updated_on"][0]
assert_not_nil pirate.previous_changes["updated_on"][1]
assert_not pirate.previous_changes.key?("parrot_id")
@@ -563,6 +568,7 @@ class DirtyTest < ActiveRecord::TestCase
assert_equal 2, pirate.previous_changes.size
assert_equal ["Ahoy!", "Ninjas suck!"], pirate.previous_changes["catchphrase"]
+ assert_equal "Ahoy!", pirate.catchphrase_previously_was
assert_not_nil pirate.previous_changes["updated_on"][0]
assert_not_nil pirate.previous_changes["updated_on"][1]
assert_not pirate.previous_changes.key?("parrot_id")
diff --git a/activerecord/test/cases/enum_test.rb b/activerecord/test/cases/enum_test.rb
index ae0ce195b3..0ae156320a 100644
--- a/activerecord/test/cases/enum_test.rb
+++ b/activerecord/test/cases/enum_test.rb
@@ -3,6 +3,7 @@
require "cases/helper"
require "models/author"
require "models/book"
+require "active_support/log_subscriber/test_helper"
class EnumTest < ActiveRecord::TestCase
fixtures :books, :authors, :author_addresses
@@ -565,4 +566,27 @@ class EnumTest < ActiveRecord::TestCase
assert_raises(NoMethodError) { klass.proposed }
end
+
+ test "enums with a negative condition log a warning" do
+ old_logger = ActiveRecord::Base.logger
+ logger = ActiveSupport::LogSubscriber::TestHelper::MockLogger.new
+
+ ActiveRecord::Base.logger = logger
+
+ expected_message = "An enum element in Book uses the prefix 'not_'."\
+ " This will cause a conflict with auto generated negative scopes."
+
+ Class.new(ActiveRecord::Base) do
+ def self.name
+ "Book"
+ end
+ silence_warnings do
+ enum status: [:sent, :not_sent]
+ end
+ end
+
+ assert_match(expected_message, logger.logged(:warn).first)
+ ensure
+ ActiveRecord::Base.logger = old_logger
+ end
end
diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb
index 3aa610f86b..1f2058cc0a 100644
--- a/activerecord/test/cases/finder_test.rb
+++ b/activerecord/test/cases/finder_test.rb
@@ -245,7 +245,8 @@ class FinderTest < ActiveRecord::TestCase
end
def test_exists_does_not_select_columns_without_alias
- assert_sql(/SELECT\W+1 AS one FROM ["`]topics["`]/i) do
+ c = Topic.connection
+ assert_sql(/SELECT 1 AS one FROM #{Regexp.escape(c.quote_table_name("topics"))}/i) do
Topic.exists?
end
end
@@ -282,6 +283,11 @@ class FinderTest < ActiveRecord::TestCase
assert_not Post.select(:body).distinct.offset(4).exists?
end
+ def test_exists_with_distinct_and_offset_and_eagerload_and_order
+ assert Post.eager_load(:comments).distinct.offset(10).merge(Comment.order(post_id: :asc)).exists?
+ assert_not Post.eager_load(:comments).distinct.offset(11).merge(Comment.order(post_id: :asc)).exists?
+ end
+
# Ensure +exists?+ runs without an error by excluding distinct value.
# See https://github.com/rails/rails/pull/26981.
def test_exists_with_order_and_distinct
diff --git a/activerecord/test/cases/fixtures_test.rb b/activerecord/test/cases/fixtures_test.rb
index a7f01e898e..0861d938c5 100644
--- a/activerecord/test/cases/fixtures_test.rb
+++ b/activerecord/test/cases/fixtures_test.rb
@@ -67,7 +67,7 @@ class FixturesTest < ActiveRecord::TestCase
end
def call(_, _, _, _, values)
- @events << values[:sql] if values[:sql] =~ /INSERT/
+ @events << values[:sql] if /INSERT/.match?(values[:sql])
end
end
@@ -1279,6 +1279,33 @@ class CustomNameForFixtureOrModelTest < ActiveRecord::TestCase
end
end
+class IgnoreFixturesTest < ActiveRecord::TestCase
+ fixtures :other_books, :parrots
+
+ test "ignores books fixtures" do
+ assert_raise(StandardError) { other_books(:published) }
+ assert_raise(StandardError) { other_books(:published_paperback) }
+ assert_raise(StandardError) { other_books(:published_ebook) }
+
+ assert_equal 2, Book.count
+ assert_equal "Agile Web Development with Rails", other_books(:awdr).name
+ assert_equal "published", other_books(:awdr).status
+ assert_equal "paperback", other_books(:awdr).format
+ assert_equal "english", other_books(:awdr).language
+
+ assert_equal "Ruby for Rails", other_books(:rfr).name
+ assert_equal "ebook", other_books(:rfr).format
+ assert_equal "published", other_books(:rfr).status
+ end
+
+ test "ignores parrots fixtures" do
+ assert_raise(StandardError) { parrots(:DEFAULT) }
+ assert_raise(StandardError) { parrots(:DEAD_PARROT) }
+
+ assert_equal "DeadParrot", parrots(:polly).parrot_sti_class
+ end
+end
+
class FixturesWithDefaultScopeTest < ActiveRecord::TestCase
fixtures :bulbs
diff --git a/activerecord/test/cases/inheritance_test.rb b/activerecord/test/cases/inheritance_test.rb
index 629167e9ed..01e4878c3f 100644
--- a/activerecord/test/cases/inheritance_test.rb
+++ b/activerecord/test/cases/inheritance_test.rb
@@ -471,9 +471,9 @@ class InheritanceTest < ActiveRecord::TestCase
end
def test_eager_load_belongs_to_primary_key_quoting
- con = Account.connection
+ c = Account.connection
bind_param = Arel::Nodes::BindParam.new(nil)
- assert_sql(/#{con.quote_table_name('companies')}\.#{con.quote_column_name('id')} = (?:#{Regexp.quote(bind_param.to_sql)}|1)/) do
+ assert_sql(/#{Regexp.escape(c.quote_table_name("companies.id"))} = (?:#{Regexp.escape(bind_param.to_sql)}|1)/i) do
Account.all.merge!(includes: :firm).find(1)
end
end
diff --git a/activerecord/test/cases/insert_all_test.rb b/activerecord/test/cases/insert_all_test.rb
index d086d77081..42c623fafb 100644
--- a/activerecord/test/cases/insert_all_test.rb
+++ b/activerecord/test/cases/insert_all_test.rb
@@ -2,6 +2,7 @@
require "cases/helper"
require "models/book"
+require "models/speedometer"
class ReadonlyNameBook < Book
attr_readonly :name
@@ -225,6 +226,23 @@ class InsertAllTest < ActiveRecord::TestCase
assert_equal new_name, Book.find(1).name
end
+ def test_upsert_all_updates_existing_record_by_primary_key
+ skip unless supports_insert_on_duplicate_update?
+
+ Book.upsert_all [{ id: 1, name: "New edition" }], unique_by: :id
+
+ assert_equal "New edition", Book.find(1).name
+ end
+
+ def test_upsert_all_updates_existing_record_by_configured_primary_key
+ skip unless supports_insert_on_duplicate_update?
+
+ error = assert_raises ArgumentError do
+ Speedometer.upsert_all [{ speedometer_id: "s1", name: "New Speedometer" }]
+ end
+ assert_match "No unique index found for speedometer_id", error.message
+ end
+
def test_upsert_all_does_not_update_readonly_attributes
skip unless supports_insert_on_duplicate_update?
diff --git a/activerecord/test/cases/query_cache_test.rb b/activerecord/test/cases/query_cache_test.rb
index 53a4963909..79bd6906d1 100644
--- a/activerecord/test/cases/query_cache_test.rb
+++ b/activerecord/test/cases/query_cache_test.rb
@@ -536,6 +536,23 @@ class QueryCacheTest < ActiveRecord::TestCase
ActiveRecord::Base.connection_handlers = { writing: ActiveRecord::Base.default_connection_handler }
end
+ test "query cache is enabled in threads with shared connection" do
+ ActiveRecord::Base.connection_pool.lock_thread = true
+
+ assert_cache :off
+
+ thread_a = Thread.new do
+ middleware { |env|
+ assert_cache :clean
+ [200, {}, nil]
+ }.call({})
+ end
+
+ thread_a.join
+
+ ActiveRecord::Base.connection_pool.lock_thread = false
+ end
+
private
def with_temporary_connection_pool
old_pool = ActiveRecord::Base.connection_handler.retrieve_connection_pool(ActiveRecord::Base.connection_specification_name)
diff --git a/activerecord/test/cases/relation_test.rb b/activerecord/test/cases/relation_test.rb
index e0743de94b..e74fb1a098 100644
--- a/activerecord/test/cases/relation_test.rb
+++ b/activerecord/test/cases/relation_test.rb
@@ -363,6 +363,13 @@ module ActiveRecord
assert_match %r{/\*\+ BADHINT \*/}, post_with_hint.to_sql
end
+ def test_does_not_duplicate_optimizer_hints_on_merge
+ escaped_table = Post.connection.quote_table_name("posts")
+ expected = "SELECT /*+ OMGHINT */ #{escaped_table}.* FROM #{escaped_table}"
+ query = Post.optimizer_hints("OMGHINT").merge(Post.optimizer_hints("OMGHINT")).to_sql
+ assert_equal expected, query
+ end
+
class EnsureRoundTripTypeCasting < ActiveRecord::Type::Value
def type
:string
diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb
index 5df1e3ccf9..8b3ae02947 100644
--- a/activerecord/test/cases/relations_test.rb
+++ b/activerecord/test/cases/relations_test.rb
@@ -308,9 +308,9 @@ class RelationTest < ActiveRecord::TestCase
end
def test_reverse_order_with_function_other_predicates
- topics = Topic.order(Arel.sql("author_name, length(title), id")).reverse_order
+ topics = Topic.order("author_name, length(title), id").reverse_order
assert_equal topics(:second).title, topics.first.title
- topics = Topic.order(Arel.sql("length(author_name), id, length(title)")).reverse_order
+ topics = Topic.order("length(author_name), id, length(title)").reverse_order
assert_equal topics(:fifth).title, topics.first.title
end
@@ -1932,7 +1932,7 @@ class RelationTest < ActiveRecord::TestCase
assert_no_queries do
result = authors_count.map do |post|
- [post.num_posts, post.author.try(:name)]
+ [post.num_posts, post.author&.name]
end
expected = [[1, nil], [5, "David"], [3, "Mary"], [2, "Bob"]]
diff --git a/activerecord/test/cases/tasks/mysql_rake_test.rb b/activerecord/test/cases/tasks/mysql_rake_test.rb
index 258132835f..4b039395e8 100644
--- a/activerecord/test/cases/tasks/mysql_rake_test.rb
+++ b/activerecord/test/cases/tasks/mysql_rake_test.rb
@@ -7,7 +7,10 @@ if current_adapter?(:Mysql2Adapter)
module ActiveRecord
class MysqlDBCreateTest < ActiveRecord::TestCase
def setup
- @connection = Class.new { def create_database(*); end }.new
+ @connection = Class.new do
+ def create_database(*); end
+ def error_number(_); end
+ end.new
@configuration = {
"adapter" => "mysql2",
"database" => "my-app-db"
@@ -90,7 +93,7 @@ if current_adapter?(:Mysql2Adapter)
with_stubbed_connection_establish_connection do
ActiveRecord::Base.connection.stub(
:create_database,
- proc { raise ActiveRecord::Tasks::DatabaseAlreadyExists }
+ proc { raise ActiveRecord::DatabaseAlreadyExists }
) do
ActiveRecord::Tasks::DatabaseTasks.create @configuration
diff --git a/activerecord/test/cases/tasks/postgresql_rake_test.rb b/activerecord/test/cases/tasks/postgresql_rake_test.rb
index f9df650687..d74ba0580d 100644
--- a/activerecord/test/cases/tasks/postgresql_rake_test.rb
+++ b/activerecord/test/cases/tasks/postgresql_rake_test.rb
@@ -129,7 +129,7 @@ if current_adapter?(:PostgreSQLAdapter)
with_stubbed_connection_establish_connection do
ActiveRecord::Base.connection.stub(
:create_database,
- proc { raise ActiveRecord::Tasks::DatabaseAlreadyExists }
+ proc { raise ActiveRecord::DatabaseAlreadyExists }
) do
ActiveRecord::Tasks::DatabaseTasks.create @configuration
diff --git a/activerecord/test/cases/validations/i18n_validation_test.rb b/activerecord/test/cases/validations/i18n_validation_test.rb
index 2645776ab7..4dd8a4a82b 100644
--- a/activerecord/test/cases/validations/i18n_validation_test.rb
+++ b/activerecord/test/cases/validations/i18n_validation_test.rb
@@ -51,7 +51,7 @@ class I18nValidationTest < ActiveRecord::TestCase
test "validates_uniqueness_of on generated message #{name}" do
Topic.validates_uniqueness_of :title, validation_options
@topic.title = unique_topic.title
- assert_called_with(@topic.errors, :generate_message, [:title, :taken, generate_message_options.merge(value: "unique!")]) do
+ assert_called_with(ActiveModel::Error, :generate_message, [:title, :taken, @topic, generate_message_options.merge(value: "unique!")]) do
@topic.valid?
@topic.errors.messages
end
@@ -61,7 +61,7 @@ class I18nValidationTest < ActiveRecord::TestCase
COMMON_CASES.each do |name, validation_options, generate_message_options|
test "validates_associated on generated message #{name}" do
Topic.validates_associated :replies, validation_options
- assert_called_with(replied_topic.errors, :generate_message, [:replies, :invalid, generate_message_options.merge(value: replied_topic.replies)]) do
+ assert_called_with(ActiveModel::Error, :generate_message, [:replies, :invalid, replied_topic, generate_message_options.merge(value: replied_topic.replies)]) do
replied_topic.save
replied_topic.errors.messages
end