assert_efficient_sql 0.3.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -8,7 +8,7 @@ require 'assert2'
8
8
  # ERGO check for valid options
9
9
  # ERGO highlite the offending row in the analysis
10
10
  # ERGO cite http://hackmysql.com/selectandsort
11
- # ERGO link from http://efficient-sql.rubyforge.org/files/README.html to
11
+ # ERGO link from http://efficient-sql.rubyforge.org/files/README.html to
12
12
  # project page
13
13
  # ERGO hamachi.cc
14
14
  # ERGO is flunk susceptible to <?> bug?
@@ -60,7 +60,7 @@ module ActiveRecord
60
60
 
61
61
  # TODO use extended?
62
62
  # http://www.mysqlperformanceblog.com/2006/07/24/extended-explain/
63
- #
63
+ #
64
64
  # ERGO p select_without_analyzer('show warnings')
65
65
  # hence, show warnings caused by your queries, too
66
66
 
@@ -73,7 +73,7 @@ module ActiveRecord
73
73
  end
74
74
  ) if sql =~ /^select/i
75
75
  end
76
-
76
+
77
77
  explained = [sql, name, analysis.map(&:with_indifferent_access)]
78
78
  (@analyses ||= []) << explained
79
79
  end
@@ -87,14 +87,14 @@ end
87
87
  module AssertEfficientSql; end
88
88
 
89
89
  class AssertEfficientSql::SqlEfficiencyAsserter
90
-
90
+
91
91
  def initialize(options, context)
92
92
  @issues = []
93
93
  @options, @context = options, context
94
94
  @analyses = ActiveRecord::Base.connection.analyses
95
95
  @session_before = fetch_database_session
96
96
  yield
97
- check_for_query_statements
97
+ check_for_query_statements
98
98
  @session_after = fetch_database_session
99
99
  check_session_status
100
100
 
@@ -103,7 +103,7 @@ class AssertEfficientSql::SqlEfficiencyAsserter
103
103
  analyze_efficiency
104
104
  end
105
105
  end
106
-
106
+
107
107
  puts explain_all if @options[:verbose]
108
108
  end
109
109
 
@@ -138,27 +138,27 @@ class AssertEfficientSql::SqlEfficiencyAsserter
138
138
  end
139
139
  end
140
140
  end
141
-
141
+
142
142
  def analyze_efficiency
143
143
  rows = @explanation[:rows].to_i
144
144
  throttle = @options[:throttle]
145
-
145
+
146
146
  check rows <= throttle do
147
147
  "row count #{ rows } is more than :throttle => #{ throttle }"
148
148
  end
149
-
150
- check @options[:ALL] || 'ALL' != @explanation[:type] do
151
- 'full table scan'
149
+
150
+ check @options[:ALL] || 'ALL' != @explanation[:type] do
151
+ 'full table scan'
152
152
  end
153
-
153
+
154
154
  check @options[:Using_filesort] ||
155
155
  @explanation[:Extra] !~ /(Using filesort)/ do
156
156
  $1
157
157
  end
158
-
158
+
159
159
  flunk 'Pessimistic ' + format_explanation unless @issues.empty?
160
160
  end
161
-
161
+
162
162
  def check_for_query_statements
163
163
  flunk 'assert_efficient_sql saw no queries!' if @analyses.empty?
164
164
  end
@@ -166,35 +166,37 @@ class AssertEfficientSql::SqlEfficiencyAsserter
166
166
  def flunk(why)
167
167
  @context.flunk @context.build_message(@options[:diagnostic], why)
168
168
  end
169
-
169
+
170
170
  def format_explanation
171
171
  @name = 'for ' + @name unless @name.blank?
172
172
 
173
173
  return "\nquery #{ @name }\n" +
174
174
  @issues.join("\n") +
175
- "\n#{ @sql }\n " +
175
+ "\n#{ @sql }\n " +
176
176
  @analysis.qa_columnized
177
177
  end
