gmail_search_syntax 0.1.0

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,691 @@
1
+ require "test_helper"
2
+
3
+ class GmailSearchSyntaxTest < Minitest::Test
4
+ include GmailSearchSyntax::AST
5
+
6
+ def test_version
7
+ assert_equal "0.1.0", GmailSearchSyntax::VERSION
8
+ end
9
+
10
+ def test_simple_from_operator
11
+ ast = GmailSearchSyntax.parse!("from:amy@example.com")
12
+ assert_instance_of Operator, ast
13
+ assert_equal "from", ast.name
14
+ assert_equal "amy@example.com", ast.value
15
+ end
16
+
17
+ def test_from_me
18
+ ast = GmailSearchSyntax.parse!("from:me")
19
+ assert_instance_of Operator, ast
20
+ assert_equal "from", ast.name
21
+ assert_equal "me", ast.value
22
+ end
23
+
24
+ def test_to_operator
25
+ ast = GmailSearchSyntax.parse!("to:john@example.com")
26
+ assert_instance_of Operator, ast
27
+ assert_equal "to", ast.name
28
+ assert_equal "john@example.com", ast.value
29
+ end
30
+
31
+ def test_subject_with_single_word
32
+ ast = GmailSearchSyntax.parse!("subject:dinner")
33
+ assert_instance_of Operator, ast
34
+ assert_equal "subject", ast.name
35
+ assert_equal "dinner", ast.value
36
+ end
37
+
38
+ def test_subject_with_quoted_phrase
39
+ ast = GmailSearchSyntax.parse!('subject:"anniversary party"')
40
+ assert_instance_of Operator, ast
41
+ assert_equal "subject", ast.name
42
+ assert_equal "anniversary party", ast.value
43
+ end
44
+
45
+ def test_after_date
46
+ ast = GmailSearchSyntax.parse!("after:2004/04/16")
47
+ assert_instance_of Operator, ast
48
+ assert_equal "after", ast.name
49
+ assert_equal "2004/04/16", ast.value
50
+ end
51
+
52
+ def test_before_date
53
+ ast = GmailSearchSyntax.parse!("before:04/18/2004")
54
+ assert_instance_of Operator, ast
55
+ assert_equal "before", ast.name
56
+ assert_equal "04/18/2004", ast.value
57
+ end
58
+
59
+ def test_older_than_relative
60
+ ast = GmailSearchSyntax.parse!("older_than:1y")
61
+ assert_instance_of Operator, ast
62
+ assert_equal "older_than", ast.name
63
+ assert_equal "1y", ast.value
64
+ end
65
+
66
+ def test_newer_than_relative
67
+ ast = GmailSearchSyntax.parse!("newer_than:2d")
68
+ assert_instance_of Operator, ast
69
+ assert_equal "newer_than", ast.name
70
+ assert_equal "2d", ast.value
71
+ end
72
+
73
+ def test_or_operator_with_from
74
+ ast = GmailSearchSyntax.parse!("from:amy OR from:david")
75
+ assert_instance_of Or, ast
76
+
77
+ assert_equal 2, ast.operands.length
78
+ assert_instance_of Operator, ast.operands[0]
79
+ assert_equal "from", ast.operands[0].name
80
+ assert_equal "amy", ast.operands[0].value
81
+
82
+ assert_instance_of Operator, ast.operands[1]
83
+ assert_equal "from", ast.operands[1].name
84
+ assert_equal "david", ast.operands[1].value
85
+ end
86
+
87
+ def test_braces_as_or
88
+ ast = GmailSearchSyntax.parse!("{from:amy from:david}")
89
+ assert_instance_of Or, ast
90
+
91
+ assert_equal 2, ast.operands.length
92
+ assert_instance_of Operator, ast.operands[0]
93
+ assert_equal "from", ast.operands[0].name
94
+ assert_equal "amy", ast.operands[0].value
95
+
96
+ assert_instance_of Operator, ast.operands[1]
97
+ assert_equal "from", ast.operands[1].name
98
+ assert_equal "david", ast.operands[1].value
99
+ end
100
+
101
+ def test_and_operator
102
+ ast = GmailSearchSyntax.parse!("from:amy AND to:david")
103
+ assert_instance_of And, ast
104
+
105
+ assert_equal 2, ast.operands.length
106
+ assert_instance_of Operator, ast.operands[0]
107
+ assert_equal "from", ast.operands[0].name
108
+ assert_equal "amy", ast.operands[0].value
109
+
110
+ assert_instance_of Operator, ast.operands[1]
111
+ assert_equal "to", ast.operands[1].name
112
+ assert_equal "david", ast.operands[1].value
113
+ end
114
+
115
+ def test_implicit_and
116
+ ast = GmailSearchSyntax.parse!("from:amy to:david")
117
+ assert_instance_of And, ast
118
+
119
+ assert_equal 2, ast.operands.length
120
+ assert_instance_of Operator, ast.operands[0]
121
+ assert_equal "from", ast.operands[0].name
122
+
123
+ assert_instance_of Operator, ast.operands[1]
124
+ assert_equal "to", ast.operands[1].name
125
+ end
126
+
127
+ def test_negation_with_minus
128
+ ast = GmailSearchSyntax.parse!("dinner -movie")
129
+ assert_instance_of And, ast
130
+
131
+ assert_equal 2, ast.operands.length
132
+ assert_instance_of Text, ast.operands[0]
133
+ assert_equal "dinner", ast.operands[0].value
134
+
135
+ assert_instance_of Not, ast.operands[1]
136
+ assert_instance_of Text, ast.operands[1].child
137
+ assert_equal "movie", ast.operands[1].child.value
138
+ end
139
+
140
+ def test_around_operator
141
+ ast = GmailSearchSyntax.parse!("holiday AROUND 10 vacation")
142
+ assert_instance_of Around, ast
143
+
144
+ assert_instance_of Text, ast.left
145
+ assert_equal "holiday", ast.left.value
146
+ assert_equal 10, ast.distance
147
+
148
+ assert_instance_of Text, ast.right
149
+ assert_equal "vacation", ast.right.value
150
+ end
151
+
152
+ def test_around_with_quoted_string
153
+ ast = GmailSearchSyntax.parse!('"secret AROUND 25 birthday"')
154
+ assert_instance_of Text, ast
155
+ assert_equal "secret AROUND 25 birthday", ast.value
156
+ end
157
+
158
+ def test_label_operator
159
+ ast = GmailSearchSyntax.parse!("label:friends")
160
+ assert_instance_of Operator, ast
161
+ assert_equal "label", ast.name
162
+ assert_equal "friends", ast.value
163
+ end
164
+
165
+ def test_category_operator
166
+ ast = GmailSearchSyntax.parse!("category:primary")
167
+ assert_instance_of Operator, ast
168
+ assert_equal "category", ast.name
169
+ assert_equal "primary", ast.value
170
+ end
171
+
172
+ def test_has_attachment
173
+ ast = GmailSearchSyntax.parse!("has:attachment")
174
+ assert_instance_of Operator, ast
175
+ assert_equal "has", ast.name
176
+ assert_equal "attachment", ast.value
177
+ end
178
+
179
+ def test_filename_operator
180
+ ast = GmailSearchSyntax.parse!("filename:pdf")
181
+ assert_instance_of Operator, ast
182
+ assert_equal "filename", ast.name
183
+ assert_equal "pdf", ast.value
184
+ end
185
+
186
+ def test_filename_with_extension
187
+ ast = GmailSearchSyntax.parse!("filename:homework.txt")
188
+ assert_instance_of Operator, ast
189
+ assert_equal "filename", ast.name
190
+ assert_equal "homework.txt", ast.value
191
+ end
192
+
193
+ def test_quoted_exact_phrase
194
+ ast = GmailSearchSyntax.parse!('"dinner and movie tonight"')
195
+ assert_instance_of Text, ast
196
+ assert_equal "dinner and movie tonight", ast.value
197
+ end
198
+
199
+ def test_parentheses_grouping
200
+ ast = GmailSearchSyntax.parse!("subject:(dinner movie)")
201
+ assert_instance_of Operator, ast
202
+ assert_equal "subject", ast.name
203
+
204
+ assert_instance_of And, ast.value
205
+ assert_equal 2, ast.value.operands.length
206
+ assert_instance_of Text, ast.value.operands[0]
207
+ assert_equal "dinner", ast.value.operands[0].value
208
+ assert_instance_of Text, ast.value.operands[1]
209
+ assert_equal "movie", ast.value.operands[1].value
210
+ end
211
+
212
+ def test_in_anywhere
213
+ ast = GmailSearchSyntax.parse!("in:anywhere movie")
214
+ assert_instance_of And, ast
215
+
216
+ assert_equal 2, ast.operands.length
217
+ assert_instance_of Operator, ast.operands[0]
218
+ assert_equal "in", ast.operands[0].name
219
+ assert_equal "anywhere", ast.operands[0].value
220
+
221
+ assert_instance_of Text, ast.operands[1]
222
+ assert_equal "movie", ast.operands[1].value
223
+ end
224
+
225
+ def test_is_starred
226
+ ast = GmailSearchSyntax.parse!("is:starred")
227
+ assert_instance_of Operator, ast
228
+ assert_equal "is", ast.name
229
+ assert_equal "starred", ast.value
230
+ end
231
+
232
+ def test_is_unread
233
+ ast = GmailSearchSyntax.parse!("is:unread")
234
+ assert_instance_of Operator, ast
235
+ assert_equal "is", ast.name
236
+ assert_equal "unread", ast.value
237
+ end
238
+
239
+ def test_size_operator
240
+ ast = GmailSearchSyntax.parse!("size:1000000")
241
+ assert_instance_of Operator, ast
242
+ assert_equal "size", ast.name
243
+ assert_equal 1000000, ast.value
244
+ end
245
+
246
+ def test_larger_operator
247
+ ast = GmailSearchSyntax.parse!("larger:10M")
248
+ assert_instance_of Operator, ast
249
+ assert_equal "larger", ast.name
250
+ assert_equal "10M", ast.value
251
+ end
252
+
253
+ def test_complex_query_with_multiple_operators
254
+ ast = GmailSearchSyntax.parse!("from:amy subject:meeting has:attachment")
255
+ assert_instance_of And, ast
256
+
257
+ assert_equal 3, ast.operands.length
258
+ assert_instance_of Operator, ast.operands[0]
259
+ assert_equal "from", ast.operands[0].name
260
+
261
+ assert_instance_of Operator, ast.operands[1]
262
+ assert_equal "subject", ast.operands[1].name
263
+
264
+ assert_instance_of Operator, ast.operands[2]
265
+ assert_equal "has", ast.operands[2].name
266
+ end
267
+
268
+ def test_complex_or_and_combination
269
+ ast = GmailSearchSyntax.parse!("from:amy OR from:bob to:me")
270
+ assert_instance_of Or, ast
271
+
272
+ assert_equal 2, ast.operands.length
273
+ assert_instance_of Operator, ast.operands[0]
274
+
275
+ assert_instance_of And, ast.operands[1]
276
+ assert_equal 2, ast.operands[1].operands.length
277
+ assert_instance_of Operator, ast.operands[1].operands[0]
278
+ assert_equal "from", ast.operands[1].operands[0].name
279
+ assert_equal "bob", ast.operands[1].operands[0].value
280
+
281
+ assert_instance_of Operator, ast.operands[1].operands[1]
282
+ assert_equal "to", ast.operands[1].operands[1].name
283
+ end
284
+
285
+ def test_negation_with_operator
286
+ ast = GmailSearchSyntax.parse!("-from:spam@example.com")
287
+ assert_instance_of Not, ast
288
+ assert_instance_of Operator, ast.child
289
+ assert_equal "from", ast.child.name
290
+ assert_equal "spam@example.com", ast.child.value
291
+ end
292
+
293
+ def test_list_operator
294
+ ast = GmailSearchSyntax.parse!("list:info@example.com")
295
+ assert_instance_of Operator, ast
296
+ assert_equal "list", ast.name
297
+ assert_equal "info@example.com", ast.value
298
+ end
299
+
300
+ def test_deliveredto_operator
301
+ ast = GmailSearchSyntax.parse!("deliveredto:username@example.com")
302
+ assert_instance_of Operator, ast
303
+ assert_equal "deliveredto", ast.name
304
+ assert_equal "username@example.com", ast.value
305
+ end
306
+
307
+ def test_rfc822msgid_operator
308
+ ast = GmailSearchSyntax.parse!("rfc822msgid:200503292@example.com")
309
+ assert_instance_of Operator, ast
310
+ assert_equal "rfc822msgid", ast.name
311
+ assert_equal "200503292@example.com", ast.value
312
+ end
313
+
314
+ def test_cc_operator
315
+ ast = GmailSearchSyntax.parse!("cc:john@example.com")
316
+ assert_instance_of Operator, ast
317
+ assert_equal "cc", ast.name
318
+ assert_equal "john@example.com", ast.value
319
+ end
320
+
321
+ def test_bcc_operator
322
+ ast = GmailSearchSyntax.parse!("bcc:david@example.com")
323
+ assert_instance_of Operator, ast
324
+ assert_equal "bcc", ast.name
325
+ assert_equal "david@example.com", ast.value
326
+ end
327
+
328
+ def test_plain_text_search
329
+ ast = GmailSearchSyntax.parse!("meeting")
330
+ assert_instance_of Text, ast
331
+ assert_equal "meeting", ast.value
332
+ end
333
+
334
+ def test_multiple_plain_text_words
335
+ ast = GmailSearchSyntax.parse!("project report")
336
+ assert_instance_of And, ast
337
+
338
+ assert_equal 2, ast.operands.length
339
+ assert_instance_of Text, ast.operands[0]
340
+ assert_equal "project", ast.operands[0].value
341
+
342
+ assert_instance_of Text, ast.operands[1]
343
+ assert_equal "report", ast.operands[1].value
344
+ end
345
+
346
+ def test_empty_query
347
+ error = assert_raises(GmailSearchSyntax::EmptyQueryError) do
348
+ GmailSearchSyntax.parse!("")
349
+ end
350
+ assert_equal "Query cannot be empty", error.message
351
+ end
352
+
353
+ def test_whitespace_only
354
+ error = assert_raises(GmailSearchSyntax::EmptyQueryError) do
355
+ GmailSearchSyntax.parse!(" ")
356
+ end
357
+ assert_equal "Query cannot be empty", error.message
358
+ end
359
+
360
+ def test_nested_parentheses_with_operators
361
+ ast = GmailSearchSyntax.parse!("from:amy (subject:meeting OR subject:call)")
362
+ assert_instance_of And, ast
363
+
364
+ assert_equal 2, ast.operands.length
365
+ assert_instance_of Operator, ast.operands[0]
366
+ assert_equal "from", ast.operands[0].name
367
+
368
+ assert_instance_of Or, ast.operands[1]
369
+ end
370
+
371
+ def test_multiple_negations
372
+ ast = GmailSearchSyntax.parse!("-from:spam -subject:junk")
373
+ assert_instance_of And, ast
374
+
375
+ assert_equal 2, ast.operands.length
376
+ assert_instance_of Not, ast.operands[0]
377
+ assert_instance_of Operator, ast.operands[0].child
378
+ assert_equal "from", ast.operands[0].child.name
379
+
380
+ assert_instance_of Not, ast.operands[1]
381
+ assert_instance_of Operator, ast.operands[1].child
382
+ assert_equal "subject", ast.operands[1].child.name
383
+ end
384
+
385
+ def test_or_with_three_terms
386
+ ast = GmailSearchSyntax.parse!("{from:a from:b from:c}")
387
+ assert_instance_of Or, ast
388
+
389
+ assert_equal 3, ast.operands.length
390
+ assert_instance_of Operator, ast.operands[0]
391
+ assert_equal "a", ast.operands[0].value
392
+ assert_instance_of Operator, ast.operands[1]
393
+ assert_equal "b", ast.operands[1].value
394
+ assert_instance_of Operator, ast.operands[2]
395
+ assert_equal "c", ast.operands[2].value
396
+ end
397
+
398
+ def test_complex_mixed_query
399
+ ast = GmailSearchSyntax.parse!("from:boss subject:urgent has:attachment -label:archive")
400
+ assert_instance_of And, ast
401
+
402
+ assert_equal 4, ast.operands.length
403
+
404
+ assert_instance_of Operator, ast.operands[0]
405
+ assert_equal "from", ast.operands[0].name
406
+
407
+ assert_instance_of Operator, ast.operands[1]
408
+ assert_equal "subject", ast.operands[1].name
409
+
410
+ assert_instance_of Operator, ast.operands[2]
411
+ assert_equal "has", ast.operands[2].name
412
+
413
+ assert_instance_of Not, ast.operands[3]
414
+ assert_instance_of Operator, ast.operands[3].child
415
+ assert_equal "label", ast.operands[3].child.name
416
+ end
417
+
418
+ def test_quoted_string_with_operators_inside
419
+ ast = GmailSearchSyntax.parse!('"from:amy to:bob"')
420
+ assert_instance_of Text, ast
421
+ assert_equal "from:amy to:bob", ast.value
422
+ end
423
+
424
+ def test_email_with_plus_sign
425
+ ast = GmailSearchSyntax.parse!("to:user+tag@example.com")
426
+ assert_instance_of Operator, ast
427
+ assert_equal "to", ast.name
428
+ assert_equal "user+tag@example.com", ast.value
429
+ end
430
+
431
+ def test_in_operator_with_location
432
+ ast = GmailSearchSyntax.parse!("in:inbox from:manager")
433
+ assert_instance_of And, ast
434
+
435
+ assert_equal 2, ast.operands.length
436
+ assert_instance_of Operator, ast.operands[0]
437
+ assert_equal "in", ast.operands[0].name
438
+ assert_equal "inbox", ast.operands[0].value
439
+ end
440
+
441
+ def test_has_drive_operator
442
+ ast = GmailSearchSyntax.parse!("has:drive")
443
+ assert_instance_of Operator, ast
444
+ assert_equal "has", ast.name
445
+ assert_equal "drive", ast.value
446
+ end
447
+
448
+ def test_category_updates
449
+ ast = GmailSearchSyntax.parse!("category:updates")
450
+ assert_instance_of Operator, ast
451
+ assert_equal "category", ast.name
452
+ assert_equal "updates", ast.value
453
+ end
454
+
455
+ def test_around_default_distance
456
+ ast = GmailSearchSyntax.parse!("meeting AROUND project")
457
+ assert_instance_of Around, ast
458
+ assert_equal 5, ast.distance
459
+ end
460
+
461
+ def test_parentheses_with_single_term
462
+ ast = GmailSearchSyntax.parse!("(meeting)")
463
+ assert_instance_of Text, ast
464
+ assert_equal "meeting", ast.value
465
+ end
466
+
467
+ def test_subject_with_parentheses_multiple_words
468
+ ast = GmailSearchSyntax.parse!("subject:(project status update)")
469
+ assert_instance_of Operator, ast
470
+ assert_equal "subject", ast.name
471
+
472
+ assert_instance_of And, ast.value
473
+ assert_equal 3, ast.value.operands.length
474
+ assert_instance_of Text, ast.value.operands[0]
475
+ assert_equal "project", ast.value.operands[0].value
476
+ assert_instance_of Text, ast.value.operands[1]
477
+ assert_equal "status", ast.value.operands[1].value
478
+ assert_instance_of Text, ast.value.operands[2]
479
+ assert_equal "update", ast.value.operands[2].value
480
+ end
481
+
482
+ def test_and_explicit_with_text
483
+ ast = GmailSearchSyntax.parse!("meeting AND project")
484
+ assert_instance_of And, ast
485
+
486
+ assert_equal 2, ast.operands.length
487
+ assert_instance_of Text, ast.operands[0]
488
+ assert_equal "meeting", ast.operands[0].value
489
+
490
+ assert_instance_of Text, ast.operands[1]
491
+ assert_equal "project", ast.operands[1].value
492
+ end
493
+
494
+ def test_smaller_operator
495
+ ast = GmailSearchSyntax.parse!("smaller:1M")
496
+ assert_instance_of Operator, ast
497
+ assert_equal "smaller", ast.name
498
+ assert_equal "1M", ast.value
499
+ end
500
+
501
+ def test_or_inside_operator_value
502
+ ast = GmailSearchSyntax.parse!("from:(mischa@ OR julik@)")
503
+ assert_instance_of Operator, ast
504
+ assert_equal "from", ast.name
505
+
506
+ assert_instance_of Or, ast.value
507
+ assert_equal 2, ast.value.operands.length
508
+ assert_instance_of Text, ast.value.operands[0]
509
+ assert_equal "mischa@", ast.value.operands[0].value
510
+ assert_instance_of Text, ast.value.operands[1]
511
+ assert_equal "julik@", ast.value.operands[1].value
512
+ end
513
+
514
+ def test_or_with_emails_inside_operator
515
+ ast = GmailSearchSyntax.parse!("from:(amy@example.com OR bob@example.com)")
516
+ assert_instance_of Operator, ast
517
+ assert_equal "from", ast.name
518
+
519
+ assert_instance_of Or, ast.value
520
+ assert_equal 2, ast.value.operands.length
521
+ assert_instance_of Text, ast.value.operands[0]
522
+ assert_equal "amy@example.com", ast.value.operands[0].value
523
+ assert_instance_of Text, ast.value.operands[1]
524
+ assert_equal "bob@example.com", ast.value.operands[1].value
525
+ end
526
+
527
+ def test_multiple_or_inside_operator
528
+ ast = GmailSearchSyntax.parse!("from:(a@ OR b@ OR c@)")
529
+ assert_instance_of Operator, ast
530
+ assert_equal "from", ast.name
531
+
532
+ assert_instance_of Or, ast.value
533
+ assert_equal 3, ast.value.operands.length
534
+ assert_equal "a@", ast.value.operands[0].value
535
+ assert_equal "b@", ast.value.operands[1].value
536
+ assert_equal "c@", ast.value.operands[2].value
537
+ end
538
+
539
+ def test_and_inside_operator_value
540
+ ast = GmailSearchSyntax.parse!("subject:(urgent AND meeting)")
541
+ assert_instance_of Operator, ast
542
+ assert_equal "subject", ast.name
543
+
544
+ assert_instance_of And, ast.value
545
+ assert_equal 2, ast.value.operands.length
546
+ assert_instance_of Text, ast.value.operands[0]
547
+ assert_equal "urgent", ast.value.operands[0].value
548
+ assert_instance_of Text, ast.value.operands[1]
549
+ assert_equal "meeting", ast.value.operands[1].value
550
+ end
551
+
552
+ def test_operator_with_or_combined_with_other_conditions
553
+ ast = GmailSearchSyntax.parse!("from:(alice@ OR bob@) subject:meeting")
554
+ assert_instance_of And, ast
555
+
556
+ assert_equal 2, ast.operands.length
557
+ assert_instance_of Operator, ast.operands[0]
558
+ assert_equal "from", ast.operands[0].name
559
+ assert_instance_of Or, ast.operands[0].value
560
+
561
+ assert_instance_of Operator, ast.operands[1]
562
+ assert_equal "subject", ast.operands[1].name
563
+ assert_equal "meeting", ast.operands[1].value
564
+ end
565
+
566
+ def test_negation_inside_operator_value
567
+ ast = GmailSearchSyntax.parse!("subject:(meeting -cancelled)")
568
+ assert_instance_of Operator, ast
569
+ assert_equal "subject", ast.name
570
+
571
+ assert_instance_of And, ast.value
572
+ assert_equal 2, ast.value.operands.length
573
+ assert_instance_of Text, ast.value.operands[0]
574
+ assert_equal "meeting", ast.value.operands[0].value
575
+ assert_instance_of Not, ast.value.operands[1]
576
+ assert_equal "cancelled", ast.value.operands[1].child.value
577
+ end
578
+
579
+ def test_complex_expression_inside_operator
580
+ ast = GmailSearchSyntax.parse!("from:(alice@ OR bob@) to:(charlie@ OR david@)")
581
+ assert_instance_of And, ast
582
+
583
+ assert_equal 2, ast.operands.length
584
+
585
+ assert_instance_of Operator, ast.operands[0]
586
+ assert_equal "from", ast.operands[0].name
587
+ assert_instance_of Or, ast.operands[0].value
588
+ assert_equal 2, ast.operands[0].value.operands.length
589
+
590
+ assert_instance_of Operator, ast.operands[1]
591
+ assert_equal "to", ast.operands[1].name
592
+ assert_instance_of Or, ast.operands[1].value
593
+ assert_equal 2, ast.operands[1].value.operands.length
594
+ end
595
+
596
+ def test_nested_parentheses_in_operator_value
597
+ ast = GmailSearchSyntax.parse!("subject:((urgent OR important) meeting)")
598
+ assert_instance_of Operator, ast
599
+ assert_equal "subject", ast.name
600
+
601
+ assert_instance_of And, ast.value
602
+ assert_equal 2, ast.value.operands.length
603
+ assert_instance_of Or, ast.value.operands[0]
604
+ assert_equal 2, ast.value.operands[0].operands.length
605
+ assert_equal "urgent", ast.value.operands[0].operands[0].value
606
+ assert_equal "important", ast.value.operands[0].operands[1].value
607
+ assert_instance_of Text, ast.value.operands[1]
608
+ assert_equal "meeting", ast.value.operands[1].value
609
+ end
610
+
611
+ def test_curly_braces_inside_operator_value
612
+ ast = GmailSearchSyntax.parse!("from:{mischa@ marc@}")
613
+ assert_instance_of Operator, ast
614
+ assert_equal "from", ast.name
615
+
616
+ assert_instance_of Or, ast.value
617
+ assert_equal 2, ast.value.operands.length
618
+ assert_instance_of Text, ast.value.operands[0]
619
+ assert_equal "mischa@", ast.value.operands[0].value
620
+ assert_instance_of Text, ast.value.operands[1]
621
+ assert_equal "marc@", ast.value.operands[1].value
622
+ end
623
+
624
+ def test_curly_braces_with_emails_inside_operator
625
+ ast = GmailSearchSyntax.parse!("from:{amy@example.com bob@example.com}")
626
+ assert_instance_of Operator, ast
627
+ assert_equal "from", ast.name
628
+
629
+ assert_instance_of Or, ast.value
630
+ assert_equal 2, ast.value.operands.length
631
+ assert_equal "amy@example.com", ast.value.operands[0].value
632
+ assert_equal "bob@example.com", ast.value.operands[1].value
633
+ end
634
+
635
+ def test_multiple_items_in_curly_braces
636
+ ast = GmailSearchSyntax.parse!("from:{a@ b@ c@ d@}")
637
+ assert_instance_of Operator, ast
638
+ assert_equal "from", ast.name
639
+
640
+ assert_instance_of Or, ast.value
641
+ assert_equal 4, ast.value.operands.length
642
+ assert_equal "a@", ast.value.operands[0].value
643
+ assert_equal "b@", ast.value.operands[1].value
644
+ assert_equal "c@", ast.value.operands[2].value
645
+ assert_equal "d@", ast.value.operands[3].value
646
+ end
647
+
648
+ def test_curly_braces_combined_with_other_conditions
649
+ ast = GmailSearchSyntax.parse!("from:{alice@ bob@} subject:meeting")
650
+ assert_instance_of And, ast
651
+
652
+ assert_equal 2, ast.operands.length
653
+ assert_instance_of Operator, ast.operands[0]
654
+ assert_equal "from", ast.operands[0].name
655
+ assert_instance_of Or, ast.operands[0].value
656
+ assert_equal 2, ast.operands[0].value.operands.length
657
+
658
+ assert_instance_of Operator, ast.operands[1]
659
+ assert_equal "subject", ast.operands[1].name
660
+ assert_equal "meeting", ast.operands[1].value
661
+ end
662
+
663
+ def test_multiple_operators_with_curly_braces
664
+ ast = GmailSearchSyntax.parse!("from:{alice@ bob@} to:{charlie@ david@}")
665
+ assert_instance_of And, ast
666
+
667
+ assert_equal 2, ast.operands.length
668
+
669
+ assert_instance_of Operator, ast.operands[0]
670
+ assert_equal "from", ast.operands[0].name
671
+ assert_instance_of Or, ast.operands[0].value
672
+
673
+ assert_instance_of Operator, ast.operands[1]
674
+ assert_equal "to", ast.operands[1].name
675
+ assert_instance_of Or, ast.operands[1].value
676
+ end
677
+
678
+ def test_mixing_parentheses_and_curly_braces
679
+ ast = GmailSearchSyntax.parse!("from:{alice@ bob@} subject:(urgent meeting)")
680
+ assert_instance_of And, ast
681
+
682
+ assert_equal 2, ast.operands.length
683
+ assert_instance_of Operator, ast.operands[0]
684
+ assert_equal "from", ast.operands[0].name
685
+ assert_instance_of Or, ast.operands[0].value
686
+
687
+ assert_instance_of Operator, ast.operands[1]
688
+ assert_equal "subject", ast.operands[1].name
689
+ assert_instance_of And, ast.operands[1].value
690
+ end
691
+ end