diff options
Diffstat (limited to 'activerecord/test')
30 files changed, 438 insertions, 52 deletions
diff --git a/activerecord/test/cases/adapters/postgresql/range_test.rb b/activerecord/test/cases/adapters/postgresql/range_test.rb index b4a776d04d..a75fdef698 100644 --- a/activerecord/test/cases/adapters/postgresql/range_test.rb +++ b/activerecord/test/cases/adapters/postgresql/range_test.rb @@ -232,6 +232,57 @@ _SQL end end + def test_create_tstzrange_preserve_usec + tstzrange = Time.parse("2010-01-01 14:30:00.670277 +0100")...Time.parse("2011-02-02 14:30:00.745125 CDT") + round_trip(@new_range, :tstz_range, tstzrange) + assert_equal @new_range.tstz_range, tstzrange + assert_equal @new_range.tstz_range, Time.parse("2010-01-01 13:30:00.670277 UTC")...Time.parse("2011-02-02 19:30:00.745125 UTC") + end + + def test_update_tstzrange_preserve_usec + assert_equal_round_trip(@first_range, :tstz_range, + Time.parse("2010-01-01 14:30:00.245124 CDT")...Time.parse("2011-02-02 14:30:00.451274 CET")) + assert_nil_round_trip(@first_range, :tstz_range, + Time.parse("2010-01-01 14:30:00.245124 +0100")...Time.parse("2010-01-01 13:30:00.245124 +0000")) + end + + def test_create_tsrange_preseve_usec + tz = ::ActiveRecord::Base.default_timezone + assert_equal_round_trip(@new_range, :ts_range, + Time.send(tz, 2010, 1, 1, 14, 30, 0, 125435)...Time.send(tz, 2011, 2, 2, 14, 30, 0, 225435)) + end + + def test_update_tsrange_preserve_usec + tz = ::ActiveRecord::Base.default_timezone + assert_equal_round_trip(@first_range, :ts_range, + Time.send(tz, 2010, 1, 1, 14, 30, 0, 142432)...Time.send(tz, 2011, 2, 2, 14, 30, 0, 224242)) + assert_nil_round_trip(@first_range, :ts_range, + Time.send(tz, 2010, 1, 1, 14, 30, 0, 142432)...Time.send(tz, 2010, 1, 1, 14, 30, 0, 142432)) + end + + def test_timezone_awareness_tsrange_preserve_usec + tz = "Pacific Time (US & Canada)" + + in_time_zone tz do + PostgresqlRange.reset_column_information + time_string = "2017-09-26 07:30:59.132451 -0700" + time = Time.zone.parse(time_string) + assert time.usec > 0 + + record = PostgresqlRange.new(ts_range: time_string..time_string) + assert_equal time..time, record.ts_range + assert_equal ActiveSupport::TimeZone[tz], record.ts_range.begin.time_zone + assert_equal time.usec, record.ts_range.begin.usec + + record.save! + record.reload + + assert_equal time..time, record.ts_range + assert_equal ActiveSupport::TimeZone[tz], record.ts_range.begin.time_zone + assert_equal time.usec, record.ts_range.begin.usec + end + end + def test_create_numrange assert_equal_round_trip(@new_range, :num_range, BigDecimal.new("0.5")...BigDecimal.new("1")) diff --git a/activerecord/test/cases/adapters/postgresql/serial_test.rb b/activerecord/test/cases/adapters/postgresql/serial_test.rb index 3c020a88d0..df7875dbf2 100644 --- a/activerecord/test/cases/adapters/postgresql/serial_test.rb +++ b/activerecord/test/cases/adapters/postgresql/serial_test.rb @@ -86,3 +86,39 @@ class PostgresqlBigSerialTest < ActiveRecord::PostgreSQLTestCase assert_match %r{t\.bigint\s+"serials_id",\s+default: -> \{ "nextval\('postgresql_big_serials_id_seq'::regclass\)" \}$}, output end end + +module SequenceNameDetectionTestCases + class CollidedSequenceNameTest < ActiveRecord::PostgreSQLTestCase + include SchemaDumpingHelper + + def setup + @connection = ActiveRecord::Base.connection + @connection.create_table :foo_bar, force: true do |t| + t.serial :baz_id + end + @connection.create_table :foo, force: true do |t| + t.serial :bar_id + t.bigserial :bar_baz_id + end + end + + def teardown + @connection.drop_table :foo_bar, if_exists: true + @connection.drop_table :foo, if_exists: true + end + + def test_serial_columns + columns = @connection.columns(:foo) + columns.each do |column| + assert_equal :integer, column.type + assert column.serial? + end + end + + def test_schema_dump_with_collided_sequence_name + output = dump_table_schema "foo" + assert_match %r{t\.serial\s+"bar_id",\s+null: false$}, output + assert_match %r{t\.bigserial\s+"bar_baz_id",\s+null: false$}, output + end + end +end diff --git a/activerecord/test/cases/adapters/postgresql/type_lookup_test.rb b/activerecord/test/cases/adapters/postgresql/type_lookup_test.rb index 449023b6eb..8212ed4263 100644 --- a/activerecord/test/cases/adapters/postgresql/type_lookup_test.rb +++ b/activerecord/test/cases/adapters/postgresql/type_lookup_test.rb @@ -30,6 +30,6 @@ class PostgresqlTypeLookupTest < ActiveRecord::PostgreSQLTestCase big_range = 0..123456789123456789 assert_raises(ActiveModel::RangeError) { int_range.serialize(big_range) } - assert_equal "[0,123456789123456789]", bigint_range.serialize(big_range) + assert_equal "[0,123456789123456789]", @connection.type_cast(bigint_range.serialize(big_range)) end end diff --git a/activerecord/test/cases/adapters/postgresql/uuid_test.rb b/activerecord/test/cases/adapters/postgresql/uuid_test.rb index 76cb1bc354..466d281e85 100644 --- a/activerecord/test/cases/adapters/postgresql/uuid_test.rb +++ b/activerecord/test/cases/adapters/postgresql/uuid_test.rb @@ -76,6 +76,19 @@ class PostgresqlUUIDTest < ActiveRecord::PostgreSQLTestCase assert_nil column.default end + def test_add_column_with_default_array + connection.add_column :uuid_data_type, :thingy, :uuid, array: true, default: [] + + UUIDType.reset_column_information + column = UUIDType.columns_hash["thingy"] + + assert column.array? + assert_equal "{}", column.default + + schema = dump_table_schema "uuid_data_type" + assert_match %r{t\.uuid "thingy", default: \[\], array: true$}, schema + end + def test_data_type_of_uuid_types column = UUIDType.columns_hash["guid"] assert_equal :uuid, column.type diff --git a/activerecord/test/cases/associations/eager_test.rb b/activerecord/test/cases/associations/eager_test.rb index 44bf7f7d2f..559f0e9338 100644 --- a/activerecord/test/cases/associations/eager_test.rb +++ b/activerecord/test/cases/associations/eager_test.rb @@ -530,6 +530,14 @@ class EagerAssociationTest < ActiveRecord::TestCase assert_equal [comments(:does_it_hurt)], assert_no_queries { author.special_post_comments } end + def test_preloading_has_many_through_with_implicit_source + authors = Author.includes(:very_special_comments).to_a + assert_no_queries do + special_comment_authors = authors.map { |author| [author.name, author.very_special_comments.size] } + assert_equal [["David", 1], ["Mary", 0], ["Bob", 0]], special_comment_authors + end + end + def test_eager_with_has_many_through_an_sti_join_model_with_conditions_on_both author = Author.all.merge!(includes: :special_nonexistent_post_comments, order: "authors.id").first assert_equal [], author.special_nonexistent_post_comments @@ -861,10 +869,6 @@ class EagerAssociationTest < ActiveRecord::TestCase end end - def find_all_ordered(className, include = nil) - className.all.merge!(order: "#{className.table_name}.#{className.primary_key}", includes: include).to_a - end - def test_limited_eager_with_order assert_equal( posts(:thinking, :sti_comments), @@ -1299,6 +1303,11 @@ class EagerAssociationTest < ActiveRecord::TestCase assert_equal projects.last.mentor.developers.first.contracts, projects.last.developers.last.contracts end + def test_preloading_has_many_through_with_custom_scope + project = Project.includes(:developers_named_david_with_hash_conditions).find(projects(:active_record).id) + assert_equal [developers(:david)], project.developers_named_david_with_hash_conditions + end + test "scoping with a circular preload" do assert_equal Comment.find(1), Comment.preload(post: :comments).scoping { Comment.find(1) } end @@ -1497,4 +1506,9 @@ class EagerAssociationTest < ActiveRecord::TestCase ActiveRecord::Associations::HasManyAssociation.any_instance.expects(:reader).never Author.preload(:readonly_comments).first! end + + private + def find_all_ordered(klass, include = nil) + klass.order("#{klass.table_name}.#{klass.primary_key}").includes(include).to_a + end end diff --git a/activerecord/test/cases/associations/has_many_through_associations_test.rb b/activerecord/test/cases/associations/has_many_through_associations_test.rb index f8ea51225a..521b388cee 100644 --- a/activerecord/test/cases/associations/has_many_through_associations_test.rb +++ b/activerecord/test/cases/associations/has_many_through_associations_test.rb @@ -1250,6 +1250,10 @@ class HasManyThroughAssociationsTest < ActiveRecord::TestCase TenantMembership.current_member = nil end + def test_has_many_through_with_unscope_should_affect_to_through_scope + assert_equal [comments(:eager_other_comment1)], authors(:mary).unordered_comments + end + def test_has_many_through_with_scope_should_respect_table_alias family = Family.create! users = 3.times.map { User.create! } diff --git a/activerecord/test/cases/associations/join_model_test.rb b/activerecord/test/cases/associations/join_model_test.rb index 3abdcf3564..87694b0788 100644 --- a/activerecord/test/cases/associations/join_model_test.rb +++ b/activerecord/test/cases/associations/join_model_test.rb @@ -496,25 +496,25 @@ class AssociationsJoinModelTest < ActiveRecord::TestCase post_thinking = posts(:thinking) assert_nothing_raised { post_thinking.tags << push } assert_nil(wrong = post_thinking.tags.detect { |t| t.class != Tag }, - message = "Expected a Tag in tags collection, got #{wrong.class}.") + "Expected a Tag in tags collection, got #{wrong.class}.") assert_nil(wrong = post_thinking.taggings.detect { |t| t.class != Tagging }, - message = "Expected a Tagging in taggings collection, got #{wrong.class}.") + "Expected a Tagging in taggings collection, got #{wrong.class}.") assert_equal(count + 1, post_thinking.reload.tags.size) assert_equal(count + 1, post_thinking.tags.reload.size) assert_kind_of Tag, post_thinking.tags.create!(name: "foo") assert_nil(wrong = post_thinking.tags.detect { |t| t.class != Tag }, - message = "Expected a Tag in tags collection, got #{wrong.class}.") + "Expected a Tag in tags collection, got #{wrong.class}.") assert_nil(wrong = post_thinking.taggings.detect { |t| t.class != Tagging }, - message = "Expected a Tagging in taggings collection, got #{wrong.class}.") + "Expected a Tagging in taggings collection, got #{wrong.class}.") assert_equal(count + 2, post_thinking.reload.tags.size) assert_equal(count + 2, post_thinking.tags.reload.size) assert_nothing_raised { post_thinking.tags.concat(Tag.create!(name: "abc"), Tag.create!(name: "def")) } assert_nil(wrong = post_thinking.tags.detect { |t| t.class != Tag }, - message = "Expected a Tag in tags collection, got #{wrong.class}.") + "Expected a Tag in tags collection, got #{wrong.class}.") assert_nil(wrong = post_thinking.taggings.detect { |t| t.class != Tagging }, - message = "Expected a Tagging in taggings collection, got #{wrong.class}.") + "Expected a Tagging in taggings collection, got #{wrong.class}.") assert_equal(count + 4, post_thinking.reload.tags.size) assert_equal(count + 4, post_thinking.tags.reload.size) diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb index 0ea8ef5cea..2d67c57cfb 100644 --- a/activerecord/test/cases/attribute_methods_test.rb +++ b/activerecord/test/cases/attribute_methods_test.rb @@ -1019,14 +1019,6 @@ class AttributeMethodsTest < ActiveRecord::TestCase ActiveRecord::Base.time_zone_aware_types = old_types end - def cached_columns - Topic.columns.map(&:name) - end - - def time_related_columns_on_topic - Topic.columns.select { |c| [:time, :date, :datetime, :timestamp].include?(c.type) } - end - def privatize(method_signature) @target.class_eval(<<-private_method, __FILE__, __LINE__ + 1) private diff --git a/activerecord/test/cases/attributes_test.rb b/activerecord/test/cases/attributes_test.rb index 29a25b4461..2caf2a63d4 100644 --- a/activerecord/test/cases/attributes_test.rb +++ b/activerecord/test/cases/attributes_test.rb @@ -108,12 +108,14 @@ module ActiveRecord assert_equal 6, klass.attribute_types.length assert_equal 6, klass.column_defaults.length + assert_equal 6, klass.attribute_names.length assert_not klass.attribute_types.include?("wibble") klass.attribute :wibble, Type::Value.new assert_equal 7, klass.attribute_types.length assert_equal 7, klass.column_defaults.length + assert_equal 7, klass.attribute_names.length assert_includes klass.attribute_types, "wibble" end diff --git a/activerecord/test/cases/batches_test.rb b/activerecord/test/cases/batches_test.rb index 53c1e61ad1..c965404b07 100644 --- a/activerecord/test/cases/batches_test.rb +++ b/activerecord/test/cases/batches_test.rb @@ -614,6 +614,17 @@ class EachTest < ActiveRecord::TestCase assert_equal expected, actual end + test ".find_each respects table alias" do + assert_queries(1) do + table_alias = Post.arel_table.alias("omg_posts") + table_metadata = ActiveRecord::TableMetadata.new(Post, table_alias) + predicate_builder = ActiveRecord::PredicateBuilder.new(table_metadata) + + posts = ActiveRecord::Relation.create(Post, table_alias, predicate_builder) + posts.find_each {} + end + end + test ".find_each bypasses the query cache for its own queries" do Post.cache do assert_queries(2) do diff --git a/activerecord/test/cases/calculations_test.rb b/activerecord/test/cases/calculations_test.rb index 39dff19b78..b47fd0af41 100644 --- a/activerecord/test/cases/calculations_test.rb +++ b/activerecord/test/cases/calculations_test.rb @@ -518,8 +518,7 @@ class CalculationsTest < ActiveRecord::TestCase end def test_should_sum_expression - # Oracle adapter returns floating point value 636.0 after SUM - if current_adapter?(:OracleAdapter) + if current_adapter?(:SQLite3Adapter, :Mysql2Adapter, :PostgreSQLAdapter, :OracleAdapter) assert_equal 636, Account.sum("2 * credit_limit") else assert_equal 636, Account.sum("2 * credit_limit").to_i diff --git a/activerecord/test/cases/collection_cache_key_test.rb b/activerecord/test/cases/collection_cache_key_test.rb index dbe6857487..c137693211 100644 --- a/activerecord/test/cases/collection_cache_key_test.rb +++ b/activerecord/test/cases/collection_cache_key_test.rb @@ -55,6 +55,24 @@ module ActiveRecord assert_equal last_developer_timestamp.to_s(ActiveRecord::Base.cache_timestamp_format), $3 end + test "cache_key for relation with table alias" do + table_alias = Developer.arel_table.alias("omg_developers") + table_metadata = ActiveRecord::TableMetadata.new(Developer, table_alias) + predicate_builder = ActiveRecord::PredicateBuilder.new(table_metadata) + + developers = ActiveRecord::Relation.create(Developer, table_alias, predicate_builder) + developers = developers.where(salary: 100000).order(updated_at: :desc) + last_developer_timestamp = developers.first.updated_at + + assert_match(/\Adevelopers\/query-(\h+)-(\d+)-(\d+)\z/, developers.cache_key) + + /\Adevelopers\/query-(\h+)-(\d+)-(\d+)\z/ =~ developers.cache_key + + assert_equal Digest::MD5.hexdigest(developers.to_sql), $1 + assert_equal developers.count.to_s, $2 + assert_equal last_developer_timestamp.to_s(ActiveRecord::Base.cache_timestamp_format), $3 + end + test "it triggers at most one query" do developers = Developer.where(name: "David") diff --git a/activerecord/test/cases/comment_test.rb b/activerecord/test/cases/comment_test.rb index 1bcafd4b55..584e03d196 100644 --- a/activerecord/test/cases/comment_test.rb +++ b/activerecord/test/cases/comment_test.rb @@ -142,5 +142,27 @@ if ActiveRecord::Base.connection.supports_comments? assert_match %r[t\.string\s+"absent_comment"\n], output assert_no_match %r[t\.string\s+"absent_comment", comment:\n], output end + + def test_change_table_comment + @connection.change_table_comment :commenteds, "Edited table comment" + assert_equal "Edited table comment", @connection.table_comment("commenteds") + end + + def test_change_table_comment_to_nil + @connection.change_table_comment :commenteds, nil + assert_nil @connection.table_comment("commenteds") + end + + def test_change_column_comment + @connection.change_column_comment :commenteds, :name, "Edited column comment" + column = Commented.columns_hash["name"] + assert_equal "Edited column comment", column.comment + end + + def test_change_column_comment_to_nil + @connection.change_column_comment :commenteds, :name, nil + column = Commented.columns_hash["name"] + assert_nil column.comment + end end end diff --git a/activerecord/test/cases/instrumentation_test.rb b/activerecord/test/cases/instrumentation_test.rb new file mode 100644 index 0000000000..e6e8468757 --- /dev/null +++ b/activerecord/test/cases/instrumentation_test.rb @@ -0,0 +1,72 @@ +# frozen_string_literal: true + +require "cases/helper" +require "models/book" + +module ActiveRecord + class InstrumentationTest < ActiveRecord::TestCase + def test_payload_name_on_load + Book.create(name: "test book") + subscriber = ActiveSupport::Notifications.subscribe("sql.active_record") do |*args| + event = ActiveSupport::Notifications::Event.new(*args) + if event.payload[:sql].match "SELECT" + assert_equal "Book Load", event.payload[:name] + end + end + Book.first + ensure + ActiveSupport::Notifications.unsubscribe(subscriber) if subscriber + end + + def test_payload_name_on_create + subscriber = ActiveSupport::Notifications.subscribe("sql.active_record") do |*args| + event = ActiveSupport::Notifications::Event.new(*args) + if event.payload[:sql].match "INSERT" + assert_equal "Book Create", event.payload[:name] + end + end + Book.create(name: "test book") + ensure + ActiveSupport::Notifications.unsubscribe(subscriber) if subscriber + end + + def test_payload_name_on_update + subscriber = ActiveSupport::Notifications.subscribe("sql.active_record") do |*args| + event = ActiveSupport::Notifications::Event.new(*args) + if event.payload[:sql].match "UPDATE" + assert_equal "Book Update", event.payload[:name] + end + end + book = Book.create(name: "test book") + book.update_attribute(:name, "new name") + ensure + ActiveSupport::Notifications.unsubscribe(subscriber) if subscriber + end + + def test_payload_name_on_update_all + subscriber = ActiveSupport::Notifications.subscribe("sql.active_record") do |*args| + event = ActiveSupport::Notifications::Event.new(*args) + if event.payload[:sql].match "UPDATE" + assert_equal "Book Update All", event.payload[:name] + end + end + Book.create(name: "test book") + Book.update_all(name: "new name") + ensure + ActiveSupport::Notifications.unsubscribe(subscriber) if subscriber + end + + def test_payload_name_on_destroy + subscriber = ActiveSupport::Notifications.subscribe("sql.active_record") do |*args| + event = ActiveSupport::Notifications::Event.new(*args) + if event.payload[:sql].match "DELETE" + assert_equal "Book Destroy", event.payload[:name] + end + end + book = Book.create(name: "test book") + book.destroy + ensure + ActiveSupport::Notifications.unsubscribe(subscriber) if subscriber + end + end +end diff --git a/activerecord/test/cases/migration/column_positioning_test.rb b/activerecord/test/cases/migration/column_positioning_test.rb index 23414419dc..1c62a68cf9 100644 --- a/activerecord/test/cases/migration/column_positioning_test.rb +++ b/activerecord/test/cases/migration/column_positioning_test.rb @@ -52,6 +52,16 @@ module ActiveRecord conn.change_column :testings, :second, :integer, after: :third assert_equal %w(first third second), conn.columns(:testings).map(&:name) end + + def test_add_reference_with_positioning_first + conn.add_reference :testings, :new, polymorphic: true, first: true + assert_equal %w(new_id new_type first second third), conn.columns(:testings).map(&:name) + end + + def test_add_reference_with_positioning_after + conn.add_reference :testings, :new, polymorphic: true, after: :first + assert_equal %w(first new_id new_type second third), conn.columns(:testings).map(&:name) + end end end end diff --git a/activerecord/test/cases/migration/columns_test.rb b/activerecord/test/cases/migration/columns_test.rb index 1b1d0af132..8ca20b6172 100644 --- a/activerecord/test/cases/migration/columns_test.rb +++ b/activerecord/test/cases/migration/columns_test.rb @@ -142,6 +142,10 @@ module ActiveRecord end def test_remove_column_with_multi_column_index + # MariaDB starting with 10.2.8 + # Dropping a column that is part of a multi-column UNIQUE constraint is not permitted. + skip if current_adapter?(:Mysql2Adapter) && connection.mariadb? && connection.version >= "10.2.8" + add_column "test_models", :hat_size, :integer add_column "test_models", :hat_style, :string, limit: 100 add_index "test_models", ["hat_style", "hat_size"], unique: true diff --git a/activerecord/test/cases/migration/command_recorder_test.rb b/activerecord/test/cases/migration/command_recorder_test.rb index 0b5e983f14..58bc558619 100644 --- a/activerecord/test/cases/migration/command_recorder_test.rb +++ b/activerecord/test/cases/migration/command_recorder_test.rb @@ -213,6 +213,11 @@ module ActiveRecord assert_equal [:remove_index, [:table, { name: "new_index" }]], remove end + def test_invert_add_index_with_algorithm_option + remove = @recorder.inverse_of :add_index, [:table, :one, algorithm: :concurrently] + assert_equal [:remove_index, [:table, { column: :one, algorithm: :concurrently }]], remove + end + def test_invert_remove_index add = @recorder.inverse_of :remove_index, [:table, :one] assert_equal [:add_index, [:table, :one]], add diff --git a/activerecord/test/cases/migration/compatibility_test.rb b/activerecord/test/cases/migration/compatibility_test.rb index cb3b02c02a..1ae15eb439 100644 --- a/activerecord/test/cases/migration/compatibility_test.rb +++ b/activerecord/test/cases/migration/compatibility_test.rb @@ -199,6 +199,87 @@ class LegacyPrimaryKeyTest < ActiveRecord::TestCase assert_match %r{create_table "legacy_primary_keys", id: :integer, default: nil}, schema end + if current_adapter?(:Mysql2Adapter, :PostgreSQLAdapter) + def test_legacy_primary_key_in_create_table_should_be_integer + @migration = Class.new(ActiveRecord::Migration[5.0]) { + def change + create_table :legacy_primary_keys, id: false do |t| + t.primary_key :id + end + end + }.new + + @migration.migrate(:up) + + schema = dump_table_schema "legacy_primary_keys" + assert_match %r{create_table "legacy_primary_keys", id: :(?:integer|serial), (?!default: nil)}, schema + end + + def test_legacy_primary_key_in_change_table_should_be_integer + @migration = Class.new(ActiveRecord::Migration[5.0]) { + def change + create_table :legacy_primary_keys, id: false do |t| + t.integer :dummy + end + change_table :legacy_primary_keys do |t| + t.primary_key :id + end + end + }.new + + @migration.migrate(:up) + + schema = dump_table_schema "legacy_primary_keys" + assert_match %r{create_table "legacy_primary_keys", id: :(?:integer|serial), (?!default: nil)}, schema + end + + def test_add_column_with_legacy_primary_key_should_be_integer + @migration = Class.new(ActiveRecord::Migration[5.0]) { + def change + create_table :legacy_primary_keys, id: false do |t| + t.integer :dummy + end + add_column :legacy_primary_keys, :id, :primary_key + end + }.new + + @migration.migrate(:up) + + schema = dump_table_schema "legacy_primary_keys" + assert_match %r{create_table "legacy_primary_keys", id: :(?:integer|serial), (?!default: nil)}, schema + end + end + + def test_legacy_join_table_foreign_keys_should_be_integer + @migration = Class.new(ActiveRecord::Migration[5.0]) { + def change + create_join_table :apples, :bananas do |t| + end + end + }.new + + @migration.migrate(:up) + + schema = dump_table_schema "apples_bananas" + assert_match %r{integer "apple_id", null: false}, schema + assert_match %r{integer "banana_id", null: false}, schema + end + + def test_legacy_join_table_column_options_should_be_overwritten + @migration = Class.new(ActiveRecord::Migration[5.0]) { + def change + create_join_table :apples, :bananas, column_options: { type: :bigint } do |t| + end + end + }.new + + @migration.migrate(:up) + + schema = dump_table_schema "apples_bananas" + assert_match %r{bigint "apple_id", null: false}, schema + assert_match %r{bigint "banana_id", null: false}, schema + end + if current_adapter?(:Mysql2Adapter) def test_legacy_bigint_primary_key_should_be_auto_incremented @migration = Class.new(ActiveRecord::Migration[5.0]) { diff --git a/activerecord/test/cases/persistence_test.rb b/activerecord/test/cases/persistence_test.rb index 170fd02b6f..6cbe18cc8c 100644 --- a/activerecord/test/cases/persistence_test.rb +++ b/activerecord/test/cases/persistence_test.rb @@ -70,10 +70,10 @@ class PersistenceTest < ActiveRecord::TestCase end def test_update_many - topic_data = { 1 => { "content" => "1 updated" }, 2 => { "content" => "2 updated" } } + topic_data = { 1 => { "content" => "1 updated" }, 2 => { "content" => "2 updated" }, nil => {} } updated = Topic.update(topic_data.keys, topic_data.values) - assert_equal 2, updated.size + assert_equal [1, 2], updated.map(&:id) assert_equal "1 updated", Topic.find(1).content assert_equal "2 updated", Topic.find(2).content end @@ -81,9 +81,8 @@ class PersistenceTest < ActiveRecord::TestCase def test_class_level_update_is_affected_by_scoping topic_data = { 1 => { "content" => "1 updated" }, 2 => { "content" => "2 updated" } } - assert_raise(ActiveRecord::RecordNotFound) do - Topic.where("1=0").scoping { Topic.update(topic_data.keys, topic_data.values) } - end + assert_equal [], Topic.where("1=0").scoping { Topic.update(topic_data.keys, topic_data.values) } + assert_not_equal "1 updated", Topic.find(1).content assert_not_equal "2 updated", Topic.find(2).content end @@ -175,7 +174,7 @@ class PersistenceTest < ActiveRecord::TestCase clients = Client.all.merge!(order: "id").find([2, 3]) assert_difference("Client.count", -2) do - destroyed = Client.destroy([2, 3]).sort_by(&:id) + destroyed = Client.destroy([2, 3, nil]).sort_by(&:id) assert_equal clients, destroyed assert destroyed.all?(&:frozen?), "destroyed clients should be frozen" end @@ -917,7 +916,9 @@ class PersistenceTest < ActiveRecord::TestCase should_be_destroyed_reply = Reply.create("title" => "hello", "content" => "world") Topic.find(1).replies << should_be_destroyed_reply - Topic.destroy(1) + topic = Topic.destroy(1) + assert topic.destroyed? + assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1) } assert_raise(ActiveRecord::RecordNotFound) { Reply.find(should_be_destroyed_reply.id) } end @@ -926,9 +927,8 @@ class PersistenceTest < ActiveRecord::TestCase should_not_be_destroyed_reply = Reply.create("title" => "hello", "content" => "world") Topic.find(1).replies << should_not_be_destroyed_reply - assert_raise(ActiveRecord::RecordNotFound) do - Topic.where("1=0").scoping { Topic.destroy(1) } - end + assert_nil Topic.where("1=0").scoping { Topic.destroy(1) } + assert_nothing_raised { Topic.find(1) } assert_nothing_raised { Reply.find(should_not_be_destroyed_reply.id) } end diff --git a/activerecord/test/cases/primary_keys_test.rb b/activerecord/test/cases/primary_keys_test.rb index e0888f5fe9..a36d786b40 100644 --- a/activerecord/test/cases/primary_keys_test.rb +++ b/activerecord/test/cases/primary_keys_test.rb @@ -334,16 +334,26 @@ class CompositePrimaryKeyTest < ActiveRecord::TestCase t.string :region t.integer :code end + @connection.create_table(:travels, primary_key: ["from", "to"], force: true) do |t| + t.string :from + t.string :to + end end def teardown - @connection.drop_table(:uber_barcodes, if_exists: true) + @connection.drop_table :uber_barcodes, if_exists: true + @connection.drop_table :barcodes_reverse, if_exists: true + @connection.drop_table :travels, if_exists: true end def test_composite_primary_key assert_equal ["region", "code"], @connection.primary_keys("uber_barcodes") end + def test_composite_primary_key_with_reserved_words + assert_equal ["from", "to"], @connection.primary_keys("travels") + end + def test_composite_primary_key_out_of_order skip if current_adapter?(:SQLite3Adapter) assert_equal ["code", "region"], @connection.primary_keys("barcodes_reverse") diff --git a/activerecord/test/cases/query_cache_test.rb b/activerecord/test/cases/query_cache_test.rb index d3f4b5bf75..5cb537b623 100644 --- a/activerecord/test/cases/query_cache_test.rb +++ b/activerecord/test/cases/query_cache_test.rb @@ -302,14 +302,10 @@ class QueryCacheTest < ActiveRecord::TestCase end end - def test_cache_does_not_wrap_string_results_in_arrays + def test_cache_does_not_wrap_results_in_arrays Task.cache do - # Oracle adapter returns count() as Integer or Float - if current_adapter?(:OracleAdapter) - assert_kind_of Numeric, Task.connection.select_value("SELECT count(*) AS count_all FROM tasks") - elsif current_adapter?(:SQLite3Adapter, :Mysql2Adapter, :PostgreSQLAdapter) - # Future versions of the sqlite3 adapter will return numeric - assert_instance_of 0.class, Task.connection.select_value("SELECT count(*) AS count_all FROM tasks") + if current_adapter?(:SQLite3Adapter, :Mysql2Adapter, :PostgreSQLAdapter, :OracleAdapter) + assert_equal 2, Task.connection.select_value("SELECT count(*) AS count_all FROM tasks") else assert_instance_of String, Task.connection.select_value("SELECT count(*) AS count_all FROM tasks") end diff --git a/activerecord/test/cases/relation/delegation_test.rb b/activerecord/test/cases/relation/delegation_test.rb index 04d688ac53..2696d1bb00 100644 --- a/activerecord/test/cases/relation/delegation_test.rb +++ b/activerecord/test/cases/relation/delegation_test.rb @@ -10,8 +10,8 @@ module ActiveRecord :+, :-, :|, :&, :[], :shuffle, :all?, :collect, :compact, :detect, :each, :each_cons, :each_with_index, :exclude?, :find_all, :flat_map, :group_by, :include?, :length, - :map, :none?, :one?, :partition, :reject, :reverse, - :sample, :second, :sort, :sort_by, :third, + :map, :none?, :one?, :partition, :reject, :reverse, :rotate, + :sample, :second, :sort, :sort_by, :slice, :third, :index, :rindex, :to_ary, :to_set, :to_xml, :to_yaml, :join, :in_groups, :in_groups_of, :to_sentence, :to_formatted_s, :as_json ] diff --git a/activerecord/test/cases/relation/or_test.rb b/activerecord/test/cases/relation/or_test.rb index 955e9fc9ce..7e418f9c7d 100644 --- a/activerecord/test/cases/relation/or_test.rb +++ b/activerecord/test/cases/relation/or_test.rb @@ -31,7 +31,7 @@ module ActiveRecord end def test_or_with_bind_params - assert_equal Post.find([1, 2]), Post.where(id: 1).or(Post.where(id: 2)).to_a + assert_equal Post.find([1, 2]).sort_by(&:id), Post.where(id: 1).or(Post.where(id: 2)).sort_by(&:id) end def test_or_with_null_both diff --git a/activerecord/test/cases/relation_test.rb b/activerecord/test/cases/relation_test.rb index c1805aa592..fd5985ffe7 100644 --- a/activerecord/test/cases/relation_test.rb +++ b/activerecord/test/cases/relation_test.rb @@ -73,11 +73,6 @@ module ActiveRecord assert_equal({}, relation.where_values_hash) end - def test_table_name_delegates_to_klass - relation = Relation.new(FakeKlass, :b, Post.predicate_builder) - assert_equal "posts", relation.table_name - end - def test_scope_for_create relation = Relation.new(FakeKlass, :b, nil) assert_equal({}, relation.scope_for_create) diff --git a/activerecord/test/cases/relations_test.rb b/activerecord/test/cases/relations_test.rb index ae1dc35bff..4edaf79e9a 100644 --- a/activerecord/test/cases/relations_test.rb +++ b/activerecord/test/cases/relations_test.rb @@ -1806,6 +1806,10 @@ class RelationTest < ActiveRecord::TestCase assert_equal post, custom_post_relation.joins(:author).where!(title: post.title).take end + test "arel_attribute respects a custom table" do + assert_equal [posts(:welcome)], custom_post_relation.ranked_by_comments.limit_by(1).to_a + end + test "#load" do relation = Post.all assert_queries(1) do @@ -1912,6 +1916,19 @@ class RelationTest < ActiveRecord::TestCase end end + test "#where with set" do + david = authors(:david) + mary = authors(:mary) + + authors = Author.where(name: ["David", "Mary"].to_set) + assert_equal [david, mary], authors + end + + test "#where with empty set" do + authors = Author.where(name: Set.new) + assert_empty authors + end + private def custom_post_relation table_alias = Post.arel_table.alias("omg_posts") diff --git a/activerecord/test/cases/tasks/postgresql_rake_test.rb b/activerecord/test/cases/tasks/postgresql_rake_test.rb index 693503250b..ca1defa332 100644 --- a/activerecord/test/cases/tasks/postgresql_rake_test.rb +++ b/activerecord/test/cases/tasks/postgresql_rake_test.rb @@ -229,7 +229,6 @@ if current_adapter?(:PostgreSQLAdapter) ActiveRecord::Base.stubs(:connection).returns(@connection) ActiveRecord::Base.stubs(:establish_connection).returns(true) - Kernel.stubs(:system) end def teardown @@ -295,6 +294,16 @@ if current_adapter?(:PostgreSQLAdapter) end end + def test_structure_dump_execution_fails + filename = "awesome-file.sql" + Kernel.expects(:system).with("pg_dump", "-s", "-x", "-O", "-f", filename, "my-app-db").returns(nil) + + e = assert_raise(RuntimeError) do + ActiveRecord::Tasks::DatabaseTasks.structure_dump(@configuration, filename) + end + assert_match("failed to execute:", e.message) + end + private def with_dump_schemas(value, &block) old_dump_schemas = ActiveRecord::Base.dump_schemas @@ -323,7 +332,6 @@ if current_adapter?(:PostgreSQLAdapter) ActiveRecord::Base.stubs(:connection).returns(@connection) ActiveRecord::Base.stubs(:establish_connection).returns(true) - Kernel.stubs(:system) end def test_structure_load diff --git a/activerecord/test/cases/tasks/sqlite_rake_test.rb b/activerecord/test/cases/tasks/sqlite_rake_test.rb index 8ac4878c37..d7e3caa2ff 100644 --- a/activerecord/test/cases/tasks/sqlite_rake_test.rb +++ b/activerecord/test/cases/tasks/sqlite_rake_test.rb @@ -215,6 +215,31 @@ if current_adapter?(:SQLite3Adapter) FileUtils.rm_f(filename) FileUtils.rm_f(dbfile) end + + def test_structure_dump_execution_fails + dbfile = @database + filename = "awesome-file.sql" + Kernel.expects(:system).with("sqlite3", "--noop", "db_create.sqlite3", ".schema", out: "awesome-file.sql").returns(nil) + + e = assert_raise(RuntimeError) do + with_structure_dump_flags(["--noop"]) do + quietly { ActiveRecord::Tasks::DatabaseTasks.structure_dump(@configuration, filename, "/rails/root") } + end + end + assert_match("failed to execute:", e.message) + ensure + FileUtils.rm_f(filename) + FileUtils.rm_f(dbfile) + end + + private + def with_structure_dump_flags(flags) + old = ActiveRecord::Tasks::DatabaseTasks.structure_dump_flags + ActiveRecord::Tasks::DatabaseTasks.structure_dump_flags = flags + yield + ensure + ActiveRecord::Tasks::DatabaseTasks.structure_dump_flags = old + end end class SqliteStructureLoadTest < ActiveRecord::TestCase diff --git a/activerecord/test/migrations/empty/.gitkeep b/activerecord/test/migrations/empty/.keep index e69de29bb2..e69de29bb2 100644 --- a/activerecord/test/migrations/empty/.gitkeep +++ b/activerecord/test/migrations/empty/.keep diff --git a/activerecord/test/models/author.rb b/activerecord/test/models/author.rb index 09958ca257..3371fcbfcc 100644 --- a/activerecord/test/models/author.rb +++ b/activerecord/test/models/author.rb @@ -21,7 +21,7 @@ class Author < ActiveRecord::Base end has_many :comments_containing_the_letter_e, through: :posts, source: :comments has_many :comments_with_order_and_conditions, -> { order("comments.body").where("comments.body like 'Thank%'") }, through: :posts, source: :comments - has_many :comments_with_include, -> { includes(:post) }, through: :posts, source: :comments + has_many :comments_with_include, -> { includes(:post).where(posts: { type: "Post" }) }, through: :posts, source: :comments has_many :first_posts has_many :comments_on_first_posts, -> { order("posts.id desc, comments.id asc") }, through: :first_posts, source: :comments @@ -40,6 +40,7 @@ class Author < ActiveRecord::Base class_name: "Post" has_many :comments_desc, -> { order("comments.id DESC") }, through: :posts, source: :comments + has_many :unordered_comments, -> { unscope(:order).distinct }, through: :posts_sorted_by_id_limited, source: :comments has_many :funky_comments, through: :posts, source: :comments has_many :ordered_uniq_comments, -> { distinct.order("comments.id") }, through: :posts, source: :comments has_many :ordered_uniq_comments_desc, -> { distinct.order("comments.id DESC") }, through: :posts, source: :comments @@ -78,7 +79,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/post.rb b/activerecord/test/models/post.rb index 4c8e847354..935a11e811 100644 --- a/activerecord/test/models/post.rb +++ b/activerecord/test/models/post.rb @@ -21,7 +21,7 @@ class Post < ActiveRecord::Base scope :containing_the_letter_a, -> { where("body LIKE '%a%'") } scope :titled_with_an_apostrophe, -> { where("title LIKE '%''%'") } - scope :ranked_by_comments, -> { order("comments_count DESC") } + scope :ranked_by_comments, -> { order(arel_attribute(:comments_count).desc) } scope :limit_by, lambda { |l| limit(l) } scope :locked, -> { lock } |