178
178
 
179
179
  end
180
-
180
+
181
181
  #:startdoc:
182
182
 
183
183
 
184
184
  module AssertEfficientSql
185
-
186
- # See: http://www.oreillynet.com/onlamp/blog/2007/07/assert_latest_and_greatest.html
187
-
185
+
186
+ # This collects every model in the given class that appears while its block runs
188
187
  def assert_latest(*models, &block)
189
188
  models, diagnostic = _get_latest_args(models, 'assert')
190
- get_latest(models, &block) or _flunk_latest(models, diagnostic, true, block)
189
+ latests = get_latest(models, &block)
190
+ latests.compact.length == models.length or
191
+ _flunk_latest(models, latests, diagnostic, true, block)
192
+ return *latests
191
193
  end
192
194
 
193
- def _get_latest_args(models, what)
195
+ def _get_latest_args(models, what) #:nodoc:
194
196
  diagnostic = nil
195
197
  diagnostic = models.pop if models.last.kind_of? String
196
-
197
- unless models.length > 0 and
198
+
199
+ unless models.length > 0 and
198
200
  (diagnostic.nil? or diagnostic.kind_of? String)
199
201
  raise "call #{ what }_latest(models..., diagnostic) with any number " +
200
202
  'of Model classes, followed by an optional diagnostic message'
@@ -202,49 +204,55 @@ module AssertEfficientSql
202
204
  return models, diagnostic
203
205
  end
204
206
  private :_get_latest_args
205
-
207
+
206
208
  def deny_latest(*models, &block)
207
209
  models, diagnostic = _get_latest_args(models, 'deny')
208
- return unless got = get_latest(models, &block)
209
- models = [got].flatten.compact.map(&:class)
210
- _flunk_latest(models, diagnostic, false, block)
210
+ latests = get_latest(models, &block)
211
+ return if latests.compact.empty?
212
+ models = [latests].flatten.compact.map(&:class)
213
+ _flunk_latest(models, latests, diagnostic, false, block)
211
214
  end
212
215
 
213
216
  def get_latest(models, &block)
214
217
  max_ids = models.map{|model| model.maximum(:id) || 0 }
215
218
  block.call
216
219
  index = -1
217
- return *models.map{|model|
220
+ return models.map{|model|
218
221
  all = *model.find( :all,
219
222
  :conditions => "id > #{max_ids[index += 1]}",
220
223
  :order => "id asc" )
221
- all # * returns nil for [],
222
- # one object for [x],
224
+ all # * returns nil for [],
225
+ # one object for [x],
223
226
  # or an array with more than one item
224
227
  }
225
228
  end
226
-
227
- def _flunk_latest(models, diagnostic, polarity, block)
228
- model_names = models.map(&:name).join(', ')
229
- rationale = "should#{ ' not' unless polarity
229
+
230
+ def _flunk_latest(models, latests, diagnostic, polarity, block) #:nodoc:
231
+ model_names = models.map(&:name)
232
+ model_names.each_with_index do |it, index|
233
+ model_names[index] = nil if !!latests[index] == polarity
234
+ end
235
+ model_names = model_names.compact.join(', ')
236
+ rationale = "should#{ ' not' unless polarity
230
237
  } create new #{ model_names
231
- } record(s) in block:\n\t\t#{
232
- reflect_source(&block).gsub("\n", "\n\t\t")
238
+ } record(s) in block:\n\t\t#{
239
+ reflect_source(&block).gsub("\n", "\n\t\t")
233
240
  }\n"
234
241
  # RubyNodeReflector::RubyReflector.new(block, false).result }"
235
242
  # note we don't evaluate...
236
243
  flunk build_message(diagnostic, rationale)
237
244
  end
238
245
  private :_flunk_latest
239
-
240
-
246
+
247
+
241
248
  def _exec(cmd) #:nodoc:
242
249
  ActiveRecord::Base.connection.execute(cmd)
243
250
  end
