hoodoo 1.13.0 → 1.14.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (28) hide show
  1. checksums.yaml +4 -4
  2. data/lib/hoodoo.rb +1 -0
  3. data/lib/hoodoo/services/services/session.rb +8 -104
  4. data/lib/hoodoo/transient_store.rb +19 -0
  5. data/lib/hoodoo/transient_store/mocks/dalli_client.rb +148 -0
  6. data/lib/hoodoo/transient_store/mocks/redis.rb +138 -0
  7. data/lib/hoodoo/transient_store/transient_store.rb +344 -0
  8. data/lib/hoodoo/transient_store/transient_store/base.rb +81 -0
  9. data/lib/hoodoo/transient_store/transient_store/memcached.rb +116 -0
  10. data/lib/hoodoo/transient_store/transient_store/memcached_redis_mirror.rb +181 -0
  11. data/lib/hoodoo/transient_store/transient_store/redis.rb +126 -0
  12. data/lib/hoodoo/version.rb +1 -1
  13. data/spec/active/active_record/support_spec.rb +3 -9
  14. data/spec/active/active_record/translated_spec.rb +2 -5
  15. data/spec/logger/writers/file_writer_spec.rb +1 -4
  16. data/spec/logger/writers/stream_writer_spec.rb +2 -9
  17. data/spec/services/middleware/middleware_logging_spec.rb +1 -4
  18. data/spec/services/middleware/middleware_permissions_spec.rb +2 -2
  19. data/spec/services/services/interface_spec.rb +2 -2
  20. data/spec/services/services/session_spec.rb +26 -19
  21. data/spec/transient_store/transient_store/base_spec.rb +52 -0
  22. data/spec/transient_store/transient_store/memcached_redis_mirror_spec.rb +380 -0
  23. data/spec/transient_store/transient_store/memcached_spec.rb +244 -0
  24. data/spec/transient_store/transient_store/mocks/dalli_client_spec.rb +44 -0
  25. data/spec/transient_store/transient_store/mocks/redis_spec.rb +28 -0
  26. data/spec/transient_store/transient_store/redis_spec.rb +242 -0
  27. data/spec/transient_store/transient_store_spec.rb +448 -0
  28. metadata +31 -9
