aboutsummaryrefslogblamecommitdiffstats
path: root/activerecord/test/cases/migration/compatibility_test.rb
blob: 726ccf925e684a3844005f5d6d0a7fd4229144ed (plain) (tree)
1
2
3
4

                             
                      
                                       













                                                      
                                          
                                            





                                                      
                                                           


                                                  
                                                                       











                                                                                            














                                                             

                                                       
                                                             














                                                                    

                                                                                           
                                                             








                                                            

                                                                                 



                                                       










                                                                                           

                                                                            

         
















                                                                                                       
                                                                                                                   
                                                             






                                                            

                                                                            
         
 





























































                                                                                                                
                                                                

                                                                            
           
                                                                                    
         
 













                                                                             




























                                                                               






                                                               
                                                                                               


               
                         

                                                              

                                         

           



                                             
                            
              
                              

             


       
 
                                

                             
















                                                        
                                             

                                                
                                  





                           
                             
 
                                                               
                                             
 












                                                                    
                                             















                                                                                           




                                                               
           

         
 
                           
 

                             
 




                                                               
           

                                                
           

         
 
                           
 
                             

     




                                                               
           


                                                          
 
                           
 
                             

     
                                                           
                                             













                                                                 
                                             












                                                                                     

                                                                 
                                               







                                                        

                                                  





                                                                                                
                                               















                                                                                            







                                                     
                                              






                                                                                                              
   























                                        
# frozen_string_literal: true

require "cases/helper"
require "support/schema_dumping_helper"