244
251
 
252
+ # See: http://www.oreillynet.com/onlamp/blog/2007/07/assert_latest_and_greatest.html
245
253
  def assert_efficient_sql(options = {}, &block)
246
254
  options = { :verbose => true } if options == :verbose
247
-
255
+
248
256
  if options.class == Hash
249
257
  options.reverse_merge! default_options
250
258
 
@@ -257,13 +265,83 @@ module AssertEfficientSql
257
265
  else
258
266
  print_syntax
259
267
  end
260
-
268
+
261
269
  return []
262
270
  end
263
271
 
272
+ def self._singulars #:nodoc:
273
+ return [ [:ids, :id ], [:key_lens, :key_len ],
274
+ [:keys, :key ], [:refs, :ref ],
275
+ [:rows, :rows ], [:select_types, :select_type],
276
+ [:tables, :table ], [:types, :type ] ]
277
+ end
278
+
279
+ def self._multiples #:nodoc:
280
+ return [ [:extras, :Extra ],
281
+ [:possible_keys, :possible_keys] ]
282
+ end
283
+
284
+ # def test_order_transactions_by_id
285
+ # sqls = inspect_sql :match => /SELECT conversations.\*/ do
286
+ # get_sales_after_transaction_id
287
+ # end
288
+ # statement = sqls.first[:statement]
289
+ # assert{ statement =~ /ORDER BY id asc/ }
290
+ # end
291
+ #
292
+ def inspect_sql(options = {}, &block)
293
+ verbose = options.fetch(:verbose, false)
294
+ match = options.fetch(:match, /./)
295
+ args = { :ALL => true, :Using_filesort => true, :verbose => verbose }
296
+ inspections = assert_efficient_sql(args, &block)
297
+ # pp inspections.first
298
+
299
+ explains = inspections.inject([]) do |a, inspection|
300
+ if inspection.first =~ match
301
+ explanations = inspection.last
302
+
303
+ h = { :statement => inspection.first,
304
+ :explanations => explanations }
305
+
306
+ AssertEfficientSql._singulars.each do |rollup, explainer|
307
+ h[rollup] = explanations.map{|e| q = e[explainer] and q.to_sym }
308
+ end
309
+
310
+ AssertEfficientSql._multiples.each do |rollup, explainer|
311
+ h[rollup] = explanations.map do |e|
312
+ q = e[explainer]
313
+ [q].flatten.map do |e|
314
+ e and
315
+ e.any? and
316
+ (rollup == :extras ? e.gsub!(' ', '_') : e ) and
317
+ e.to_sym
318
+ end
319
+ end
320
+ end
321
+ a << h
322
+ end
323
+ a
324
+ end
325
+ return AssertEfficientSql._decorate_explainers(explains)
326
+ end
327
+
328
+ def self._decorate_explainers(explains) #:nodoc:
329
+ def explains.find_statements(regexp)
330
+ return AssertEfficientSql._decorate_explainers(select{|explanation|
331
+ explanation[:statement] =~ regexp
332
+ })
333
+ end
334
+
335
+ (_multiples + _singulars).each do |rollup, explainer|
336
+ eval "def explains.#{rollup}; map{|q|q[:#{rollup}]}.flatten.uniq; end"
337
+ end
338
+
339
+ return explains
340
+ end # FIXME gotta be private
341
+
264
342
  class BufferStdout #:nodoc:
265
343
  def write(stuff)
266
- (@output ||= '') << stuff
344
+ (@output ||= '') << stuff
267
345
  end
268
346
  def output; @output || '' end
269
347
  end
@@ -284,19 +362,19 @@ module AssertEfficientSql
284
362
  end
285
363
 
286
364
  private
287
-
365
+
288
366
  def current_adapter?(type) #:nodoc:
289
367
  ActiveRecord::ConnectionAdapters.const_defined?(type) and
290
368
  ActiveRecord::Base.connection.instance_of?(ActiveRecord::ConnectionAdapters.const_get(type))