@@ -0,0 +1,448 @@
1
+ require 'spec_helper'
2
+
3
+ # These tests assume the Memcached plugin is available and has been unit
4
+ # tested thoroughly elsewhere.
5
+ #
6
+ describe Hoodoo::TransientStore do
7
+
8
+ RDOC_STATED_DEFAULT_LIFESPAN = 604800
9
+ RDOC_STATED_DEFAULT_NAMESPACE = 'nz_co_loyalty_hoodoo_transient_store_'
10
+
11
+ class TestTransientStore < Hoodoo::TransientStore::Base
12
+ def initialize( storage_host_uri:, namespace: )
13
+ @pool = {}
14
+ end
15
+
16
+ def set( key:, payload:, maximum_lifespan: )
17
+ @pool[ key ] = payload
18
+ true
19
+ end
20
+
21
+ def get( key: )
22
+ @pool[ key ]
23
+ end
24
+
25
+ def delete( key: )
26
+ @pool.delete( key )
27
+ true
28
+ end
29
+ end
30
+
31
+ # ==========================================================================
32
+
33
+ context 'registration' do
34
+ it 'allows registration' do
35
+ random_name = Hoodoo::UUID.generate().to_sym()
36
+ Hoodoo::TransientStore.register( as: random_name, using: TestTransientStore )
37
+
38
+ expect( Hoodoo::TransientStore.supported_storage_engines() ).to include( random_name )
39
+ end
40
+
41
+ it 'complains about re-registration' do
42
+ random_name = Hoodoo::UUID.generate().to_sym()
43
+ Hoodoo::TransientStore.register( as: random_name, using: TestTransientStore )
44
+
45
+ expect {
46
+ Hoodoo::TransientStore.register( as: random_name, using: TestTransientStore )
47
+ }.to raise_error( RuntimeError, "Hoodoo::TransientStore.register: A storage engine called '#{ random_name }' is already registered" )
48
+ end
49
+
50
+ it 'complains about incorrect subclassing' do
51
+ random_name = Hoodoo::UUID.generate().to_sym()
52
+
53
+ expect {
54
+ Hoodoo::TransientStore.register( as: random_name, using: Class )
55
+ }.to raise_error( RuntimeError, "Hoodoo::TransientStore.register requires a Hoodoo::TransientStore::Base subclass - got 'Class'" )
56
+ end
57
+
58
+ it 'allows deregistration' do
59
+ random_name = Hoodoo::UUID.generate().to_sym()
60
+
61
+ Hoodoo::TransientStore.register( as: random_name, using: TestTransientStore )
62
+ expect( Hoodoo::TransientStore.supported_storage_engines() ).to include( random_name )
63
+
64
+ Hoodoo::TransientStore.deregister( as: random_name )
65
+ expect( Hoodoo::TransientStore.supported_storage_engines() ).to_not include( random_name )
66
+ end
67
+ end
68
+
69
+ # ==========================================================================
70
+
71
+ context 'with' do
72
+
73
+ before :each do
74
+ @engine_name = Hoodoo::UUID.generate().to_sym
75
+ Hoodoo::TransientStore.register( as: @engine_name, using: TestTransientStore )
76
+ end
77
+
78
+ context '#initialize' do
79
+ it 'initialises' do
80
+ max_life = 120
81
+ namespace = Hoodoo::UUID.generate()
82
+ uri = 'localhost'
83
+
84
+ expect( TestTransientStore ).to receive( :new ).with( storage_host_uri: uri, namespace: namespace )
85
+
86
+ result = Hoodoo::TransientStore.new(
87
+ storage_engine: @engine_name,
88
+ storage_host_uri: uri,
89
+ default_maximum_lifespan: max_life,
90
+ default_namespace: namespace
91
+ )
92
+
93
+ expect( result ).to be_a( Hoodoo::TransientStore )
94
+ expect( result.storage_engine ).to eql( @engine_name )
95
+ expect( result.default_maximum_lifespan ).to eql( max_life )
96
+ expect( result.default_namespace ).to eql( namespace )
97
+ end
98
+
99
+ it 'initialises with defaults' do
100
+ result = Hoodoo::TransientStore.new(
101
+ storage_engine: @engine_name,
102
+ storage_host_uri: 'localhost'
103
+ )
104
+
105
+ expect( result.default_maximum_lifespan ).to eql( RDOC_STATED_DEFAULT_LIFESPAN )
106
+ expect( result.default_namespace ).to eql( RDOC_STATED_DEFAULT_NAMESPACE )
107
+ end
108
+
109
+ context 'unknown storage engines' do
110
+ before :each do
111
+ @old_engines = Hoodoo::TransientStore.class_variable_get( '@@supported_storage_engines' )
112
+ Hoodoo::TransientStore.class_variable_set( '@@supported_storage_engines', { @engine_name => TestTransientStore } ) # Hack for test!
113
+ end
114
+
115
+ after :each do
116
+ Hoodoo::TransientStore.class_variable_set( '@@supported_storage_engines', @old_engines ) # Hack for test!
117
+ end
118
+
119
+ it 'cause complaint' do
120
+ # Add two more engine names
121
+ #
122
+ Hoodoo::TransientStore.register( as: 'foo', using: TestTransientStore )
123
+ Hoodoo::TransientStore.register( as: 'bar', using: TestTransientStore )
124
+
125
+ expect {
126
+ result = Hoodoo::TransientStore.new(
127
+ storage_engine: :baz,
128
+ storage_host_uri: 'localhost'
129
+ )
130
+ }.to raise_error( RuntimeError, "Hoodoo::TransientStore: Unrecognised storage engine ':baz' requested; allowed values: '#{ @engine_name.to_sym.inspect }', ':foo', ':bar'" )
131
+ end
132
+ end
133
+ end
134
+
135
+ # ========================================================================
136
+
137
+ context 'attribute accessor' do
138
+ before :each do
139
+ @ttl = 120
140
+ @namespace = Hoodoo::UUID.generate()
141
+ @store = Hoodoo::TransientStore.new(
142
+ storage_engine: @engine_name,
143
+ storage_host_uri: 'localhost',
144
+ default_maximum_lifespan: @ttl,
145
+ default_namespace: @namespace
146
+ )
147
+ end
148
+
149
+ it '#storage_engine' do
150
+ expect( @store.storage_engine ).to eql( @engine_name )
151
+ end
152
+
153
+ it '#storage_engine_instance' do
154
+ expect( @store.storage_engine_instance ).to be_a( TestTransientStore )
155
+ end
156
+
157
+ it '#default_maximum_lifespan' do
158
+ expect( @store.default_maximum_lifespan ).to eql( @ttl )
159
+ end
160
+
161
+ it '#default_namespace' do
162
+ expect( @store.default_namespace ).to eql( @namespace )
163
+ end
164
+ end
165
+
166
+ # ========================================================================
167
+
168
+ context '#set' do
169
+ before :each do
170
+ @store = Hoodoo::TransientStore.new(
171
+ storage_engine: @engine_name,
172
+ storage_host_uri: 'localhost'
173
+ )
174
+ end
175
+
176
+ it 'sets with default lifespan' do
177
+ key = Hoodoo::UUID.generate()
178
+ payload = { 'foo' => 'bar' }
179
+
180
+ expect_any_instance_of( TestTransientStore ).to(
181
+ receive( :set ).
182
+ with(
183
+ key: key,
184
+ payload: payload,
185
+ maximum_lifespan: RDOC_STATED_DEFAULT_LIFESPAN
186
+ ).
187
+ and_call_original()
188
+ )
189
+
190
+ result = @store.set( key: key, payload: payload )
191
+ expect( result ).to eq( true )
192
+ end
193
+
194
+ it 'sets with explicit lifespan' do
195
+ key = Hoodoo::UUID.generate()
196
+ payload = { 'foo' => 'bar' }
197
+ lifespan = 1
198
+
199
+ expect_any_instance_of( TestTransientStore ).to(
200
+ receive( :set ).
201
+ with(
202
+ key: key,
203
+ payload: payload,
204
+ maximum_lifespan: lifespan
205
+ ).
206
+ and_call_original()
207
+ )
208
+
209
+ result = @store.set( key: key, payload: payload, maximum_lifespan: lifespan )
210
+ expect( result ).to eq( true )
211
+ end
212
+
213
+ it 'consumes and returns exceptions' do
214
+ expect_any_instance_of( TestTransientStore ).to receive( :set ) do
215
+ raise 'Hello world'
216
+ end
217
+
218
+ result = @store.set( key: '1', payload: '2' )
219
+
220
+ expect( result ).to be_a( RuntimeError )
221
+ expect( result.message ).to eql( 'Hello world' )
222
+ end
223
+
224
+ it 'prohibits "nil" payloads' do
225
+ expect {
226
+ result = @store.set( key: '1', payload: nil )
227
+ }.to raise_error( RuntimeError, "Hoodoo::TransientStore\#set: Payloads of 'nil' are prohibited" )
228
+ end
229
+
230
+ it 'detects faulty engines' do
231
+ expect_any_instance_of( TestTransientStore ).to receive( :set ) do
232
+ 'not a boolean'
233
+ end
234
+
235
+ result = @store.set( key: '1', payload: '2' )
236
+
237
+ expect( result ).to be_a( RuntimeError )
238
+ expect( result.message ).to eql( "Hoodoo::TransientStore\#set: Engine '#{ @engine_name }' returned an invalid response" )
239
+ end
240
+
241
+ context 'key normalisation' do
242
+ it 'normalises the key' do
243
+ key = :some_symbol
244
+
245
+ expect_any_instance_of( TestTransientStore ).to(
246
+ receive( :set ).with(
247
+ key: key.to_s, # i.e. Symbol has been normalised to String
248
+ payload: {},
249
+ maximum_lifespan: RDOC_STATED_DEFAULT_LIFESPAN
250
+ )
251
+ )
252
+
253
+ @store.set( key: key, payload: {} )
254
+ end
255
+
256
+ it 'complains about unsupported key types' do
257
+ expect {
258
+ @store.set( key: Hash.new, payload: {} )
259
+ }.to raise_error( RuntimeError, "Hoodoo::TransientStore\#set: Keys must be of String or Symbol class; you provided 'Hash'" )
260
+ end
261
+
262
+ it 'complains about empty keys' do
263
+ expect {
264
+ @store.set( key: '', payload: {} )
265
+ }.to raise_error( RuntimeError, 'Hoodoo::TransientStore#set: Empty String or Symbol keys are prohibited' )
266
+ end
267
+ end
268
+ end
269
+
270
+ # ========================================================================
271
+
272
+ context '#get' do
273
+ before :each do
274
+ @store = Hoodoo::TransientStore.new(
275
+ storage_engine: @engine_name,
276
+ storage_host_uri: 'localhost'
277
+ )
278
+
279
+ @key = Hoodoo::UUID.generate()
280
+ @payload = { 'foo' => 'bar' }
281
+
282
+ @store.set( key: @key, payload: @payload )
283
+ end
284
+
285
+ it 'gets known keys' do
286
+ expect_any_instance_of( TestTransientStore ).to(
287
+ receive( :get ).with( key: @key ).and_call_original()
288
+ )
289
+
290
+ expect( @store.get( key: @key ) ).to eql( @payload )
291
+ end
292
+
293
+ it 'returns "nil" for unknown keys' do
294
+ random_key = Hoodoo::UUID.generate()
295
+
296
+ expect_any_instance_of( TestTransientStore ).to(
297
+ receive( :get ).with( key: random_key ).and_call_original()
298
+ )
299
+
300
+ expect( @store.get( key: random_key ) ).to be_nil
301
+ end
302
+
303
+ it 'consumes exceptions' do
304
+ expect_any_instance_of( TestTransientStore ).to receive( :get ) do
305
+ raise 'Hello world'
306
+ end
307
+
308
+ expect( @store.get( key: @key ) ).to be_nil
309
+ end
310
+
311
+ context 'key normalisation' do
312
+ it 'normalises the key' do
313
+ key = :some_symbol
314
+
315
+ expect_any_instance_of( TestTransientStore ).to(
316
+ receive( :get ).with(
317
+ key: key.to_s # i.e. Symbol has been normalised to String
318
+ )
319
+ )
320
+
321
+ @store.get( key: key )
322
+ end
323
+
324
+ it 'complains about unsupported key types' do
325
+ expect {
326
+ @store.get( key: Hash.new )
327
+ }.to raise_error( RuntimeError, "Hoodoo::TransientStore\#get: Keys must be of String or Symbol class; you provided 'Hash'" )
328
+ end
329
+
330
+ it 'complains about empty keys' do
331
+ expect {
332
+ @store.get( key: '' )
333
+ }.to raise_error( RuntimeError, 'Hoodoo::TransientStore#get: Empty String or Symbol keys are prohibited' )
334
+ end
335
+ end
336
+ end
337
+
338
+ # ========================================================================
339
+
340
+ context '#delete' do
341
+ before :each do
342
+ @store = Hoodoo::TransientStore.new(
343
+ storage_engine: @engine_name,
344
+ storage_host_uri: 'localhost'
345
+ )
346
+
347
+ @key = Hoodoo::UUID.generate()
348
+ @payload = { 'foo' => 'bar' }
349
+
350
+ @store.set( key: @key, payload: @payload )
351
+ end
352
+
353
+ it 'deletes known keys' do
354
+ expect( @store.get( key: @key ) ).to eql( @payload )
355
+
356
+ expect_any_instance_of( TestTransientStore ).to(
357
+ receive( :delete ).with( key: @key ).and_call_original()
358
+ )
359
+
360
+ expect( @store.delete( key: @key ) ).to eql( true )
361
+ expect( @store.get( key: @key ) ).to be_nil
362
+ end
363
+
364
+ it 'ignores unknown keys' do
365
+ random_key = Hoodoo::UUID.generate()
366
+
367
+ expect_any_instance_of( TestTransientStore ).to(
368
+ receive( :delete ).with( key: random_key ).and_call_original()
369
+ )
370
+
371
+ expect( @store.delete( key: random_key ) ).to eql( true )
372
+ end
373
+
374
+ it 'consumes and returns exceptions' do
375
+ expect_any_instance_of( TestTransientStore ).to receive( :delete ) do
376
+ raise 'Hello world'
377
+ end
378
+
379
+ result = @store.delete( key: @key )
380
+
381
+ expect( result ).to be_a( RuntimeError )
382
+ expect( result.message ).to eql( 'Hello world' )
383
+ end
384
+
385
+ it 'detects faulty engines' do
386
+ expect_any_instance_of( TestTransientStore ).to receive( :delete ) do
387
+ 'not a boolean'
388
+ end
389
+
390
+ result = @store.delete( key: @key )
391
+
392
+ expect( result ).to be_a( RuntimeError )
393
+ expect( result.message ).to eql( "Hoodoo::TransientStore\#delete: Engine '#{ @engine_name }' returned an invalid response" )
394
+ end
395
+
396
+ context 'key normalisation' do
397
+ it 'normalises the key' do
398
+ key = :some_symbol
399
+
400
+ expect_any_instance_of( TestTransientStore ).to(
401
+ receive( :delete ).with(
402
+ key: key.to_s # i.e. Symbol has been normalised to String
403
+ )
404
+ )
405
+
406
+ @store.delete( key: key )
407
+ end
408
+
409
+ it 'complains about unsupported key types' do
410
+ expect {
411
+ @store.delete( key: Hash.new )
412
+ }.to raise_error( RuntimeError, "Hoodoo::TransientStore\#delete: Keys must be of String or Symbol class; you provided 'Hash'" )
413
+ end
414
+
415
+ it 'complains about empty keys' do
416
+ expect {
417
+ @store.delete( key: '' )
418
+ }.to raise_error( RuntimeError, 'Hoodoo::TransientStore#delete: Empty String or Symbol keys are prohibited' )
419
+ end
420
+ end
421
+ end
422
+
423
+ # ========================================================================
424
+
425
+ context '#close' do
426
+ before :each do
427
+ @store = Hoodoo::TransientStore.new(
428
+ storage_engine: @engine_name,
429
+ storage_host_uri: 'localhost'
430
+ )
431
+ end
432
+
433
+ it 'closes' do
434
+ expect_any_instance_of( TestTransientStore ).to receive( :close )
435
+ @store.close()
436
+ end
437
+
438
+ it 'consumes and ignores exceptions' do
439
+ expect_any_instance_of( TestTransientStore ).to receive( :close ) do
440
+ raise 'Hello world'
441
+ end
442
+
443
+ @store.close()
444
+ end
445
+ end
446
+
447
+ end
448
+ end
metadata CHANGED
@@ -1,43 +1,43 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hoodoo
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.13.0
4
+ version: 1.14.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Loyalty New Zealand
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-02-01 00:00:00.000000000 Z
11
+ date: 2017-02-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: kgio
14
+ name: dalli
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: '2.9'
19
+ version: '2.7'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: '2.9'
26
+ version: '2.7'
27
27
  - !ruby/object:Gem::Dependency
