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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +7 -0
- data/LICENSE-MPL-2.0.txt +373 -0
- data/LICENSE.txt +9 -0
- data/README.md +39 -0
- data/lib/foobara/spec_helpers/it_behaves_like_a_crud_driver.rb +912 -0
- metadata +62 -0
@@ -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
|