s3db 0.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,612 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe S3DB::FileBackend do
4
+ before :all do
5
+ S3DB::FileBackend.delete(TEST_DB_BASE_PATH)
6
+ S3DB::FileBackend.new(TEST_DB_BASE_PATH).delete_db('testdb')
7
+ end
8
+
9
+ after :all do
10
+ S3DB::FileBackend.new(TEST_DB_BASE_PATH).delete_db('testdb')
11
+ end
12
+
13
+ context 'class' do
14
+ describe '::initialize' do
15
+ it 'sets the base path' do
16
+ expect(S3DB::FileBackend.new(TEST_DB_BASE_PATH).path).to eq TEST_DB_BASE_PATH
17
+ end
18
+
19
+ it 'sets empty errors' do
20
+ expect(S3DB::FileBackend.new(TEST_DB_BASE_PATH).errors).to eq []
21
+ end
22
+
23
+ it 'returns a new Database' do
24
+ expect(S3DB::FileBackend.new(TEST_DB_BASE_PATH)).to be_a S3DB::FileBackend
25
+ end
26
+ end
27
+
28
+ describe '::create' do
29
+ it 'builds a FileBackend and calls save' do
30
+ expect_any_instance_of(S3DB::FileBackend).to receive(:save).and_call_original
31
+
32
+ S3DB::FileBackend.create(TEST_DB_BASE_PATH)
33
+ end
34
+ end
35
+
36
+ describe '::create!' do
37
+ it 'builds a FileBackend and calls save' do
38
+ expect_any_instance_of(S3DB::FileBackend).to receive(:save!)
39
+
40
+ S3DB::FileBackend.create!(TEST_DB_BASE_PATH)
41
+ end
42
+ end
43
+
44
+ describe '::destroy' do
45
+ it 'builds a backend and calls destroy' do
46
+ expect_any_instance_of(S3DB::FileBackend).to receive(:destroy)
47
+
48
+ S3DB::FileBackend.destroy(TEST_DB_BASE_PATH)
49
+ end
50
+ end
51
+ end
52
+
53
+ context 'instance' do
54
+ subject do
55
+ S3DB::FileBackend.new(TEST_DB_BASE_PATH)
56
+ end
57
+
58
+ let(:dbname) { 'testdb' }
59
+ let(:coll_name) { 'testcoll' }
60
+ let(:record_name) { 'testrec' }
61
+
62
+ describe '#validate_path' do
63
+ before :each do
64
+ subject.instance_variable_set(:@errors, [])
65
+ end
66
+
67
+ it 'accepts good paths' do
68
+ %w{/tmp /tmp/test tmp}.each do |path|
69
+ subject.instance_variable_set(:@path, path)
70
+
71
+ subject.validate_path
72
+
73
+ expect(subject.errors).to eq []
74
+ end
75
+ end
76
+
77
+ it 'rejects blacklist paths' do
78
+ %w{/badpath badpath}.each do |path|
79
+ subject.instance_variable_set(:@path, path)
80
+ subject.instance_variable_set(:@errors, [])
81
+
82
+ subject.validate_path
83
+
84
+ expect(subject.errors.length).to eq 1
85
+ expect(subject.errors.join('')).to match(/insane.*base.*path/)
86
+ end
87
+ end
88
+
89
+ it 'rejects malformed paths' do
90
+ ['mal\formed', 'bad path'].each do |path|
91
+ subject.instance_variable_set(:@path, path)
92
+ subject.instance_variable_set(:@errors, [])
93
+
94
+ subject.validate_path
95
+
96
+ expect(subject.errors.length).to eq 1
97
+ expect(subject.errors.join('')).to match(/path does not match/)
98
+ end
99
+ end
100
+ end
101
+
102
+ describe '#valid?' do
103
+ it 'returns true with no errors' do
104
+ expect(subject).to receive(:validate_path)
105
+
106
+ expect(subject.valid?).to be true
107
+ end
108
+
109
+ it 'returns false with errors' do
110
+ subject.instance_variable_set(:@path, ['badpath'])
111
+
112
+ expect(subject.valid?).to be false
113
+ end
114
+ end
115
+
116
+ describe '#valid!' do
117
+ it 'raises an error when valid? = false' do
118
+ allow(subject).to receive(:valid?).and_return(false)
119
+
120
+ expect do
121
+ subject.valid!
122
+ end.to raise_error ArgumentError, ''
123
+ end
124
+
125
+ it 'does not raise an error when valid? = true' do
126
+ allow(subject).to receive(:valid?).and_return(true)
127
+
128
+ expect do
129
+ subject.valid!
130
+ end.to_not raise_error
131
+ end
132
+ end
133
+
134
+ describe '#save' do
135
+ it 'validates the path' do
136
+ expect(subject).to receive(:valid?).and_call_original
137
+
138
+ subject.save
139
+ end
140
+
141
+ it 'writes the directory, if it does not exist' do
142
+ S3DB::FileBackend.delete(TEST_DB_BASE_PATH)
143
+ expect(Dir.exist?(TEST_DB_BASE_PATH)).to be false
144
+
145
+ subject.save
146
+ expect(Dir.exist?(TEST_DB_BASE_PATH)).to be true
147
+ end
148
+
149
+ it 'does not raise an error if the dir does exist' do
150
+ S3DB::FileBackend.create(TEST_DB_BASE_PATH)
151
+ expect(Dir.exist?(TEST_DB_BASE_PATH)).to be true
152
+
153
+ expect do
154
+ subject.save
155
+ end.to_not raise_error
156
+ end
157
+ end
158
+
159
+ describe '#save!' do
160
+ it 'validates the path' do
161
+ S3DB::FileBackend.destroy(TEST_DB_BASE_PATH)
162
+
163
+ expect(subject).to receive(:valid?).and_call_original
164
+
165
+ subject.save!
166
+ end
167
+
168
+ it 'writes the path, if it does not exist' do
169
+ S3DB::FileBackend.destroy(TEST_DB_BASE_PATH)
170
+ expect(Dir.exist?(TEST_DB_BASE_PATH)).to be false
171
+
172
+ subject.save!
173
+ expect(Dir.exist?(TEST_DB_BASE_PATH)).to be true
174
+ end
175
+
176
+ it 'raises an error if the path exists' do
177
+ S3DB::FileBackend.create(TEST_DB_BASE_PATH)
178
+ expect(Dir.exist?(TEST_DB_BASE_PATH)).to be true
179
+
180
+ expect do
181
+ subject.save!
182
+ end.to raise_error 'base path exists!'
183
+ end
184
+ end
185
+
186
+ describe '#destroy' do
187
+ it 'returns false if invalid' do
188
+ allow(subject).to receive(:valid?).and_return false
189
+
190
+ expect(subject.destroy).to be false
191
+ end
192
+
193
+ it 'returns false if the directory is not empty' do
194
+ subject.save
195
+ Dir.mkdir(File.join(TEST_DB_BASE_PATH, 'randomdir'))
196
+ expect(subject.destroy).to be false
197
+ S3DB::FileBackend.delete(TEST_DB_BASE_PATH)
198
+ expect(subject.destroy).to be false
199
+ end
200
+
201
+ it 'removes the dir and returns itself if valid' do
202
+ allow(subject).to receive(:valid?).and_return true
203
+
204
+ subject.save
205
+ expect(Dir.exist?(TEST_DB_BASE_PATH)).to be true
206
+ expect(subject.destroy).to be subject
207
+ expect(Dir.exist?(TEST_DB_BASE_PATH)).to be false
208
+ end
209
+ end
210
+
211
+ describe '#destroy!' do
212
+ it 'raises an error if not empty' do
213
+ subject.save
214
+ Dir.mkdir(File.join(TEST_DB_BASE_PATH, 'randomdir'))
215
+ allow(subject).to receive(:valid!)
216
+
217
+ expect do
218
+ subject.destroy!
219
+ end.to raise_error ArgumentError, 'basepath not empty!'
220
+ S3DB::FileBackend.delete(TEST_DB_BASE_PATH)
221
+ expect do
222
+ subject.destroy!
223
+ end.to raise_error ArgumentError, 'basepath does not exist!'
224
+ end
225
+
226
+ it 'removes the dir and returns itself if empty' do
227
+ allow(subject).to receive(:valid!).and_return true
228
+
229
+ subject.save
230
+ expect(Dir.exist?(TEST_DB_BASE_PATH)).to be true
231
+ expect(subject.destroy!).to be subject
232
+ expect(Dir.exist?(TEST_DB_BASE_PATH)).to be false
233
+ end
234
+ end
235
+
236
+ describe '#db_path' do
237
+ it 'builds a path from the base path and db_name' do
238
+ expect(subject.db_path('test')).to eq "#{TEST_DB_BASE_PATH}/test"
239
+ end
240
+ end
241
+
242
+ describe '#collection_path' do
243
+ it 'builds a path from the base path, db_name and collection name' do
244
+ expect(subject.collection_path('test', 'coll')).to eq "#{TEST_DB_BASE_PATH}/test/coll"
245
+ end
246
+ end
247
+
248
+ describe '#schema_path' do
249
+ it 'builds a path from the base path, db_name and coll name' do
250
+ expect(subject.schema_path('test', 'coll')).to \
251
+ eq "#{TEST_DB_BASE_PATH}/test/coll/schema.json"
252
+ end
253
+ end
254
+
255
+ describe '#data_path' do
256
+ it 'builds a path from the base path, db_name and collection_name' do
257
+ expect(subject.data_path('test', 'coll')).to \
258
+ eq "#{TEST_DB_BASE_PATH}/test/coll/data"
259
+ end
260
+ end
261
+
262
+ describe '#record_path' do
263
+ it 'builds a path from the base path, db name, coll name and filename' do
264
+ expect(subject.record_path('test', 'coll', 'file.json')).to \
265
+ eq "#{TEST_DB_BASE_PATH}/test/coll/data/file.json"
266
+ end
267
+ end
268
+
269
+ describe '#write_db' do
270
+ after :each do
271
+ S3DB::FileBackend.delete(TEST_DB_BASE_PATH)
272
+ end
273
+
274
+ it 'creates a db dir' do
275
+ expect(Dir.exist?(TEST_DB_BASE_PATH + '/' + dbname)).to be false
276
+
277
+ subject.save
278
+ expect(subject.write_db(dbname)).to eq dbname
279
+
280
+ expect(Dir.exist?(TEST_DB_BASE_PATH + '/' + dbname)).to be true
281
+ end
282
+ end
283
+
284
+ describe '#write_collection' do
285
+ before :each do
286
+ subject.save
287
+ subject.write_db(dbname)
288
+ end
289
+
290
+ after :each do
291
+ S3DB::FileBackend.delete(TEST_DB_BASE_PATH)
292
+ end
293
+
294
+ let(:path) { File.join(TEST_DB_BASE_PATH, dbname, coll_name) }
295
+
296
+ it 'creates a collection dir' do
297
+ expect(Dir.exist?(path)).to be false
298
+
299
+ expect(subject.write_collection(dbname, coll_name)).to eq coll_name
300
+
301
+ expect(Dir.exist?(path)).to be true
302
+ end
303
+
304
+ it 'creates a data dir' do
305
+ expect(Dir.exist?(File.join(path, 'data'))).to be false
306
+
307
+ expect(subject.write_collection(dbname, coll_name)).to eq coll_name
308
+
309
+ expect(Dir.exist?(File.join(path, 'data'))).to be true
310
+ end
311
+
312
+ it 'raises an error if the db does not exist' do
313
+ S3DB::FileBackend.delete(TEST_DB_BASE_PATH)
314
+ expect(Dir.exist?(path)).to be false
315
+
316
+ expect { subject.write_collection(dbname, coll_name) }.to \
317
+ raise_error Errno::ENOENT
318
+ end
319
+
320
+ it 'does not raise error if the dir already exists' do
321
+ subject.write_collection(dbname, coll_name)
322
+ expect(Dir.exist?(path)).to be true
323
+
324
+ expect { subject.write_collection(dbname, coll_name) }.to_not raise_error
325
+ end
326
+ end
327
+
328
+ describe '#write_schema' do
329
+ before :each do
330
+ subject.save
331
+ subject.write_db(dbname)
332
+ subject.write_collection(dbname, coll_name)
333
+ end
334
+
335
+ after :each do
336
+ S3DB::FileBackend.delete(TEST_DB_BASE_PATH)
337
+ end
338
+
339
+ let(:path) { File.join(TEST_DB_BASE_PATH, dbname, coll_name, 'schema.json') }
340
+
341
+ it 'creates a schema' do
342
+ expect(File.exist?(path)).to be false
343
+
344
+ expect(subject.write_schema(dbname, coll_name, '{}')).to eq '{}'
345
+
346
+ expect(File.exist?(path)).to be true
347
+ end
348
+
349
+ it 'does not raise an error if the schema already exists' do
350
+ subject.write_schema(dbname, coll_name, '{}')
351
+ expect(File.exist?(path)).to be true
352
+
353
+ expect { subject.write_schema(dbname, coll_name, '{}') }.to_not raise_error
354
+ end
355
+ end
356
+
357
+ describe '#write_record' do
358
+ before :each do
359
+ subject.save
360
+ subject.write_db(dbname)
361
+ subject.write_collection(dbname, coll_name)
362
+ end
363
+
364
+ after :each do
365
+ S3DB::FileBackend.delete(TEST_DB_BASE_PATH)
366
+ end
367
+
368
+ let(:path) do
369
+ File.join(TEST_DB_BASE_PATH, dbname, coll_name, 'data', record_name)
370
+ end
371
+
372
+ it 'creates a record' do
373
+ expect(File.exist?(path)).to be false
374
+
375
+ expect(subject.write_record(dbname, coll_name, record_name, '{}')).to \
376
+ eq '{}'
377
+
378
+ expect(File.exist?(path)).to be true
379
+ end
380
+
381
+ it 'overwrites the record if it exists' do
382
+ subject.write_record(dbname, coll_name, record_name, '{test: true}')
383
+ expect(File.exist?(path)).to be true
384
+ expect(File.read(path)).to eq "{test: true}\n"
385
+
386
+ expect do
387
+ subject.write_record(dbname, coll_name, record_name, '{}')
388
+ end.to_not raise_error
389
+
390
+ expect(File.read(path)).to eq "{}\n"
391
+ end
392
+
393
+ it 'raises an error if the data is not a string' do
394
+ expect do
395
+ subject.write_record(dbname, coll_name, record_name, 1)
396
+ end.to raise_error ArgumentError, 'data must be a string!'
397
+ end
398
+ end
399
+
400
+ describe '#list_collections' do
401
+ before :each do
402
+ subject.save
403
+ subject.write_db(dbname)
404
+ end
405
+
406
+ after :each do
407
+ S3DB::FileBackend.delete(TEST_DB_BASE_PATH)
408
+ end
409
+
410
+ it 'returns an array of directories' do
411
+ expect(subject.list_collections(dbname)).to eq []
412
+ subject.write_collection(dbname, coll_name)
413
+ expect(subject.list_collections(dbname)).to eq [coll_name]
414
+ end
415
+ end
416
+
417
+ describe '#list_records' do
418
+ before :each do
419
+ subject.save
420
+ subject.write_db(dbname)
421
+ subject.write_collection(dbname, coll_name)
422
+ end
423
+
424
+ after :each do
425
+ S3DB::FileBackend.delete(TEST_DB_BASE_PATH)
426
+ end
427
+
428
+ it 'returns an array of files' do
429
+ expect(subject.list_records(dbname, coll_name)).to eq []
430
+ subject.write_record(dbname, coll_name, record_name, '{}')
431
+ expect(subject.list_records(dbname, coll_name)).to eq [record_name]
432
+ end
433
+ end
434
+
435
+ describe '#read_schema' do
436
+ before :each do
437
+ subject.save
438
+ subject.write_db(dbname)
439
+ subject.write_collection(dbname, coll_name)
440
+ end
441
+
442
+ after :each do
443
+ S3DB::FileBackend.delete(TEST_DB_BASE_PATH)
444
+ end
445
+
446
+ it 'returns the contents of the schema file' do
447
+ subject.write_schema(dbname, coll_name, '{test: true}')
448
+ expect(subject.read_schema(dbname, coll_name)).to eq "{test: true}\n"
449
+ end
450
+
451
+ it 'raises an error if the schema is missing' do
452
+ expect do
453
+ subject.read_schema(dbname, coll_name)
454
+ end.to raise_error ArgumentError, 'schema does not exist!'
455
+ end
456
+ end
457
+
458
+ describe '#read_record' do
459
+ before :each do
460
+ subject.save
461
+ subject.write_db(dbname)
462
+ subject.write_collection(dbname, coll_name)
463
+ end
464
+
465
+ after :each do
466
+ S3DB::FileBackend.delete(TEST_DB_BASE_PATH)
467
+ end
468
+
469
+ it 'returns the contents of the record' do
470
+ subject.write_record(dbname, coll_name, record_name, '{test: true}')
471
+ expect(subject.read_record(dbname, coll_name, record_name)).to \
472
+ eq "{test: true}\n"
473
+ end
474
+
475
+ it 'raises an error if the record is missing' do
476
+ expect do
477
+ subject.read_record(dbname, coll_name, record_name)
478
+ end.to raise_error ArgumentError, 'record does not exist!'
479
+ end
480
+ end
481
+
482
+ describe '#delete_db' do
483
+ before :each do
484
+ subject.save
485
+ subject.write_db(dbname)
486
+ end
487
+
488
+ after :each do
489
+ S3DB::FileBackend.delete(TEST_DB_BASE_PATH)
490
+ end
491
+
492
+ it 'deletes an empty db' do
493
+ expect(subject.delete_db(dbname)).to eq [dbname]
494
+ end
495
+
496
+ it 'raises an error if the directory is not empty' do
497
+ subject.write_collection(dbname, coll_name)
498
+
499
+ expect { subject.delete_db(dbname) }.to raise_error 'database is not empty!'
500
+ end
501
+
502
+ it 'returns an empty array if there was no db' do
503
+ subject.delete_db(dbname)
504
+ expect(subject.delete_db(dbname)).to eq []
505
+ end
506
+ end
507
+
508
+ describe '#delete_collection' do
509
+ before :each do
510
+ subject.save
511
+ subject.write_db(dbname)
512
+ subject.write_collection(dbname, coll_name)
513
+ end
514
+
515
+ after :each do
516
+ S3DB::FileBackend.delete(TEST_DB_BASE_PATH)
517
+ end
518
+
519
+ it 'deletes an empty collection' do
520
+ expect(subject.delete_collection(dbname, coll_name)).to eq [coll_name]
521
+ end
522
+
523
+ it 'raises an error if the directory is not empty' do
524
+ subject.write_record(dbname, coll_name, record_name, '{}')
525
+
526
+ expect do
527
+ subject.delete_collection(dbname, coll_name)
528
+ end.to raise_error 'collection/data is not empty!'
529
+ end
530
+
531
+ it 'returns an empty array if there was no collection' do
532
+ subject.delete_collection(dbname, coll_name)
533
+ expect(subject.delete_collection(dbname, coll_name)).to eq []
534
+ end
535
+ end
536
+
537
+ describe '#delete_record' do
538
+ let(:record_name) { 'testrecord.json' }
539
+ let(:record_data) do
540
+ {
541
+ id: 'testrecord',
542
+ name: 'jack',
543
+ }.to_json + "\n"
544
+ end
545
+
546
+ after :each do
547
+ S3DB::FileBackend.delete(TEST_DB_BASE_PATH)
548
+ end
549
+
550
+ before :each do
551
+ subject.save
552
+ subject.write_db(dbname)
553
+ subject.write_collection(dbname, coll_name)
554
+ subject.write_record(dbname, coll_name, record_name, record_data)
555
+ end
556
+
557
+ it 'deletes the record' do
558
+ expect(subject.read_record(dbname, coll_name, record_name)).to eq(record_data)
559
+ expect(subject.delete_record(dbname, coll_name, record_name)).to eq(record_name)
560
+ expect(File.exist?(subject.record_path(dbname, coll_name, record_name))).to \
561
+ be false
562
+ end
563
+
564
+ it 'returns an empty string on missing record' do
565
+ subject.delete_record(dbname, coll_name, 'rando')
566
+ end
567
+ end
568
+
569
+ describe '#delete_record' do
570
+ let(:record_name) { 'testrecord.json' }
571
+ let(:record_data) do
572
+ {
573
+ id: 'testrecord',
574
+ name: 'jack',
575
+ }.to_json + "\n"
576
+ end
577
+
578
+ after :each do
579
+ S3DB::FileBackend.delete(TEST_DB_BASE_PATH)
580
+ end
581
+
582
+ before :each do
583
+ subject.save
584
+ subject.write_db(dbname)
585
+ subject.write_collection(dbname, coll_name)
586
+ subject.write_record(dbname, coll_name, record_name, record_data)
587
+ end
588
+
589
+ it 'deletes the record' do
590
+ expect(subject.read_record(dbname, coll_name, record_name)).to eq(record_data)
591
+ expect(subject.delete_record!(dbname, coll_name, record_name)).to eq(record_name)
592
+ expect(File.exist?(subject.record_path(dbname, coll_name, record_name))).to \
593
+ be false
594
+ end
595
+
596
+ it 'raises an error on a missing record' do
597
+ expect do
598
+ subject.delete_record!(dbname, coll_name, 'rando')
599
+ end.to raise_error ArgumentError, 'record does not exist!'
600
+ end
601
+ end
602
+
603
+ describe '#exist?' do
604
+ it 'returns true when the db exists' do
605
+ subject.save
606
+ subject.write_db(dbname)
607
+
608
+ expect(subject.db_exist?(dbname)).to be true
609
+ end
610
+ end
611
+ end
612
+ end