activerecord-refined 0.3.1 → 0.3.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -2,31 +2,31 @@ require_relative 'test_helper'
2
2
 
3
3
  class TestBlockSyntax < Minitest::Test
4
4
  def test_equal
5
- assert_match(/WHERE "users"."name" = 'matz'/, User.where { :name == 'matz' }.to_sql)
5
+ assert_sql(/WHERE "users"."name" = 'matz'/, User.where { :name == 'matz' }.to_sql)
6
6
  end
7
7
 
8
8
  def test_not_equal
9
- assert_match(/WHERE "users"."name" != 'nobu'/, User.where { :name != 'nobu' }.to_sql)
9
+ assert_sql(/WHERE "users"."name" != 'nobu'/, User.where { :name != 'nobu' }.to_sql)
10
10
  end
11
11
 
12
12
  def test_greater_than
13
- assert_match(/WHERE "users"."age" > 3/, User.where { :age > 3 }.to_sql)
13
+ assert_sql(/WHERE "users"."age" > 3/, User.where { :age > 3 }.to_sql)
14
14
  end
15
15
 
16
16
  def test_greater_than_or_equal
17
- assert_match(/WHERE "users"."age" >= 18/, User.where { :age >= 18 }.to_sql)
17
+ assert_sql(/WHERE "users"."age" >= 18/, User.where { :age >= 18 }.to_sql)
18
18
  end
19
19
 
20
20
  def test_less_than
21
- assert_match(/WHERE "users"."age" < 60/, User.where { :age < 60 }.to_sql)
21
+ assert_sql(/WHERE "users"."age" < 60/, User.where { :age < 60 }.to_sql)
22
22
  end
23
23
 
24
24
  def test_less_than_or_equal
25
- assert_match(/WHERE "users"."age" <= 35/, User.where { :age <= 35 }.to_sql)
25
+ assert_sql(/WHERE "users"."age" <= 35/, User.where { :age <= 35 }.to_sql)
26
26
  end
27
27
 
28
28
  def test_like
29
- assert_match(/WHERE "users"."name" LIKE 'tender%'/, User.where { :name =~ 'tender%' }.to_sql)
29
+ assert_sql(/WHERE "users"."name" LIKE 'tender%'/, User.where { :name.like?('tender%') }.to_sql)
30
30
  end
31
31
 
32
32
  def test_outside_of_where_block
@@ -34,149 +34,432 @@ class TestBlockSyntax < Minitest::Test
34
34
  end
35
35
 
36
36
  def test_and
