2013-07-02 16:08:04 -04:00
require 'active_support/test_case'
2015-01-15 01:42:33 -05:00
require 'active_support/testing/stream'
2013-07-02 16:08:04 -04:00
module ActiveRecord
2014-07-16 10:15:16 -04:00
# = Active Record Test Case
#
# Defines some test assertions to test against SQL queries.
class TestCase < ActiveSupport :: TestCase #:nodoc:
2015-01-15 01:42:33 -05:00
include ActiveSupport :: Testing :: Stream
2014-07-16 10:15:16 -04:00
def teardown
SQLCounter . clear_log
end
def assert_date_from_db ( expected , actual , message = nil )
assert_equal expected . to_s , actual . to_s , message
end
2014-03-31 19:18:04 -04:00
def capture_sql
SQLCounter . clear_log
yield
SQLCounter . log_all . dup
end
2013-07-02 16:08:04 -04:00
def assert_sql ( * patterns_to_match )
2014-03-31 22:53:45 -04:00
capture_sql { yield }
2013-07-02 16:08:04 -04:00
ensure
failed_patterns = [ ]
patterns_to_match . each do | pattern |
failed_patterns << pattern unless SQLCounter . log_all . any? { | sql | pattern === sql }
end
2014-10-27 12:28:53 -04:00
assert failed_patterns . empty? , " Query pattern(s) #{ failed_patterns . map ( & :inspect ) . join ( ', ' ) } not found. #{ SQLCounter . log . size == 0 ? '' : " \n Queries: \n #{ SQLCounter . log . join ( " \n " ) } " } "
2013-07-02 16:08:04 -04:00
end
def assert_queries ( num = 1 , options = { } )
ignore_none = options . fetch ( :ignore_none ) { num == :any }
SQLCounter . clear_log
x = yield
the_log = ignore_none ? SQLCounter . log_all : SQLCounter . log
if num == :any
assert_operator the_log . size , :>= , 1 , " 1 or more queries expected, but none were executed. "
else
mesg = " #{ the_log . size } instead of #{ num } queries were executed. #{ the_log . size == 0 ? '' : " \n Queries: \n #{ the_log . join ( " \n " ) } " } "
assert_equal num , the_log . size , mesg
end
x
end
2013-07-30 04:01:00 -04:00
def assert_no_queries ( options = { } , & block )
options . reverse_merge! ignore_none : true
assert_queries ( 0 , options , & block )
2013-07-02 16:08:04 -04:00
end
2013-07-16 08:19:24 -04:00
def assert_column ( model , column_name , msg = nil )
assert has_column? ( model , column_name ) , msg
end
def assert_no_column ( model , column_name , msg = nil )
assert_not has_column? ( model , column_name ) , msg
end
def has_column? ( model , column_name )
model . reset_column_information
model . column_names . include? ( column_name . to_s )
end
2013-07-02 16:08:04 -04:00
end
class SQLCounter
class << self
attr_accessor :ignored_sql , :log , :log_all
def clear_log ; self . log = [ ] ; self . log_all = [ ] ; end
end
self . clear_log
self . ignored_sql = [ / ^PRAGMA / , / ^SELECT currval / , / ^SELECT CAST / , / ^SELECT @@IDENTITY / , / ^SELECT @@ROWCOUNT / , / ^SAVEPOINT / , / ^ROLLBACK TO SAVEPOINT / , / ^RELEASE SAVEPOINT / , / ^SHOW max_identifier_length / , / ^BEGIN / , / ^COMMIT / ]
# FIXME: this needs to be refactored so specific database can add their own
# ignored SQL, or better yet, use a different notification for the queries
# instead examining the SQL content.
2013-11-17 21:25:29 -05:00
oracle_ignored = [ / ^select .*nextval /i , / ^SAVEPOINT / , / ^ROLLBACK TO / , / ^ \ s*select .* from all_triggers /im , / ^ \ s*select .* from all_constraints /im , / ^ \ s*select .* from all_tab_cols /im ]
2014-08-14 13:41:17 -04:00
mysql_ignored = [ / ^SHOW TABLES /i , / ^SHOW FULL FIELDS / , / ^SHOW CREATE TABLE /i , / ^SHOW VARIABLES / ]
2014-08-20 04:49:03 -04:00
postgresql_ignored = [ / ^ \ s*select \ b.* \ bfrom \ b.*pg_namespace \ b /im , / ^ \ s*select tablename \ b.*from pg_tables \ b /im , / ^ \ s*select \ b.* \ battname \ b.* \ bfrom \ b.* \ bpg_attribute \ b /im , / ^SHOW search_path /i ]
2015-05-24 02:24:02 -04:00
sqlite3_ignored = [ / ^ \ s*SELECT name \ b.* \ bFROM sqlite_master /im , / ^ \ s*SELECT sql \ b.* \ bFROM sqlite_master /im ]
2013-07-02 16:08:04 -04:00
[ oracle_ignored , mysql_ignored , postgresql_ignored , sqlite3_ignored ] . each do | db_ignored_sql |
ignored_sql . concat db_ignored_sql
end
attr_reader :ignore
def initialize ( ignore = Regexp . union ( self . class . ignored_sql ) )
@ignore = ignore
end
def call ( name , start , finish , message_id , values )
sql = values [ :sql ]
# FIXME: this seems bad. we should probably have a better way to indicate
# the query was cached
return if 'CACHE' == values [ :name ]
self . class . log_all << sql
self . class . log << sql unless ignore =~ sql
end
end
ActiveSupport :: Notifications . subscribe ( 'sql.active_record' , SQLCounter . new )
2012-02-06 01:09:09 -05:00
end