291
369
  end
292
-
370
+
293
371
  def warn_adapter_required(options)
294
372
  if options[:warn_mysql_required]
295
373
  puts 'assert_efficient_sql requires MySQL' unless $warned_once
296
374
  $warned_once = true
297
- end
375
+ end
298
376
  end
299
-
377
+
300
378
  def assert_efficient_mysql(options, &block)
301
379
  outer_block_analyses = ActiveRecord::Base.connection.analyses
302
380
  ActiveRecord::Base.connection.analyses = []
@@ -306,7 +384,7 @@ module AssertEfficientSql
306
384
  ensure
307
385
  ActiveRecord::Base.connection.analyses = outer_block_analyses
308
386
  end
309
-
387
+
310
388
  def syntax
311
389
  return {
312
390
  :diagnostic => [nil , 'supplementary message in failure reports'],
@@ -316,22 +394,22 @@ module AssertEfficientSql
316
394
  :verbose => [false, 'if the test passes, print the EXPLAIN'],
317
395
  :warn_mysql_required => [true , 'disable the spew advising we only work with MySQL'] }
318
396
  end
319
-
397
+
320
398
  def default_options
321
399
  options = syntax.dup
322
400
  options.each{|k,(v,m)| options[k] = v}
323
401
  return options
324
402
  end
325
-
403
+
326
404
  def print_syntax
327
405
  puts "\n\nassert_efficient_sql called with invalid argument.\n"
328
406
  puts " __flag__ __default__ __effect__"
329
-
407
+
330
408
  syntax.each do |k,(v,m)|
331
409
  printf " :%-14s => %-8s # %s\n", k, v.inspect, m
332
410
  end
333
411
  end
334
-
412
+
335
413
  end
336
414
 
337
415
  #:stopdoc:
