lotus-dynamodb 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,940 @@
1
+ require 'test_helper'
2
+
3
+ describe Lotus::Model::Adapters::DynamodbAdapter do
4
+ before do
5
+ TestUser = Struct.new(:id, :name, :age) do
6
+ include Lotus::Entity
7
+ end
8
+
9
+ TestDevice = Struct.new(:id, :created_at) do
10
+ include Lotus::Entity
11
+ end
12
+
13
+ TestPurchase = Struct.new(:id, :region, :subtotal, :item_ids, :content, :created_at) do
14
+ include Lotus::Entity
15
+ end
16
+
17
+ coercer = Lotus::Model::Adapters::Dynamodb::Coercer
18
+ @mapper = Lotus::Model::Mapper.new(coercer) do
19
+ collection :test_users do
20
+ entity TestUser
21
+
22
+ attribute :id, String
23
+ attribute :name, String
24
+ attribute :age, Integer
25
+ end
26
+
27
+ collection :test_devices do
28
+ entity TestDevice
29
+
30
+ attribute :id, String, as: :uuid
31
+ attribute :created_at, Time
32
+
33
+ identity :uuid
34
+ end
35
+
36
+ collection :test_purchases do
37
+ entity TestPurchase
38
+
39
+ attribute :id, String, as: :uuid
40
+ attribute :region, String
41
+ attribute :subtotal, Float
42
+ attribute :item_ids, Set
43
+ attribute :content, AWS::DynamoDB::Binary
44
+ attribute :created_at, Time
45
+
46
+ identity :uuid
47
+ end
48
+ end.load!
49
+
50
+ @adapter = Lotus::Model::Adapters::DynamodbAdapter.new(@mapper)
51
+ @adapter.clear(collection)
52
+ end
53
+
54
+ after do
55
+ Object.send(:remove_const, :TestUser)
56
+ Object.send(:remove_const, :TestDevice)
57
+ Object.send(:remove_const, :TestPurchase)
58
+ end
59
+
60
+ let(:collection) { :test_users }
61
+
62
+ describe '#first' do
63
+ it 'raises an error' do
64
+ -> { @adapter.first(collection) }.must_raise NotImplementedError
65
+ end
66
+ end
67
+
68
+ describe '#last' do
69
+ it 'raises an error' do
70
+ -> { @adapter.last(collection) }.must_raise NotImplementedError
71
+ end
72
+ end
73
+
74
+ describe 'multiple collections' do
75
+ it 'create records' do
76
+ user = TestUser.new
77
+ device = TestDevice.new(created_at: Time.new)
78
+
79
+ @adapter.clear(:test_users)
80
+ @adapter.clear(:test_devices)
81
+
82
+ @adapter.create(:test_users, user)
83
+ @adapter.create(:test_devices, device)
84
+
85
+ @adapter.all(:test_users).must_equal [user]
86
+ @adapter.all(:test_devices).must_equal [device]
87
+ end
88
+ end
89
+
90
+ describe '#persist' do
91
+ describe 'when the given entity is not persisted' do
92
+ let(:entity) { TestUser.new }
93
+
94
+ it 'stores the record and assigns an id' do
95
+ @adapter.persist(collection, entity)
96
+
97
+ entity.id.wont_be_nil
98
+ @adapter.find(collection, entity.id).must_equal entity
99
+ end
100
+ end
101
+
102
+ describe 'when the given entity is persisted' do
103
+ before do
104
+ @adapter.create(collection, entity)
105
+ end
106
+
107
+ let(:entity) { TestUser.new }
108
+
109
+ it 'updates the record and leaves untouched the id' do
110
+ id = entity.id
111
+ id.wont_be_nil
112
+
113
+ entity.name = 'L'
114
+ @adapter.persist(collection, entity)
115
+
116
+ entity.id.must_equal(id)
117
+ @adapter.find(collection, entity.id).must_equal entity
118
+ end
119
+ end
120
+ end
121
+
122
+ describe '#create' do
123
+ let(:entity) { TestUser.new }
124
+
125
+ it 'stores the record and assigns an id' do
126
+ @adapter.create(collection, entity)
127
+
128
+ entity.id.wont_be_nil
129
+ @adapter.find(collection, entity.id).must_equal entity
130
+ end
131
+ end
132
+
133
+ describe '#update' do
134
+ before do
135
+ @adapter.create(collection, entity)
136
+ end
137
+
138
+ let(:entity) { TestUser.new(id: nil, name: 'L') }
139
+
140
+ it 'stores the changes and leave the id untouched' do
141
+ id = entity.id
142
+
143
+ entity.name = 'MG'
144
+ @adapter.update(collection, entity)
145
+
146
+ entity.id.must_equal id
147
+ @adapter.find(collection, entity.id).must_equal entity
148
+ end
149
+ end
150
+
151
+ describe '#delete' do
152
+ before do
153
+ @adapter.create(collection, entity)
154
+ end
155
+
156
+ let(:entity) { TestUser.new }
157
+
158
+ it 'removes the given identity' do
159
+ @adapter.delete(collection, entity)
160
+ @adapter.find(collection, entity.id).must_be_nil
161
+ end
162
+ end
163
+
164
+ describe '#all' do
165
+ describe 'when no records are persisted' do
166
+ it 'returns an empty collection' do
167
+ @adapter.all(collection).must_be_empty
168
+ end
169
+ end
170
+
171
+ describe 'when some records are persisted' do
172
+ before do
173
+ @adapter.create(collection, entity)
174
+ end
175
+
176
+ let(:entity) { TestUser.new }
177
+
178
+ it 'returns all of them' do
179
+ @adapter.all(collection).must_equal [entity]
180
+ end
181
+ end
182
+ end
183
+
184
+ describe '#find' do
185
+ before do
186
+ @adapter.create(collection, entity)
187
+ end
188
+
189
+ describe 'simple key' do
190
+ let(:entity) { TestUser.new }
191
+
192
+ it 'returns the record by id' do
193
+ @adapter.find(collection, entity.id).must_equal entity
194
+ end
195
+
196
+ it 'returns nil when the record cannot be found' do
197
+ @adapter.find(collection, 1_000_000.to_s).must_be_nil
198
+ end
199
+
200
+ it 'returns nil when the given id is nil' do
201
+ @adapter.find(collection, nil).must_be_nil
202
+ end
203
+
204
+ it 'returns nil when the given id is empty string' do
205
+ @adapter.find(collection, nil).must_be_nil
206
+ end
207
+ end
208
+
209
+ describe 'complex key' do
210
+ let(:entity) { TestDevice.new(created_at: Time.new) }
211
+ let(:collection) { :test_devices }
212
+
213
+ it 'returns the record by id' do
214
+ @adapter.find(collection, entity.id, entity.created_at).must_equal entity
215
+ end
216
+
217
+ it 'returns nil when not enough keys' do
218
+ @adapter.find(collection, entity.id).must_be_nil
219
+ end
220
+ end
221
+ end
222
+
223
+ describe '#clear' do
224
+ before do
225
+ @adapter.create(collection, entity)
226
+ end
227
+
228
+ let(:entity) { TestUser.new }
229
+
230
+ it 'removes all the records' do
231
+ @adapter.clear(collection)
232
+ @adapter.all(collection).must_be_empty
233
+ end
234
+ end
235
+
236
+ describe '#query' do
237
+ let(:collection) { :test_purchases }
238
+ let(:purchase1) do
239
+ TestPurchase.new(
240
+ region: 'europe',
241
+ subtotal: 15.0,
242
+ item_ids: [1, 2, 3],
243
+ content: "OMG",
244
+ created_at: Time.new,
245
+ )
246
+ end
247
+ let(:purchase2) do
248
+ TestPurchase.new(
249
+ region: 'europe',
250
+ subtotal: 10.0,
251
+ item_ids: ["2", "3", "4"],
252
+ content: AWS::DynamoDB::Binary.new("SO"),
253
+ created_at: Time.new,
254
+ )
255
+ end
256
+ let(:purchase3) do
257
+ TestPurchase.new(
258
+ region: 'usa',
259
+ subtotal: 5.0,
260
+ item_ids: [AWS::DynamoDB::Binary.new("WOW")],
261
+ content: AWS::DynamoDB::Binary.new("MUCH"),
262
+ created_at: Time.new,
263
+ )
264
+ end
265
+ let(:purchase4) do
266
+ TestPurchase.new(
267
+ region: 'asia',
268
+ subtotal: 100.0,
269
+ item_ids: [4, 5, 6],
270
+ content: AWS::DynamoDB::Binary.new("CONTENT"),
271
+ created_at: Time.new,
272
+ )
273
+ end
274
+ let(:purchase5) do
275
+ TestPurchase.new(
276
+ region: 'europe',
277
+ subtotal: 1.0,
278
+ created_at: Time.new,
279
+ )
280
+ end
281
+ let(:purchases) { [purchase1, purchase2, purchase3, purchase4] }
282
+
283
+ describe 'types' do
284
+ before do
285
+ purchases.each do |purchase|
286
+ @adapter.create(collection, purchase)
287
+ end
288
+
289
+ @purchases = @adapter.query(collection).all.sort_by(&:created_at)
290
+ end
291
+
292
+ it 'has string type' do
293
+ @purchases.first.region.class.must_equal String
294
+ end
295
+
296
+ it 'has number type' do
297
+ @purchases.first.subtotal.class.must_equal Float
298
+ end
299
+
300
+ it 'has set type' do
301
+ @purchases.first.item_ids.class.must_equal Set
302
+ @purchases.at(0).item_ids.map(&:class).must_equal [BigDecimal, BigDecimal, BigDecimal]
303
+ @purchases.at(1).item_ids.map(&:class).must_equal [String, String, String]
304
+ @purchases.at(2).item_ids.map(&:class).must_equal [AWS::DynamoDB::Binary]
305
+ end
306
+
307
+ it 'has binary type' do
308
+ @purchases.each do |purchase|
309
+ purchase.content.class.must_equal AWS::DynamoDB::Binary
310
+ end
311
+ end
312
+ end
313
+
314
+ describe 'where' do
315
+ describe 'with an empty collection' do
316
+ it 'returns an empty result set' do
317
+ result = @adapter.query(collection) do
318
+ where(region: 'europe')
319
+ end.all
320
+
321
+ result.must_be_empty
322
+ end
323
+ end
324
+
325
+ describe 'with a filled collection' do
326
+ before do
327
+ purchases.each do |purchase|
328
+ @adapter.create(collection, purchase)
329
+ end
330
+ end
331
+
332
+ describe 'without key schema' do
333
+ it 'returns selected records' do
334
+ query = Proc.new {
335
+ where(subtotal: 100.0)
336
+ }
337
+
338
+ result = @adapter.query(collection, &query).all
339
+ result.must_equal [purchase4]
340
+ end
341
+ end
342
+
343
+ describe 'with key schema' do
344
+ it 'returns selected records' do
345
+ query = Proc.new {
346
+ where(region: 'europe')
347
+ }
348
+
349
+ result = @adapter.query(collection, &query).all
350
+ result.must_equal [purchase1, purchase2]
351
+ end
352
+
353
+ it 'can use multiple where conditions' do
354
+ created_at = purchase1.created_at
355
+ query = Proc.new {
356
+ where(region: 'europe').where(created_at: created_at)
357
+ }
358
+
359
+ result = @adapter.query(collection, &query).all
360
+ result.must_equal [purchase1]
361
+ end
362
+
363
+ it 'can use array as where condition' do
364
+ query = Proc.new {
365
+ where(region: 'europe').where(subtotal: [15.0, 10.0])
366
+ }
367
+
368
+ result = @adapter.query(collection, &query).all
369
+ result.must_equal [purchase1, purchase2]
370
+ end
371
+
372
+ it 'can use range as where condition' do
373
+ query = Proc.new {
374
+ where(region: 'europe').where(subtotal: 8..14)
375
+ }
376
+
377
+ result = @adapter.query(collection, &query).all
378
+ result.must_equal [purchase2]
379
+ end
380
+
381
+ it 'can use "eq" alias' do
382
+ query = Proc.new {
383
+ eq(region: 'europe')
384
+ }
385
+
386
+ @adapter.query(collection, &query).count.must_equal 2
387
+ end
388
+
389
+ it 'can use "in" alias' do
390
+ @adapter.create(collection, purchase5)
391
+
392
+ query = Proc.new {
393
+ where(region: 'europe').in(subtotal: [10.0, 1.0])
394
+ }
395
+
396
+ @adapter.query(collection, &query).count.must_equal 2
397
+ end
398
+
399
+ it 'can use "between" alias' do
400
+ @adapter.create(collection, purchase5)
401
+
402
+ query = Proc.new {
403
+ where(region: 'europe').between(subtotal: 0.0..11.0)
404
+ }
405
+
406
+ @adapter.query(collection, &query).count.must_equal 2
407
+ end
408
+ end
409
+ end
410
+ end
411
+
412
+ describe 'exclude' do
413
+ describe 'with an empty collection' do
414
+ it 'returns an empty result set' do
415
+ result = @adapter.query(collection) do
416
+ exclude(subtotal: 10.0)
417
+ end.all
418
+
419
+ result.must_be_empty
420
+ end
421
+ end
422
+
423
+ describe 'with a filled collection' do
424
+ before do
425
+ purchases.each do |purchase|
426
+ @adapter.create(collection, purchase)
427
+ end
428
+ end
429
+
430
+ describe 'without key schema' do
431
+ it 'returns selected records' do
432
+ query = Proc.new {
433
+ exclude(subtotal: 100.0)
434
+ }
435
+
436
+ result = @adapter.query(collection, &query).all
437
+ result.must_include purchase1
438
+ result.must_include purchase2
439
+ result.must_include purchase3
440
+ end
441
+ end
442
+
443
+ describe 'with key schema' do
444
+ it 'returns selected records' do
445
+ query = Proc.new {
446
+ where(region: 'europe').exclude(subtotal: 15.0)
447
+ }
448
+
449
+ result = @adapter.query(collection, &query).all
450
+ result.must_equal [purchase2]
451
+ end
452
+
453
+ it 'can use multiple exclude conditions' do
454
+ id = purchase2.id
455
+
456
+ query = Proc.new {
457
+ where(region: 'europe').exclude(subtotal: 15.0).exclude(uuid: id)
458
+ }
459
+
460
+ result = @adapter.query(collection, &query).all
461
+ result.must_equal []
462
+ end
463
+
464
+ it 'can use multiple exclude conditions with "not" alias' do
465
+ id = purchase2.id
466
+
467
+ query = Proc.new {
468
+ where(region: 'europe').not(subtotal: 15.0).not(uuid: id)
469
+ }
470
+
471
+ result = @adapter.query(collection, &query).all
472
+ result.must_equal []
473
+ end
474
+
475
+ it 'can not use array as exclude condition' do
476
+ query = Proc.new {
477
+ exclude(subtotal: [15.0, 10.0])
478
+ }
479
+
480
+ ->{ @adapter.query(collection, &query).all }.must_raise \
481
+ NotImplementedError
482
+ end
483
+
484
+ it 'can use "ne" alias' do
485
+ query = Proc.new {
486
+ where(region: 'europe').ne(subtotal: 1.0)
487
+ }
488
+
489
+ @adapter.query(collection, &query).count.must_equal 2
490
+ end
491
+ end
492
+ end
493
+ end
494
+
495
+ describe 'comparison' do
496
+ describe 'with an empty collection' do
497
+ it 'returns an empty result set' do
498
+ result = @adapter.query(collection) do
499
+ le(subtotal: 10.0)
500
+ end.all
501
+
502
+ result.must_be_empty
503
+ end
504
+ end
505
+
506
+ describe 'with a filled collection' do
507
+ before do
508
+ purchases.each do |purchase|
509
+ @adapter.create(collection, purchase)
510
+ end
511
+ end
512
+
513
+ it 'can use "le" method' do
514
+ query = Proc.new {
515
+ where(region: 'europe').le(subtotal: 15.0)
516
+ }
517
+
518
+ @adapter.query(collection, &query).count.must_equal 2
519
+ end
520
+
521
+ it 'can use "lt" method' do
522
+ query = Proc.new {
523
+ where(region: 'europe').lt(subtotal: 15.0)
524
+ }
525
+
526
+ @adapter.query(collection, &query).count.must_equal 1
527
+ end
528
+
529
+ it 'can use "ge" method' do
530
+ query = Proc.new {
531
+ where(region: 'europe').ge(subtotal: 10.0)
532
+ }
533
+
534
+ @adapter.query(collection, &query).count.must_equal 2
535
+ end
536
+
537
+ it 'can use "gt" method' do
538
+ query = Proc.new {
539
+ where(region: 'europe').gt(subtotal: 10.0)
540
+ }
541
+
542
+ @adapter.query(collection, &query).count.must_equal 1
543
+ end
544
+
545
+ it 'can use "contains" method' do
546
+ query = Proc.new {
547
+ where(region: 'europe').contains(item_ids: 2)
548
+ }
549
+
550
+ @adapter.query(collection, &query).count.must_equal 1
551
+ end
552
+
553
+ it 'can use "not_contains" method' do
554
+ skip_for_fake_dynamo
555
+ query = Proc.new {
556
+ where(region: 'europe').not_contains(item_ids: '2')
557
+ }
558
+
559
+ @adapter.query(collection, &query).count.must_equal 1
560
+ end
561
+
562
+ it 'can use "begins_with" method' do
563
+ query = Proc.new {
564
+ where(region: 'asia').begins_with(content: "CON")
565
+ }
566
+
567
+ @adapter.query(collection, &query).count.must_equal 1
568
+ end
569
+
570
+ it 'can use "null" method' do
571
+ skip_for_fake_dynamo
572
+ @adapter.create(collection, purchase5)
573
+
574
+ query = Proc.new {
575
+ where(region: 'europe').null(:content)
576
+ }
577
+
578
+ @adapter.query(collection, &query).count.must_equal 1
579
+ end
580
+
581
+ it 'can use "not_null" method' do
582
+ skip_for_fake_dynamo
583
+ @adapter.create(collection, purchase5)
584
+
585
+ query = Proc.new {
586
+ where(region: 'europe').not_null(:content)
587
+ }
588
+
589
+ @adapter.query(collection, &query).count.must_equal 2
590
+ end
591
+ end
592
+ end
593
+
594
+ describe 'or' do
595
+ describe 'with an empty collection' do
596
+ it 'returns an empty result set' do
597
+ result = @adapter.query(collection) do
598
+ where(subtotal: 10.0).or.where(uuid: "omg")
599
+ end.all
600
+
601
+ result.must_be_empty
602
+ end
603
+ end
604
+
605
+ describe 'with a filled collection' do
606
+ before do
607
+ purchases.each do |purchase|
608
+ @adapter.create(collection, purchase)
609
+ end
610
+ end
611
+
612
+ it 'returns selected records' do
613
+ skip_for_fake_dynamo
614
+ id = purchase1.id
615
+
616
+ query = Proc.new {
617
+ where(region: 'europe').where(subtotal: 10.0).or.where(uuid: id)
618
+ }
619
+
620
+ result = @adapter.query(collection, &query).all
621
+ result.must_equal [purchase1, purchase2]
622
+ end
623
+ end
624
+ end
625
+
626
+ describe 'select' do
627
+ describe 'with an empty collection' do
628
+ it 'returns an empty result' do
629
+ result = @adapter.query(collection) do
630
+ select(:subtotal)
631
+ end.all
632
+
633
+ result.must_be_empty
634
+ end
635
+ end
636
+
637
+ describe 'with a filled collection' do
638
+ before do
639
+ purchases.each do |purchase|
640
+ @adapter.create(collection, purchase)
641
+ end
642
+ end
643
+
644
+ it 'returns the selected columns from all the records' do
645
+ query = Proc.new {
646
+ select(:subtotal)
647
+ }
648
+
649
+ result = @adapter.query(collection, &query).all
650
+ purchases.each do |purchase|
651
+ record = result.find { |r| r.subtotal == purchase.subtotal }
652
+ record.wont_be_nil
653
+ record.region.must_be_nil
654
+ end
655
+ end
656
+
657
+ it 'returns only the select of requested records' do
658
+ query = Proc.new {
659
+ where(region: 'europe').select(:subtotal)
660
+ }
661
+
662
+ result = @adapter.query(collection, &query).all
663
+
664
+ record = result.first
665
+ record.subtotal.must_equal(purchase1.subtotal)
666
+ record.region.must_be_nil
667
+ end
668
+
669
+ it 'returns only the multiple select of requested records' do
670
+ query = Proc.new {
671
+ where(region: 'europe').select(:subtotal, :region)
672
+ }
673
+
674
+ result = @adapter.query(collection, &query).all
675
+
676
+ record = result.first
677
+ record.subtotal.must_equal(purchase1.subtotal)
678
+ record.region.must_equal(purchase1.region)
679
+ record.id.must_be_nil
680
+ end
681
+ end
682
+ end
683
+
684
+ describe 'order' do
685
+ let(:collection) { :test_devices }
686
+ let(:device1) { TestDevice.new(id: 'device', created_at: Time.new) }
687
+ let(:device2) { TestDevice.new(id: 'device', created_at: Time.new) }
688
+ let(:devices) { [device1, device2] }
689
+
690
+ describe 'asc' do
691
+ describe 'with an empty collection' do
692
+ it 'returns an empty result set' do
693
+ result = @adapter.query(collection) do
694
+ where(uuid: 'device').asc
695
+ end.all
696
+
697
+ result.must_be_empty
698
+ end
699
+ end
700
+
701
+ describe 'with a filled collection' do
702
+ before do
703
+ devices.each do |device|
704
+ @adapter.create(collection, device)
705
+ end
706
+ end
707
+
708
+ it 'returns sorted records' do
709
+ query = Proc.new {
710
+ where(uuid: 'device').asc
711
+ }
712
+
713
+ result = @adapter.query(collection, &query).all
714
+ result.must_equal [device1, device2]
715
+ end
716
+ end
717
+ end
718
+
719
+ describe 'desc' do
720
+ describe 'with an empty collection' do
721
+ it 'returns an empty result set' do
722
+ result = @adapter.query(collection) do
723
+ where(uuid: 'device').desc
724
+ end.all
725
+
726
+ result.must_be_empty
727
+ end
728
+ end
729
+
730
+ describe 'with a filled collection' do
731
+ before do
732
+ devices.each do |device|
733
+ @adapter.create(collection, device)
734
+ end
735
+ end
736
+
737
+ it 'returns reverse sorted records' do
738
+ query = Proc.new {
739
+ where(uuid: 'device').desc
740
+ }
741
+
742
+ result = @adapter.query(collection, &query).all
743
+ result.must_equal [device1, device2]
744
+ end
745
+ end
746
+ end
747
+ end
748
+
749
+ describe 'limit' do
750
+ describe 'with an empty collection' do
751
+ it 'returns an empty result set' do
752
+ result = @adapter.query(collection) do
753
+ limit(1)
754
+ end.all
755
+
756
+ result.must_be_empty
757
+ end
758
+ end
759
+
760
+ describe 'with a filled collection' do
761
+ before do
762
+ purchases.each do |purchase|
763
+ @adapter.create(collection, purchase)
764
+ end
765
+ end
766
+
767
+ it 'returns only the number of requested records' do
768
+ query = Proc.new {
769
+ where(region: 'europe').limit(1)
770
+ }
771
+
772
+ result = @adapter.query(collection, &query).all
773
+ result.must_equal [purchase1]
774
+ end
775
+ end
776
+ end
777
+
778
+ describe 'exists?' do
779
+ describe 'with an empty collection' do
780
+ it 'returns false' do
781
+ result = @adapter.query(collection) do
782
+ where(region: 'wow')
783
+ end.exists?
784
+
785
+ result.must_equal false
786
+ end
787
+ end
788
+
789
+ describe 'with a filled collection' do
790
+ before do
791
+ purchases.each do |purchase|
792
+ @adapter.create(collection, purchase)
793
+ end
794
+ end
795
+
796
+ it 'returns true when there are matched records' do
797
+ query = Proc.new {
798
+ where(region: 'asia')
799
+ }
800
+
801
+ result = @adapter.query(collection, &query).exists?
802
+ result.must_equal true
803
+ end
804
+
805
+ it 'returns false when there are matched records' do
806
+ query = Proc.new {
807
+ where(region: 'wtf')
808
+ }
809
+
810
+ result = @adapter.query(collection, &query).exists?
811
+ result.must_equal false
812
+ end
813
+
814
+ it 'can use "exist?" alias' do
815
+ query = Proc.new {
816
+ where(region: 'usa')
817
+ }
818
+
819
+ result = @adapter.query(collection, &query).exist?
820
+ result.must_equal true
821
+ end
822
+ end
823
+ end
824
+
825
+ describe 'count' do
826
+ describe 'with an empty collection' do
827
+ it 'returns 0' do
828
+ result = @adapter.query(collection) do
829
+ all
830
+ end.count
831
+
832
+ result.must_equal 0
833
+ end
834
+ end
835
+
836
+ describe 'with a filled collection' do
837
+ before do
838
+ purchases.each do |purchase|
839
+ @adapter.create(collection, purchase)
840
+ end
841
+ end
842
+
843
+ it 'returns the count of all the records' do
844
+ query = Proc.new {
845
+ all
846
+ }
847
+
848
+ result = @adapter.query(collection, &query).count
849
+ result.must_equal 4
850
+ end
851
+
852
+ it 'returns the count from an empty query block' do
853
+ query = Proc.new {
854
+ }
855
+
856
+ result = @adapter.query(collection, &query).count
857
+ result.must_equal 4
858
+ end
859
+
860
+ it 'returns only the count of requested records' do
861
+ query = Proc.new {
862
+ where(region: 'europe')
863
+ }
864
+
865
+ result = @adapter.query(collection, &query).count
866
+ result.must_equal 2
867
+ end
868
+ end
869
+ end
870
+
871
+ describe 'consistent' do
872
+ before do
873
+ purchases.each do |purchase|
874
+ @adapter.create(collection, purchase)
875
+ end
876
+ end
877
+
878
+ it 'does not fail' do
879
+ query = Proc.new {
880
+ where(region: 'europe').consistent
881
+ }
882
+
883
+ result = @adapter.query(collection, &query).count
884
+ result.must_equal 2
885
+ end
886
+ end
887
+
888
+ describe 'index' do
889
+ describe 'with an empty collection' do
890
+ it 'returns an empty result for local index' do
891
+ result = @adapter.query(collection) do
892
+ index('by_subtotal').where(region: 'europe')
893
+ end.all
894
+
895
+ result.must_be_empty
896
+ end
897
+
898
+ it 'returns an empty result for global index' do
899
+ result = @adapter.query(collection) do
900
+ index('by_uuid').where(uuid: 'wow')
901
+ end.all
902
+
903
+ result.must_be_empty
904
+ end
905
+ end
906
+
907
+ describe 'with a filled collection' do
908
+ before do
909
+ purchases.each do |purchase|
910
+ @adapter.create(collection, purchase)
911
+ end
912
+ end
913
+
914
+ describe 'local index' do
915
+ it 'returns selected records' do
916
+ query = Proc.new {
917
+ index('by_subtotal').where(region: 'europe')
918
+ }
919
+
920
+ result = @adapter.query(collection, &query).all
921
+ result.must_equal [purchase2, purchase1]
922
+ end
923
+ end
924
+
925
+ describe 'global index' do
926
+ it 'returns selected records' do
927
+ id = purchase3.id
928
+
929
+ query = Proc.new {
930
+ index('by_uuid').where(uuid: id)
931
+ }
932
+
933
+ result = @adapter.query(collection, &query).all
934
+ result.must_equal [purchase3]
935
+ end
936
+ end
937
+ end
938
+ end
939
+ end
940
+ end