yax-fauna 3.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,1104 @@
1
+ RSpec.describe Fauna::Query do
2
+ before(:all) do
3
+ create_test_db
4
+ @test_class = client.query { create ref('classes'), name: 'query_test' }[:ref]
5
+
6
+ index_x = client.query do
7
+ create ref('indexes'), name: 'query_by_x', source: @test_class, terms: [{ field: %w(data x) }]
8
+ end
9
+ index_y = client.query do
10
+ create ref('indexes'), name: 'query_by_y', source: @test_class, terms: [{ field: %w(data y) }]
11
+ end
12
+
13
+ wait_for_index(index_x[:ref], index_y[:ref])
14
+
15
+ @test_by_x = index_x[:ref]
16
+ @test_by_y = index_y[:ref]
17
+ end
18
+
19
+ after(:all) do
20
+ destroy_test_db
21
+ end
22
+
23
+ # Alias for creating test class instance with provided data
24
+ def create_instance(data = {})
25
+ client.query { create(@test_class, data: data) }
26
+ end
27
+
28
+ # Helper to collect all the contents of a set
29
+ def get_set_data(set, params = {})
30
+ data = []
31
+
32
+ page = client.query { paginate(set, params) }
33
+ data += page[:data]
34
+ while page.key? :after
35
+ page = client.query { paginate(set, params.merge(after: page[:after])) }
36
+ data += page[:data]
37
+ end
38
+
39
+ data
40
+ end
41
+
42
+ describe Fauna::Query::Expr do
43
+ describe '#to_s' do
44
+ it 'converts to string' do
45
+ expr = Fauna::Query::Expr.new(
46
+ add: Fauna::Query::Expr.new(
47
+ [1, Fauna::Query::Expr.new(divide: Fauna::Query::Expr.new([4, 2]))]
48
+ )
49
+ )
50
+ as_string = 'Expr({:add=>Expr([1, Expr({:divide=>Expr([4, 2])})])})'
51
+
52
+ expect(expr.to_s).to eq(as_string)
53
+ end
54
+ end
55
+
56
+ describe '#==' do
57
+ it 'equals identical expression' do
58
+ expr1 = Fauna::Query::Expr.new(add: Fauna::Query::Expr.new([1, 2]))
59
+ expr2 = Fauna::Query::Expr.new(add: Fauna::Query::Expr.new([1, 2]))
60
+
61
+ expect(expr1).to eq(expr2)
62
+ end
63
+
64
+ it 'does not equal different expression' do
65
+ expr1 = Fauna::Query::Expr.new(add: Fauna::Query::Expr.new([1, 2]))
66
+ expr2 = Fauna::Query::Expr.new(
67
+ add: Fauna::Query::Expr.new(
68
+ [1, Fauna::Query::Expr.new(divide: Fauna::Query::Expr.new([4, 2]))]
69
+ )
70
+ )
71
+
72
+ expect(expr1).not_to eq(expr2)
73
+ end
74
+ end
75
+ end
76
+
77
+ describe '#expr' do
78
+ it 'maintains lexical scope' do
79
+ def test_method
80
+ 'foo'
81
+ end
82
+ test_var = 'bar'
83
+
84
+ expect(Fauna::Query.expr { test_method }).to eq('foo')
85
+ expect(Fauna::Query.expr { test_var }).to eq('bar')
86
+ end
87
+
88
+ it 'converts proc blocks' do
89
+ expr = Fauna::Query.expr { proc { |a| add(a, a) } }
90
+ query = { lambda: :a, expr: { add: [{ var: :a }, { var: :a }] } }
91
+
92
+ expect(to_json(expr)).to eq(to_json(query))
93
+ end
94
+
95
+ it 'recursively wraps arrays' do
96
+ expr = Fauna::Query.expr { [1, { foo: 2 }, add(1, 2)] }
97
+ query = [1, { object: { foo: 2 } }, { add: [1, 2] }]
98
+
99
+ expect(to_json(expr)).to eq(to_json(query))
100
+ end
101
+
102
+ it 'recursively wraps hashes' do
103
+ expr = Fauna::Query.expr { { x: 1, y: { foo: 2 }, z: add(1, 2), a: [1, 2, { b: 3, c: add(1, 3) }] } }
104
+ query = { object: { x: 1, y: { object: { foo: 2 } }, z: { add: [1, 2] }, a: [1, 2, { object: { b: 3, c: { add: [1, 3] } } }] } }
105
+
106
+ expect(to_json(expr)).to eq(to_json(query))
107
+ end
108
+
109
+ it 'recursively wraps to_h objects' do
110
+ class ToH
111
+ def initialize(obj)
112
+ @obj = obj
113
+ end
114
+
115
+ def to_h
116
+ @obj
117
+ end
118
+ end
119
+
120
+ expr = Fauna::Query.expr { ToH.new(x: 1, y: ToH.new(foo: 2), z: add(1, 2)) }
121
+ query = { object: { x: 1, y: { object: { foo: 2 } }, z: { add: [1, 2] } } }
122
+
123
+ expect(to_json(expr)).to eq(to_json(query))
124
+ end
125
+
126
+ it 'recursively wraps to_hash objects' do
127
+ class ToHash
128
+ def initialize(obj)
129
+ @obj = obj
130
+ end
131
+
132
+ def to_hash
133
+ @obj
134
+ end
135
+ end
136
+
137
+ expr = Fauna::Query.expr { ToHash.new(x: 1, y: ToHash.new(foo: 2), z: add(1, 2)) }
138
+ query = { object: { x: 1, y: { object: { foo: 2 } }, z: { add: [1, 2] } } }
139
+
140
+ expect(to_json(expr)).to eq(to_json(query))
141
+ end
142
+
143
+ it 'recursively wraps special types' do
144
+ expr = Fauna::Query.expr { { x: { y: Time.at(0).utc } } }
145
+ query = { object: { x: { object: { y: { :@ts => '1970-01-01T00:00:00.000000000Z' } } } } }
146
+
147
+ expect(to_json(expr)).to eq(to_json(query))
148
+ end
149
+
150
+ it 'round-trips special types', skip: 'Support for auto-escaping of special types is deferred' do
151
+ expect(client.query { { '@ref' => 'foo' } }).to eq(:@ref => 'foo')
152
+ end
153
+
154
+ it 'fails on unserializable objects' do
155
+ obj = DummyClass.new(random_string)
156
+
157
+ expect { Fauna::Query.expr { obj } }.to raise_error(Fauna::SerializationError)
158
+ end
159
+ end
160
+
161
+ describe '#ref' do
162
+ it 'returns a ref from a string' do
163
+ cls = random_string
164
+ id = random_number.to_s
165
+ str = "classes/#{cls}/#{id}"
166
+ expect(client.query { ref(str) }).to eq(Fauna::Ref.new(id, Fauna::Ref.new(cls, Fauna::Native.classes)))
167
+ end
168
+
169
+ it 'constructs a ref' do
170
+ expect(client.query { ref(@test_class, '123') }).to eq(Fauna::Ref.new('123', @test_class))
171
+ expect(client.query { ref(@test_class, new_id) }.id).to match(%r{\d+$})
172
+ end
173
+ end
174
+
175
+ describe '#abort' do
176
+ it 'aborts the execution' do
177
+ expect { client.query { abort('message') }}.to raise_error(Fauna::BadRequest)
178
+ end
179
+ end
180
+
181
+ describe '#object' do
182
+ it 'wraps fields in object' do
183
+ data = { a: random_string, b: random_number }
184
+ expect(Fauna::Query.object(data).raw).to eq(object: data)
185
+ end
186
+ end
187
+
188
+ describe '#query' do
189
+ it 'wraps fields in query' do
190
+ data = Fauna::Query::Expr.new(lambda: random_string, expr: Fauna::Query::Expr.new(add: Fauna::Query::Expr.wrap([1, 1])))
191
+ expect(Fauna::Query.query(data).raw).to eq(query: data)
192
+ end
193
+ end
194
+
195
+ describe '#at' do
196
+ it 'performs at for given expression' do
197
+ instance = create_instance
198
+ ref = instance[:ref]
199
+ ts = instance[:ts]
200
+
201
+ prev_ts = ts - 1
202
+ value = random_number
203
+ client.query { insert(ref, prev_ts, :create, data: { x: value }) }
204
+
205
+ expect(client.query { at(prev_ts, get(ref)) }[:data]).to eq(x: value)
206
+ end
207
+ end
208
+
209
+ describe '#let' do
210
+ it 'performs let with expression' do
211
+ x = random_number
212
+ expect(client.query { let({ x: x }, var(:x)) }).to eq(x)
213
+ end
214
+
215
+ it 'performs let with block' do
216
+ x = random_number
217
+ expect(client.query { let(x: x) { x } }).to eq(x)
218
+ end
219
+ end
220
+
221
+ describe '#var' do
222
+ it 'creates a var' do
223
+ name = random_string
224
+ expect(Fauna::Query.var(name).raw).to eq(var: name)
225
+ end
226
+ end
227
+
228
+ describe '#if_' do
229
+ it 'performs an if' do
230
+ expect(client.query { if_(true, 't', 'f') }).to eq('t')
231
+ expect(client.query { if_(false, 't', 'f') }).to eq('f')
232
+ end
233
+ end
234
+
235
+ describe '#do_' do
236
+ it 'performs do' do
237
+ instance = create_instance
238
+ expect(client.query { do_(delete(instance[:ref]), 1) }).to eq(1)
239
+ expect(client.query { exists instance[:ref] }).to be(false)
240
+ end
241
+ end
242
+
243
+ describe '#lambda' do
244
+ it 'raises when block takes no arguments' do
245
+ expect { Fauna::Query.lambda {} }.to raise_error(ArgumentError)
246
+ end
247
+
248
+ it 'raises when block takes splat argument' do
249
+ expect { Fauna::Query.lambda { |*vars| add(vars) } }.to raise_error(ArgumentError)
250
+ end
251
+
252
+ it 'performs lambda from single argument' do
253
+ expr = Fauna::Query.expr { lambda { |a| add(a, a) } }
254
+ query = { lambda: :a, expr: { add: [{ var: :a }, { var: :a }] } }
255
+
256
+ expect(to_json(expr)).to eq(to_json(query))
257
+ expect(client.query { map([1, 2, 3], expr) }).to eq([2, 4, 6])
258
+ end
259
+
260
+ it 'performs lambda from multiple arguments' do
261
+ expr = Fauna::Query.expr { lambda { |a, b| [b, a] } }
262
+ query = { lambda: [:a, :b], expr: [{ var: :b }, { var: :a }] }
263
+
264
+ expect(to_json(expr)).to eq(to_json(query))
265
+ expect(client.query { map([[1, 2], [3, 4]], expr) }).to eq([[2, 1], [4, 3]])
266
+ end
267
+ end
268
+
269
+ describe '#lambda_expr' do
270
+ it 'performs lambda from expression' do
271
+ expr = Fauna::Query.expr { lambda_expr(:a, add(var(:a), var(:a))) }
272
+ query = { lambda: :a, expr: { add: [{ var: :a }, { var: :a }] } }
273
+
274
+ expect(to_json(expr)).to eq(to_json(query))
275
+ expect(client.query { map([1, 2, 3], expr) }).to eq([2, 4, 6])
276
+ end
277
+
278
+ it 'destructures single element arrays' do
279
+ expr = Fauna::Query.expr { lambda_expr([:a], add(var(:a), var(:a))) }
280
+ query = { lambda: [:a], expr: { add: [{ var: :a }, { var: :a }] } }
281
+
282
+ expect(to_json(expr)).to eq(to_json(query))
283
+ expect(client.query { map([[1], [2], [3]], expr) }).to eq([2, 4, 6])
284
+ end
285
+ end
286
+
287
+ describe '#call' do
288
+ it 'performs called function' do
289
+ test_func = client.query do
290
+ func_body = lambda do |x|
291
+ [add(x, 1), add(x, 2), add(x, 3)]
292
+ end
293
+
294
+ create ref('functions'), name: 'call_func_test', body: query(func_body)
295
+ end
296
+
297
+ x = random_number
298
+
299
+ expect(client.query { call(test_func[:ref], x) }).to eq([x + 1, x + 2, x + 3])
300
+ end
301
+
302
+ it 'handles multiple arguments' do
303
+ test_func = client.query do
304
+ func_body = lambda do |x, y, z|
305
+ [multiply(x, 2), multiply(y, 3), multiply(z, 4)]
306
+ end
307
+
308
+ create ref('functions'), name: 'call_multiarg_test', body: query(func_body)
309
+ end
310
+
311
+ x = random_number
312
+ y = random_number
313
+ z = random_number
314
+
315
+ expect(client.query { call(test_func[:ref], x, y, z) }).to eq([x * 2, y * 3, z * 4])
316
+ end
317
+ end
318
+
319
+ describe '#map' do
320
+ it 'performs map from expression' do
321
+ input = (1..3).collect { random_number }
322
+ output = input.collect { |x| 2 * x }
323
+
324
+ expect(client.query { map(input, lambda { |a| multiply 2, a }) }).to eq(output)
325
+ end
326
+
327
+ it 'performs map from block' do
328
+ input = (1..3).collect { random_number }
329
+ output = input.collect { |x| 2 * x }
330
+
331
+ expect(client.query { map(input) { |a| multiply 2, a } }).to eq(output)
332
+ end
333
+ end
334
+
335
+ describe '#foreach' do
336
+ before(:each) do
337
+ @refs = (1..3).collect { create_instance[:ref] }
338
+
339
+ # Sanity check
340
+ expect(client.query { @refs.collect { |ref| exists ref } }).to eq(@refs.collect { true })
341
+ end
342
+
343
+ it 'performs foreach from expression' do
344
+ client.query { foreach @refs, lambda { |a| delete a } }
345
+
346
+ expect(client.query { @refs.collect { |ref| exists ref } }).to eq(@refs.collect { false })
347
+ end
348
+
349
+ it 'performs foreach from block' do
350
+ client.query { foreach(@refs) { |a| delete a } }
351
+
352
+ expect(client.query { @refs.collect { |ref| exists ref } }).to eq(@refs.collect { false })
353
+ end
354
+ end
355
+
356
+ describe '#filter' do
357
+ it 'performs filter from expression' do
358
+ expect(client.query { filter([1, 2, 3, 4], lambda { |a| equals modulo(a, 2), 0 }) }).to eq([2, 4])
359
+ end
360
+
361
+ it 'performs filter from block' do
362
+ expect(client.query { filter([1, 2, 3, 4]) { |a| equals modulo(a, 2), 0 } }).to eq([2, 4])
363
+ end
364
+ end
365
+
366
+ describe '#take' do
367
+ it 'performs take' do
368
+ expect(client.query { take(1, [1, 2]) }).to eq([1])
369
+ expect(client.query { take(3, [1, 2]) }).to eq([1, 2])
370
+ expect(client.query { take(-1, [1, 2]) }).to eq([])
371
+ end
372
+ end
373
+
374
+ describe '#drop' do
375
+ it 'performs drop' do
376
+ expect(client.query { drop(1, [1, 2]) }).to eq([2])
377
+ expect(client.query { drop(3, [1, 2]) }).to eq([])
378
+ expect(client.query { drop(-1, [1, 2]) }).to eq([1, 2])
379
+ end
380
+ end
381
+
382
+ describe '#prepend' do
383
+ it 'performs prepend' do
384
+ expect(client.query { prepend([1, 2, 3], [4, 5, 6]) }).to eq([1, 2, 3, 4, 5, 6])
385
+ end
386
+ end
387
+
388
+ describe '#append' do
389
+ it 'performs append' do
390
+ expect(client.query { append([1, 2, 3], [4, 5, 6]) }).to eq([4, 5, 6, 1, 2, 3])
391
+ end
392
+ end
393
+
394
+ describe '#is_empty' do
395
+ it 'performs is_empty on collections' do
396
+ expect(client.query { is_empty([]) }).to be(true)
397
+ expect(client.query { is_empty([1, 2, 3]) }).to be(false)
398
+ end
399
+
400
+ it 'performs is_empty on pages' do
401
+ expect(client.query { is_empty(paginate(classes(), size: 0)) }).to be(true)
402
+ expect(client.query { is_empty(paginate(classes(), size: 1)) }).to be(false)
403
+ end
404
+ end
405
+
406
+ describe '#is_nonempty' do
407
+ it 'performs is_nonempty on collections' do
408
+ expect(client.query { is_nonempty([]) }).to be(false)
409
+ expect(client.query { is_nonempty([1, 2, 3]) }).to be(true)
410
+ end
411
+
412
+ it 'performs is_empty on pages' do
413
+ expect(client.query { is_nonempty(paginate(classes(), size: 0)) }).to be(false)
414
+ expect(client.query { is_nonempty(paginate(classes(), size: 1)) }).to be(true)
415
+ end
416
+ end
417
+
418
+ describe '#get' do
419
+ it 'performs get' do
420
+ instance = create_instance
421
+
422
+ expect(client.query { get instance[:ref] }).to eq(instance)
423
+ end
424
+ end
425
+
426
+ describe '#key_from_secret' do
427
+ it 'gets key from secret' do
428
+ # Create a key
429
+ db_ref = admin_client.query { create(ref('databases'), name: random_string) }[:ref]
430
+ key = admin_client.query { create_key(database: db_ref, role: 'server') }
431
+
432
+ expect(admin_client.query { key_from_secret key[:secret] }).to eq(admin_client.query { get key[:ref] })
433
+ end
434
+ end
435
+
436
+ describe '#paginate' do
437
+ before do
438
+ @x_value = random_number
439
+ @x_refs = (1..3).collect { create_instance(x: @x_value)[:ref] }
440
+ end
441
+
442
+ it 'performs paginate' do
443
+ set = Fauna::Query.match(@test_by_x, @x_value)
444
+
445
+ expect(get_set_data(set, size: 1)).to eq(@x_refs)
446
+ end
447
+
448
+ it 'performs paginate with sources' do
449
+ response = {
450
+ data: @x_refs.collect do |ref|
451
+ { sources: [Fauna::SetRef.new(match: @test_by_x, terms: @x_value)], value: ref }
452
+ end
453
+ }
454
+
455
+ expect(client.query { paginate(match(@test_by_x, @x_value), sources: true) }).to eq(response)
456
+ end
457
+ end
458
+
459
+ describe '#exists' do
460
+ it 'performs exists' do
461
+ ref = create_instance[:ref]
462
+
463
+ expect(client.query { exists ref }).to be(true)
464
+ client.query { delete ref }
465
+ expect(client.query { exists ref }).to be(false)
466
+
467
+ # Sanity check
468
+ expect { client.query { get ref } }.to raise_error(Fauna::NotFound)
469
+ end
470
+ end
471
+
472
+ describe '#create' do
473
+ it 'performs create' do
474
+ instance = client.query { create(@test_class, {}) }
475
+
476
+ expect(instance[:ref].class_).to eq(@test_class)
477
+ expect(client.query { exists instance[:ref] }).to be(true)
478
+ end
479
+ end
480
+
481
+ describe '#update' do
482
+ it 'performs update' do
483
+ x = random_number
484
+ y = random_number
485
+ ref = create_instance(x: x)[:ref]
486
+
487
+ instance = client.query { update(ref, data: { y: y }) }
488
+ expect(instance[:data]).to eq(x: x, y: y)
489
+ end
490
+ end
491
+
492
+ describe '#replace' do
493
+ it 'performs replace' do
494
+ x = random_number
495
+ y = random_number
496
+ ref = create_instance(x: x)[:ref]
497
+
498
+ instance = client.query { replace(ref, data: { y: y }) }
499
+ expect(instance[:data]).to eq(y: y)
500
+ end
501
+ end
502
+
503
+ describe '#delete' do
504
+ it 'performs delete' do
505
+ ref = create_instance[:ref]
506
+
507
+ client.query { delete ref }
508
+ expect(client.query { exists ref }).to be(false)
509
+ end
510
+ end
511
+
512
+ describe '#insert' do
513
+ it 'performs insert' do
514
+ instance = create_instance
515
+ ref = instance[:ref]
516
+ ts = instance[:ts]
517
+
518
+ prev_ts = ts - 1
519
+ value = random_number
520
+ client.query { insert(ref, prev_ts, :create, data: { x: value }) }
521
+
522
+ expect(client.query { get(ref, ts: prev_ts) }[:data]).to eq(x: value)
523
+ end
524
+ end
525
+
526
+ describe '#remove' do
527
+ it 'performs remove' do
528
+ # Create the instance
529
+ instance = create_instance
530
+ ref = instance[:ref]
531
+
532
+ # Change the instance
533
+ new_instance = client.query { replace(ref, data: { x: random_number }) }
534
+ expect(client.query { get(ref) }).to eq(new_instance)
535
+
536
+ # Delete the event
537
+ client.query { remove(ref, new_instance[:ts], :create) }
538
+
539
+ # Assert it changed
540
+ expect(client.query { get(ref) }).to eq(instance)
541
+ end
542
+ end
543
+
544
+ describe '#create_class' do
545
+ it 'creates a class' do
546
+ # Create a class
547
+ ref = client.query { create_class(name: random_string) }[:ref]
548
+
549
+ # Assert it was created
550
+ expect(client.query { exists(ref) }).to be(true)
551
+ end
552
+ end
553
+
554
+ describe '#create_index' do
555
+ it 'creates an index' do
556
+ # Create an index
557
+ class_ref = client.query { create(ref('classes'), name: random_string) }[:ref]
558
+ ref = client.query { create_index(name: random_string, source: class_ref) }[:ref]
559
+
560
+ # Assert it was created
561
+ expect(client.query { exists(ref) }).to be(true)
562
+ end
563
+ end
564
+
565
+ describe '#create_database' do
566
+ it 'creates a database' do
567
+ # Create a database
568
+ ref = admin_client.query { create_database(name: random_string) }[:ref]
569
+
570
+ # Assert it was created
571
+ expect(admin_client.query { exists(ref) }).to be(true)
572
+ end
573
+ end
574
+
575
+ describe '#create_key' do
576
+ it 'creates a key' do
577
+ # Create a key
578
+ db_ref = admin_client.query { create(ref('databases'), name: random_string) }[:ref]
579
+ ref = admin_client.query { create_key(database: db_ref, role: 'server') }[:ref]
580
+
581
+ # Assert it was created
582
+ expect(admin_client.query { exists(ref) }).to be(true)
583
+ end
584
+ end
585
+
586
+ describe '#create_function' do
587
+ it 'creates a function' do
588
+ # Create a function
589
+ ref = client.query { create_function(name: random_string, body: query(lambda { |a| add(a, 1) })) }[:ref]
590
+
591
+ # Assert it was created
592
+ expect(client.query { exists(ref) }).to be(true)
593
+ end
594
+ end
595
+
596
+ describe 'sets' do
597
+ before do
598
+ @x_value = random_number
599
+ @y_value = random_number
600
+
601
+ @ref_x = create_instance(x: @x_value)[:ref]
602
+ @ref_y = create_instance(y: @y_value)[:ref]
603
+ @ref_xy = create_instance(x: @x_value, y: @y_value)[:ref]
604
+ end
605
+
606
+ describe '#events' do
607
+ it 'performs events' do
608
+ client.query { update @ref_x, data: {x: random_number} }
609
+ client.query { delete @ref_x }
610
+
611
+ events = client.query { paginate events(@ref_x) }[:data]
612
+
613
+ expect(events.count).to be(3)
614
+
615
+ expect(events[0][:action]).to eq('create')
616
+ expect(events[0][:instance]).to eq(@ref_x)
617
+
618
+ expect(events[1][:action]).to eq('update')
619
+ expect(events[1][:instance]).to eq(@ref_x)
620
+
621
+ expect(events[2][:action]).to eq('delete')
622
+ expect(events[2][:instance]).to eq(@ref_x)
623
+ end
624
+ end
625
+
626
+ describe '#singleton' do
627
+ it 'performs singleton' do
628
+ client.query { update @ref_x, data: {x: random_number} }
629
+ client.query { delete @ref_x }
630
+
631
+ events = client.query { paginate events(singleton(@ref_x)) }[:data]
632
+
633
+ expect(events.count).to be(2)
634
+
635
+ expect(events[0][:action]).to eq('add')
636
+ expect(events[0][:instance]).to eq(@ref_x)
637
+
638
+ expect(events[1][:action]).to eq('remove')
639
+ expect(events[1][:instance]).to eq(@ref_x)
640
+ end
641
+ end
642
+
643
+ describe '#match' do
644
+ it 'performs match' do
645
+ set = Fauna::Query.expr { match(@test_by_x, @x_value) }
646
+ expect(get_set_data(set)).to contain_exactly(@ref_x, @ref_xy)
647
+ end
648
+ end
649
+
650
+ describe '#union' do
651
+ it 'performs union' do
652
+ set = Fauna::Query.expr { union(match(@test_by_x, @x_value), match(@test_by_y, @y_value)) }
653
+ expect(get_set_data(set)).to contain_exactly(@ref_x, @ref_y, @ref_xy)
654
+ end
655
+ end
656
+
657
+ describe '#intersection' do
658
+ it 'performs intersection' do
659
+ set = Fauna::Query.expr { intersection(match(@test_by_x, @x_value), match(@test_by_y, @y_value)) }
660
+ expect(get_set_data(set)).to contain_exactly(@ref_xy)
661
+ end
662
+ end
663
+
664
+ describe '#difference' do
665
+ it 'performs difference' do
666
+ set = Fauna::Query.expr { difference(match(@test_by_x, @x_value), match(@test_by_y, @y_value)) }
667
+ expect(get_set_data(set)).to contain_exactly(@ref_x)
668
+ end
669
+ end
670
+ end
671
+
672
+ describe '#distinct' do
673
+ before do
674
+ over_z = client.query do
675
+ create ref('indexes'), name: 'query_over_z', source: @test_class, values: [{ field: %w(data z) }]
676
+ end
677
+ wait_for_index(over_z[:ref])
678
+ @test_over_z = over_z[:ref]
679
+
680
+ @refs = []
681
+ @refs << client.query { create @test_class, data: { z: 0 } }[:ref]
682
+ @refs << client.query { create @test_class, data: { z: 1 } }[:ref]
683
+ @refs << client.query { create @test_class, data: { z: 1 } }[:ref]
684
+ end
685
+
686
+ it 'performs distinct' do
687
+ set = Fauna::Query.match(@test_over_z)
688
+ distinct = Fauna::Query.distinct(set)
689
+
690
+ expect(get_set_data(set)).to eq([0, 1, 1])
691
+ expect(get_set_data(distinct)).to eq([0, 1])
692
+ end
693
+ end
694
+
695
+ describe '#join' do
696
+ before do
697
+ @x_value = random_number
698
+ @join_refs = (1..3).collect { create_instance(x: @x_value)[:ref] }
699
+ @assoc_refs = @join_refs.collect { |ref| create_instance(y: ref)[:ref] }
700
+ end
701
+
702
+ context 'with expression' do
703
+ it 'performs join' do
704
+ source = Fauna::Query.match(@test_by_x, @x_value)
705
+ expect(get_set_data(source)).to eq(@join_refs)
706
+
707
+ # Get associated refs
708
+ set = Fauna::Query.expr { join(source, lambda { |a| match(@test_by_y, a) }) }
709
+ expect(get_set_data(set)).to eq(@assoc_refs)
710
+ end
711
+ end
712
+
713
+ context 'with block' do
714
+ it 'performs join' do
715
+ source = Fauna::Query.match(@test_by_x, @x_value)
716
+ expect(get_set_data(source)).to eq(@join_refs)
717
+
718
+ # Get associated refs
719
+ set = Fauna::Query.expr { join(source) { |a| match(@test_by_y, a) } }
720
+ expect(get_set_data(set)).to eq(@assoc_refs)
721
+ end
722
+ end
723
+
724
+ context 'with index' do
725
+ it 'performs join' do
726
+ source = Fauna::Query.match(@test_by_x, @x_value)
727
+ expect(get_set_data(source)).to eq(@join_refs)
728
+
729
+ # Get associated refs
730
+ set = Fauna::Query.expr { join(source, @test_by_y) }
731
+ expect(get_set_data(set)).to eq(@assoc_refs)
732
+ end
733
+ end
734
+ end
735
+
736
+ describe 'authentication' do
737
+ before do
738
+ @password = random_string
739
+ @user = client.query { create @test_class, credentials: { password: @password } }
740
+ end
741
+
742
+ describe '#login' do
743
+ it 'performs login' do
744
+ token = client.query { login @user[:ref], password: @password }
745
+ user_client = get_client secret: token[:secret]
746
+
747
+ self_ = Fauna::Ref.new('self', Fauna::Native.tokens)
748
+ expect(user_client.query { select(:ref, get(self_)) }).to eq(token[:ref])
749
+ end
750
+ end
751
+
752
+ describe '#logout' do
753
+ it 'performs logout' do
754
+ token = client.query { login @user[:ref], password: @password }
755
+ user_client = get_client secret: token[:secret]
756
+
757
+ expect(user_client.query { logout true }).to be(true)
758
+ end
759
+ end
760
+
761
+ describe '#identify' do
762
+ it 'performs identify' do
763
+ expect(client.query { identify(@user[:ref], @password) }).to be(true)
764
+ end
765
+ end
766
+
767
+ describe '#has_identity' do
768
+ it 'performs has_identity' do
769
+ token = client.query { login @user[:ref], password: @password }
770
+ user_client = get_client secret: token[:secret]
771
+
772
+ expect(client.query { has_identity }).to be(false)
773
+ expect(user_client.query { has_identity }).to be(true)
774
+ end
775
+ end
776
+
777
+ describe '#identity' do
778
+ it 'performs identity' do
779
+ token = client.query { login @user[:ref], password: @password }
780
+ user_client = get_client secret: token[:secret]
781
+
782
+ expect(user_client.query { identity }).to eq(@user[:ref])
783
+ end
784
+ end
785
+ end
786
+
787
+ describe '#concat' do
788
+ it 'performs concat' do
789
+ expect(client.query { concat ['a', 'b', 'c'] }).to eq('abc')
790
+ expect(client.query { concat [] }).to eq('')
791
+ end
792
+
793
+ it 'performs concat with separator' do
794
+ expect(client.query { concat(['a', 'b', 'c'], '.') }).to eq('a.b.c')
795
+ end
796
+ end
797
+
798
+ describe '#casefold' do
799
+ it 'performs casefold' do
800
+ expect(client.query { casefold 'Hen Wen' }).to eq('hen wen')
801
+
802
+ # https://unicode.org/reports/tr15/
803
+ expect(client.query { casefold("\u212B", "NFD") } ).to eq("A\u030A")
804
+ expect(client.query { casefold("\u212B", "NFC") } ).to eq("\u00C5")
805
+ expect(client.query { casefold("\u1E9B\u0323", "NFKD") } ).to eq("\u0073\u0323\u0307")
806
+ expect(client.query { casefold("\u1E9B\u0323", "NFKC") } ).to eq("\u1E69")
807
+
808
+ expect(client.query { casefold("\u212B", "NFKCCaseFold") } ).to eq("\u00E5")
809
+ end
810
+ end
811
+
812
+ describe '#ngram' do
813
+ it 'performs ngram' do
814
+ expect(client.query { ngram 'what' }).to eq(['w', 'wh', 'h', 'ha', 'a', 'at', 't'])
815
+ expect(client.query { ngram 'what', min: 2, max: 3 }).to eq(['wh', 'wha', 'ha', 'hat', 'at'])
816
+
817
+ expect(client.query { ngram ['john', 'doe'] }).to eq(['j', 'jo', 'o', 'oh', 'h', 'hn', 'n', 'd', 'do', 'o', 'oe', 'e'])
818
+ expect(client.query { ngram ['john', 'doe'], min: 3, max: 4 }).to eq(['joh', 'john', 'ohn', 'doe'])
819
+ end
820
+ end
821
+
822
+ describe '#test' do
823
+ it 'performs time' do
824
+ # `.round 9` is necessary because MRI 1.9.3 stores with greater precision than just nanoseconds.
825
+ # This cuts it down to just nanoseconds so that the times compare as equal.
826
+ time = Time.at(0, 123_456.789).round 9
827
+ expect(client.query { time '1970-01-01T00:00:00.123456789Z' }).to eq(time)
828
+
829
+ # 'now' refers to the current time.
830
+ expect(client.query { time 'now' }).to be_a(Time)
831
+ end
832
+ end
833
+
834
+ describe '#epoch' do
835
+ it 'performs epoch for seconds' do
836
+ secs = random_number
837
+ expect(client.query { epoch(secs, 'second') }).to eq(Time.at(secs).utc)
838
+ end
839
+
840
+ it 'performs epoch for nanoseconds' do
841
+ nanos = random_number
842
+ expect(client.query { epoch(nanos, 'nanosecond') }).to eq(Time.at(Rational(nanos, 1_000_000_000)).utc)
843
+ end
844
+ end
845
+
846
+ describe '#date' do
847
+ it 'performs date' do
848
+ expect(client.query { date('1970-01-01') }).to eq(Date.new(1970, 1, 1))
849
+ end
850
+ end
851
+
852
+ describe '#new_id' do
853
+ it 'gets a new id' do
854
+ expect(client.query { new_id }).to be_a(String)
855
+ end
856
+ end
857
+
858
+ describe '#database' do
859
+ it 'gets an existing database' do
860
+ # Create a database
861
+ name = random_string
862
+ ref = admin_client.query { create_database(name: name) }[:ref]
863
+
864
+ # Get the database ref
865
+ expect(admin_client.query { database(name) }).to eq(ref)
866
+ end
867
+ end
868
+
869
+ describe '#class_' do
870
+ it 'gets an existing class' do
871
+ # Create a class
872
+ name = random_string
873
+ ref = client.query { create_class(name: name) }[:ref]
874
+
875
+ # Get the class ref
876
+ expect(client.query { class_(name) }).to eq(ref)
877
+ end
878
+ end
879
+
880
+ describe '#index' do
881
+ it 'gets an existing index' do
882
+ # Create an index
883
+ class_ref = client.query { create_class(name: random_string) }[:ref]
884
+ name = random_string
885
+ ref = client.query { create_index(name: name, source: class_ref) }[:ref]
886
+
887
+ # Get the index ref
888
+ expect(client.query { index(name) }).to eq(ref)
889
+ end
890
+ end
891
+
892
+ describe '#function' do
893
+ it 'gets an existing function' do
894
+ # Create a function
895
+ name = random_string
896
+ ref = client.query { create_function(name: name, body: query(lambda { |a| add(a, 1) })) }[:ref]
897
+
898
+ # Get the function ref
899
+ expect(client.query { function(name) }).to eq(ref)
900
+ end
901
+ end
902
+
903
+ describe '#equals' do
904
+ it 'performs equals' do
905
+ expect(client.query { equals(1, 1, 1) }).to be(true)
906
+ expect(client.query { equals(1, 1, 2) }).to be(false)
907
+ expect(client.query { equals 1 }).to be(true)
908
+ end
909
+ end
910
+
911
+ describe '#contains' do
912
+ it 'performs contains' do
913
+ obj = { a: { b: 1 } }
914
+
915
+ expect(client.query { contains([:a, :b], obj) }).to be(true)
916
+ expect(client.query { contains(:a, obj) }).to be(true)
917
+ expect(client.query { contains([:a, :c], obj) }).to be(false)
918
+ end
919
+ end
920
+
921
+ describe '#select' do
922
+ it 'performs select with hash' do
923
+ obj = { a: { b: 1 } }
924
+
925
+ expect(client.query { select(:a, obj) }).to eq(b: 1)
926
+ expect(client.query { select([:a, :b], obj) }).to eq(1)
927
+ expect(client.query { select(:c, obj, default: nil) }).to be_nil
928
+ expect { client.query { select(:c, obj) } }.to raise_error(Fauna::NotFound)
929
+ end
930
+
931
+ it 'performs select with array' do
932
+ arr = [1, 2, 3]
933
+
934
+ expect(client.query { select(2, arr) }).to eq(3)
935
+ expect { client.query { select(3, arr) } }.to raise_error(Fauna::NotFound)
936
+ end
937
+ end
938
+
939
+ describe '#select_all' do
940
+ it 'performs select_all with hash' do
941
+ obj1 = { foo: 'bar' }
942
+ obj2 = { foo: 'baz' }
943
+
944
+ expect(client.query { select_all(:foo, [obj1, obj2]) }).to eq(['bar', 'baz'])
945
+ end
946
+
947
+ it 'performs select_all with array' do
948
+ obj1 = { foo: [0, 1] }
949
+ obj2 = { foo: [2, 3] }
950
+
951
+ expect(client.query { select_all([:foo, 0], [obj1, obj2]) }).to eq([0, 2])
952
+ end
953
+ end
954
+
955
+ describe '#add' do
956
+ it 'performs add' do
957
+ expect(client.query { add(2, 3, 5) }).to eq(10)
958
+ end
959
+ end
960
+
961
+ describe '#multiply' do
962
+ it 'performs multiply' do
963
+ expect(client.query { multiply(2, 3, 5) }).to eq(30)
964
+ end
965
+ end
966
+
967
+ describe '#subtract' do
968
+ it 'performs subtract' do
969
+ expect(client.query { subtract(2, 3, 5) }).to eq(-6)
970
+ expect(client.query { subtract(2) }).to eq(2)
971
+ end
972
+ end
973
+
974
+ describe '#divide' do
975
+ it 'performs divide' do
976
+ expect(client.query { divide(2.0, 3, 5) }).to eq(2.0 / 15)
977
+ expect(client.query { divide(2) }).to eq(2)
978
+ end
979
+ end
980
+
981
+ describe '#modulo' do
982
+ it 'performs modulo' do
983
+ expect(client.query { modulo(5, 2) }).to eq(1)
984
+ expect(client.query { modulo(15, 10, 2) }).to eq(1)
985
+ expect(client.query { modulo(2) }).to eq(2)
986
+ end
987
+ end
988
+
989
+ describe '#lt' do
990
+ it 'performs lt' do
991
+ expect(client.query { lt(1, 2) }).to be(true)
992
+ expect(client.query { lt(2, 2) }).to be(false)
993
+ end
994
+ end
995
+
996
+ describe '#lte' do
997
+ it 'performs lte' do
998
+ expect(client.query { lte(1, 1) }).to be(true)
999
+ expect(client.query { lte(2, 1) }).to be(false)
1000
+ end
1001
+ end
1002
+
1003
+ describe '#gt' do
1004
+ it 'performs gt' do
1005
+ expect(client.query { gt(2, 1) }).to be(true)
1006
+ expect(client.query { gt(2, 2) }).to be(false)
1007
+ end
1008
+ end
1009
+
1010
+ describe '#gte' do
1011
+ it 'performs gte' do
1012
+ expect(client.query { gte(2, 2) }).to be(true)
1013
+ expect(client.query { gte(2, 3) }).to be(false)
1014
+ end
1015
+ end
1016
+
1017
+ describe '#and_' do
1018
+ it 'performs and' do
1019
+ expect(client.query { and_(true, true, false) }).to be(false)
1020
+ expect(client.query { and_(true, true, true) }).to be(true)
1021
+ expect(client.query { and_(true) }).to be(true)
1022
+ expect(client.query { and_(false) }).to be(false)
1023
+ end
1024
+ end
1025
+
1026
+ describe '#or_' do
1027
+ it 'performs or' do
1028
+ expect(client.query { or_(false, false, true) }).to be(true)
1029
+ expect(client.query { or_(false, false, false) }).to be(false)
1030
+ expect(client.query { or_(true) }).to be(true)
1031
+ expect(client.query { or_(false) }).to be(false)
1032
+ end
1033
+ end
1034
+
1035
+ describe '#not_' do
1036
+ it 'performs not' do
1037
+ expect(client.query { not_(true) }).to be(false)
1038
+ expect(client.query { not_(false) }).to be(true)
1039
+ end
1040
+ end
1041
+
1042
+ describe '#to_string' do
1043
+ it 'performs to_string' do
1044
+ expect(client.query { to_string(42) }).to eq("42")
1045
+ end
1046
+ end
1047
+
1048
+ describe '#to_number' do
1049
+ it 'performs to_number' do
1050
+ expect(client.query { to_number("42") }).to be(42)
1051
+ end
1052
+ end
1053
+
1054
+ describe '#to_time' do
1055
+ it 'performs to_time' do
1056
+ expect(client.query { to_time("1970-01-01T00:00:00Z") }).to eq(Time.at(0).utc)
1057
+ end
1058
+ end
1059
+
1060
+ describe '#to_date' do
1061
+ it 'performs to_date' do
1062
+ expect(client.query { to_date("1970-01-01") }).to eq(Date.new(1970, 1, 1))
1063
+ end
1064
+ end
1065
+
1066
+ describe '#recursive references' do
1067
+ it 'create nested keys' do
1068
+ new_client = create_new_database(admin_client, 'db-for-keys')
1069
+ new_client.query { create_database name: 'db-test' }
1070
+
1071
+ server_key = new_client.query { create_key database: database('db-test'), role: 'server' }
1072
+ admin_key = new_client.query { create_key database: database('db-test'), role: 'admin' }
1073
+
1074
+ expect(new_client.query { paginate keys() }).to eq(data: [server_key[:ref], admin_key[:ref]])
1075
+ expect(admin_client.query { paginate keys(database('db-for-keys')) }).to eq(data: [server_key[:ref], admin_key[:ref]])
1076
+ end
1077
+
1078
+ it 'create nested class' do
1079
+ client1 = create_new_database(admin_client, 'parent-database')
1080
+ create_new_database(client1, 'child-database')
1081
+
1082
+ key = client1.query { create_key database: database('child-database'), role: 'server' }
1083
+ client2 = get_client secret: key[:secret]
1084
+ client2.query { create_class name: 'a_class' }
1085
+
1086
+ nested_database = Fauna::Query.database('child-database', Fauna::Query.database('parent-database'))
1087
+ nested_class = Fauna::Query.class_('a_class', nested_database)
1088
+ all_nested_classes = Fauna::Query.classes(nested_database)
1089
+
1090
+ parent_database_ref = Fauna::Ref.new('parent-database', Fauna::Native.databases)
1091
+ child_database_ref = Fauna::Ref.new('child-database', Fauna::Native.databases, parent_database_ref)
1092
+ a_class_ref = Fauna::Ref.new('a_class', Fauna::Native.classes, child_database_ref)
1093
+
1094
+ expect(client.query { exists nested_class }).to be(true)
1095
+ expect(client.query { paginate all_nested_classes }).to eq(data: [a_class_ref])
1096
+ end
1097
+ end
1098
+
1099
+ def create_new_database(client, name)
1100
+ client.query { create_database name: name }
1101
+ key = client.query { create_key database: database(name), role: 'admin' }
1102
+ get_client secret: key[:secret]
1103
+ end
1104
+ end