@@ -0,0 +1,384 @@
1
+
2
+ #:stopdoc:
3
+
4
+ require 'rubygems'
5
+ require 'active_record'
6
+ require 'active_record/connection_adapters/mysql_adapter.rb'
7
+ require 'test/unit'
8
+ require 'pathname'
9
+ require 'yaml'
10
+ require 'optparse'
11
+ require 'erb'
12
+ Assert_efficient_sql_root = (Pathname.new(__FILE__).expand_path.dirname + '..').expand_path
13
+ load Assert_efficient_sql_root + 'lib/assert_efficient_sql.rb'
14
+
15
+ # ERGO even work if you are sqLite3
16
+ class Foo < ActiveRecord::Base; end
17
+ class T2 < ActiveRecord::Base; self.table_name ='t2'; end
18
+
19
+ #:startdoc:
20
+
21
+ class AssertEfficientSqlTest < Test::Unit::TestCase
22
+
23
+ def setup #:nodoc:
24
+ config_file = (Assert_efficient_sql_root + 'config/database.yml').to_s
25
+ config = YAML.load(ERB.new(IO.read(config_file)).result)['test']
26
+ ActiveRecord::Base.establish_connection(config)
27
+
28
+ ActiveRecord::Base.connection.
29
+ create_table(:foos) do |t|
30
+ t.column :name, :string, :limit => 60
31
+ end
32
+
33
+ 43.times{ Foo.create!(:name => 'whatever') }
34
+ _exec %[ CREATE TABLE `t2` (
35
+ `id` int(11) auto_increment,
36
+ `a` int(11) NOT NULL DEFAULT '0',
37
+ `b` blob,
38
+ `c` int(11) NOT NULL,
39
+ PRIMARY KEY (id)
40
+ ) ENGINE=InnoDB ] rescue nil
41
+ #system('mysql -u root beast_test')
42
+ end
43
+
44
+ # If +assert_efficient_sql+ (generally) dislikes your arguments,
45
+ # it will print out its default options, each with an explanation
46
+ #
47
+ def test_help
48
+ assert_stdout /invalid.*argument.*
49
+ verbose .* =\> .* false/mx do
50
+ assert_efficient_sql(:help){}
51
+ end
52
+ end
53
+
54
+ def test_assert_efficient_sql
55
+ assert_efficient_sql{ Foo.find(2) }
56
+ end
57
+
58
+ # If your SQL is already efficient, use <b>:verbose</b> to diagnose
59
+ # <i>why</i> it's efficient.
60
+ #
61
+ def test_verbose
62
+ assert_stdout /select_type/ do
63
+ assert_efficient_sql :verbose do
64
+ Foo.find_by_id(42)
65
+ end
66
+ end
67
+ end
68
+
69
+ # If your block did not call any SQL SELECT statements,
70
+ # you probably need a warning!
71
+ #
72
+ def test_require_sql
73
+ assert_flunked /no queries/ do
74
+ assert_efficient_sql{}
75
+ end
76
+ end
77
+
78
+ # This test case uses
79
+ # <code>assert_raise_message[http://www.oreillynet.com/onlamp/blog/2007/07/assert_raise_on_ruby_dont_just.html]</code>
80
+ # to demonstrate <code>assert_efficient_sql</code> failing:
81
+ #
82
+ def test_assert_inefficient_sql
83
+ assert_flunked /Pessimistic.*
84
+ full.table.scan.*
85
+ Foo.Load/mx do
86
+ assert_efficient_sql do
87
+ Foo.find_by_sql('select * from foos a')
88
+ end
89
+ end
90
+ end
91
+
92
+ # One common pessimization is a query that reads thousands
93
+ # of rows just to return a few. +assert_efficient_sql+
94
+ # counts the rows hit in each phase of an SQL +SELECT+,
95
+ # and faults if any row count exceeds <b>1,000</b>.
96
+ #
97
+ # Adjust this count with <b><code>:throttle => 42</code></b>.
98
+ #
99
+ def test_throttle
100
+ 101.times{|x| Foo.create :name => "foo_#{ x }" }
101
+
102
+ assert_flunked /Pessimistic.*
103
+ more.than.*100.*
104
+ Foo.Load/mx do
105
+ assert_efficient_sql :throttle => 100, :ALL => true do
106
+ Foo.find(:all)
107
+ end
108
+ end
109
+ end
110
+
111
+ # Sometimes you need an +ALL+, even while other <code>assert_efficient_sql</code>
112
+ # checks must pass. To positively declare we like +ALL+, pass it as the key of a
113
+ # +true+ option into the assertion:
114
+ #
115
+ def test_assert_all
116
+ assert_efficient_sql :ALL => true do
117
+ Foo.find(:all)
118
+ end
119
+ end
120
+
121
+ # If your +WHERE+ and +ORDER+ clauses are too complex,
122
+ # MySQL might need to write a file (or worse), just to
123
+ # satisfy a query. +assert_efficient_sql+ detects this
124
+ # pernicious situation:
125
+ #
126
+ def test_prevent_filesorts
127
+ _exec %[ CREATE TABLE `t1` (
128
+ `a` int(11) NOT NULL DEFAULT '0',
129
+ `b` blob NOT NULL,
130
+ `c` text NOT NULL,
131
+ PRIMARY KEY (`a`,`b`(255),`c`(255)),
132
+ KEY `t1ba` (`b`(10),`a`)
133
+ ) ENGINE=InnoDB ]
134
+
135
+ assert_flunked /Using.filesort/ do
136
+ assert_efficient_sql do
137
+ Foo.find_by_sql('SELECT a FROM t1 ORDER BY b')
138
+ end
139
+ end
140
+ ensure
141
+ _exec 'drop table t1' # ERGO if exist
142
+ end
143
+
144
+ def test_ALL_with_possible_keys
145
+ assert_efficient_sql :ALL => true, :Using_filesort => true, :key => false do
146
+ Foo.find_by_sql('SELECT a FROM t2 ORDER BY c')
147
+ end
148
+ end
149
+
150
+ # <code>assert_efficient_sql</code> calls
151
+ # <code>SHOW SESSION STATUS</code> before and after
152
+ # its sampled block. If you need to prevent an advanced
153
+ # pessimization, such as <code>Created_tmp_disk_tables</code>,
154
+ # pass <code>:Created_tmp_disk_tables => 0</code>. The
155
+ # assertion will compare difference in STATUS before
156
+ # and after calling its block. A difference greater
157
+ # than the allowed difference will trigger a fault.
158
+ #
159
+ # To test this, we simply detect a STATUS variable
160
+ # which is not a warning.
161
+ #
162
+ def test_declare_futile_war_on_Innodb_rows_read
163
+ assert_flunked /just.for.test.*
164
+ Innodb_rows_read/mx do
165
+ assert_efficient_sql :diagnostic => 'just for test!',
166
+ :Innodb_rows_read => 0 do
167
+ Foo.find(:all)
168
+ end
169
+ end
170
+ end
171
+
172
+ # You can also nest the assertion, to provide different
173
+ # options for different blocks. The assertion allows
174
+ # this because your test might also have some other
175
+ # reason to use blocks
176
+ #
177
+ def test_nest
178
+ outer_result = assert_efficient_sql do
179
+ inner_result = assert_efficient_sql :ALL => true do
180
+ Foo.find(:all)
181
+ end
182
+
183
+ assert_no_match /where/i, inner_result[0][0]
184
+ Foo.find(42)
185
+ end
186
+ assert_match /where/i, outer_result[0][0]
187
+ end
188
+
189
+ def test_inspect_sql
190
+ inspections = inspect_sql do
191
+ Foo.find_by_sql('SELECT id FROM t2 ORDER BY c')
192
+ Foo.find_by_sql('SELECT c FROM t2 ORDER BY a')
193
+ Foo.find_by_sql('SELECT * FROM foos, t2')
194
+ end
195
+ assert{ inspections.length == 3 }
196
+ assert{ inspections[0][:statement] == 'SELECT id FROM t2 ORDER BY c' }
197
+ assert{ inspections[0][:possible_keys] == [[nil]] }
198
+ assert{ inspections[0][:extras] == [[:Using_filesort]] }
199
+ assert{ inspections[0][:types] == [:ALL] }
200
+ assert{ inspections[1][:tables] == [:t2] }
201
+ assert{ inspections[1][:possible_keys] == [[nil]] }
202
+ assert{ inspections[2][:tables] == [:t2, :foos] } # note the order is constrained!
203
+ assert{ inspections[2][:possible_keys] == [[nil], [nil]] }
204
+ assert{ inspections[1][:keys] == [nil] } # [:PRIMARY] }
205
+ assert{ inspections[2][:keys] == [nil, nil] }
206
+ end
207
+
208
+ def test_inspect_sql_explanations
209
+ inspections = inspect_sql do
210
+ Foo.find_by_sql('SELECT a FROM t2 ORDER BY c')
211
+ Foo.find_by_sql('SELECT c FROM t2 ORDER BY a')
212
+ Foo.find_by_sql('SELECT * FROM foos, t2')
213
+ end
214
+ assert{ inspections.length == 3 }
215
+ assert{ inspections[0][:statement] == 'SELECT a FROM t2 ORDER BY c' }
216
+ assert{ inspections[0][:explanations].first[:possible_keys] == nil }
217
+ assert{ inspections[0][:explanations].first[:Extra] == 'Using_filesort' }
218
+ assert{ inspections[0][:explanations].first[:type] == 'ALL' }
219
+ assert{ inspections[1][:explanations].first[:table] == 't2' }
220
+ assert{ inspections[1][:explanations].first[:possible_keys] == nil }
221
+ assert{ inspections[2][:explanations].first[:table] == 't2' } # note the order is constrained!
222
+ assert{ inspections[1][:explanations].first[:possible_keys] == nil }
223
+ assert{ inspections[2][:explanations].first[:possible_keys] == nil }
224
+ end
225
+
226
+ def test_inspect_sql_find_statements
227
+ inspections = inspect_sql do
228
+ Foo.find_by_sql('SELECT a FROM t2 ORDER BY c')
229
+ Foo.find_by_sql('SELECT c FROM t2 ORDER BY a')
230
+ Foo.find_by_sql('SELECT * FROM foos, t2')
231
+ end
232
+ inspections = inspections.find_statements(/from t2/i)
233
+ assert{ inspections[0][:statement] == 'SELECT a FROM t2 ORDER BY c' }
234
+ assert{ inspections[1][:explanations].first[:table] == 't2' }
235
+ deny{ inspections[2] }
236
+ assert{ inspections.tables == [:t2] }
237
+ #assert{ inspections.keys.to_set == [nil, :PRIMARY].to_set }
238
+ end
239
+
240
+ def test_inspect_certain_sql_statements
241
+ inspections = inspect_sql :match => /from t2/i do
242
+ Foo.find_by_sql('SELECT a FROM t2 ORDER BY c')
243
+ Foo.find_by_sql('SELECT c FROM t2 ORDER BY a')
244
+ Foo.find_by_sql('SELECT * FROM foos, t2')
245
+ end
246
+ assert{ inspections[0][:statement] == 'SELECT a FROM t2 ORDER BY c' }
247
+ assert{ inspections[1][:explanations].first[:table] == 't2' }
248
+ deny{ inspections[2] }
249
+ end
250
+
251
+ def test_inspect_sql_rollups
252
+ inspections = inspect_sql :verbose => false do
253
+ Foo.find_by_sql('SELECT a FROM t2 ORDER BY c')
254
+ Foo.find_by_sql('SELECT c FROM t2 ORDER BY a')
255
+ Foo.find_by_sql('SELECT * FROM foos, t2')
256
+ end
257
+ assert{ inspections.tables.to_set == [:t2, :foos].to_set }
258
+ assert{ inspections.keys.to_set == [nil].to_set }
259
+ end
260
+
261
+ def test_assert_latest
262
+ assert_flunked(/false/){ assert false }
263
+
264
+ assert_flunked /should create new Foo record\(s\) in/ do
265
+ assert_latest(Foo){ 'nada!' }
266
+ end
267
+
268
+ assert_flunked /because.*should not create new Foo record\(s\) in/m do
269
+ deny_latest Foo, 'because' do
270
+ Foo.create
271
+ end
272
+ end
273
+ nada = 42
274
+
275
+ assert_flunked /in block:\n.*nada/m do
276
+ assert_latest(Foo){ nada }
277
+ end
278
+
279
+ deny_flunked /42/ do # assert_latest shall not evaluate thy block
280
+ assert_latest(Foo){ nada }
281
+ end
282
+ end
283
+
284
+ def test_assert_polygamous_latest
285
+ f, t = assert_latest Foo, T2, 'diagnostic' do
286
+ Foo.create
287
+ T2.create(:c => 42)
288
+ end
289
+
290
+ assert{ f.id > 0 and t.id > 0 }
291
+
292
+ f1, f2 = assert_latest Foo do
293
+ 2.times{ Foo.create }
294
+ end
295
+
296
+ assert 'items return ordered by id' do
297
+ f1.id > 0 and f2.id > f1.id
298
+ end
299
+
300
+ t, fz = assert_latest T2, Foo do
301
+ T2.create(:c => 42)
302
+ 2.times{ Foo.create }
303
+ end
304
+
305
+ assert do
306
+ t.id > 0 and
307
+ fz[0].id > 0 and
308
+ fz[1].id > fz[0].id
309
+ end
310
+
311
+ t, (f1, f2) = assert_latest T2, Foo do
312
+ T2.create(:c => 42)
313
+ 2.times{ Foo.create }
314
+ end
315
+
316
+ assert do
317
+ t.id > 0 and
318
+ f1.id > 0 and
319
+ f2.id > f1.id
320
+ end
321
+ end
322
+
323
+
324
+ def test_assert_polygamous_latest_fails
325
+ assert_flunked /diagnostic.*new T2 rcord/m do
326
+ f, t = assert_latest Foo, T2, 'diagnostic' do
327
+ Foo.create
328
+ end
329
+ end
330
+ end
331
+
332
+ def test_deny_polygamous_latest_fails
333
+ assert_flunked /diagnostic.*new Foo record/m do
334
+ f, t = deny_latest Foo, T2, 'diagnostic' do
335
+ Foo.create
336
+ end
337
+ end
338
+ end
339
+
340
+ def teardown #:nodoc:
341
+ ActiveRecord::Base.connection.drop_table(:foos) rescue nil
342
+ ActiveRecord::Base.connection.drop_table(:t2) rescue nil
343
+ end
344
+
345
+ end
346
+
347
+ #:enddoc:
348
+
349
+ class AssertNonMysqlTest < Test::Unit::TestCase
350
+
351
+ # The assertion requires MySQL. To peacefully coexist with
352
+ # test rigs that use multiple adapters, we only warn, once,
353
+ # if MySQL is not found. If you don't need this warning,
354
+ # turn it off with :warn_mysql_required => false
355
+ #
356
+ def test_gently_forwarn_non_mysql_connectors
357
+ ActiveRecord::Base.establish_connection( :adapter => 'sqlite3',
358
+ :dbfile => ':memory:' )
359
+
360
+ deny_stdout /requires MySQL/, 'disabled warning' do
361
+ assert_efficient_sql :warn_mysql_required => false
362
+ end
363
+
364
+ assert_stdout /requires MySQL/, 'warn the first time' do
365
+ assert_efficient_sql
366
+ end
367
+
368
+ deny_stdout /requires MySQL/, 'don\'t warn the second time' do
369
+ assert_efficient_sql
370
+ end
371
+
372
+ ensure
373
+ config_file = (Assert_efficient_sql_root + 'config/database.yml').to_s
374
+ config = YAML.load(ERB.new(IO.read(config_file)).result)['test']
375
+ ActiveRecord::Base.establish_connection(config)
376
+ end
377
+
378
+ end
379
+
380
+
381
+ def rails_root # ERGO is this used?
382
+ return (Pathname.new(__FILE__).expand_path.dirname + '../../../..').expand_path
383
+ end
384
+
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: assert_efficient_sql
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Phlip
@@ -9,11 +9,12 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2008-03-12 00:00:00 -07:00
12
+ date: 2008-09-08 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
- name: rubynode
16
+ name: assert2
17
+ type: :runtime
17
18
  version_requirement:
18
19
  version_requirements: !ruby/object:Gem::Requirement
19
20
  requirements:
@@ -22,7 +23,8 @@ dependencies:
22
23
  version: "0"
23
24
  version:
24
25
  - !ruby/object:Gem::Dependency
25
- name: assert2
26
+ name: rubynode
27
+ type: :runtime
26
28
  version_requirement:
27
29
  version_requirements: !ruby/object:Gem::Requirement
28
30
  requirements:
@@ -39,6 +41,7 @@ extensions: []
39
41
  extra_rdoc_files: []
40
42
 
41
43
  files:
44
+ - test/assert_efficient_sql_test.rb
42
45
  - lib/assert_efficient_sql.rb
43
46
  has_rdoc: false
44
47
  homepage: http://rubyforge.org/projects/efficient-sql
@@ -62,7 +65,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
62
65
  requirements: []
63
66
 
64
67
  rubyforge_project: efficient-sql
65
- rubygems_version: 1.0.1
68
+ rubygems_version: 1.2.0
66
69
  signing_key:
67
70
  specification_version: 2
68
71
  summary: efficient assertions for ActiveRecord tests