sparkql 0.1.8

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.
@@ -0,0 +1,437 @@
1
+ require 'test_helper'
2
+
3
+ class ParserCompatabilityTest < Test::Unit::TestCase
4
+ include Sparkql
5
+
6
+ def setup
7
+ @expression_keys = [:field, :operator, :value]
8
+ @multiple_types = [:character,:integer]
9
+ @bad_character_strings = ["'Fargo's Boat'", "Fargo", "''Fargo''", "'Fargo''s'",
10
+ "'Fargo", "Fargo'", "\\'Fargo\\'"]
11
+ @bad_multiple_character_strings = ["'Fargo's Boat'", "Fargo", "''Fargo''", "'Fargo''s'",
12
+ "'Fargo", "Fargo'", "\\'Fargo\\'"]
13
+ @all_bad_strings = @bad_character_strings + @bad_multiple_character_strings
14
+ @test_filters = [
15
+ {
16
+ :string => "City Eq 'Fargo'",
17
+ :type => :character,
18
+ :operator => "Eq"
19
+ },
20
+ {
21
+ :string => "City Ne 'Fargo'",
22
+ :type => :character,
23
+ :operator => "Not Eq"
24
+ },
25
+ {
26
+ :string => "City Eq 'Fargo','Moorhead'",
27
+ :type => :character,
28
+ :operator => "In"
29
+ },
30
+ {
31
+ :string => "City Eq 'Fargo','Moorhead','Bemidji','Duluth'",
32
+ :type => :character,
33
+ :operator => "In"
34
+ },
35
+ {
36
+ :string => "City Ne 'Fargo','Moorhead','Bemidji','Duluth'",
37
+ :type => :character,
38
+ :operator => "Not In"
39
+ },
40
+ {
41
+ :string => "IntegerField Eq 2001",
42
+ :type => :integer,
43
+ :operator => "Eq"
44
+ },
45
+ {
46
+ :string => "IntegerField Eq -2001",
47
+ :type => :integer,
48
+ :operator => "Eq"
49
+ },
50
+ {
51
+ :string => "IntegerField Eq 2001,2002",
52
+ :type => :integer,
53
+ :operator => "In"
54
+ },
55
+ {
56
+ :string => "IntegerField Eq -2001,-2002",
57
+ :type => :integer,
58
+ :operator => "In"
59
+ },
60
+ {
61
+ :string => "FloatField Eq 2001.120",
62
+ :type => :decimal,
63
+ :operator => "Eq"
64
+ },
65
+ {
66
+ :string => "FloatField Eq -2001.120",
67
+ :type => :decimal,
68
+ :operator => "Eq"
69
+ },
70
+ {
71
+ :string => "FloatField Eq -2001.120,-2002.0",
72
+ :type => :decimal,
73
+ :operator => "In"
74
+ },
75
+ {
76
+ :string => "DateField Eq 2010-10-10",
77
+ :type => :date,
78
+ :operator => "Eq"
79
+ },
80
+ {
81
+ :string => "TimestampField Eq 2010-10-10T10:10:30.000000",
82
+ :type => :datetime,
83
+ :operator => "Eq"
84
+ },
85
+ {
86
+ :string => "TimestampField Lt 2010-10-10T10:10:30.000000",
87
+ :type => :datetime,
88
+ :operator => "Lt"
89
+ },
90
+ {
91
+ :string => "TimestampField Gt 2010-10-10T10:10:30.000000",
92
+ :type => :datetime,
93
+ :operator => "Gt"
94
+ },
95
+ {
96
+ :string => "TimestampField Ge 2010-10-10T10:10:30.000000",
97
+ :type => :datetime,
98
+ :operator => "Ge"
99
+ },
100
+ {
101
+ :string => "TimestampField Le 2010-10-10T10:10:30.000000",
102
+ :type => :datetime,
103
+ :operator => "Le"
104
+ },
105
+ {
106
+ :string => "BooleanField Eq true",
107
+ :type => :boolean,
108
+ :operator => "Eq"
109
+ },
110
+ {
111
+ :string => "BooleanField Eq false",
112
+ :type => :boolean,
113
+ :operator => "Eq"
114
+ }]
115
+
116
+ end
117
+
118
+ def compare_expression_to_tokens( expression, tokens )
119
+ counter = 0
120
+ @expression_keys.each do |key|
121
+ assert_equal tokens[counter], expression[key]
122
+ counter += 1
123
+ end
124
+ end
125
+
126
+ def find_operator(string)
127
+ ["Eq","Ne","Gt","Ge","Lt","Le"].each do |op|
128
+ return op if string.include? " #{op} "
129
+ end
130
+ nil
131
+ end
132
+
133
+ test "simple tokenize" do
134
+ filter = "City Eq 'Fargo'"
135
+ filter_tokens = filter.split(" ")
136
+ parser = Parser.new
137
+ expressions = parser.tokenize( filter )
138
+
139
+ assert !parser.errors?
140
+ assert_equal 1, expressions.size, "#Expressions {expressions.inspect}"
141
+ compare_expression_to_tokens(expressions.first, filter_tokens)
142
+ end
143
+
144
+ test "types" do
145
+ @test_filters.each do |elem|
146
+ parser = Parser.new
147
+ expressions = parser.tokenize( elem[:string] )
148
+
149
+ assert !parser.errors?, "Query: #{elem.inspect}"
150
+ assert_equal elem[:type], expressions.first[:type]
151
+ end
152
+ end
153
+
154
+ test "operators" do
155
+ @test_filters.each do |elem|
156
+ parser = Parser.new
157
+ expressions = parser.tokenize( elem[:string] )
158
+ assert !parser.errors?, "Query: #{elem.inspect}"
159
+ assert_equal elem[:operator], expressions.first[:operator]
160
+ end
161
+ end
162
+
163
+ test "tokenize with and" do
164
+ filter = "City Eq 'Fargo' And PropertyType Eq 'A'"
165
+ filter_a = filter.split(" And ")
166
+ filter_tokens = []
167
+ filter_a.each do |f|
168
+ filter_tokens << f.split(" ")
169
+ end
170
+ parser = Parser.new
171
+ expressions = parser.tokenize( filter )
172
+
173
+ assert !parser.errors?
174
+ assert_equal 2, expressions.size
175
+
176
+ counter = 0
177
+ filter_tokens.each do |t|
178
+ compare_expression_to_tokens(expressions[counter], t)
179
+ counter += 1
180
+ end
181
+ end
182
+
183
+ test "tokenize with or" do
184
+ filter = "City Eq 'Fargo' Or PropertyType Eq 'A'"
185
+ filter_a = filter.split(" Or ")
186
+ filter_tokens = []
187
+ filter_a.each do |f|
188
+ filter_tokens << f.split(" ")
189
+ end
190
+ parser = Parser.new
191
+ expressions = parser.tokenize( filter )
192
+
193
+ assert !parser.errors?
194
+ assert_equal 2, expressions.size
195
+
196
+ counter = 0
197
+ filter_tokens.each do |t|
198
+ compare_expression_to_tokens(expressions[counter], t)
199
+ counter += 1
200
+ end
201
+ end
202
+
203
+ test "tokenize fail on missing" do
204
+ # We want to cut out each piece of this individually, and make sure
205
+ # tokenization fails
206
+ filter = "City Eq 'Fargo' And PropertyType Eq 'A'"
207
+ filter_tokens = filter.split(" ")
208
+
209
+ filter_tokens.each do |token|
210
+ f = filter.gsub(token, "").gsub(/\s+/," ")
211
+ parser = Parser.new
212
+ expressions = parser.tokenize( f )
213
+ assert_nil expressions
214
+ assert parser.errors?
215
+ end
216
+ end
217
+
218
+ test "tokenize fail on invalid string operator" do
219
+
220
+ filter = "City Eq "
221
+
222
+ @bad_character_strings.each do |string|
223
+ f = filter + string
224
+ parser = Parser.new
225
+ expressions = parser.tokenize( f )
226
+ assert_nil expressions
227
+ assert parser.errors?
228
+ end
229
+ end
230
+
231
+ test "tokenize fail on invalid operator or field" do
232
+ filters = ["Eq Eq 'Fargo'","City City 'Fargo'", "And Eq 'Fargo'",
233
+ "City And 'Fargo'", "city eq 'Fargo'"]
234
+ filters.each do |f|
235
+ parser = Parser.new
236
+ expressions = parser.tokenize( f )
237
+ assert_nil expressions, "filter: #{f}"
238
+ assert parser.errors?
239
+ end
240
+ end
241
+
242
+ test "tokenize edge case string value" do
243
+ good_strings = ["'Fargo\\'s Boat'", "'Fargo'", "'Fargo\\'\\'s'",
244
+ "' Fargo '", " 'Fargo' "]
245
+
246
+ filters = ["City Eq ","City Eq ", "City Eq "]
247
+
248
+ filters.each do |filter|
249
+ good_strings.each do |string|
250
+ f = filter + string
251
+ parser = Parser.new
252
+ expressions = parser.tokenize( f )
253
+ assert !parser.errors?
254
+ assert_equal 1, expressions.size
255
+ assert_equal string.strip, expressions.first[:value]
256
+ end
257
+ end
258
+ end
259
+
260
+ test "get multiple values" do
261
+ @test_filters.each do |f|
262
+ op = find_operator f[:string]
263
+ next unless @multiple_types.include?(f[:type]) || op.nil?
264
+ parser = Parser.new
265
+ val = f[:string].split(" #{op} ")[1]
266
+ vals = parser.tokenize(f[:string]).first[:value]
267
+ assert_equal val, Array(vals).join(',')
268
+ end
269
+ end
270
+
271
+ test "multiples fail with unsupported operators" do
272
+ ["Gt","Ge","Lt","Le"].each do |op|
273
+ f = "IntegerType #{op} 100,200"
274
+ parser = Parser.new
275
+ expressions = parser.tokenize( f )
276
+ assert parser.errors?
277
+ assert_equal op, parser.errors.first.token
278
+ end
279
+ end
280
+
281
+ test "bad multiples" do
282
+ @all_bad_strings.each do |bad|
283
+ parser = Parser.new
284
+ ex = parser.tokenize("City Eq #{bad}")
285
+ assert parser.errors?
286
+ assert_nil ex
287
+ end
288
+ end
289
+
290
+ test "max out values" do
291
+ parser = Parser.new
292
+ to_the_max = []
293
+ 35.times do |x|
294
+ to_the_max << x
295
+ end
296
+ ex = parser.tokenize("City Eq #{to_the_max.join(',')}")
297
+ vals = ex.first[:value]
298
+ assert_equal 25, vals.size
299
+ end
300
+
301
+ test "max out expressions" do
302
+ parser = Parser.new
303
+ to_the_max = []
304
+ 60.times do |x|
305
+ to_the_max << "City Eq 'Fargo'"
306
+ end
307
+ vals = parser.tokenize(to_the_max.join(" And "))
308
+ assert_equal 50, vals.size
309
+ end
310
+
311
+ test "API-107 And/Or in string spiel" do
312
+ search_strings = ['Tom And Jerry', 'Tom Or Jerry', 'And Or Eq', 'City Eq \\\'Fargo\\\'',
313
+ ' And Eq Or ', 'Or And Or']
314
+ search_strings.each do |s|
315
+ parser = Parser.new
316
+ parser.tokenize("City Eq '#{s}' And PropertyType Eq 'A'")
317
+ assert !parser.errors?
318
+ end
319
+ end
320
+
321
+ test "general paren test" do
322
+ [
323
+ "(City Eq 'Fargo')",
324
+ "(City Eq 'Fargo') And PropertyType Eq 'A'",
325
+ "(City Eq 'Fargo') And (City Eq 'Moorhead')"
326
+ ].each do |filter|
327
+ parser = Parser.new
328
+ p = parser.tokenize(filter)
329
+ assert !parser.errors?
330
+ end
331
+ end
332
+
333
+ test "general nesting fail test" do
334
+ [
335
+ "((City Eq 'Fargo')",
336
+ "((City Eq 'Fargo') And PropertyType Eq 'A'",
337
+ "(City Eq 'Fargo')) And (City Eq 'Moorhead')",
338
+ "City Eq 'Fargo')",
339
+ "(City Eq 'Fargo') And PropertyType Eq 'A')",
340
+ "City Eq 'Fargo' (And) City Eq 'Moorhead'"
341
+ ].each do |filter|
342
+ parser = Parser.new
343
+ p = parser.tokenize(filter)
344
+ assert parser.errors?
345
+ end
346
+ end
347
+
348
+ test "block group" do
349
+ parser = Parser.new
350
+ p = parser.tokenize("(City Eq 'Fargo' Or TotalBr Eq 2) And (City Eq 'Moorhead')")
351
+ assert !parser.errors?
352
+ assert p.first[:block_group] == p[1][:block_group]
353
+ assert p.first[:block_group] == p[2][:block_group] - 1
354
+ end
355
+
356
+ test "proper nesting" do
357
+ parser = Parser.new
358
+ p = parser.tokenize("(City Eq 'Fargo' Or TotalBr Eq 2) And PropertyType Eq 'A'")
359
+ assert !parser.errors?
360
+ p.each do |token|
361
+ if ["City","TotalBr"].include?(token[:field])
362
+ assert_equal 1, token[:level], "Token: #{token.inspect}"
363
+ else
364
+ assert_equal 0, token[:level]
365
+ end
366
+ end
367
+
368
+ parser = Parser.new
369
+ p = parser.tokenize("(City Eq 'Fargo' Or TotalBr Eq 2 Or City Eq 'Moorhead') " +
370
+ "And PropertyType Eq 'A' And (TotalBr Eq 1 And TotalBr Eq 2)")
371
+ assert !parser.errors?
372
+ p.each do |token|
373
+ if ["City","TotalBr"].include?(token[:field])
374
+ assert_equal 1, token[:level]
375
+ else
376
+ assert_equal 0, token[:level]
377
+ end
378
+ end
379
+ end
380
+
381
+ test "maximum nesting of 2" do
382
+ parser = Parser.new
383
+ p = parser.tokenize("(City Eq 'Fargo' Or (TotalBr Eq 2 And (City Eq 'Moorhead'))) And PropertyType Eq 'A'")
384
+ assert parser.errors?
385
+ assert_equal "You have exceeded the maximum nesting level. Please nest no more than 2 levels deep.", parser.errors.first.message
386
+ end
387
+
388
+ test "tokenize custom field" do
389
+ filter = '"General Property Description"."Zoning" Eq \'Commercial\''
390
+ filter_tokens = ['"General Property Description"."Zoning"', 'Eq', "'Commercial'"]
391
+ parser = Parser.new
392
+ expressions = parser.tokenize( filter )
393
+
394
+ assert !parser.errors?, "Parser errrors [#{filter}]: #{parser.errors.inspect}"
395
+ assert_equal 1, expressions.size, "Expression #{expressions.inspect}"
396
+ compare_expression_to_tokens(expressions.first, filter_tokens)
397
+ assert expressions.first[:custom_field], "Expression #{expressions.first.inspect}"
398
+ end
399
+
400
+ test "tokenize custom field with special characters" do
401
+ filter = '"Security"."@R080T$\' ` ` `#" Eq \'R2D2\''
402
+ filter_tokens = ['"Security"."@R080T$\' ` ` `#"', 'Eq', "'R2D2'"]
403
+ parser = Parser.new
404
+ expressions = parser.tokenize( filter )
405
+ assert !parser.errors?, "Parser errrors [#{filter}]: #{parser.errors.inspect}"
406
+ assert_equal 1, expressions.size, "Expression #{expressions.inspect}"
407
+ compare_expression_to_tokens(expressions.first, filter_tokens)
408
+ assert expressions.first[:custom_field], "Expression #{expressions.first.inspect}"
409
+ end
410
+
411
+ test "custom field supports all types" do
412
+ types = {
413
+ :character => "'character'",
414
+ :integer => 1234,
415
+ :decimal => 12.34,
416
+ :boolean => true
417
+ }
418
+ types.each_pair do |key, value|
419
+ filter = '"Details"."Random" Eq ' + "#{value}"
420
+ filter_tokens = ['"Details"."Random"', 'Eq', "#{value}"]
421
+ parser = Parser.new
422
+ expressions = parser.tokenize( filter )
423
+
424
+ assert !parser.errors?, "Parser errrors [#{filter}]: #{parser.errors.inspect}"
425
+ assert_equal 1, expressions.size, "Expression #{expressions.inspect}"
426
+ compare_expression_to_tokens(expressions.first, filter_tokens)
427
+ assert expressions.first[:custom_field], "Expression #{expressions.first.inspect}"
428
+ end
429
+ end
430
+
431
+ test "escape boolean value" do
432
+ parser = Parser.new
433
+ expressions = parser.tokenize( "BooleanField Eq true" )
434
+ assert_equal true, parser.escape_value(expressions.first)
435
+ end
436
+
437
+ end
@@ -0,0 +1,215 @@
1
+ require 'test_helper'
2
+
3
+ class ParserTest < Test::Unit::TestCase
4
+ include Sparkql
5
+
6
+ def test_simple
7
+ @parser = Parser.new
8
+ parse 'Test Eq 10',10.to_s
9
+ parse 'Test Eq 10.0',10.0.to_s
10
+ parse 'Test Eq true',true.to_s
11
+ parse "Test Eq 'false'","'false'"
12
+ end
13
+
14
+ def test_conjunction
15
+ @parser = Parser.new
16
+ expression = @parser.parse('Test Eq 10 And Test Ne 11')
17
+ assert_equal 10.to_s, expression.first[:value]
18
+ assert_equal 11.to_s, expression.last[:value]
19
+ assert_equal 'And', expression.last[:conjunction]
20
+ expression = @parser.parse('Test Eq 10 Or Test Ne 11')
21
+ assert_equal 10.to_s, expression.first[:value]
22
+ assert_equal 11.to_s, expression.last[:value]
23
+ assert_equal 'Or', expression.last[:conjunction]
24
+ end
25
+
26
+ def test_tough_conjunction
27
+ @parser = Parser.new
28
+ expression = @parser.parse('Test Eq 10 Or Test Ne 11 And Test Ne 9')
29
+ assert_equal 9.to_s, expression.last[:value]
30
+ assert_equal 'And', expression.last[:conjunction]
31
+ end
32
+
33
+ def test_grouping
34
+ @parser = Parser.new
35
+ expression = @parser.parse('(Test Eq 10)').first
36
+ assert_equal 10.to_s, expression[:value]
37
+ expression = @parser.parse('(Test Eq 10 Or Test Ne 11)')
38
+ assert_equal 10.to_s, expression.first[:value]
39
+ assert_equal 11.to_s, expression.last[:value]
40
+ assert_equal 'Or', expression.last[:conjunction]
41
+ expression = @parser.parse('(Test Eq 10 Or (Test Ne 11))')
42
+ assert_equal 10.to_s, expression.first[:value]
43
+ assert_equal 11.to_s, expression.last[:value]
44
+ assert_equal 'Or', expression.last[:conjunction]
45
+ end
46
+
47
+ def test_multiples
48
+ @parser = Parser.new
49
+ expression = @parser.parse('(Test Eq 10,11,12)').first
50
+ assert_equal [10.to_s,11.to_s,12.to_s], expression[:value]
51
+ end
52
+
53
+ def parse(q,v)
54
+ expressions = @parser.parse(q)
55
+ assert !@parser.errors?, "Unexpected error parsing #{q} #{@parser.errors.inspect}"
56
+ assert_equal v, expressions.first[:value], "Expression #{expressions.inspect}"
57
+ assert !expressions.first[:custom_field], "Unexepected custom field #{expressions.inspect}"
58
+ end
59
+
60
+ def test_invalid_syntax
61
+ @parser = Parser.new
62
+ expression = @parser.parse('Test Eq DERP')
63
+ assert @parser.errors?, "Should be nil: #{expression}"
64
+ end
65
+
66
+ def test_nesting
67
+ assert_nesting(
68
+ "City Eq 'Fargo' Or (BathsFull Eq 1 Or BathsFull Eq 2) Or City Eq 'Moorhead' Or City Eq 'Dilworth'",
69
+ [0,1,1,0,0]
70
+ )
71
+ end
72
+
73
+ def test_multilevel_nesting
74
+ assert_nesting(
75
+ "(City Eq 'Fargo' And (BathsFull Eq 1 Or BathsFull Eq 2)) Or City Eq 'Moorhead' Or City Eq 'Dilworth'",
76
+ [1,2,2,0,0]
77
+ )
78
+
79
+ # API-629
80
+ assert_nesting(
81
+ "((MlsStatus Eq 'A') Or (MlsStatus Eq 'D' And CloseDate Ge 2011-05-17)) And ListPrice Ge 150000.0 And PropertyType Eq 'A'",
82
+ [2,2,2,0,0],
83
+ [2,3,3,0,0]
84
+ )
85
+ assert_nesting(
86
+ "ListPrice Ge 150000.0 And PropertyType Eq 'A' And ((MlsStatus Eq 'A') Or (MlsStatus Eq 'D' And CloseDate Ge 2011-05-17))",
87
+ [0,0,2,2,2],
88
+ [0,0,2,3,3]
89
+ )
90
+ end
91
+
92
+ # verify each expression in the query is at the right nesting level and group
93
+ def assert_nesting(sparkql, levels=[], block_groups=nil)
94
+ block_groups = levels.clone if block_groups.nil?
95
+ parser = Parser.new
96
+ expressions = parser.parse(sparkql)
97
+ assert !parser.errors?, "Unexpected error parsing #{sparkql}: #{parser.errors.inspect}"
98
+ count = 0
99
+ expressions.each do |ex|
100
+ assert_equal levels[count], ex[:level], "Nesting level wrong for #{ex.inspect}"
101
+ assert_equal(block_groups[count], ex[:block_group], "Nesting block group wrong for #{ex.inspect}")
102
+ count +=1
103
+ end
104
+ end
105
+
106
+ def test_bad_queries
107
+ filter = "City IsLikeA 'Town'"
108
+ @parser = Parser.new
109
+ expressions = @parser.parse(filter)
110
+ assert @parser.errors?, "Should be nil: #{expressions}"
111
+ assert @parser.fatal_errors?, "Should be nil: #{@parser.errors.inspect}"
112
+ end
113
+
114
+ def test_function_days
115
+ d = Date.today
116
+ dt = DateTime.new(d.year, d.month,d.day, 0,0,0, DateTime.now.offset)
117
+ start = Time.parse(dt.to_s)
118
+ filter = "OriginalEntryTimestamp Ge days(-7)"
119
+ @parser = Parser.new
120
+ expressions = @parser.parse(filter)
121
+ assert !@parser.errors?, "errors #{@parser.errors.inspect}"
122
+ test_time = Time.parse(expressions.first[:value])
123
+
124
+ assert (-605000 < test_time - start && -604000 > test_time - start), "Time range off by more than five seconds #{test_time - start} '#{test_time} - #{start}'"
125
+ end
126
+
127
+ def test_function_now
128
+ start = Time.now
129
+ filter = "City Eq now()"
130
+ @parser = Parser.new
131
+ expressions = @parser.parse(filter)
132
+ assert !@parser.errors?, "errors #{@parser.errors.inspect}"
133
+ test_time = Time.parse(expressions.first[:value])
134
+ assert 5 > test_time - start, "Time range off by more than five seconds #{test_time - start}"
135
+ assert -5 < test_time - start, "Time range off by more than five seconds #{test_time - start}"
136
+ end
137
+
138
+ def test_for_reserved_words_first_literals_second
139
+ ["OrOrOr Eq true", "Equador Eq true", "Oregon Ge 10"].each do |filter|
140
+ @parser = Parser.new
141
+ expressions = @parser.parse(filter)
142
+ assert !@parser.errors?, "Filter '#{filter}' errors: #{@parser.errors.inspect}"
143
+ end
144
+ end
145
+
146
+ def test_custom_fields
147
+ filter = '"General Property Description"."Taxes" Lt 500.0'
148
+ @parser = Parser.new
149
+ expressions = @parser.parse(filter)
150
+ assert !@parser.errors?, "errors #{@parser.errors.inspect}"
151
+ assert_equal '"General Property Description"."Taxes"', expressions.first[:field], "Custom field expression #{expressions.inspect}"
152
+ assert expressions.first[:custom_field], "Custom field expression #{expressions.inspect}"
153
+ assert_equal '500.0', expressions.first[:value], "Custom field expression #{expressions.inspect}"
154
+ end
155
+
156
+ def test_valid_custom_field_filters
157
+ ['"General Property Description"."Taxes$" Lt 500.0',
158
+ '"General Property Desc\'"."Taxes" Lt 500.0',
159
+ '"General Property Description"."Taxes" Lt 500.0',
160
+ '"General \'Property\' Description"."Taxes" Lt 500.0',
161
+ '"General Property Description"."Taxes #" Lt 500.0',
162
+ '"General$Description"."Taxes" Lt 500.0',
163
+ '" a "." b " Lt 500.0'
164
+ ].each do |filter|
165
+ @parser = Parser.new
166
+ expressions = @parser.parse(filter)
167
+ assert !@parser.errors?, "errors '#{filter}'\n#{@parser.errors.inspect}"
168
+ end
169
+ end
170
+
171
+ def test_invalid_custom_field_filters
172
+ ['"$General Property Description"."Taxes" Lt 500.0',
173
+ '"General Property Description"."$Taxes" Lt 500.0',
174
+ '"General Property Description"."Tax.es" Lt 500.0',
175
+ '"General Property Description".".Taxes" Lt 500.0',
176
+ '"General Property Description".".Taxes"."SUB" Lt 500.0',
177
+ '"General.Description"."Taxes" Lt 500.0',
178
+ '""."" Lt 500.0'
179
+ ].each do |filter|
180
+ @parser = Parser.new
181
+ expressions = @parser.parse(filter)
182
+ assert @parser.errors?, "No errors? '#{filter}'\n#{@parser.inspect}"
183
+ end
184
+ end
185
+
186
+ def test_case_insensitve_ops_and_conjunctions
187
+ @parser = Parser.new
188
+ parse 'Test EQ 10',10.to_s
189
+ parse 'Test eq 10.0',10.0.to_s
190
+ parse 'Test eQ true',true.to_s
191
+ parse 'Test EQ 10 AND Test NE 11', 10.to_s
192
+ parse 'Test eq 10 or Test ne 11', 10.to_s
193
+ end
194
+
195
+ def test_null
196
+ @parser = Parser.new
197
+ parse 'Test Eq NULL', "NULL"
198
+ parse 'Test Eq NULL Or Test Ne 11', "NULL"
199
+ end
200
+
201
+ def test_invalid_operators
202
+ (Sparkql::Token::OPERATORS - Sparkql::Token::EQUALITY_OPERATORS).each do |o|
203
+ ["NULL", "true", "'My String'"].each do |v|
204
+ parser_errors("Test #{o} #{v}")
205
+ end
206
+ end
207
+ end
208
+
209
+ def parser_errors(filter)
210
+ @parser = Parser.new
211
+ expression = @parser.parse(filter)
212
+ assert @parser.errors?, "Should find errors for '#{filter}': #{expression}"
213
+ end
214
+
215
+ end