hoodoo 1.13.0 → 1.14.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.
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