28
- name: dalli
28
+ name: redis
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '2.7'
34
- type: :runtime
33
+ version: '3.2'
34
+ type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '2.7'
40
+ version: '3.2'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: rake
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -368,6 +368,14 @@ files:
368
368
  - lib/hoodoo/services/services/response.rb
369
369
  - lib/hoodoo/services/services/service.rb
370
370
  - lib/hoodoo/services/services/session.rb
371
+ - lib/hoodoo/transient_store.rb
372
+ - lib/hoodoo/transient_store/mocks/dalli_client.rb
373
+ - lib/hoodoo/transient_store/mocks/redis.rb
374
+ - lib/hoodoo/transient_store/transient_store.rb
375
+ - lib/hoodoo/transient_store/transient_store/base.rb
376
+ - lib/hoodoo/transient_store/transient_store/memcached.rb
377
+ - lib/hoodoo/transient_store/transient_store/memcached_redis_mirror.rb
378
+ - lib/hoodoo/transient_store/transient_store/redis.rb
371
379
  - lib/hoodoo/utilities.rb
372
380
  - lib/hoodoo/utilities/string_inquirer.rb
373
381
  - lib/hoodoo/utilities/utilities.rb
@@ -482,6 +490,13 @@ files:
482
490
  - spec/services/services/session_spec.rb