37
- assert_match(/WHERE "users"."name" = 'matz' AND "users"."age" > 18/,
37
+ assert_sql(/WHERE "users"."name" = 'matz' AND "users"."age" > 18/,
38
38
  User.where { (:name == 'matz') & (:age > 18) }.to_sql)
39
39
  end
40
40
 
41
41
  def test_or
42
- assert_match(/WHERE \(?\"users\".\"name\" = 'matz' OR \"users\".\"name\" = 'nobu'\)?/,
42
+ assert_sql(/WHERE \(?\"users\".\"name\" = 'matz' OR \"users\".\"name\" = 'nobu'\)?/,
43
43
  User.where { (:name == 'matz') | (:name == 'nobu') }.to_sql)
44
44
  end
45
45
 
46
46
  def test_not
47
- assert_match(/WHERE NOT \(?\"users\".\"name\" = 'matz'\)?/,
47
+ assert_sql(/WHERE NOT \(?\"users\".\"name\" = 'matz'\)?/,
48
48
  User.where { !(:name == 'matz') }.to_sql)
49
49
  end
50
50
 
51
51
  def test_complex_combination
52
52
  sql = User.where { ((:name == 'matz') & (:age > 18)) | !(:name == 'nobu') }.to_sql
53
- assert_match(/"users"."name" = 'matz'/, sql)
54
- assert_match(/"users"."age" > 18/, sql)
55
- assert_match(/NOT/, sql)
56
- assert_match(/OR/, sql)
53
+ assert_sql(/"users"."name" = 'matz'/, sql)
54
+ assert_sql(/"users"."age" > 18/, sql)
55
+ assert_sql(/NOT/, sql)
56
+ assert_sql(/OR/, sql)
57
57
  end
58
58
 
59
- def test_between
60
- assert_match(/WHERE "users"."age" BETWEEN 18 AND 65/,
61
- User.where { :age == (18..65) }.to_sql)
59
+ def test_like_qualified
60
+ assert_sql(/WHERE "users"."name" LIKE 'tender%'/,
61
+ User.where { :users[:name].like?('tender%') }.to_sql)
62
62
  end
63
63
 
64
64
  def test_not_like
65
- assert_match(/WHERE "users"."name" NOT LIKE 'tender%'/,
66
- User.where { :name !~ 'tender%' }.to_sql)
65
+ assert_sql(/WHERE NOT \("users"."name" LIKE 'tender%'\)/,
66
+ User.where { !:name.like?('tender%') }.to_sql)
67
+ end
68
+
69
+ def test_start_with
70
+ assert_sql(/WHERE "users"."name" LIKE 'tender%' ESCAPE '\\'/,
71
+ User.where { :name.start_with?('tender') }.to_sql)
72
+ end
73
+
74
+ def test_end_with
75
+ assert_sql(/WHERE "users"."name" LIKE '%love' ESCAPE '\\'/,
76
+ User.where { :name.end_with?('love') }.to_sql)
77
+ end
78
+
79
+ def test_include
80
+ assert_sql(/WHERE "users"."name" LIKE '%der%' ESCAPE '\\'/,
81
+ User.where { :name.include?('der') }.to_sql)
82
+ end
83
+
84
+ # Like their String namesakes, start_with? and end_with? take any number
85
+ # of literals; matching any one of them is enough.
86
+ def test_start_with_multiple
87
+ assert_sql(
88
+ /WHERE \("users"."name" LIKE 'ma%' ESCAPE '\\' OR "users"."name" LIKE 'no%' ESCAPE '\\'\)/,
89
+ User.where { :name.start_with?('ma', 'no') }.to_sql)
90
+ end
91
+
92
+ def test_end_with_multiple
93
+ assert_sql(
94
+ /WHERE \("users"."name" LIKE '%z' ESCAPE '\\' OR "users"."name" LIKE '%love' ESCAPE '\\'\)/,
95
+ User.where { :name.end_with?('z', 'love') }.to_sql)
96
+ end
97
+
98
+ # The OR arrives grouped, so a following & applies to the whole list.
99
+ def test_start_with_multiple_combined
100
+ assert_sql(
101
+ /WHERE \("users"."name" LIKE 'ma%' ESCAPE '\\' OR "users"."name" LIKE 'no%' ESCAPE '\\'\) AND "users"."age" > 18/,
102
+ User.where { :name.start_with?('ma', 'no') & (:age > 18) }.to_sql)
103
+ end
104
+
105
+ def test_start_with_no_arguments
106
+ assert_raises(ArgumentError) { User.where { :name.start_with? } }
107
+ end
108
+
109
+ def test_end_with_no_arguments
110
+ assert_raises(ArgumentError) { User.where { :name.end_with? } }
111
+ end
112
+
113
+ def test_start_with_escapes_wildcards
114
+ assert_sql(/WHERE "users"."name" LIKE '100\\%\\_%' ESCAPE '\\'/,
115
+ User.where { :name.start_with?('100%_') }.to_sql)
116
+ end
117
+
118
+ def test_include_escapes_wildcards
119
+ assert_sql(/WHERE "users"."name" LIKE '%100\\%%' ESCAPE '\\'/,
120
+ User.where { :name.include?('100%') }.to_sql)
121
+ end
122
+
123
+ def test_member
124
+ assert_sql(/WHERE "users"."tags" @> '\{ruby\}'/,
125
+ User.where { :tags.member?('ruby') }.to_sql)
126
+ end
127
+
128
+ def test_member_qualified
129
+ assert_sql(/WHERE "users"."tags" @> '\{ruby\}'/,
130
+ User.where { :users[:tags].member?('ruby') }.to_sql)
131
+ end
132
+
133
+ def test_member_negated
134
+ assert_sql(/WHERE NOT \("users"."tags" @> '\{ruby\}'\)/,
135
+ User.where { !:tags.member?('ruby') }.to_sql)
136
+ end
137
+
138
+ def test_member_multiple_elements
139
+ assert_sql(/WHERE "users"."tags" @> '\{ruby,rails\}'/,
140
+ User.where { :tags.member?(%w[ruby rails]) }.to_sql)
141
+ end
142
+
143
+ # MySQL additionally escapes the double quotes inside its string literal,
144
+ # so the exact spelling is only asserted where the operator is real.
145
+ def test_member_quotes_special_elements
146
+ skip_without_array_columns
147
+ assert_sql(/WHERE "users"."tags" @> '\{"with,comma"\}'/,
148
+ User.where { :tags.member?('with,comma') }.to_sql)
149
+ end
150
+
151
+ # include? is a substring match even on an array column; only member?
152
+ # means containment.
153
+ def test_include_is_like_even_on_array_columns
154
+ skip_without_array_columns
155
+ assert_sql(/WHERE "users"."tags" LIKE '%ruby%' ESCAPE '\\'/,
156
+ User.where { :tags.include?('ruby') }.to_sql)
157
+ end
158
+
159
+ # Elements survive the trip through the array literal: % is an ordinary
160
+ # character there, a comma stays inside its element, and quotes and
161
+ # backslashes are escaped.
162
+ def test_member_matches_elements_literally
163
+ skip_without_array_columns
164
+ User.delete_all
165
+ User.create!(name: 'literal', tags: ['100%', 'with,comma', 'q"uote', 'back\\slash'])
166
+ User.create!(name: 'lookalike', tags: ['100200', 'with', 'comma'])
167
+ assert_equal(['literal'], User.where { :tags.member?('100%') }.pluck(:name))
168
+ assert_equal(['literal'], User.where { :tags.member?('with,comma') }.pluck(:name))
169
+ assert_equal(['literal'], User.where { :tags.member?('q"uote') }.pluck(:name))
170
+ assert_equal(['literal'], User.where { :tags.member?('back\\slash') }.pluck(:name))
171
+ end
172
+
173
+ def test_regexp
174
+ skip_without_regexp_support
175
+ assert_sql(/WHERE "users"."name" #{regexp_operator} '\^ma'/,
176
+ User.where { :name =~ '^ma' }.to_sql)
177
+ end
178
+
179
+ def test_not_regexp
180
+ skip_without_regexp_support
181
+ assert_sql(/WHERE "users"."name" #{not_regexp_operator} '\^ma'/,
182
+ User.where { :name !~ '^ma' }.to_sql)
183
+ end
184
+
185
+ def test_regexp_qualified
186
+ skip_without_regexp_support
187
+ assert_sql(/WHERE "users"."name" #{regexp_operator} '\^ma'/,
188
+ User.where { :users[:name] =~ '^ma' }.to_sql)
189
+ end
190
+
191
+ def test_regexp_on_function
192
+ skip_without_regexp_support
193
+ assert_sql(/WHERE UPPER\("users"."name"\) #{regexp_operator} '\^MA'/,
194
+ User.where { upper(:name) =~ '^MA' }.to_sql)
195
+ end
196
+
197
+ def test_regexp_literal
198
+ skip_without_regexp_support
199
+ assert_sql(/WHERE "users"."name" #{regexp_operator} 'love\$'/,
200
+ User.where { :name =~ /love$/ }.to_sql)
201
+ end
202
+
203
+ # Rejected while the block runs, so this holds on every adapter.
204
+ def test_regexp_literal_with_options_is_rejected
205
+ assert_raises(ArgumentError) { User.where { :name =~ /^ma/i } }
206
+ end
207
+
208
+ def test_between
209
+ assert_sql(/WHERE "users"."age" BETWEEN 18 AND 65/,
210
+ User.where { :age.between?(18, 65) }.to_sql)
211
+ end
212
+
213
+ def test_in_range
214
+ assert_sql(/WHERE "users"."age" BETWEEN 18 AND 65/,
215
+ User.where { :age.in?(18..65) }.to_sql)
216
+ end
217
+
218
+ def test_in_endless_range
219
+ assert_sql(/WHERE "users"."age" >= 18/,
220
+ User.where { :age.in?(18..) }.to_sql)
221
+ end
222
+
223
+ def test_in_exclusive_range
224
+ assert_sql(/WHERE "users"."age" >= 18 AND "users"."age" < 65/,
225
+ User.where { :age.in?(18...65) }.to_sql)
67
226
  end
68
227
 
69
228
  def test_not_between
70
- # Arel expands a bounded NOT BETWEEN into an OR of comparisons
71
- assert_match(/WHERE \("users"."age" < 18 OR "users"."age" > 65\)/,
72
- User.where { :age != (18..65) }.to_sql)
229
+ assert_sql(/WHERE NOT \("users"."age" BETWEEN 18 AND 65\)/,
230
+ User.where { !:age.between?(18, 65) }.to_sql)
73
231
  end
74
232
 
75
233
  def test_is_null
76
- assert_match(/WHERE "users"."name" IS NULL/,
234
+ assert_sql(/WHERE "users"."name" IS NULL/,
77
235
  User.where { :name.null? }.to_sql)
78
236
  end
79
237
 
80
238
  def test_is_null_qualified
81
- assert_match(/WHERE "users"."name" IS NULL/,
239
+ assert_sql(/WHERE "users"."name" IS NULL/,
82
240
  User.where { :users[:name].null? }.to_sql)
83
241
  end
84
242
 
243
+ def test_equal_nil_is_rejected
244
+ e = assert_raises(ArgumentError) { User.where { :name == nil } }
245
+ assert_match(/null\?/, e.message)
246
+ end
247
+
248
+ def test_not_equal_nil_is_rejected
249
+ e = assert_raises(ArgumentError) { User.where { :name != nil } }
250
+ assert_match(/null\?/, e.message)
251
+ end
252
+
85
253
  def test_in
86
- assert_match(/WHERE "users"."age" IN \(1, 2, 3\)/,
87
- User.where { :age == [1, 2, 3] }.to_sql)
254
+ assert_sql(/WHERE "users"."age" IN \(1, 2, 3\)/,
255
+ User.where { :age.in?([1, 2, 3]) }.to_sql)
256
+ end
257
+
258
+ def test_in_qualified
259
+ assert_sql(/WHERE "users"."age" IN \(1, 2, 3\)/,
260
+ User.where { :users[:age].in?([1, 2, 3]) }.to_sql)
88
261
  end
89
262
 
90
263
  def test_not_in
91
- assert_match(/WHERE "users"."age" NOT IN \(1, 2, 3\)/,
92
- User.where { :age != [1, 2, 3] }.to_sql)
264
+ assert_sql(/WHERE NOT \("users"."age" IN \(1, 2, 3\)\)/,
265
+ User.where { !:age.in?([1, 2, 3]) }.to_sql)
266
+ end
267
+
268
+ def test_in_subquery
269
+ assert_sql(
270
+ /WHERE "authors"."id" IN \(SELECT "posts"."author_id" FROM "posts" WHERE "posts"."title" = 'pub'\)/,
271
+ Author.where { :id.in?(Post.where(title: 'pub').select(:author_id)) }.to_sql)
272
+ end
273
+
274
+ # A relation without an explicit select list selects its primary key, the
275
+ # same way ActiveRecord's own where(id: relation) does.
276
+ def test_in_subquery_selects_primary_key_by_default
277
+ assert_sql(/WHERE "authors"."id" IN \(SELECT "posts"."id" FROM "posts"\)/,
278
+ Author.where { :id.in?(Post.all) }.to_sql)
279
+ end
280
+
281
+ def test_not_in_subquery
282
+ assert_sql(/WHERE NOT \("authors"."id" IN \(SELECT "posts"."author_id" FROM "posts"\)\)/,
283
+ Author.where { !:id.in?(Post.select(:author_id)) }.to_sql)
284
+ end
285
+
286
+ # The subquery correlates with the outer table through qualified columns,
287
+ # and its own where block goes through the DSL too.
288
+ def test_exists
289
+ assert_sql(
290
+ /WHERE EXISTS \(SELECT "posts"\.\* FROM "posts" WHERE "posts"."author_id" = "authors"."id"\)/,
291
+ Author.where { exists?(Post.where { :posts[:author_id] == :authors[:id] }) }.to_sql)
292
+ end
293
+
294
+ def test_not_exists
295
+ assert_sql(/WHERE NOT \(EXISTS \(SELECT "posts"\.\* FROM "posts"\)\)/,
296
+ Author.where { !exists?(Post.all) }.to_sql)
297
+ end
298
+
299
+ def test_exists_combined
300
+ assert_sql(
301
+ /WHERE "authors"."name" = 'matz' AND EXISTS \(SELECT "posts"\.\* FROM "posts" WHERE "posts"."title" = 'pub'\)/,
302
+ Author.where { (:name == 'matz') & exists?(Post.where(title: 'pub')) }.to_sql)
303
+ end
304
+
305
+ def test_exists_execution
306
+ Author.delete_all
307
+ Post.delete_all
308
+ with_post = Author.create!(name: 'with_post')
309
+ Author.create!(name: 'without')
310
+ Post.create!(title: 'pub', author_id: with_post.id)
311
+ correlated = -> { Post.where { :posts[:author_id] == :authors[:id] } }
312
+ assert_equal(['with_post'],
313
+ Author.where { exists?(correlated.call) }.pluck(:name))
314
+ assert_equal(['without'],
315
+ Author.where { !exists?(correlated.call) }.pluck(:name))
316
+ end
317
+
318
+ def test_in_subquery_execution
319
+ Author.delete_all
320
+ Post.delete_all
321
+ published = Author.create!(name: 'published')
322
+ drafting = Author.create!(name: 'drafting')
323
+ Post.create!(title: 'pub', author_id: published.id)
324
+ Post.create!(title: 'draft', author_id: drafting.id)
325
+ subquery = -> { Post.where(title: 'pub').select(:author_id) }
326
+ assert_equal(['published'],
327
+ Author.where { :id.in?(subquery.call) }.pluck(:name))
328
+ assert_equal(['drafting'],
329
+ Author.where { !:id.in?(subquery.call) }.pluck(:name))
330
+ end
331
+
332
+ # == passes a Range or an Array through as a value rather than expanding it,
333
+ # so that it compares against a PostgreSQL range or array column. The SQL
334
+ # literal depends on the column type, so assert on the Arel node instead.
335
+ def test_equal_range_is_an_equality
336
+ node = ActiveRecord::Refined::AST::Comparison.new(:period, :==, 18..65).
337
+ to_arel(User.arel_table)
338
+ assert_instance_of(Arel::Nodes::Equality, node)
339
+ assert_equal(18..65, node.right.value)
340
+ end
341
+
342
+ def test_equal_array_is_an_equality
343
+ node = ActiveRecord::Refined::AST::Comparison.new(:tags, :==, [1, 2, 3]).
344
+ to_arel(User.arel_table)
345
+ assert_instance_of(Arel::Nodes::Equality, node)
346
+ assert_equal([1, 2, 3], node.right.value)
347
+ end
348
+
349
+ def test_not_equal_array_is_an_inequality
350
+ node = ActiveRecord::Refined::AST::Comparison.new(:tags, :!=, [1, 2, 3]).
351
+ to_arel(User.arel_table)
352
+ assert_instance_of(Arel::Nodes::NotEqual, node)
353
+ assert_equal([1, 2, 3], node.right.value)
93
354
  end
94
355
 
95
356
  def test_qualified_column
96
- assert_match(/WHERE "users"."name" = 'matz'/,
357
+ assert_sql(/WHERE "users"."name" = 'matz'/,
97
358
  User.where { :users[:name] == 'matz' }.to_sql)
98
359
  end
99
360
 
100
361
  def test_column_to_column_comparison
101
362
  sql = User.where { :users[:name] == :users[:age] }.to_sql
102
- assert_match(/"users"."name" = "users"."age"/, sql)
363
+ assert_sql(/"users"."name" = "users"."age"/, sql)
103
364
  end
104
365
 
105
366
  def test_joins_with_block
106
367
  sql = Author.joins(:posts) { :posts[:author_id] == :authors[:id] }.to_sql
107
- assert_match(/INNER JOIN "posts" ON "posts"."author_id" = "authors"."id"/, sql)
368
+ assert_sql(/INNER JOIN "posts" ON "posts"."author_id" = "authors"."id"/, sql)
108
369
  end
109
370
 
110
371
  def test_left_outer_joins_with_block
111
372
  sql = Author.left_outer_joins(:posts) { :posts[:author_id] == :authors[:id] }.to_sql
112
- assert_match(/LEFT OUTER JOIN "posts" ON "posts"."author_id" = "authors"."id"/, sql)
373
+ assert_sql(/LEFT OUTER JOIN "posts" ON "posts"."author_id" = "authors"."id"/, sql)
113
374
  end
114
375
 
115
376
  def test_select_aggregate
116
- assert_match(/SELECT SUM\("users"."age"\)/,
377
+ assert_sql(/SELECT SUM\("users"."age"\)/,
117
378
  User.select { :age.sum }.to_sql)
118
379
  end
119
380
 
120
381
  def test_select_aggregate_qualified
121
- assert_match(/SELECT COUNT\("users"."id"\)/,
382
+ assert_sql(/SELECT COUNT\("users"."id"\)/,
122
383
  User.select { :users[:id].count }.to_sql)
123
384
  end
124
385
 
125
386
  def test_select_average
126
- assert_match(/SELECT AVG\("users"."age"\)/,
387
+ assert_sql(/SELECT AVG\("users"."age"\)/,
127
388
  User.select { :age.average }.to_sql)
128
389
  end
129
390
 
130
391
  def test_select_maximum
131
- assert_match(/SELECT MAX\("users"."age"\)/,
392
+ assert_sql(/SELECT MAX\("users"."age"\)/,
132
393
  User.select { :age.maximum }.to_sql)
133
394
  end
134
395
 
135
396
  def test_select_minimum
136
- assert_match(/SELECT MIN\("users"."age"\)/,
397
+ assert_sql(/SELECT MIN\("users"."age"\)/,
137
398
  User.select { :age.minimum }.to_sql)
138
399
  end
139
400
 
140
401
  def test_having_aggregate
141
402
  sql = User.group(:name).having { :age.sum > 100 }.to_sql
142
- assert_match(/GROUP BY "users"."name"/, sql)
143
- assert_match(/HAVING SUM\("users"."age"\) > 100/, sql)
403
+ assert_sql(/GROUP BY "users"."name"/, sql)
404
+ assert_sql(/HAVING SUM\("users"."age"\) > 100/, sql)
144
405
  end
145
406
 
146
407
  def test_select_avg_function
147
- assert_match(/SELECT AVG\("users"."age"\)/,
408
+ assert_sql(/SELECT AVG\("users"."age"\)/,
148
409
  User.select { avg(:age) }.to_sql)
149
410
  end
150
411
 
151
412
  def test_select_count_function
152
- assert_match(/SELECT COUNT\("users"."id"\)/,
413
+ assert_sql(/SELECT COUNT\("users"."id"\)/,
153
414
  User.select { count(:id) }.to_sql)
154
415
  end
155
416
 
417
+ def test_select_count_star
418
+ assert_sql(/SELECT COUNT\(\*\)/,
419
+ User.select { count(:*) }.to_sql)
420
+ end
421
+
422
+ def test_select_count_star_alias
423
+ assert_sql(/SELECT COUNT\(\*\) AS cnt/,
424
+ User.select { count(:*).as(:cnt) }.to_sql)
425
+ end
426
+
427
+ def test_having_count_star
428
+ sql = Author.joins(:posts) { :posts[:author_id] == :authors[:id] }.
429
+ group { :authors[:id] }.
430
+ having { count(:*) > 1 }.to_sql
431
+ assert_sql(/HAVING COUNT\(\*\) > 1/, sql)
432
+ end
433
+
434
+ def test_order_count_star
435
+ assert_sql(/ORDER BY COUNT\(\*\) DESC/,
436
+ User.group(:name).order { count(:*).desc }.to_sql)
437
+ end
438
+
156
439
  def test_select_sum_function
157
- assert_match(/SELECT SUM\("users"."age"\)/,
440
+ assert_sql(/SELECT SUM\("users"."age"\)/,
158
441
  User.select { sum(:age) }.to_sql)
159
442
  end
160
443
 
161
444
  def test_select_min_function
162
- assert_match(/SELECT MIN\("users"."age"\)/,
445
+ assert_sql(/SELECT MIN\("users"."age"\)/,
163
446
  User.select { min(:age) }.to_sql)
164
447
  end
165
448
 
166
449
  def test_select_max_function
167
- assert_match(/SELECT MAX\("users"."age"\)/,
450
+ assert_sql(/SELECT MAX\("users"."age"\)/,
168
451
  User.select { max(:age) }.to_sql)
169
452
  end
170
453
 
171
454
  def test_function_qualified_column
172
- assert_match(/SELECT AVG\("users"."age"\)/,
455
+ assert_sql(/SELECT AVG\("users"."age"\)/,
173
456
  User.select { avg(:users[:age]) }.to_sql)
174
457
  end
175
458
 
176
459
  def test_having_function
177
460
  sql = User.group(:name).having { sum(:age) > 100 }.to_sql
178
- assert_match(/GROUP BY "users"."name"/, sql)
179
- assert_match(/HAVING SUM\("users"."age"\) > 100/, sql)
461
+ assert_sql(/GROUP BY "users"."name"/, sql)
462
+ assert_sql(/HAVING SUM\("users"."age"\) > 100/, sql)
180
463
  end
181
464
 
182
465
  def test_function_and_method_syntax_match
@@ -185,123 +468,138 @@ class TestBlockSyntax < Minitest::Test
185
468
  end
186
469
 
187
470
  def test_upper_function
188
- assert_match(/SELECT UPPER\("users"."name"\)/,
471
+ assert_sql(/SELECT UPPER\("users"."name"\)/,
189
472
  User.select { upper(:name) }.to_sql)
190
473
  end
191
474
 
192
475
  def test_lower_function
193
- assert_match(/SELECT LOWER\("users"."name"\)/,
476
+ assert_sql(/SELECT LOWER\("users"."name"\)/,
194
477
  User.select { lower(:name) }.to_sql)
195
478
  end
196
479
 
197
480
  def test_length_function_in_where
198
- assert_match(/WHERE LENGTH\("users"."name"\) > 3/,
481
+ assert_sql(/WHERE LENGTH\("users"."name"\) > 3/,
199
482
  User.where { length(:name) > 3 }.to_sql)
200
483
  end
201
484
 
202
485
  def test_coalesce_function_with_literal
203
- assert_match(/SELECT COALESCE\("users"."name", 'unknown'\)/,
486
+ assert_sql(/SELECT COALESCE\("users"."name", 'unknown'\)/,
204
487
  User.select { coalesce(:name, 'unknown') }.to_sql)
205
488
  end
206
489
 
207
490
  def test_function_comparison
208
- assert_match(/WHERE UPPER\("users"."name"\) = 'MATZ'/,
491
+ assert_sql(/WHERE UPPER\("users"."name"\) = 'MATZ'/,
209
492
  User.where { upper(:name) == 'MATZ' }.to_sql)
210
493
  end
211
494
 
495
+ def test_function_like
496
+ assert_sql(/WHERE UPPER\("users"."name"\) LIKE 'MA%'/,
497
+ User.where { upper(:name).like?('MA%') }.to_sql)
498
+ end
499
+
500
+ def test_function_in
501
+ assert_sql(/WHERE UPPER\("users"."name"\) IN \('MATZ', 'NOBU'\)/,
502
+ User.where { upper(:name).in?(%w[MATZ NOBU]) }.to_sql)
503
+ end
504
+
505
+ def test_aggregate_in
506
+ assert_sql(/HAVING SUM\("users"."age"\) BETWEEN 1 AND 10/,
507
+ User.group(:name).having { :age.sum.in?(1..10) }.to_sql)
508
+ end
509
+
212
510
  def test_nested_function
213
- assert_match(/SELECT UPPER\(COALESCE\("users"."name", 'x'\)\)/,
511
+ assert_sql(/SELECT UPPER\(COALESCE\("users"."name", 'x'\)\)/,
214
512
  User.select { upper(coalesce(:name, 'x')) }.to_sql)
215
513
  end
216
514
 
217
515
  def test_function_qualified_column_arg
218
- assert_match(/SELECT UPPER\("users"."name"\)/,
516
+ assert_sql(/SELECT UPPER\("users"."name"\)/,
219
517
  User.select { upper(:users[:name]) }.to_sql)
220
518
  end
221
519
 
222
520
  def test_select_multiple_fields
223
- assert_match(/SELECT UPPER\("users"."name"\), "users"."age"/,
521
+ assert_sql(/SELECT UPPER\("users"."name"\), "users"."age"/,
224
522
  User.select { [upper(:name), :age] }.to_sql)
225
523
  end
226
524
 
227
525
  def test_select_multiple_columns
228
- assert_match(/SELECT "users"."name", "users"."age"/,
526
+ assert_sql(/SELECT "users"."name", "users"."age"/,
229
527
  User.select { [:name, :age] }.to_sql)
230
528
  end
231
529
 
232
530
  def test_select_multiple_with_aggregate
233
- assert_match(/SELECT "users"."name", SUM\("users"."age"\)/,
531
+ assert_sql(/SELECT "users"."name", SUM\("users"."age"\)/,
234
532
  User.select { [:name, sum(:age)] }.to_sql)
235
533
  end
236
534
 
237
535
  def test_select_function_with_alias
238
- assert_match(/SELECT UPPER\("users"."name"\) AS upper_name, "users"."age"/,
536
+ assert_sql(/SELECT UPPER\("users"."name"\) AS upper_name, "users"."age"/,
239
537
  User.select { [upper(:name).as(:upper_name), :age] }.to_sql)
240
538
  end
241
539
 
242
540
  def test_select_column_alias
243
- assert_match(/SELECT "users"."name" AS n/,
541
+ assert_sql(/SELECT "users"."name" AS n/,
244
542
  User.select { :name.as(:n) }.to_sql)
245
543
  end
246
544
 
247
545
  def test_select_qualified_column_alias
248
- assert_match(/SELECT "users"."name" AS n/,
546
+ assert_sql(/SELECT "users"."name" AS n/,
249
547
  User.select { :users[:name].as(:n) }.to_sql)
250
548
  end
251
549
 
252
550
  def test_select_aggregate_alias
253
- assert_match(/SELECT COUNT\("users"."id"\) AS cnt/,
551
+ assert_sql(/SELECT COUNT\("users"."id"\) AS cnt/,
254
552
  User.select { count(:id).as(:cnt) }.to_sql)
255
553
  end
256
554
 
257
555
  def test_order_default_asc
258
- assert_match(/ORDER BY "users"."age"/,
556
+ assert_sql(/ORDER BY "users"."age"/,
259
557
  User.order { :age }.to_sql)
260
558
  end
261
559
 
262
560
  def test_order_desc
263
- assert_match(/ORDER BY "users"."age" DESC/,
561
+ assert_sql(/ORDER BY "users"."age" DESC/,
264
562
  User.order { :age.desc }.to_sql)
265
563
  end
266
564
 
267
565
  def test_order_asc
268
- assert_match(/ORDER BY "users"."age" ASC/,
566
+ assert_sql(/ORDER BY "users"."age" ASC/,
269
567
  User.order { :age.asc }.to_sql)
270
568
  end
271
569
 
272
570
  def test_order_multiple
273
- assert_match(/ORDER BY "users"."age" DESC, "users"."name" ASC/,
571
+ assert_sql(/ORDER BY "users"."age" DESC, "users"."name" ASC/,
274
572
  User.order { [:age.desc, :name.asc] }.to_sql)
275
573
  end
276
574
 
277
575
  def test_order_qualified_column
278
- assert_match(/ORDER BY "users"."name" DESC/,
576
+ assert_sql(/ORDER BY "users"."name" DESC/,
279
577
  User.order { :users[:name].desc }.to_sql)
280
578
  end
281
579
 
282
580
  def test_group_single
283
- assert_match(/GROUP BY "users"."name"/,
581
+ assert_sql(/GROUP BY "users"."name"/,
284
582
  User.group { :name }.to_sql)
285
583
  end
286
584
 
287
585
  def test_group_multiple
288
- assert_match(/GROUP BY "users"."name", "users"."age"/,
586
+ assert_sql(/GROUP BY "users"."name", "users"."age"/,
289
587
  User.group { [:name, :age] }.to_sql)
290
588
  end
291
589
 
292
590
  def test_group_qualified_column
293
- assert_match(/GROUP BY "users"."name"/,
591
+ assert_sql(/GROUP BY "users"."name"/,
294
592
  User.group { :users[:name] }.to_sql)
295
593
  end
296
594
 
297
595
  def test_group_with_having
298
596
  sql = User.group { :name }.having { sum(:age) > 100 }.to_sql
299
- assert_match(/GROUP BY "users"."name"/, sql)
300
- assert_match(/HAVING SUM\("users"."age"\) > 100/, sql)
597
+ assert_sql(/GROUP BY "users"."name"/, sql)
598
+ assert_sql(/HAVING SUM\("users"."age"\) > 100/, sql)
301
599
  end
302
600
 
303
601
  def test_default_where_syntax
304
- assert_match(/WHERE "users"."name" = 'Ruby' AND "users"."age" = 19/,
602
+ assert_sql(/WHERE "users"."name" = 'Ruby' AND "users"."age" = 19/,
305
603
  User.where(name: 'Ruby', age: 19).to_sql)
306
604
  end
307
605
  end