module ActiveRecord
  class Migration
    class CompatibilityTest < ActiveRecord::TestCase
      attr_reader :connection
      self.use_transactional_tests = false

      def setup
        super
        @connection = ActiveRecord::Base.connection
        @verbose_was = ActiveRecord::Migration.verbose
        ActiveRecord::Migration.verbose = false

        connection.create_table :testings do |t|
          t.column :foo, :string, limit: 5
          t.column :bar, :string, limit: 100
        end
      end

      teardown do
        connection.drop_table :testings rescue nil
        ActiveRecord::Migration.verbose = @verbose_was
        ActiveRecord::SchemaMigration.delete_all rescue nil
      end

      def test_migration_doesnt_remove_named_index
        connection.add_index :testings, :foo, name: "custom_index_name"

        migration = Class.new(ActiveRecord::Migration[4.2]) {
          def version; 101 end
          def migrate(x)
            remove_index :testings, :foo
          end
        }.new

        assert connection.index_exists?(:testings, :foo, name: "custom_index_name")
        assert_raise(StandardError) { ActiveRecord::Migrator.new(:up, [migration]).migrate }
        assert connection.index_exists?(:testings, :foo, name: "custom_index_name")
      end

      def test_migration_does_remove_unnamed_index
        connection.add_index :testings, :bar

        migration = Class.new(ActiveRecord::Migration[4.2]) {
          def version; 101 end
          def migrate(x)
            remove_index :testings, :bar
          end
        }.new

        assert connection.index_exists?(:testings, :bar)
        ActiveRecord::Migrator.new(:up, [migration]).migrate
        assert_not connection.index_exists?(:testings, :bar)
      end

      def test_references_does_not_add_index_by_default
        migration = Class.new(ActiveRecord::Migration[4.2]) {
          def migrate(x)
            create_table :more_testings do |t|
              t.references :foo
              t.belongs_to :bar, index: false
            end
          end
        }.new

        ActiveRecord::Migrator.new(:up, [migration]).migrate

        assert_not connection.index_exists?(:more_testings, :foo_id)
        assert_not connection.index_exists?(:more_testings, :bar_id)
      ensure
        connection.drop_table :more_testings rescue nil
      end

      def test_timestamps_have_null_constraints_if_not_present_in_migration_of_create_table
        migration = Class.new(ActiveRecord::Migration[4.2]) {
          def migrate(x)
            create_table :more_testings do |t|
              t.timestamps
            end
          end
        }.new

        ActiveRecord::Migrator.new(:up, [migration]).migrate

        assert connection.column_exists?(:more_testings, :created_at, null: true)
        assert connection.column_exists?(:more_testings, :updated_at, null: true)
      ensure
        connection.drop_table :more_testings rescue nil
      end

      def test_timestamps_have_null_constraints_if_not_present_in_migration_of_change_table
        migration = Class.new(ActiveRecord::Migration[4.2]) {
          def migrate(x)
            change_table :testings do |t|
              t.timestamps
            end
          end
        }.new

        ActiveRecord::Migrator.new(:up, [migration]).migrate

        assert connection.column_exists?(:testings, :created_at, null: true)
        assert connection.column_exists?(:testings, :updated_at, null: true)
      end

      if ActiveRecord::Base.connection.supports_bulk_alter?
        def test_timestamps_have_null_constraints_if_not_present_in_migration_of_change_table_with_bulk
          migration = Class.new(ActiveRecord::Migration[4.2]) {
            def migrate(x)
              change_table :testings, bulk: true do |t|
                t.timestamps
              end
            end
          }.new

          ActiveRecord::Migrator.new(:up, [migration]).migrate

          assert connection.column_exists?(:testings, :created_at, null: true)
          assert connection.column_exists?(:testings, :updated_at, null: true)
        end
      end

      def test_timestamps_have_null_constraints_if_not_present_in_migration_for_adding_timestamps_to_existing_table
        migration = Class.new(ActiveRecord::Migration[4.2]) {
          def migrate(x)
            add_timestamps :testings
          end
        }.new

        ActiveRecord::Migrator.new(:up, [migration]).migrate

        assert connection.column_exists?(:testings, :created_at, null: true)
        assert connection.column_exists?(:testings, :updated_at, null: true)
      end

      def test_timestamps_doesnt_set_precision_on_create_table
        migration = Class.new(ActiveRecord::Migration[5.2]) {
          def migrate(x)
            create_table :more_testings do |t|
              t.timestamps
            end
          end
        }.new

        ActiveRecord::Migrator.new(:up, [migration]).migrate

        assert connection.column_exists?(:more_testings, :created_at, null: false, **precision_implicit_default)
        assert connection.column_exists?(:more_testings, :updated_at, null: false, **precision_implicit_default)
      ensure
        connection.drop_table :more_testings rescue nil
      end

      def test_timestamps_doesnt_set_precision_on_change_table
        migration = Class.new(ActiveRecord::Migration[5.2]) {
          def migrate(x)
            change_table :testings do |t|
              t.timestamps default: Time.now
            end
          end
        }.new

        ActiveRecord::Migrator.new(:up, [migration]).migrate

        assert connection.column_exists?(:testings, :created_at, null: false, **precision_implicit_default)
        assert connection.column_exists?(:testings, :updated_at, null: false, **precision_implicit_default)
      end

      if ActiveRecord::Base.connection.supports_bulk_alter?
        def test_timestamps_doesnt_set_precision_on_change_table_with_bulk
          migration = Class.new(ActiveRecord::Migration[5.2]) {
            def migrate(x)
              change_table :testings, bulk: true do |t|
                t.timestamps
              end
            end
          }.new

          ActiveRecord::Migrator.new(:up, [migration]).migrate

          assert connection.column_exists?(:testings, :created_at, null: false, **precision_implicit_default)
          assert connection.column_exists?(:testings, :updated_at, null: false, **precision_implicit_default)
        end
      end

      def test_timestamps_doesnt_set_precision_on_add_timestamps
        migration = Class.new(ActiveRecord::Migration[5.2]) {
          def migrate(x)
            add_timestamps :testings, default: Time.now
          end
        }.new

        ActiveRecord::Migrator.new(:up, [migration]).migrate

        assert connection.column_exists?(:testings, :created_at, null: false, **precision_implicit_default)
        assert connection.column_exists?(:testings, :updated_at, null: false, **precision_implicit_default)
      end

      def test_legacy_migrations_raises_exception_when_inherited
        e = assert_raises(StandardError) do
          class_eval("class LegacyMigration < ActiveRecord::Migration; end")
        end
        assert_match(/LegacyMigration < ActiveRecord::Migration\[4\.2\]/, e.message)
      end

      def test_legacy_migrations_not_raise_exception_on_reverting_transaction
        migration = Class.new(ActiveRecord::Migration[5.2]) {
          def change
            transaction do
              execute "select 1"
            end
          end
        }.new

        assert_nothing_raised do
          migration.migrate(:down)
        end
      end

      if ActiveRecord::Base.connection.supports_comments?
        def test_change_column_comment_can_be_reverted
          migration = Class.new(ActiveRecord::Migration[5.2]) {
            def migrate(x)
              revert do
                change_column_comment(:testings, :foo, "comment")
              end
            end
          }.new

          ActiveRecord::Migrator.new(:up, [migration]).migrate
          assert connection.column_exists?(:testings, :foo, comment: "comment")
        end

        def test_change_table_comment_can_be_reverted
          migration = Class.new(ActiveRecord::Migration[5.2]) {
            def migrate(x)
              revert do
                change_table_comment(:testings, "comment")
              end
            end
          }.new

          ActiveRecord::Migrator.new(:up, [migration]).migrate

          assert_equal "comment", connection.table_comment("testings")
        end
      end

      if current_adapter?(:PostgreSQLAdapter)
        class Testing < ActiveRecord::Base
        end

        def test_legacy_change_column_with_null_executes_update
          migration = Class.new(ActiveRecord::Migration[5.1]) {
            def migrate(x)
              change_column :testings, :foo, :string, limit: 10, null: false, default: "foobar"
            end
          }.new

          Testing.create!
          ActiveRecord::Migrator.new(:up, [migration]).migrate
          assert_equal ["foobar"], Testing.all.map(&:foo)
        ensure
          ActiveRecord::Base.clear_cache!
        end
      end

      private
        def precision_implicit_default
          if current_adapter?(:Mysql2Adapter)
            { precision: 0 }
          else
            { precision: nil }
          end
        end
    end
  end
