db_migration.rb 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. # require all database migrations so we can test them without manual require
  2. Rails.root.join('db/migrate').children.each do |migration|
  3. require migration.to_s
  4. end
  5. module DbMigrationHelper
  6. # Provides a helper method to execute a migration for the current class.
  7. # Make sure to define type: :db_migration in your RSpec.describe call.
  8. #
  9. # @param [Symbol] direction the migration should take (:up or :down)
  10. # @yield [instance] Yields the created instance of the
  11. # migration to allow expectations or other changes to it
  12. #
  13. # @example
  14. # migrate
  15. #
  16. # @example
  17. # migrate(:down)
  18. #
  19. # @return [nil]
  20. def migrate(direction = :up)
  21. instance = described_class.new
  22. yield(instance) if block_given?
  23. instance.suppress_messages do
  24. instance.migrate(direction)
  25. end
  26. end
  27. # Provides a helper method to remove foreign_keys if exist.
  28. # Make sure to define type: :db_migration in your RSpec.describe call
  29. # and add `self.use_transactional_tests = false` to your context.
  30. #
  31. # ATTENTION: We do not use the same arguments as the internally
  32. # used methods since giving a table name
  33. # as a second argument as e.g.
  34. # `remove_foreign_key(:online_notifications, :users)`
  35. # doesn't remove the index at first execution on at least MySQL
  36. #
  37. # @param [Symbol] from_table the name of the table with the foreign_key column
  38. # @param [Symbol] column the name of the foreign_key column
  39. #
  40. # @example
  41. # without_foreign_key(:online_notifications, column: :user_id)
  42. #
  43. # @return [nil]
  44. def without_foreign_key(from_table, column:)
  45. suppress_messages do
  46. break if !foreign_key_exists?(from_table, column: column)
  47. remove_foreign_key(from_table, column: column)
  48. end
  49. end
  50. # Helper method for setting up specs on DB migrations that add columns.
  51. # Make sure to define type: :db_migration in your RSpec.describe call
  52. # and add `self.use_transactional_tests = false` to your context.
  53. #
  54. # @param [Symbol] from_table the name of the table with the indexed column
  55. # @param [Symbol] name(s) of indexed column(s)
  56. #
  57. # @example
  58. # without_column(:online_notifications, column: :user_id)
  59. #
  60. # @return [nil]
  61. def without_column(from_table, column:)
  62. suppress_messages do
  63. Array(column).each do |elem|
  64. next if !column_exists?(from_table, elem)
  65. remove_column(from_table, elem)
  66. end
  67. end
  68. end
  69. # Helper method for setting up specs on DB migrations that add indices.
  70. # Make sure to define type: :db_migration in your RSpec.describe call
  71. # and add `self.use_transactional_tests = false` to your context.
  72. #
  73. # @param [Symbol] from_table the name of the table with the indexed column
  74. # @param [Symbol] name(s) of indexed column(s)
  75. #
  76. # @example
  77. # without_index(:online_notifications, column: :user_id)
  78. #
  79. # @return [nil]
  80. def without_index(from_table, column:)
  81. suppress_messages do
  82. break if !index_exists?(from_table, column)
  83. remove_index(from_table, column: column)
  84. end
  85. end
  86. # Enables the usage of `ActiveRecord::Migration` methods.
  87. #
  88. # @see ActiveRecord::Migration
  89. #
  90. # @example
  91. # remove_foreign_key(:online_notifications, :users)
  92. #
  93. # @return [nil]
  94. def method_missing(method, *args, &blk)
  95. ActiveRecord::Migration.send(method, *args, &blk)
  96. rescue NoMethodError
  97. super
  98. end
  99. # Enables the usage of `ActiveRecord::Migration` methods.
  100. #
  101. # @see ActiveRecord::Migration
  102. #
  103. # @example
  104. # remove_foreign_key(:online_notifications, :users)
  105. #
  106. # @return [nil]
  107. def respond_to_missing?(*)
  108. true
  109. end
  110. # Provides a helper method to check if migration class adds a foreign key.
  111. # Make sure to define type: :db_migration in your RSpec.describe call
  112. # and add `self.use_transactional_tests = false` to your context.
  113. #
  114. # @param [Symbol] from_table the name of the table with the foreign_key column
  115. # @param [Symbol] column the name of the foreign_key column
  116. #
  117. # @example
  118. # adds_foreign_key(:online_notifications, column: :user_id)
  119. #
  120. # @return [nil]
  121. def adds_foreign_key(from_table, column:)
  122. without_foreign_key(from_table, column: column)
  123. suppress_messages do
  124. expect do
  125. migrate
  126. end.to change {
  127. foreign_key_exists?(from_table, column: column)
  128. }
  129. end
  130. end
  131. def self.included(base)
  132. # Execute in RSpec class context
  133. base.class_exec do
  134. # This method simulates a system that is is already initialized
  135. # aka `Setting.exists?(name: 'system_init_done')`
  136. # It's possible to simulate a not yet initialized system by adding the
  137. # meta tag `system_init_done` to `false` to the needing example:
  138. #
  139. # @example
  140. # it 'does stuff in an unitialized system', system_init_done: false do
  141. #
  142. before(:each) do |example|
  143. initialized = example.metadata.fetch(:system_init_done, true)
  144. system_init_done(initialized)
  145. end
  146. end
  147. end
  148. end
  149. RSpec.configure do |config|
  150. config.include DbMigrationHelper, type: :db_migration
  151. end