483
491
  - spec/shared_examples/middleware_amqp.rb
484
492
  - spec/spec_helper.rb
493
+ - spec/transient_store/transient_store/base_spec.rb
494
+ - spec/transient_store/transient_store/memcached_redis_mirror_spec.rb
495
+ - spec/transient_store/transient_store/memcached_spec.rb
496
+ - spec/transient_store/transient_store/mocks/dalli_client_spec.rb
497
+ - spec/transient_store/transient_store/mocks/redis_spec.rb
498
+ - spec/transient_store/transient_store/redis_spec.rb
499
+ - spec/transient_store/transient_store_spec.rb
485
500
  - spec/utilities/utilities_spec.rb
486
501
  - spec/utilities/uuid_spec.rb
487
502
  homepage: https://loyaltynz.github.io/hoodoo/
@@ -618,5 +633,12 @@ test_files:
618
633
  - spec/services/services/session_spec.rb
619
634
  - spec/shared_examples/middleware_amqp.rb
620
635
  - spec/spec_helper.rb
636
+ - spec/transient_store/transient_store/base_spec.rb
637
+ - spec/transient_store/transient_store/memcached_redis_mirror_spec.rb
638
+ - spec/transient_store/transient_store/memcached_spec.rb
639
+ - spec/transient_store/transient_store/mocks/dalli_client_spec.rb
640
+ - spec/transient_store/transient_store/mocks/redis_spec.rb
641
+ - spec/transient_store/transient_store/redis_spec.rb
642
+ - spec/transient_store/transient_store_spec.rb
621
643
  - spec/utilities/utilities_spec.rb
622
644
  - spec/utilities/uuid_spec.rb