foobara-crud-driver-spec-helpers 1.0.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,912 @@
1
+ require "base64"
2
+
3
+ module RspecHelpers
4
+ module ItBehavesLikeACrudDriver
5
+ module ClassMethods
6
+ def it_behaves_like_a_crud_driver
7
+ define_method :table do
8
+ entity_class.current_transaction_table.entity_attributes_crud_driver_table
9
+ end
10
+
11
+ describe ".has_real_transactions?" do
12
+ it "is a boolean" do
13
+ expect([true, false]).to include(described_class.has_real_transactions?)
14
+ end
15
+ end
16
+
17
+ context "tests from redis-crud-driver" do
18
+ let(:entity_class) do
19
+ stub_class("SomeEntity", Foobara::Entity) do
20
+ attributes do
21
+ id :integer
22
+ foo :integer
23
+ bar :symbol
24
+ created_at :datetime, :allow_nil
25
+ end
26
+
27
+ primary_key :id
28
+ end
29
+ end
30
+
31
+ describe ".transaction" do
32
+ it "can create, load, and update records" do
33
+ expect do
34
+ entity_class.create(foo: 1, bar: :baz)
35
+ end.to raise_error(Foobara::Persistence::EntityBase::Transaction::NoCurrentTransactionError)
36
+
37
+ transaction = nil
38
+
39
+ entity1 = entity_class.transaction do |tx|
40
+ transaction = tx
41
+
42
+ entity = entity_class.create(foo: 1, bar: :baz)
43
+
44
+ expect(entity).to be_a(entity_class)
45
+ expect(entity).to_not be_persisted
46
+ expect(entity).to_not be_loaded
47
+
48
+ expect(tx).to be_open
49
+ expect(Foobara::Persistence.current_transaction(entity)).to be(tx)
50
+
51
+ entity
52
+ end
53
+
54
+ expect(transaction).to be_closed
55
+ expect(Foobara::Persistence.current_transaction(entity1)).to be_nil
56
+
57
+ expect(entity1).to be_a(entity_class)
58
+ expect(entity1).to be_persisted
59
+ expect(entity1).to be_loaded
60
+
61
+ entity_class.transaction do
62
+ entity = entity_class.thunk(entity1.primary_key)
63
+
64
+ expect(entity).to be_a(entity_class)
65
+ expect(entity).to be_persisted
66
+ expect(entity).to_not be_loaded
67
+
68
+ expect(entity.bar).to eq(:baz)
69
+
70
+ expect(entity).to be_loaded
71
+
72
+ singleton = entity_class.thunk(entity.primary_key)
73
+ expect(singleton).to be(entity)
74
+
75
+ entity.bar = "bazbaz"
76
+ end
77
+
78
+ entity_class.transaction do
79
+ entity = Foobara::Persistence.current_transaction(entity_class).load(entity_class,
80
+ entity1.primary_key)
81
+ expect(entity.bar).to eq(:bazbaz)
82
+
83
+ expect(entity_class.all.to_a).to eq([entity1])
84
+ end
85
+ end
86
+
87
+ it "can rollback" do
88
+ entity1 = entity_class.transaction do
89
+ entity_class.create(foo: 10, bar: :baz)
90
+ end
91
+
92
+ entity_class.transaction do |tx|
93
+ entity = entity_class.thunk(entity1.primary_key)
94
+ expect(entity.foo).to eq(10)
95
+
96
+ entity.foo = 20
97
+
98
+ expect(entity.foo).to eq(20)
99
+
100
+ begin
101
+ tx.rollback!
102
+ rescue Foobara::Persistence::EntityBase::Transaction::RolledBack # rubocop:disable Lint/SuppressedException
103
+ end
104
+
105
+ expect(entity.foo).to eq(10)
106
+
107
+ entity_class.transaction do
108
+ expect(entity_class.load(entity.primary_key).foo).to eq(10)
109
+ end
110
+
111
+ expect do
112
+ entity.foo = 20
113
+ end.to raise_error(Foobara::Persistence::EntityBase::Transaction::NoCurrentTransactionError)
114
+ end
115
+
116
+ entity_class.transaction do |tx|
117
+ entity = entity_class.load(entity1.primary_key)
118
+ entity = entity_class.load(entity.primary_key)
119
+ expect(entity.foo).to eq(10)
120
+
121
+ entity.foo = 20
122
+
123
+ expect(entity.foo).to eq(20)
124
+
125
+ tx.flush!
126
+
127
+ expect(entity.foo).to eq(20)
128
+
129
+ entity.foo = 30
130
+
131
+ tx.revert!
132
+
133
+ expect(entity.foo).to eq(20)
134
+ end
135
+
136
+ entity_class.transaction do
137
+ entity = entity_class.load(entity1.primary_key)
138
+ expect(entity.foo).to eq(20)
139
+ end
140
+ end
141
+
142
+ it "can hard delete" do
143
+ entity_class.transaction do
144
+ expect(entity_class.all.to_a).to be_empty
145
+ entity = entity_class.create(foo: 10, bar: :baz)
146
+ expect(entity_class.all.to_a).to eq([entity])
147
+ entity.hard_delete!
148
+ expect(entity_class.all.to_a).to be_empty
149
+ end
150
+
151
+ entity1 = entity_class.transaction do
152
+ expect(entity_class.all.to_a).to be_empty
153
+ entity_class.create(foo: 10, bar: :baz)
154
+ end
155
+
156
+ entity_class.transaction do
157
+ entity = entity_class.thunk(entity1.primary_key)
158
+ expect(entity.foo).to eq(10)
159
+
160
+ entity.hard_delete!
161
+
162
+ expect(entity).to be_hard_deleted
163
+
164
+ # TODO: make this work without needing to call #to_a
165
+ expect(entity_class.all.to_a).to be_empty
166
+
167
+ expect do
168
+ entity.foo = 20
169
+ end.to raise_error(Foobara::Entity::CannotUpdateHardDeletedRecordError)
170
+
171
+ expect(entity.foo).to eq(10)
172
+
173
+ entity.restore!
174
+ expect(entity_class.all.to_a).to eq([entity])
175
+
176
+ entity.foo = 20
177
+ end
178
+
179
+ entity_class.transaction do
180
+ # TODO: make calling #to_a not necessary
181
+ expect(entity_class.all.to_a).to eq([entity1])
182
+ entity = entity_class.thunk(entity1.primary_key)
183
+
184
+ expect(entity).to be_persisted
185
+ expect(entity).to_not be_hard_deleted
186
+ expect(entity.foo).to eq(20)
187
+
188
+ entity.hard_delete!
189
+
190
+ expect(entity_class.all.to_a).to be_empty
191
+ expect(entity).to be_hard_deleted
192
+ end
193
+
194
+ entity_class.transaction do
195
+ expect do
196
+ entity_class.load(entity1.primary_key)
197
+ end.to raise_error(Foobara::Entity::NotFoundError)
198
+
199
+ expect(entity_class.all.to_a).to be_empty
200
+ end
201
+ end
202
+
203
+ describe "#hard_delete_all" do
204
+ it "deletes everything" do
205
+ entities = []
206
+
207
+ entity_class.transaction do
208
+ 4.times do
209
+ entity = entity_class.create(foo: 1, bar: :baz)
210
+ entities << entity
211
+ end
212
+
213
+ # TODO: make calling #to_a not necessary
214
+ expect(entity_class.all.to_a).to eq(entities)
215
+ end
216
+
217
+ entity_ids = entities.map(&:primary_key)
218
+
219
+ expect(entity_ids).to contain_exactly(1, 2, 3, 4)
220
+
221
+ entity_class.transaction do
222
+ entities = []
223
+
224
+ entity_class.all do |record|
225
+ entities << record
226
+ end
227
+
228
+ entity_ids = entities.map(&:primary_key)
229
+
230
+ expect(entity_ids).to contain_exactly(1, 2, 3, 4)
231
+
232
+ 4.times do
233
+ entity = entity_class.create(foo: 1, bar: :baz)
234
+ entities << entity
235
+ end
236
+
237
+ expect(entity_class.all).to match_array(entities)
238
+
239
+ Foobara::Persistence.current_transaction(entities.first).hard_delete_all!(entity_class)
240
+
241
+ expect(entities).to all be_hard_deleted
242
+ expect(entity_class.all.to_a).to be_empty
243
+ end
244
+
245
+ entity_class.transaction do
246
+ expect(entity_class.all.to_a).to be_empty
247
+ end
248
+ end
249
+ end
250
+ end
251
+
252
+ describe "#load_many" do
253
+ it "loads many" do
254
+ entities = nil
255
+ entity_ids = nil
256
+
257
+ entity_class.transaction do |tx|
258
+ [
259
+ { foo: 11, bar: :baz },
260
+ { foo: 22, bar: :baz },
261
+ { foo: 33, bar: :baz },
262
+ { foo: 44, bar: :baz }
263
+ ].map do |attributes|
264
+ entity_class.create(attributes)
265
+ end
266
+
267
+ expect(entity_class.count).to eq(4)
268
+
269
+ entity_class.transaction(mode: :use_existing) do
270
+ expect(entity_class.count).to eq(4)
271
+ end
272
+
273
+ tx2 = entity_class.transaction(mode: :use_existing)
274
+
275
+ entity_class.entity_base.using_transaction(tx2) do
276
+ expect(entity_class.count).to eq(4)
277
+ end
278
+
279
+ expect(entity_class.count).to eq(4)
280
+ entity_class.transaction(mode: :open_nested) do |tx|
281
+ # TODO: Why wouldn't this be 5????
282
+ expect(entity_class.count).to eq(0)
283
+ entity_class.create(foo: 55, bar: :baz)
284
+ expect(entity_class.count).to eq(1)
285
+ tx.rollback!
286
+ end
287
+ expect(entity_class.count).to eq(4)
288
+
289
+ tx.flush!
290
+
291
+ entity_class.transaction(mode: :use_existing) do
292
+ expect(entity_class.count).to eq(4)
293
+ end
294
+
295
+ entity_class.transaction(mode: :open_nested) do
296
+ expect(entity_class.count).to eq(4)
297
+ end
298
+
299
+ entities = entity_class.all
300
+
301
+ expect(entities).to all be_a(Foobara::Entity)
302
+ expect(entities.size).to eq(4)
303
+
304
+ entity_ids = entities.map(&:primary_key)
305
+ expect(entity_ids).to contain_exactly(1, 2, 3, 4)
306
+ end
307
+
308
+ entity_class.transaction do
309
+ entity_class.load_many([entity_class.thunk(1)])
310
+ loaded_entities = entity_class.load_many(entity_ids)
311
+ expect(loaded_entities).to all be_loaded
312
+ expect(loaded_entities).to eq(entities)
313
+ end
314
+ end
315
+ end
316
+
317
+ describe "#all_exist?" do
318
+ it "answers whether they all exist or not" do
319
+ entity_class.transaction do
320
+ expect(entity_class.all_exist?([101, 102])).to be(false)
321
+
322
+ [
323
+ { foo: 11, bar: :baz, id: 101 },
324
+ { foo: 22, bar: :baz, id: 102 },
325
+ { foo: 33, bar: :baz },
326
+ { foo: 44, bar: :baz }
327
+ ].map do |attributes|
328
+ entity_class.create(attributes)
329
+ end
330
+
331
+ entity_class.all do |record|
332
+ expect(record).to_not be_persisted
333
+ end
334
+
335
+ expect(entity_class.all_exist?([101, 102])).to be(true)
336
+ expect(entity_class.all_exist?([1, 2, 101, 102])).to be(false)
337
+ end
338
+
339
+ entity_class.transaction do
340
+ expect(entity_class.all_exist?([1, 2, 101, 102])).to be(true)
341
+ expect(entity_class.all_exist?([3])).to be(false)
342
+ end
343
+ end
344
+ end
345
+
346
+ describe "#unhard_delete!" do
347
+ context "when record was dirty when hard deleted" do
348
+ it "is still dirty" do
349
+ entity = entity_class.transaction do
350
+ entity_class.create(foo: 11, bar: :baz)
351
+ end
352
+
353
+ entity_class.transaction do
354
+ entity = entity_class.thunk(entity.primary_key)
355
+
356
+ expect(entity).to be_persisted
357
+
358
+ expect(entity).to_not be_dirty
359
+
360
+ entity.foo = 12
361
+
362
+ expect(entity).to be_dirty
363
+ expect(entity).to_not be_hard_deleted
364
+
365
+ entity.foo = 11
366
+
367
+ expect(entity).to_not be_dirty
368
+ expect(entity).to_not be_hard_deleted
369
+
370
+ entity.foo = 12
371
+
372
+ expect(entity).to be_dirty
373
+ expect(entity).to_not be_hard_deleted
374
+
375
+ entity.hard_delete!
376
+
377
+ expect(entity).to be_dirty
378
+ expect(entity).to be_hard_deleted
379
+
380
+ entity.unhard_delete!
381
+
382
+ expect(entity).to be_dirty
383
+ expect(entity).to_not be_hard_deleted
384
+ end
385
+ end
386
+ end
387
+ end
388
+
389
+ describe "#exists?" do
390
+ it "answers it exists or not" do
391
+ entity_class.transaction do
392
+ expect(entity_class.all_exist?([101, 102])).to be(false)
393
+
394
+ entity_class.create(foo: 11, bar: :baz, id: 101)
395
+
396
+ expect(entity_class.exists?(101)).to be(true)
397
+
398
+ entity_class.create(foo: 11, bar: :baz)
399
+
400
+ expect(entity_class.exists?(1)).to be(false)
401
+ end
402
+
403
+ entity_class.transaction do
404
+ expect(entity_class.exists?(101)).to be(true)
405
+
406
+ expect(entity_class.exists?(1)).to be(true)
407
+ expect(entity_class.exists?(2)).to be(false)
408
+ end
409
+ end
410
+ end
411
+
412
+ context "when creating a record with an already-in-use key" do
413
+ it "explodes" do
414
+ entity_class.transaction do
415
+ entity_class.create(foo: 11, bar: :baz, id: 101)
416
+ end
417
+
418
+ expect do
419
+ entity_class.transaction do
420
+ entity_class.create(foo: 11, bar: :baz, id: 101)
421
+ end
422
+ end.to raise_error(Foobara::Persistence::EntityAttributesCrudDriver::Table::CannotInsertError)
423
+ end
424
+ end
425
+
426
+ context "when restoring with a created record" do
427
+ it "hard deletes it" do
428
+ entity_class.transaction do |tx|
429
+ record = entity_class.create(foo: 11, bar: :baz, id: 101)
430
+
431
+ tx.revert!
432
+
433
+ expect(record).to be_hard_deleted
434
+ end
435
+
436
+ entity_class.transaction do
437
+ expect(entity_class.count).to eq(0)
438
+ end
439
+ end
440
+ end
441
+
442
+ context "when persisting entity with an association" do
443
+ let(:aggregate_class) do
444
+ entity_class
445
+ some_model_class
446
+
447
+ stub_class "SomeAggregate", Foobara::Entity do
448
+ attributes do
449
+ id :integer
450
+ foo :integer
451
+ some_model SomeModel, :required
452
+ some_entities [SomeEntity]
453
+ end
454
+
455
+ primary_key :id
456
+ end
457
+ end
458
+
459
+ let(:some_model_class) do
460
+ some_other_entity_class
461
+
462
+ stub_class "SomeModel", Foobara::Model do
463
+ attributes do
464
+ some_other_entity SomeOtherEntity, :required
465
+ end
466
+ end
467
+ end
468
+
469
+ let(:some_other_entity_class) do
470
+ stub_class "SomeOtherEntity", Foobara::Entity do
471
+ attributes do
472
+ id :integer
473
+ foo :integer, :required
474
+ end
475
+
476
+ primary_key :id
477
+ end
478
+ end
479
+
480
+ it "writes the records to disk using primary keys" do
481
+ some_entity2 = nil
482
+
483
+ some_entity1 = aggregate_class.transaction do
484
+ some_entity2 = entity_class.create(foo: 11, bar: :baz, created_at: Time.now)
485
+ entity_class.create(foo: 11, bar: :baz, id: 101)
486
+ end
487
+
488
+ some_other_entity = nil
489
+
490
+ entity_class.transaction do
491
+ some_entity3 = entity_class.create(foo: 11, bar: :baz, id: 102)
492
+ some_entity4 = entity_class.create(foo: 11, bar: :baz)
493
+ some_other_entity = SomeOtherEntity.create(foo: 11)
494
+
495
+ some_model = SomeModel.new(some_other_entity:)
496
+
497
+ aggregate_class.create(
498
+ foo: 30,
499
+ some_model:,
500
+ some_entities: [
501
+ 1,
502
+ some_entity1,
503
+ some_entity3,
504
+ some_entity4
505
+ ]
506
+ )
507
+ end
508
+
509
+ entity_class.transaction do |tx|
510
+ crud_table = aggregate_class.current_transaction_table.entity_attributes_crud_driver_table
511
+ raw_records = crud_table.all.to_a
512
+ expect(raw_records.size).to eq(1)
513
+ raw_record = raw_records.first
514
+
515
+ record = aggregate_class.build(raw_record)
516
+ expect(record.some_entities.map(&:id)).to contain_exactly(1, 2, 101, 102)
517
+ expect(record.some_model.some_other_entity.id).to eq(some_other_entity.id)
518
+
519
+ loaded_aggregate = aggregate_class.load(1)
520
+ expect(loaded_aggregate.some_entities).to all be_a(SomeEntity)
521
+ expect(loaded_aggregate.some_entities.map(&:primary_key)).to contain_exactly(1, 2, 101, 102)
522
+
523
+ new_aggregate = aggregate_class.create(
524
+ foo: "30",
525
+ some_entities: [
526
+ entity_class.create(foo: 11, bar: :baz)
527
+ ],
528
+ some_model: {
529
+ some_other_entity: {
530
+ foo: 10
531
+ }
532
+ }
533
+ )
534
+
535
+ expect(new_aggregate.some_model.some_other_entity.foo).to eq(10)
536
+
537
+ expect(aggregate_class.contains_associations?).to be(true)
538
+ expect(entity_class.contains_associations?).to be(false)
539
+
540
+ tx.flush!
541
+
542
+ record = SomeAggregate.load(new_aggregate.primary_key)
543
+ expect(record.foo).to eq(30)
544
+ expect(record.some_entities.map(&:id)).to contain_exactly(new_aggregate.some_entities.first.primary_key)
545
+ expect(record.some_model.some_other_entity.id).to eq(
546
+ new_aggregate.some_model.some_other_entity.primary_key
547
+ )
548
+ expect(record.primary_key).to eq(new_aggregate.primary_key)
549
+ end
550
+ end
551
+ end
552
+
553
+ describe "#all" do
554
+ let(:driver) { entity_class.entity_base.entity_attributes_crud_driver }
555
+
556
+ context "when using string ids" do
557
+ let(:entity_class) do
558
+ stub_class("SomeEntityStringId", Foobara::Entity) do
559
+ attributes do
560
+ id :string, :required
561
+ foo :integer, :required
562
+ bar :integer, :required
563
+ end
564
+
565
+ primary_key :id
566
+ end
567
+ end
568
+
569
+ context "when there's tons of records existing" do
570
+ let(:records) do
571
+ all = []
572
+
573
+ entity_class.transaction do
574
+ 200.times do |i|
575
+ id = Base64.strict_encode64(SecureRandom.random_bytes(20))
576
+ all << entity_class.create(id:, foo: i, bar: i)
577
+ end
578
+ end
579
+
580
+ all
581
+ end
582
+
583
+ it "returns all of the expected records" do
584
+ expect(records.size).to eq(200)
585
+
586
+ all = []
587
+
588
+ entity_class.transaction do
589
+ entity_class.all do |record|
590
+ all << record
591
+ end
592
+ end
593
+
594
+ expect(all.size).to eq(records.size)
595
+ end
596
+ end
597
+ end
598
+ end
599
+ describe "#truncate" do
600
+ it "deletes everything" do
601
+ entity_class.transaction do
602
+ 4.times do
603
+ entity_class.create(foo: 1, bar: :baz)
604
+ end
605
+
606
+ # TODO: make calling #to_a not necessary
607
+ expect(entity_class.count).to eq(4)
608
+ end
609
+
610
+ entity_class.transaction do
611
+ expect(entity_class.count).to eq(4)
612
+
613
+ Foobara::Persistence.current_transaction(entity_class).truncate!
614
+
615
+ expect(entity_class.count).to eq(0)
616
+ expect(entity_class.all.to_a).to be_empty
617
+ end
618
+
619
+ entity_class.transaction do
620
+ expect(entity_class.count).to eq(0)
621
+ expect(entity_class.all.to_a).to be_empty
622
+ end
623
+ end
624
+ end
625
+ end
626
+
627
+ # TODO: come up with better names that just where the tests were move from
628
+ context "tests from postgresql-crud-driver" do
629
+ let(:entity_class) do
630
+ stub_class("SomeEntity", Foobara::Entity) do
631
+ attributes do
632
+ id :integer
633
+ foo :integer
634
+ bar :symbol
635
+ created_at :datetime, default: -> { Time.now }
636
+ end
637
+ primary_key :id
638
+ end
639
+ end
640
+
641
+ describe "#all" do
642
+ it "yields all records" do
643
+ entity_class.transaction do
644
+ 111.times do
645
+ entity_class.create(foo: 1, bar: :foo)
646
+ end
647
+ end
648
+
649
+ entity_class.transaction do
650
+ expect(table.all.to_a.size).to eq(111)
651
+ expect(table.all(page_size: 10).to_a.size).to eq(111)
652
+ expect(entity_class.all.first.foo).to eq(1)
653
+ end
654
+ end
655
+ end
656
+
657
+ describe "#insert" do
658
+ it "inserts a record" do
659
+ expect do
660
+ entity_class.transaction do
661
+ entity_class.create(foo: 1, bar: :foo)
662
+ end
663
+ end.to change {
664
+ entity_class.transaction { entity_class.count }
665
+ }.from(0).to(1)
666
+ end
667
+ end
668
+
669
+ describe "#find" do
670
+ it "can find a record" do
671
+ created_record = entity_class.transaction do
672
+ entity_class.create(foo: 1, bar: :foo)
673
+ end
674
+
675
+ record_id = created_record.id
676
+
677
+ entity_class.transaction do |tx|
678
+ attributes = tx.table_for(entity_class).entity_attributes_crud_driver_table.find(record_id)
679
+ expect(attributes[:foo]).to eq(1)
680
+ record = entity_class.load(record_id)
681
+
682
+ expect(record).to be_a(entity_class)
683
+ expect(record.id).to eq(record_id)
684
+ expect(record.foo).to eq(1)
685
+ expect(record.bar).to eq(:foo)
686
+ expect(record.created_at).to be_a(Time)
687
+ end
688
+ end
689
+ end
690
+
691
+ describe "#update" do
692
+ it "can update a record" do
693
+ created_record = entity_class.transaction do
694
+ entity_class.create(foo: 1, bar: :foo)
695
+ end
696
+
697
+ record_id = created_record.id
698
+
699
+ entity_class.transaction do
700
+ record = entity_class.load(record_id)
701
+ record.foo = 2
702
+ end
703
+
704
+ record = entity_class.transaction do
705
+ entity_class.load(record_id)
706
+ end
707
+
708
+ expect(record.foo).to eq(2)
709
+ end
710
+ end
711
+
712
+ describe "#hard_delete" do
713
+ it "can delete a record" do
714
+ created_record = entity_class.transaction do
715
+ entity_class.create(foo: 1, bar: :foo)
716
+ entity_class.create(foo: 2, bar: :baz)
717
+ end
718
+
719
+ record_id = created_record.id
720
+
721
+ expect do
722
+ entity_class.transaction do
723
+ record = entity_class.load(record_id)
724
+ record.hard_delete!
725
+ end
726
+ end.to change {
727
+ entity_class.transaction { entity_class.count }
728
+ }.from(2).to(1)
729
+ end
730
+ end
731
+
732
+ describe "#hard_delete_all" do
733
+ it "deletes all records" do
734
+ entity_class.transaction do
735
+ entity_class.create(foo: 1, bar: :foo)
736
+ entity_class.create(foo: 2, bar: :baz)
737
+ end
738
+
739
+ expect do
740
+ entity_class.transaction do
741
+ table.hard_delete_all
742
+ end
743
+ end.to change {
744
+ entity_class.transaction { entity_class.count }
745
+ }.from(2).to(0)
746
+ end
747
+ end
748
+ end
749
+
750
+ context "tests from in-memory-crud-driver" do
751
+ let(:entity_class) do
752
+ stub_class "Details", Foobara::Model do
753
+ attributes do
754
+ name :string, :required
755
+ end
756
+ end
757
+
758
+ stub_class "Item", Foobara::Entity do
759
+ attributes do
760
+ id :integer
761
+ details Details, :required
762
+ end
763
+
764
+ primary_key :id
765
+ end
766
+
767
+ stub_class "YetAnotherEntity", Foobara::Entity do
768
+ attributes do
769
+ pk :integer
770
+ foo :integer
771
+ bar :symbol
772
+ stuff :allow_nil do
773
+ items [Item]
774
+ end
775
+ end
776
+
777
+ primary_key :pk
778
+ end
779
+ end
780
+
781
+ describe "#find_by/#find_many_by" do
782
+ it "can find by an attribute" do
783
+ entity1 = entity2 = entity3 = entity4 = nil
784
+
785
+ entity_class.transaction do
786
+ entity1 = entity_class.create(foo: 11, bar: :baz)
787
+ entity2 = entity_class.create(foo: 22, bar: :baz)
788
+ entity3 = entity_class.create(foo: 33, bar: :basil)
789
+ entity4 = entity_class.create(foo: 44, bar: :basil)
790
+
791
+ # non-persisted records
792
+ expect(entity_class.find_by(foo: "22")).to eq(entity2)
793
+ expect(entity_class.find_many_by(foo: "11").to_a).to eq([entity1])
794
+ expect(entity_class.find_many_by(bar: "basil").to_a).to eq([entity3, entity4])
795
+ end
796
+
797
+ entity8 = nil
798
+
799
+ entity_class.transaction do
800
+ entity5 = entity_class.create(foo: 55, bar: :baz)
801
+ entity6 = entity_class.create(foo: 66, bar: :baz)
802
+ entity7 = entity_class.create(foo: 77, bar: :basil)
803
+ entity8 = entity_class.create(foo: 88, bar: :basil)
804
+
805
+ # mixture of persisted and non-persisted records
806
+ expect(entity_class.find_by(foo: "22")).to eq(entity2)
807
+ expect(entity_class.find_by(foo: "55")).to eq(entity5)
808
+
809
+ expect(entity_class.find_many_by(foo: "11").to_a).to eq([entity1])
810
+ expect(entity_class.find_many_by(foo: "66").to_a).to eq([entity6])
811
+ expect(entity_class.find_many_by(bar: "basil").to_a).to eq([entity7, entity8, entity3, entity4])
812
+ end
813
+
814
+ item = nil
815
+
816
+ entity_class.transaction do
817
+ item = Item.create(details: { name: "foo" })
818
+ entity8 = entity_class.load(entity8.pk)
819
+ entity8.stuff = { items: [item] }
820
+
821
+ expect(entity_class.find_by(stuff: { items: [item] })).to eq(entity8)
822
+ expect(Item.find_by(details: Details.new(name: "foo"))).to eq(item)
823
+ end
824
+
825
+ entity_class.transaction do
826
+ expect(entity_class.find_by(stuff: { items: [item] })).to eq(entity8)
827
+ expect(Item.find_by(details: Details.new(name: "foo"))).to eq(item)
828
+ end
829
+ end
830
+ end
831
+
832
+ describe ".has_real_transactions?" do
833
+ it "is a boolean" do
834
+ expect(described_class.has_real_transactions?).to(satisfy { |v| [true, false].include?(v) })
835
+ end
836
+ end
837
+ end
838
+
839
+ context "tests from local-files-crud-driver" do
840
+ let(:capybara_class) do
841
+ stub_class "Capybara", Foobara::Entity do
842
+ attributes do
843
+ id :integer
844
+ name :string, :required
845
+ age :integer, :required
846
+ date_stuff do
847
+ birthdays [:date]
848
+ created_at :datetime
849
+ end
850
+ end
851
+
852
+ primary_key :id
853
+ end
854
+ end
855
+
856
+ it "can persist records" do
857
+ capybara_class
858
+
859
+ fumiko = nil
860
+ Capybara.transaction do
861
+ fumiko = Capybara.create(name: "Fumiko", age: 100,
862
+ date_stuff: { birthdays: [Date.today], created_at: Time.now })
863
+ Capybara.create(name: "Barbara", age: 200, date_stuff: { birthdays: [Date.today], created_at: Time.now })
864
+ Capybara.create(name: "Basil", age: 300, date_stuff: { birthdays: [Date.today], created_at: Time.now })
865
+ end
866
+
867
+ capybaras = Capybara.transaction do
868
+ Capybara.all
869
+ end
870
+
871
+ expect(capybaras.map(&:name)).to match_array(["Fumiko", "Barbara", "Basil"])
872
+
873
+ Capybara.transaction do
874
+ fumiko = Capybara.load(fumiko.id)
875
+ expect(fumiko.age).to eq(100)
876
+ fumiko.age += 1
877
+ end
878
+
879
+ Capybara.transaction do
880
+ fumiko = Capybara.load(fumiko.id)
881
+ expect(fumiko.age).to eq(101)
882
+ expect(Capybara.count).to eq(3)
883
+ end
884
+
885
+ Capybara.transaction do
886
+ fumiko = Capybara.load(fumiko.id)
887
+ fumiko.hard_delete!
888
+ expect(Capybara.count).to eq(2)
889
+ end
890
+
891
+ Capybara.transaction do
892
+ expect(Capybara.count).to eq(2)
893
+ end
894
+
895
+ Capybara.transaction do |tx|
896
+ tx.hard_delete_all!(Capybara)
897
+ expect(Capybara.count).to eq(0)
898
+ end
899
+
900
+ Capybara.transaction do
901
+ expect(Capybara.count).to eq(0)
902
+ end
903
+ end
904
+ end
905
+ end
906
+ end
907
+ end
908
+ end
909
+
910
+ RSpec.configure do |c|
911
+ c.extend RspecHelpers::ItBehavesLikeACrudDriver::ClassMethods
912
+ end