db_migration.rb 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  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 indices.
  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_index(:online_notifications, column: :user_id)
  59. #
  60. # @return [nil]
  61. def without_index(from_table, column:)
  62. suppress_messages do
  63. break if !index_exists?(from_table, column)
  64. remove_index(from_table, column: column)
  65. end
  66. end
  67. # Enables the usage of `ActiveRecord::Migration` methods.
  68. #
  69. # @see ActiveRecord::Migration
  70. #
  71. # @example
  72. # remove_foreign_key(:online_notifications, :users)
  73. #
  74. # @return [nil]
  75. def method_missing(method, *args, &blk)
  76. ActiveRecord::Migration.send(method, *args, &blk)
  77. rescue NoMethodError
  78. super
  79. end
  80. # Enables the usage of `ActiveRecord::Migration` methods.
  81. #
  82. # @see ActiveRecord::Migration
  83. #
  84. # @example
  85. # remove_foreign_key(:online_notifications, :users)
  86. #
  87. # @return [nil]
  88. def respond_to_missing?(*)
  89. true
  90. end
  91. # Provides a helper method to check if migration class adds a foreign key.
  92. # Make sure to define type: :db_migration in your RSpec.describe call
  93. # and add `self.use_transactional_tests = false` to your context.
  94. #
  95. # @param [Symbol] from_table the name of the table with the foreign_key column
  96. # @param [Symbol] column the name of the foreign_key column
  97. #
  98. # @example
  99. # adds_foreign_key(:online_notifications, column: :user_id)
  100. #
  101. # @return [nil]
  102. def adds_foreign_key(from_table, column:)
  103. without_foreign_key(from_table, column: column)
  104. suppress_messages do
  105. expect do
  106. migrate
  107. end.to change {
  108. foreign_key_exists?(from_table, column: column)
  109. }
  110. end
  111. end
  112. def self.included(base)
  113. # Execute in RSpec class context
  114. base.class_exec do
  115. # This method simulates a system that is is already initialized
  116. # aka `Setting.exists?(name: 'system_init_done')`
  117. # It's possible to simulate a not yet initialized system by adding the
  118. # meta tag `system_init_done` to `false` to the needing example:
  119. #
  120. # @example
  121. # it 'does stuff in an unitialized system', system_init_done: false do
  122. #
  123. before(:each) do |example|
  124. initialized = example.metadata.fetch(:system_init_done, true)
  125. system_init_done(initialized)
  126. end
  127. end
  128. end
  129. end
  130. RSpec.configure do |config|
  131. config.include DbMigrationHelper, type: :db_migration
  132. end