spec: make specs resilient to pre-existing records, refs SD-1570
in jenkins-land we've historically used db snapshots so the db will be empty. but if you `rake db:migrate` your test db, you'll have things like default accounts, notifications, etc., which can break certain specs Change-Id: I175e651d1b50671e630dc0181d1c4d25e78abe97 Reviewed-on: https://gerrit.instructure.com/95249 Reviewed-by: Landon Wilkins <lwilkins@instructure.com> Product-Review: Landon Wilkins <lwilkins@instructure.com> QA-Review: Landon Wilkins <lwilkins@instructure.com> Tested-by: Jenkins
This commit is contained in:
parent
26242baa7f
commit
82eac2107c
|
@ -60,19 +60,15 @@ module WebMock::API
|
|||
end
|
||||
end
|
||||
|
||||
# ensure people aren't creating records outside the rspec lifecycle, e.g.
|
||||
# inside a describe/context block rather than a let/before/example
|
||||
# nuke the db (say, if `rake db:migrate RAILS_ENV=test` created records),
|
||||
# and then ensure people aren't creating records outside the rspec
|
||||
# lifecycle, e.g. inside a describe/context block rather than a
|
||||
# let/before/example
|
||||
require_relative 'support/blank_slate_protection'
|
||||
BlankSlateProtection.enable!
|
||||
BlankSlateProtection.install!
|
||||
|
||||
require_relative 'support/discourage_slow_specs'
|
||||
|
||||
RSpec::Core::ExampleGroup.singleton_class.prepend(Module.new {
|
||||
def run_examples(*)
|
||||
BlankSlateProtection.disable { super }
|
||||
end
|
||||
})
|
||||
|
||||
Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f }
|
||||
|
||||
ActionView::TestCase::TestController.view_paths = ApplicationController.view_paths
|
||||
|
|
|
@ -1,28 +1,36 @@
|
|||
require_relative "./call_stack_utils"
|
||||
|
||||
module BlankSlateProtection
|
||||
def create_or_update
|
||||
return super unless BlankSlateProtection.enabled?
|
||||
return super if caller.grep(BlankSlateProtection.exempt_patterns).present?
|
||||
module ActiveRecord
|
||||
def create_or_update
|
||||
return super unless BlankSlateProtection.enabled?
|
||||
return super if caller.grep(BlankSlateProtection.exempt_patterns).present?
|
||||
|
||||
location = CallStackUtils.best_line_for(caller).sub(/:in .*/, '')
|
||||
if caller.grep(/_context_hooks/).present?
|
||||
$stderr.puts "\e[31mError: Don't create records inside `:all` hooks!"
|
||||
$stderr.puts "See: " + location + "\e[0m"
|
||||
location = CallStackUtils.best_line_for(caller).sub(/:in .*/, '')
|
||||
if caller.grep(/_context_hooks/).present?
|
||||
$stderr.puts "\e[31mError: Don't create records inside `:all` hooks!"
|
||||
$stderr.puts "See: " + location + "\e[0m"
|
||||
$stderr.puts
|
||||
$stderr.puts "\e[33mTIP:\e[0m change this to `:each`, or if you are really concerned"
|
||||
$stderr.puts "about performance, use `:once`. `:all` hooks are dangerous because"
|
||||
$stderr.puts "they can leave around garbage that affects later specs"
|
||||
else
|
||||
$stderr.puts "\e[31mError: Don't create records outside the rspec lifecycle!"
|
||||
$stderr.puts "See: " + location + "\e[0m"
|
||||
$stderr.puts
|
||||
$stderr.puts "\e[33mTIP:\e[0m move this into a `before`, `let` or `it`. Otherwise it will exist"
|
||||
$stderr.puts "before *any* specs start, and possibly be deleted/modified before the"
|
||||
$stderr.puts "spec that needs it actually runs."
|
||||
end
|
||||
$stderr.puts
|
||||
$stderr.puts "\e[33mTIP:\e[0m change this to `:each`, or if you are really concerned"
|
||||
$stderr.puts "about performance, use `:once`. `:all` hooks are dangerous because"
|
||||
$stderr.puts "they can leave around garbage that affects later specs"
|
||||
else
|
||||
$stderr.puts "\e[31mError: Don't create records outside the rspec lifecycle!"
|
||||
$stderr.puts "See: " + location + "\e[0m"
|
||||
$stderr.puts
|
||||
$stderr.puts "\e[33mTIP:\e[0m move this into a `before`, `let` or `it`. Otherwise it will exist"
|
||||
$stderr.puts "before *any* specs start, and possibly be deleted/modified before the"
|
||||
$stderr.puts "spec that needs it actually runs."
|
||||
exit! 1
|
||||
end
|
||||
end
|
||||
|
||||
module ExampleGroup
|
||||
def run_examples(*)
|
||||
BlankSlateProtection.disable { super }
|
||||
end
|
||||
$stderr.puts
|
||||
exit! 1
|
||||
end
|
||||
|
||||
# switchman and once-ler have special snowflake context hooks where data
|
||||
|
@ -34,26 +42,51 @@ module BlankSlateProtection
|
|||
@enabled
|
||||
end
|
||||
|
||||
def enable!
|
||||
def install!
|
||||
truncate_all_tables!
|
||||
::RSpec::Core::ExampleGroup.singleton_class.prepend ExampleGroup
|
||||
::ActiveRecord::Base.include ActiveRecord
|
||||
@enabled = true
|
||||
end
|
||||
|
||||
def disable!
|
||||
@enabled = false
|
||||
end
|
||||
|
||||
def disable
|
||||
enabled = @enabled
|
||||
disable!
|
||||
@enabled = false
|
||||
yield
|
||||
ensure
|
||||
@enabled = enabled
|
||||
@enabled = true
|
||||
end
|
||||
|
||||
def exempt_patterns
|
||||
Regexp.new(EXEMPT_PATTERNS.map { |pattern| Regexp.escape(pattern) }.join("|"))
|
||||
end
|
||||
|
||||
def get_table_names(connection)
|
||||
# use custom SQL to exclude tables from extensions
|
||||
schema = connection.shard.name if connection.instance_variable_get(:@config)[:use_qualified_names]
|
||||
table_names = connection.query(<<-SQL, 'SCHEMA').map(&:first)
|
||||
SELECT relname
|
||||
FROM pg_class INNER JOIN pg_namespace ON relnamespace=pg_namespace.oid
|
||||
WHERE nspname = #{schema ? "'#{schema}'" : 'ANY (current_schemas(false))'}
|
||||
AND relkind='r'
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM pg_depend WHERE deptype='e' AND objid=pg_class.oid
|
||||
)
|
||||
SQL
|
||||
table_names.delete('schema_migrations')
|
||||
table_names
|
||||
end
|
||||
|
||||
def truncate_all_tables!(quick: true)
|
||||
return if quick && Account.all.empty? # this is the most likely table to have stuff
|
||||
puts "truncating all tables..."
|
||||
Shard.with_each_shard do
|
||||
model_connections = ::ActiveRecord::Base.descendants.map(&:connection).uniq
|
||||
model_connections.each do |connection|
|
||||
table_names = get_table_names(connection)
|
||||
next if table_names.empty?
|
||||
connection.execute("TRUNCATE TABLE #{table_names.map { |t| connection.quote_table_name(t) }.join(',')}")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
ActiveRecord::Base.include BlankSlateProtection
|
||||
|
|
Loading…
Reference in New Issue