# Copyright (C) 2012-2022 Zammad Foundation, https://zammad-foundation.org/ # require all database migrations so we can test them without manual require Rails.root.join('db/migrate').children.each do |migration| require migration.to_s end module DbMigrationHelper # Provides a helper method to execute a migration for the current class. # Make sure to define type: :db_migration in your RSpec.describe call. # # @param [Symbol] direction the migration should take (:up or :down) # @yield [instance] Yields the created instance of the # migration to allow expectations or other changes to it # # @example # migrate # # @example # migrate(:down) # # @return [nil] def migrate(direction = :up) instance = described_class.new yield(instance) if block_given? instance.suppress_messages do instance.migrate(direction) end end # Provides a helper method to remove foreign_keys if exist. # Make sure to define type: :db_migration in your RSpec.describe call # and add `self.use_transactional_tests = false` to your context. # # ATTENTION: We do not use the same arguments as the internally # used methods since giving a table name # as a second argument as e.g. # `remove_foreign_key(:online_notifications, :users)` # doesn't remove the index at first execution on at least MySQL # # @param [Symbol] from_table the name of the table with the foreign_key column # @param [Symbol] column the name of the foreign_key column # # @example # without_foreign_key(:online_notifications, column: :user_id) # # @return [nil] def without_foreign_key(from_table, column:) suppress_messages do break if !foreign_key_exists?(from_table, column: column) remove_foreign_key(from_table, column: column) end end # Helper method for setting up specs on DB migrations that add columns. # Make sure to define type: :db_migration in your RSpec.describe call # and add `self.use_transactional_tests = false` to your context. # # @param [Symbol] from_table the name of the table with the indexed column # @param [Symbol] name(s) of indexed column(s) # # @example # without_column(:online_notifications, column: :user_id) # # @return [nil] def without_column(from_table, column:) suppress_messages do Array(column).each do |elem| next if !column_exists?(from_table, elem) remove_column(from_table, elem) end end end # Helper method for setting up specs on DB migrations that add indices. # Make sure to define type: :db_migration in your RSpec.describe call # and add `self.use_transactional_tests = false` to your context. # # @param [Symbol] from_table the name of the table with the indexed column # @param [Symbol] name(s) of indexed column(s) # # @example # without_index(:online_notifications, column: :user_id) # # @return [nil] def without_index(from_table, column:) suppress_messages do break if !index_exists?(from_table, column) remove_index(from_table, column: column) end end # Enables the usage of `ActiveRecord::Migration` methods. # # @see ActiveRecord::Migration # # @example # remove_foreign_key(:online_notifications, :users) # # @return [nil] def method_missing(method, *args, &blk) ActiveRecord::Migration.send(method, *args, &blk) rescue NoMethodError super end # Enables the usage of `ActiveRecord::Migration` methods. # # @see ActiveRecord::Migration # # @example # remove_foreign_key(:online_notifications, :users) # # @return [nil] def respond_to_missing?(...) true end # Provides a helper method to check if migration class adds a foreign key. # Make sure to define type: :db_migration in your RSpec.describe call # and add `self.use_transactional_tests = false` to your context. # # @param [Symbol] from_table the name of the table with the foreign_key column # @param [Symbol] column the name of the foreign_key column # # @example # adds_foreign_key(:online_notifications, column: :user_id) # # @return [nil] def adds_foreign_key(from_table, column:) without_foreign_key(from_table, column: column) suppress_messages do expect do migrate end.to change { foreign_key_exists?(from_table, column: column) } end end def self.included(base) # Execute in RSpec class context base.class_exec do # This method simulates a system that is is already initialized # aka `Setting.exists?(name: 'system_init_done')` # It's possible to simulate a not yet initialized system by adding the # meta tag `system_init_done` to `false` to the needing example: # # @example # it 'does stuff in an unitialized system', system_init_done: false do # before do |example| initialized = example.metadata.fetch(:system_init_done, true) system_init_done(initialized) end end end end RSpec.configure do |config| config.include DbMigrationHelper, type: :db_migration end