end

module LegacyPrimaryKeyTestCases
  include SchemaDumpingHelper

  class LegacyPrimaryKey < ActiveRecord::Base
  end

  def setup
    @migration = nil
    @verbose_was = ActiveRecord::Migration.verbose
    ActiveRecord::Migration.verbose = false
  end

  def teardown
    @migration.migrate(:down) if @migration
    ActiveRecord::Migration.verbose = @verbose_was
    ActiveRecord::SchemaMigration.delete_all rescue nil
    LegacyPrimaryKey.reset_column_information
  end

  def test_legacy_primary_key_should_be_auto_incremented
    @migration = Class.new(migration_class) {
      def change
        create_table :legacy_primary_keys do |t|
          t.references :legacy_ref
        end
      end
    }.new

    @migration.migrate(:up)

    assert_legacy_primary_key

    legacy_ref = LegacyPrimaryKey.columns_hash["legacy_ref_id"]
    assert_not_predicate legacy_ref, :bigint?

    record1 = LegacyPrimaryKey.create!
    assert_not_nil record1.id

    record1.destroy

    record2 = LegacyPrimaryKey.create!
    assert_not_nil record2.id
    assert_operator record2.id, :>, record1.id
  end

  def test_legacy_integer_primary_key_should_not_be_auto_incremented
    skip if current_adapter?(:SQLite3Adapter)

    @migration = Class.new(migration_class) {
      def change
        create_table :legacy_primary_keys, id: :integer do |t|
        end
      end
    }.new

    @migration.migrate(:up)

    assert_raises(ActiveRecord::NotNullViolation) do
      LegacyPrimaryKey.create!
    end

    schema = dump_table_schema "legacy_primary_keys"
    assert_match %r{create_table "legacy_primary_keys", id: :integer, default: nil}, schema
  end

  def test_legacy_primary_key_in_create_table_should_be_integer
    @migration = Class.new(migration_class) {
      def change
        create_table :legacy_primary_keys, id: false do |t|
          t.primary_key :id
        end
      end
    }.new

    @migration.migrate(:up)

    assert_legacy_primary_key
  end

  def test_legacy_primary_key_in_change_table_should_be_integer
    @migration = Class.new(migration_class) {
      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)

    assert_legacy_primary_key
  end

  def test_add_column_with_legacy_primary_key_should_be_integer
    @migration = Class.new(migration_class) {
      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)

    assert_legacy_primary_key
  end

  def test_legacy_join_table_foreign_keys_should_be_integer
    @migration = Class.new(migration_class) {
      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(migration_class) {
      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(migration_class) {
        def change
          create_table :legacy_primary_keys, id: :bigint
        end
      }.new

      @migration.migrate(:up)

      legacy_pk = LegacyPrimaryKey.columns_hash["id"]
      assert_predicate legacy_pk, :bigint?
      assert_predicate legacy_pk, :auto_increment?

      schema = dump_table_schema "legacy_primary_keys"
      assert_match %r{create_table "legacy_primary_keys", (?!id: :bigint, default: nil)}, schema
    end
  else
    def test_legacy_bigint_primary_key_should_not_be_auto_incremented
      @migration = Class.new(migration_class) {
        def change
          create_table :legacy_primary_keys, id: :bigint do |t|
          end
        end
      }.new

      @migration.migrate(:up)

      assert_raises(ActiveRecord::NotNullViolation) do
        LegacyPrimaryKey.create!
      end

      schema = dump_table_schema "legacy_primary_keys"
      assert_match %r{create_table "legacy_primary_keys", id: :bigint, default: nil}, schema
    end
  end

  private
    def assert_legacy_primary_key
      assert_equal "id", LegacyPrimaryKey.primary_key

      legacy_pk = LegacyPrimaryKey.columns_hash["id"]

      assert_equal :integer, legacy_pk.type
      assert_not_predicate legacy_pk, :bigint?
      assert_not legacy_pk.null

      if current_adapter?(:Mysql2Adapter, :PostgreSQLAdapter)
        schema = dump_table_schema "legacy_primary_keys"
        assert_match %r{create_table "legacy_primary_keys", id: :(?:integer|serial), (?!default: nil)}, schema
      end
    end
end

module LegacyPrimaryKeyTest
  class V5_0 < ActiveRecord::TestCase
    include LegacyPrimaryKeyTestCases

    self.use_transactional_tests = false

    private
      def migration_class
        ActiveRecord::Migration[5.0]
      end
  end

  class V4_2 < ActiveRecord::TestCase
    include LegacyPrimaryKeyTestCases

    self.use_transactional_tests = false

    private
      def migration_class
        ActiveRecord::Migration[4.2]
      end
  end
end