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.
- checksums.yaml +4 -4
- data/lib/hoodoo.rb +1 -0
- data/lib/hoodoo/services/services/session.rb +8 -104
- data/lib/hoodoo/transient_store.rb +19 -0
- data/lib/hoodoo/transient_store/mocks/dalli_client.rb +148 -0
- data/lib/hoodoo/transient_store/mocks/redis.rb +138 -0
- data/lib/hoodoo/transient_store/transient_store.rb +344 -0
- data/lib/hoodoo/transient_store/transient_store/base.rb +81 -0
- data/lib/hoodoo/transient_store/transient_store/memcached.rb +116 -0
- data/lib/hoodoo/transient_store/transient_store/memcached_redis_mirror.rb +181 -0
- data/lib/hoodoo/transient_store/transient_store/redis.rb +126 -0
- data/lib/hoodoo/version.rb +1 -1
- data/spec/active/active_record/support_spec.rb +3 -9
- data/spec/active/active_record/translated_spec.rb +2 -5
- data/spec/logger/writers/file_writer_spec.rb +1 -4
- data/spec/logger/writers/stream_writer_spec.rb +2 -9
- data/spec/services/middleware/middleware_logging_spec.rb +1 -4
- data/spec/services/middleware/middleware_permissions_spec.rb +2 -2
- data/spec/services/services/interface_spec.rb +2 -2
- data/spec/services/services/session_spec.rb +26 -19
- data/spec/transient_store/transient_store/base_spec.rb +52 -0
- data/spec/transient_store/transient_store/memcached_redis_mirror_spec.rb +380 -0
- data/spec/transient_store/transient_store/memcached_spec.rb +244 -0
- data/spec/transient_store/transient_store/mocks/dalli_client_spec.rb +44 -0
- data/spec/transient_store/transient_store/mocks/redis_spec.rb +28 -0
- data/spec/transient_store/transient_store/redis_spec.rb +242 -0
- data/spec/transient_store/transient_store_spec.rb +448 -0
- metadata +31 -9
@@ -3,7 +3,14 @@ require 'spec_helper'
|
|
3
3
|
describe Hoodoo::Services::Session do
|
4
4
|
|
5
5
|
before :each do
|
6
|
-
Hoodoo::
|
6
|
+
Hoodoo::TransientStore::Mocks::DalliClient.reset()
|
7
|
+
end
|
8
|
+
|
9
|
+
it 'includes a legacy alias to the TransientStore mock back-end' do
|
10
|
+
expect( Hoodoo::Services::Session::MockDalliClient == Hoodoo::TransientStore::Mocks::DalliClient ).to eql( true )
|
11
|
+
expect {
|
12
|
+
Hoodoo::TransientStore::Mocks::DalliClient.reset()
|
13
|
+
}.to_not raise_error
|
7
14
|
end
|
8
15
|
|
9
16
|
it 'initialises with default options' do
|
@@ -109,7 +116,7 @@ describe Hoodoo::Services::Session do
|
|
109
116
|
|
110
117
|
expect( s1.save_to_memcached ).to eq( :ok )
|
111
118
|
|
112
|
-
store = Hoodoo::
|
119
|
+
store = Hoodoo::TransientStore::Mocks::DalliClient.store()
|
113
120
|
expect( store[ '1234' ] ).to_not be_nil
|
114
121
|
expect( store[ '0987' ] ).to eq( { :expires_at => nil, :value => { 'version' => 2 } } )
|
115
122
|
|
@@ -135,7 +142,7 @@ describe Hoodoo::Services::Session do
|
|
135
142
|
end
|
136
143
|
|
137
144
|
it 'refuses to save if a newer caller version is present' do
|
138
|
-
expect( described_class ).to receive( :connect_to_memcached ).twice.and_return( Hoodoo::
|
145
|
+
expect( described_class ).to receive( :connect_to_memcached ).twice.and_return( Hoodoo::TransientStore::Mocks::DalliClient.new )
|
139
146
|
|
140
147
|
# Save a session with a high caller version
|
141
148
|
|
@@ -162,7 +169,7 @@ describe Hoodoo::Services::Session do
|
|
162
169
|
end
|
163
170
|
|
164
171
|
it 'invalidates a session if the client ID advances during its lifetime' do
|
165
|
-
expect( described_class ).to receive( :connect_to_memcached ).exactly( 4 ).times.and_return( Hoodoo::
|
172
|
+
expect( described_class ).to receive( :connect_to_memcached ).exactly( 4 ).times.and_return( Hoodoo::TransientStore::Mocks::DalliClient.new )
|
166
173
|
loader = described_class.new
|
167
174
|
|
168
175
|
# Save a session with a low caller version.
|
@@ -194,7 +201,7 @@ describe Hoodoo::Services::Session do
|
|
194
201
|
end
|
195
202
|
|
196
203
|
it 'refuses to load if the caller version is outdated' do
|
197
|
-
expect( described_class ).to receive( :connect_to_memcached ).exactly( 5 ).times.and_return( Hoodoo::
|
204
|
+
expect( described_class ).to receive( :connect_to_memcached ).exactly( 5 ).times.and_return( Hoodoo::TransientStore::Mocks::DalliClient.new )
|
198
205
|
loader = described_class.new
|
199
206
|
|
200
207
|
# Save a session with a low caller version
|
@@ -233,7 +240,7 @@ describe Hoodoo::Services::Session do
|
|
233
240
|
end
|
234
241
|
|
235
242
|
it 'refuses to load if expired' do
|
236
|
-
expect( described_class ).to receive( :connect_to_memcached ).twice.and_return( Hoodoo::
|
243
|
+
expect( described_class ).to receive( :connect_to_memcached ).twice.and_return( Hoodoo::TransientStore::Mocks::DalliClient.new )
|
237
244
|
loader = described_class.new
|
238
245
|
|
239
246
|
# Save a session with a high caller version
|
@@ -263,18 +270,18 @@ describe Hoodoo::Services::Session do
|
|
263
270
|
:caller_version => 1
|
264
271
|
)
|
265
272
|
|
266
|
-
expect( described_class ).to receive( :connect_to_memcached ).once.and_return( Hoodoo::
|
273
|
+
expect( described_class ).to receive( :connect_to_memcached ).once.and_return( Hoodoo::TransientStore::Mocks::DalliClient.new )
|
267
274
|
|
268
275
|
expect( s.update_caller_version_in_memcached( '9944', 23 ) ).to eq( :ok )
|
269
|
-
expect( s.update_caller_version_in_memcached( 'abcd', 2, Hoodoo::
|
276
|
+
expect( s.update_caller_version_in_memcached( 'abcd', 2, Hoodoo::TransientStore::Mocks::DalliClient.new ) ).to eq( :ok )
|
270
277
|
|
271
|
-
store = Hoodoo::
|
278
|
+
store = Hoodoo::TransientStore::Mocks::DalliClient.store()
|
272
279
|
expect( store[ '9944' ] ).to eq( { :expires_at => nil, :value => { 'version' => 23 } } )
|
273
280
|
expect( store[ 'abcd' ] ).to eq( { :expires_at => nil, :value => { 'version' => 2 } } )
|
274
281
|
end
|
275
282
|
|
276
283
|
it 'handles invalid session IDs when loading' do
|
277
|
-
expect( described_class ).to receive( :connect_to_memcached ).once.and_return( Hoodoo::
|
284
|
+
expect( described_class ).to receive( :connect_to_memcached ).once.and_return( Hoodoo::TransientStore::Mocks::DalliClient.new )
|
278
285
|
loader = described_class.new
|
279
286
|
expect( loader.load_from_memcached!( '1234' ) ).to eq( :not_found )
|
280
287
|
end
|
@@ -289,7 +296,7 @@ describe Hoodoo::Services::Session do
|
|
289
296
|
it 'logs Memcached exceptions when loading' do
|
290
297
|
loader = described_class.new
|
291
298
|
|
292
|
-
expect_any_instance_of( Hoodoo::
|
299
|
+
expect_any_instance_of( Hoodoo::TransientStore::Mocks::DalliClient ).to receive( :get ).once do
|
293
300
|
raise 'Mock Memcached connection failure'
|
294
301
|
end
|
295
302
|
|
@@ -312,7 +319,7 @@ describe Hoodoo::Services::Session do
|
|
312
319
|
# The first 'set' call is an attempt to update the caller version before
|
313
320
|
# the updated session is saved.
|
314
321
|
|
315
|
-
expect_any_instance_of( Hoodoo::
|
322
|
+
expect_any_instance_of( Hoodoo::TransientStore::Mocks::DalliClient ).to receive( :set ).once do
|
316
323
|
raise 'Mock Memcached connection failure'
|
317
324
|
end
|
318
325
|
|
@@ -332,8 +339,8 @@ describe Hoodoo::Services::Session do
|
|
332
339
|
:caller_version => 1
|
333
340
|
)
|
334
341
|
|
335
|
-
expect_any_instance_of( Hoodoo::
|
336
|
-
expect_any_instance_of( Hoodoo::
|
342
|
+
expect_any_instance_of( Hoodoo::TransientStore::Mocks::DalliClient ).to receive( :set ).once.and_call_original
|
343
|
+
expect_any_instance_of( Hoodoo::TransientStore::Mocks::DalliClient ).to receive( :set ).once do
|
337
344
|
raise 'Mock Memcached connection failure'
|
338
345
|
end
|
339
346
|
|
@@ -372,7 +379,7 @@ describe Hoodoo::Services::Session do
|
|
372
379
|
end
|
373
380
|
|
374
381
|
it 'logs and reports deletion exceptions' do
|
375
|
-
fdc = Hoodoo::
|
382
|
+
fdc = Hoodoo::TransientStore::Mocks::DalliClient.new
|
376
383
|
s = described_class.new(
|
377
384
|
:session_id => '1234',
|
378
385
|
:memcached_host => 'abcd',
|
@@ -397,11 +404,11 @@ describe Hoodoo::Services::Session do
|
|
397
404
|
before :example do
|
398
405
|
# Clear the connection cache for each test
|
399
406
|
Hoodoo::Services::Session.class_variable_set( '@@dalli_clients', nil ) # Hack for test!
|
400
|
-
Hoodoo::
|
407
|
+
Hoodoo::TransientStore::Mocks::DalliClient.bypass( true )
|
401
408
|
end
|
402
409
|
|
403
410
|
after :example do
|
404
|
-
Hoodoo::
|
411
|
+
Hoodoo::TransientStore::Mocks::DalliClient.bypass( false )
|
405
412
|
end
|
406
413
|
|
407
414
|
it 'complains about a missing host' do
|
@@ -437,13 +444,13 @@ describe Hoodoo::Services::Session do
|
|
437
444
|
end
|
438
445
|
|
439
446
|
it 'only initialises once for one given host' do
|
440
|
-
expect( Dalli::Client ).to receive( :new ).once.and_return( Hoodoo::
|
447
|
+
expect( Dalli::Client ).to receive( :new ).once.and_return( Hoodoo::TransientStore::Mocks::DalliClient.new )
|
441
448
|
|
442
449
|
1.upto( 3 ) do
|
443
450
|
described_class.connect_to_memcached( 'one' )
|
444
451
|
end
|
445
452
|
|
446
|
-
expect( Dalli::Client ).to receive( :new ).once.and_return( Hoodoo::
|
453
|
+
expect( Dalli::Client ).to receive( :new ).once.and_return( Hoodoo::TransientStore::Mocks::DalliClient.new )
|
447
454
|
|
448
455
|
1.upto( 3 ) do
|
449
456
|
described_class.connect_to_memcached( 'two' )
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Hoodoo::TransientStore::Base do
|
4
|
+
context 'creation' do
|
5
|
+
it 'initialises' do
|
6
|
+
base = Hoodoo::TransientStore::Base.new(
|
7
|
+
storage_host_uri: 'localhost',
|
8
|
+
namespace: 'foo'
|
9
|
+
)
|
10
|
+
|
11
|
+
expect( base ).to_not be_nil
|
12
|
+
|
13
|
+
# Subclasses may rightly rely on these, so they should be tested.
|
14
|
+
#
|
15
|
+
expect( base.instance_variable_get( '@storage_host_uri' ) ).to eq( 'localhost' )
|
16
|
+
expect( base.instance_variable_get( '@namespace' ) ).to eq( 'foo' )
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
context 'subclass author warning' do
|
21
|
+
before :each do
|
22
|
+
@base = Hoodoo::TransientStore::Base.new(
|
23
|
+
storage_host_uri: 'localhost',
|
24
|
+
namespace: 'foo'
|
25
|
+
)
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'is generated for #get' do
|
29
|
+
expect {
|
30
|
+
@base.set( key: 'foo', payload: {}, maximum_lifespan: 5 )
|
31
|
+
}.to raise_error( RuntimeError, 'Subclasses must implement Hoodoo::TransientStore::Base#set' )
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'is generated for #set' do
|
35
|
+
expect {
|
36
|
+
@base.get( key: 'foo' )
|
37
|
+
}.to raise_error( RuntimeError, 'Subclasses must implement Hoodoo::TransientStore::Base#get' )
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'is generated for #delete' do
|
41
|
+
expect {
|
42
|
+
@base.delete( key: 'foo' )
|
43
|
+
}.to raise_error( RuntimeError, 'Subclasses must implement Hoodoo::TransientStore::Base#delete' )
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'is generated for #close' do
|
47
|
+
expect {
|
48
|
+
@base.close()
|
49
|
+
}.to raise_error( RuntimeError, 'Subclasses must implement Hoodoo::TransientStore::Base#close' )
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,380 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
require 'hoodoo/transient_store/mocks/dalli_client'
|
4
|
+
require 'hoodoo/transient_store/mocks/redis'
|
5
|
+
|
6
|
+
# These tests make sure that the mirror class calls down to the Memcached and
|
7
|
+
# Redis abstractions, but assumes those abstractions are thoroughly tested by
|
8
|
+
# their own unit tests. So it makes sure it gets expected call and result
|
9
|
+
# behaviour, but doesn't worry about mock or real Redis and so-on.
|
10
|
+
#
|
11
|
+
describe Hoodoo::TransientStore::MemcachedRedisMirror do
|
12
|
+
|
13
|
+
it 'registers itself' do
|
14
|
+
expect( Hoodoo::TransientStore.supported_storage_engines() ).to include( :memcached_redis_mirror )
|
15
|
+
end
|
16
|
+
|
17
|
+
before :each do
|
18
|
+
@memcached_uri = 'localhost:11211'
|
19
|
+
@redis_uri = 'redis://localhost:6379'
|
20
|
+
@namespace = Hoodoo::UUID.generate()
|
21
|
+
@storage_engine_uri = {
|
22
|
+
:memcached => @memcached_uri,
|
23
|
+
:redis => @redis_uri
|
24
|
+
}
|
25
|
+
|
26
|
+
# Use pure mock back-ends behind the Memcached and Redis abstraction
|
27
|
+
# layers; real back-end tests are done for them in their unit tests.
|
28
|
+
|
29
|
+
Hoodoo::TransientStore::Mocks::DalliClient.reset()
|
30
|
+
Hoodoo::TransientStore::Mocks::Redis.reset()
|
31
|
+
|
32
|
+
allow( Dalli::Client ).to(
|
33
|
+
receive( :new ).
|
34
|
+
and_return( Hoodoo::TransientStore::Mocks::DalliClient.new )
|
35
|
+
)
|
36
|
+
|
37
|
+
allow( Redis ).to(
|
38
|
+
receive( :new ).
|
39
|
+
and_return( Hoodoo::TransientStore::Mocks::Redis.new )
|
40
|
+
)
|
41
|
+
|
42
|
+
@instance = Hoodoo::TransientStore::MemcachedRedisMirror.new(
|
43
|
+
storage_host_uri: @storage_engine_uri,
|
44
|
+
namespace: @namespace
|
45
|
+
)
|
46
|
+
end
|
47
|
+
|
48
|
+
# ==========================================================================
|
49
|
+
|
50
|
+
context '#initialize' do
|
51
|
+
it 'initialises' do
|
52
|
+
expect( @instance ).to be_a( Hoodoo::TransientStore::MemcachedRedisMirror )
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'complains about bad parameters' do
|
56
|
+
expect {
|
57
|
+
Hoodoo::TransientStore::MemcachedRedisMirror.new(
|
58
|
+
storage_host_uri: 'some string',
|
59
|
+
namespace: @namespace
|
60
|
+
)
|
61
|
+
}.to raise_error( RuntimeError, 'Hoodoo::TransientStore::MemcachedRedisMirror: Bad storage host URI data passed to constructor' )
|
62
|
+
|
63
|
+
expect {
|
64
|
+
Hoodoo::TransientStore::MemcachedRedisMirror.new(
|
65
|
+
storage_host_uri: { :hash => 'without required keys' },
|
66
|
+
namespace: @namespace
|
67
|
+
)
|
68
|
+
}.to raise_error( RuntimeError, 'Hoodoo::TransientStore::MemcachedRedisMirror: Bad storage host URI data passed to constructor' )
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'creates Memcached and Redis instances' do
|
72
|
+
expect( Hoodoo::TransientStore::Memcached ).to receive( :new )
|
73
|
+
expect( Hoodoo::TransientStore::Redis ).to receive( :new )
|
74
|
+
|
75
|
+
Hoodoo::TransientStore::MemcachedRedisMirror.new(
|
76
|
+
storage_host_uri: @storage_engine_uri,
|
77
|
+
namespace: @namespace
|
78
|
+
)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# ==========================================================================
|
83
|
+
|
84
|
+
context 'when initialised' do
|
85
|
+
before :each do
|
86
|
+
@key = Hoodoo::UUID.generate()
|
87
|
+
@payload = { 'bar' => 'baz' }
|
88
|
+
@ttl = 120
|
89
|
+
end
|
90
|
+
|
91
|
+
context '#set' do
|
92
|
+
it 'sets' do
|
93
|
+
expect_any_instance_of( Hoodoo::TransientStore::Memcached ).to receive( :set ).with( key: @key, payload: @payload, maximum_lifespan: @ttl ).and_call_original()
|
94
|
+
expect_any_instance_of( Hoodoo::TransientStore::Redis ).to receive( :set ).with( key: @key, payload: @payload, maximum_lifespan: @ttl ).and_call_original()
|
95
|
+
|
96
|
+
result = @instance.set(
|
97
|
+
key: @key,
|
98
|
+
payload: @payload,
|
99
|
+
maximum_lifespan: @ttl
|
100
|
+
)
|
101
|
+
|
102
|
+
expect( result ).to eq( true )
|
103
|
+
end
|
104
|
+
|
105
|
+
it 'allows Memcached exceptions to propagate' do
|
106
|
+
expect_any_instance_of( Hoodoo::TransientStore::Memcached ).to receive( :set ).and_raise( 'Hello world' )
|
107
|
+
|
108
|
+
expect {
|
109
|
+
@instance.set(
|
110
|
+
key: @key,
|
111
|
+
payload: @payload,
|
112
|
+
maximum_lifespan: @ttl
|
113
|
+
)
|
114
|
+
}.to raise_error( RuntimeError, "Hello world" )
|
115
|
+
end
|
116
|
+
|
117
|
+
it 'allows Redis exceptions to propagate' do
|
118
|
+
expect_any_instance_of( Hoodoo::TransientStore::Redis ).to receive( :set ).and_raise( 'Hello world' )
|
119
|
+
|
120
|
+
expect {
|
121
|
+
@instance.set(
|
122
|
+
key: @key,
|
123
|
+
payload: @payload,
|
124
|
+
maximum_lifespan: @ttl
|
125
|
+
)
|
126
|
+
}.to raise_error( RuntimeError, "Hello world" )
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
context '#get' do
|
131
|
+
before :each do
|
132
|
+
expect_any_instance_of( Hoodoo::TransientStore::Memcached ).to receive( :set ).with( key: @key, payload: @payload, maximum_lifespan: @ttl ).and_call_original()
|
133
|
+
expect_any_instance_of( Hoodoo::TransientStore::Redis ).to receive( :set ).with( key: @key, payload: @payload, maximum_lifespan: @ttl ).and_call_original()
|
134
|
+
|
135
|
+
@instance.set(
|
136
|
+
key: @key,
|
137
|
+
payload: @payload,
|
138
|
+
maximum_lifespan: @ttl
|
139
|
+
)
|
140
|
+
end
|
141
|
+
|
142
|
+
it 'gets known keys' do
|
143
|
+
expect_any_instance_of( Hoodoo::TransientStore::Memcached ).to receive( :get ).with( key: @key ).and_call_original()
|
144
|
+
expect_any_instance_of( Hoodoo::TransientStore::Redis ).to receive( :get ).with( key: @key ).and_call_original()
|
145
|
+
|
146
|
+
expect( @instance.get( key: @key ) ).to eql( @payload )
|
147
|
+
end
|
148
|
+
|
149
|
+
it 'returns "nil" for unknown keys' do
|
150
|
+
expect( @instance.get( key: Hoodoo::UUID.generate() ) ).to be_nil
|
151
|
+
end
|
152
|
+
|
153
|
+
it 'returns "nil" if Memcached is missing the data' do
|
154
|
+
expect_any_instance_of( Hoodoo::TransientStore::Memcached ).to receive( :get ).with( key: @key ).and_return( nil )
|
155
|
+
expect_any_instance_of( Hoodoo::TransientStore::Redis ).to receive( :get ).with( key: @key ).and_call_original()
|
156
|
+
|
157
|
+
expect( @instance.get( key: @key ) ).to be_nil
|
158
|
+
end
|
159
|
+
|
160
|
+
it 'returns "nil" if Redis is missing the data' do
|
161
|
+
expect_any_instance_of( Hoodoo::TransientStore::Memcached ).to receive( :get ).with( key: @key ).and_call_original()
|
162
|
+
expect_any_instance_of( Hoodoo::TransientStore::Redis ).to receive( :get ).with( key: @key ).and_return( nil )
|
163
|
+
|
164
|
+
expect( @instance.get( key: @key ) ).to be_nil
|
165
|
+
end
|
166
|
+
|
167
|
+
it 'allows Memcached exceptions to propagate' do
|
168
|
+
expect_any_instance_of( Hoodoo::TransientStore::Memcached ).to receive( :get ).and_raise( 'Hello world' )
|
169
|
+
|
170
|
+
expect {
|
171
|
+
@instance.get( key: @key )
|
172
|
+
}.to raise_error( RuntimeError, "Hello world" )
|
173
|
+
end
|
174
|
+
|
175
|
+
it 'allows Redis exceptions to propagate' do
|
176
|
+
expect_any_instance_of( Hoodoo::TransientStore::Redis ).to receive( :get ).and_raise( 'Hello world' )
|
177
|
+
|
178
|
+
expect {
|
179
|
+
@instance.get( key: @key )
|
180
|
+
}.to raise_error( RuntimeError, "Hello world" )
|
181
|
+
end
|
182
|
+
|
183
|
+
context 'with migration selector of' do
|
184
|
+
context ':both' do
|
185
|
+
it 'is the default' do
|
186
|
+
expect( @instance.get_keys_from ).to eql( :both )
|
187
|
+
end
|
188
|
+
|
189
|
+
# This is covered elsewhere too but belt-and-braces checks don't hurt
|
190
|
+
# and the test list looks cleaner when this one section covers all
|
191
|
+
# allowed values of the selector.
|
192
|
+
|
193
|
+
it 'returns data if the key is in both engines' do
|
194
|
+
expect_any_instance_of( Hoodoo::TransientStore::Memcached ).to receive( :get ).with( key: @key ).and_call_original()
|
195
|
+
expect_any_instance_of( Hoodoo::TransientStore::Redis ).to receive( :get ).with( key: @key ).and_call_original()
|
196
|
+
|
197
|
+
expect( @instance.get( key: @key ) ).to eql( @payload )
|
198
|
+
end
|
199
|
+
|
200
|
+
it 'returns "nil" if the key is only in Memcached' do
|
201
|
+
expect_any_instance_of( Hoodoo::TransientStore::Memcached ).to receive( :get ).with( key: @key ).and_call_original()
|
202
|
+
expect_any_instance_of( Hoodoo::TransientStore::Redis ).to receive( :get ).with( key: @key ).and_return( nil )
|
203
|
+
|
204
|
+
expect( @instance.get( key: @key ) ).to be_nil
|
205
|
+
end
|
206
|
+
|
207
|
+
it 'returns "nil" if the key is only in Redis' do
|
208
|
+
expect_any_instance_of( Hoodoo::TransientStore::Memcached ).to receive( :get ).with( key: @key ).and_return( nil )
|
209
|
+
expect_any_instance_of( Hoodoo::TransientStore::Redis ).to receive( :get ).with( key: @key ).and_call_original()
|
210
|
+
|
211
|
+
expect( @instance.get( key: @key ) ).to be_nil
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
context ':memcached' do
|
216
|
+
before( :each ) { @instance.get_keys_from = :memcached }
|
217
|
+
after( :each ) { @instance.get_keys_from = :both }
|
218
|
+
|
219
|
+
it 'returns data if the key is in both engines' do
|
220
|
+
expect_any_instance_of( Hoodoo::TransientStore::Memcached ).to receive( :get ).with( key: @key ).and_call_original()
|
221
|
+
expect_any_instance_of( Hoodoo::TransientStore::Redis ).to_not receive( :get )
|
222
|
+
|
223
|
+
expect( @instance.get( key: @key ) ).to eql( @payload )
|
224
|
+
end
|
225
|
+
|
226
|
+
it 'returns data if the key is only in Memcached' do
|
227
|
+
expect_any_instance_of( Hoodoo::TransientStore::Memcached ).to receive( :get ).with( key: @key ).and_call_original()
|
228
|
+
expect_any_instance_of( Hoodoo::TransientStore::Redis ).to_not receive( :get )
|
229
|
+
|
230
|
+
expect( @instance.get( key: @key ) ).to eql( @payload )
|
231
|
+
end
|
232
|
+
|
233
|
+
it 'returns "nil" if the key is only in Redis' do
|
234
|
+
expect_any_instance_of( Hoodoo::TransientStore::Memcached ).to receive( :get ).with( key: @key ).and_return( nil )
|
235
|
+
expect_any_instance_of( Hoodoo::TransientStore::Redis ).to_not receive( :get )
|
236
|
+
|
237
|
+
expect( @instance.get( key: @key ) ).to be_nil
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
context ':redis' do
|
242
|
+
before( :each ) { @instance.get_keys_from = :redis }
|
243
|
+
after( :each ) { @instance.get_keys_from = :both }
|
244
|
+
|
245
|
+
it 'returns data if the key is in both engines' do
|
246
|
+
expect_any_instance_of( Hoodoo::TransientStore::Memcached ).to_not receive( :get )
|
247
|
+
expect_any_instance_of( Hoodoo::TransientStore::Redis ).to receive( :get ).with( key: @key ).and_call_original()
|
248
|
+
|
249
|
+
expect( @instance.get( key: @key ) ).to eql( @payload )
|
250
|
+
end
|
251
|
+
|
252
|
+
it 'returns "nil" if the key is only in Memcached' do
|
253
|
+
expect_any_instance_of( Hoodoo::TransientStore::Memcached ).to_not receive( :get )
|
254
|
+
expect_any_instance_of( Hoodoo::TransientStore::Redis ).to receive( :get ).with( key: @key ).and_return( nil )
|
255
|
+
|
256
|
+
expect( @instance.get( key: @key ) ).to be_nil
|
257
|
+
end
|
258
|
+
|
259
|
+
it 'returns data if the key is only in Redis' do
|
260
|
+
expect_any_instance_of( Hoodoo::TransientStore::Memcached ).to_not receive( :get )
|
261
|
+
expect_any_instance_of( Hoodoo::TransientStore::Redis ).to receive( :get ).with( key: @key ).and_call_original()
|
262
|
+
|
263
|
+
expect( @instance.get( key: @key ) ).to eql( @payload )
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
context 'an unrecognised value' do
|
268
|
+
before( :each ) { @instance.get_keys_from = :foo }
|
269
|
+
after( :each ) { @instance.get_keys_from = :both }
|
270
|
+
|
271
|
+
it 'causes complaint' do
|
272
|
+
expect {
|
273
|
+
@instance.get( key: @key )
|
274
|
+
}.to raise_error( RuntimeError, "Hoodoo::TransientStore::Base\#get: Invalid prior value given in \#get_keys_from= of ':foo' - only ':both', ':memcached' or ':redis' are allowed" )
|
275
|
+
end
|
276
|
+
end
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
context '#delete' do
|
281
|
+
before :each do
|
282
|
+
expect_any_instance_of( Hoodoo::TransientStore::Memcached ).to receive( :set ).with( key: @key, payload: @payload, maximum_lifespan: @ttl ).and_call_original()
|
283
|
+
expect_any_instance_of( Hoodoo::TransientStore::Redis ).to receive( :set ).with( key: @key, payload: @payload, maximum_lifespan: @ttl ).and_call_original()
|
284
|
+
|
285
|
+
@instance.set(
|
286
|
+
key: @key,
|
287
|
+
payload: @payload,
|
288
|
+
maximum_lifespan: @ttl
|
289
|
+
)
|
290
|
+
end
|
291
|
+
|
292
|
+
it 'deletes known keys' do
|
293
|
+
expect( @instance.get( key: @key ) ).to eql( @payload )
|
294
|
+
|
295
|
+
expect_any_instance_of( Hoodoo::TransientStore::Memcached ).to receive( :delete ).with( key: @key ).and_call_original()
|
296
|
+
expect_any_instance_of( Hoodoo::TransientStore::Redis ).to receive( :delete ).with( key: @key ).and_call_original()
|
297
|
+
|
298
|
+
result = @instance.delete( key: @key )
|
299
|
+
|
300
|
+
expect( result ).to eq( true )
|
301
|
+
expect( @instance.get( key: @key ) ).to eql( nil )
|
302
|
+
end
|
303
|
+
|
304
|
+
it 'ignores unknown keys' do
|
305
|
+
expect( @instance.delete( key: Hoodoo::UUID.generate() ) ).to eql( true )
|
306
|
+
end
|
307
|
+
|
308
|
+
it 'allows Memcached exceptions to propagate but still calls Redis' do
|
309
|
+
expect_any_instance_of( Hoodoo::TransientStore::Memcached ).to receive( :delete ).and_raise( 'Hello world' )
|
310
|
+
expect_any_instance_of( Hoodoo::TransientStore::Redis ).to receive( :delete ).with( key: @key ).and_call_original()
|
311
|
+
|
312
|
+
expect {
|
313
|
+
@instance.delete( key: @key )
|
314
|
+
}.to raise_error( RuntimeError, "Hello world" )
|
315
|
+
end
|
316
|
+
|
317
|
+
it 'allows Redis exceptions to propagate but still calls Memcached' do
|
318
|
+
expect_any_instance_of( Hoodoo::TransientStore::Memcached ).to receive( :delete ).with( key: @key ).and_call_original()
|
319
|
+
expect_any_instance_of( Hoodoo::TransientStore::Redis ).to receive( :delete ).and_raise( 'Hello world' )
|
320
|
+
|
321
|
+
expect {
|
322
|
+
@instance.delete( key: @key )
|
323
|
+
}.to raise_error( RuntimeError, "Hello world" )
|
324
|
+
end
|
325
|
+
|
326
|
+
it 'deletes from Memcached even if the Redis data is missing' do
|
327
|
+
expect( @instance.get( key: @key ) ).to eql( @payload )
|
328
|
+
|
329
|
+
expect_any_instance_of( Hoodoo::TransientStore::Memcached ).to receive( :delete ).with( key: @key ).and_call_original()
|
330
|
+
expect_any_instance_of( Hoodoo::TransientStore::Redis ).to receive( :delete ).with( key: @key ).and_call_original()
|
331
|
+
|
332
|
+
Hoodoo::TransientStore::Mocks::Redis.reset()
|
333
|
+
|
334
|
+
result = @instance.delete( key: @key )
|
335
|
+
|
336
|
+
expect( result ).to eq( true )
|
337
|
+
expect( @instance.get( key: @key ) ).to eql( nil )
|
338
|
+
end
|
339
|
+
|
340
|
+
it 'deletes from Redis even if the Memcached data is missing' do
|
341
|
+
expect( @instance.get( key: @key ) ).to eql( @payload )
|
342
|
+
|
343
|
+
expect_any_instance_of( Hoodoo::TransientStore::Memcached ).to receive( :delete ).with( key: @key ).and_call_original()
|
344
|
+
expect_any_instance_of( Hoodoo::TransientStore::Redis ).to receive( :delete ).with( key: @key ).and_call_original()
|
345
|
+
|
346
|
+
Hoodoo::TransientStore::Mocks::DalliClient.reset()
|
347
|
+
|
348
|
+
result = @instance.delete( key: @key )
|
349
|
+
|
350
|
+
expect( result ).to eq( true )
|
351
|
+
expect( @instance.get( key: @key ) ).to eql( nil )
|
352
|
+
end
|
353
|
+
end
|
354
|
+
|
355
|
+
context '#close' do
|
356
|
+
it 'closes normally' do
|
357
|
+
expect_any_instance_of( Hoodoo::TransientStore::Memcached ).to receive( :close ).and_call_original()
|
358
|
+
expect_any_instance_of( Hoodoo::TransientStore::Redis ).to receive( :close ).and_call_original()
|
359
|
+
|
360
|
+
@instance.close()
|
361
|
+
end
|
362
|
+
|
363
|
+
it 'still closes Redis if Memcached raises an exception' do
|
364
|
+
expect_any_instance_of( Hoodoo::TransientStore::Memcached ).to receive( :close ).and_raise( "Hello world" )
|
365
|
+
expect_any_instance_of( Hoodoo::TransientStore::Redis ).to receive( :close ).and_call_original()
|
366
|
+
|
367
|
+
@instance.close() rescue nil
|
368
|
+
end
|
369
|
+
|
370
|
+
it 'still closes Memcached if Redis raises an exception' do
|
371
|
+
expect_any_instance_of( Hoodoo::TransientStore::Memcached ).to receive( :close ).and_call_original()
|
372
|
+
expect_any_instance_of( Hoodoo::TransientStore::Redis ).to receive( :close ).and_raise( "Hello world" )
|
373
|
+
|
374
|
+
@instance.close() rescue nil
|
375
|
+
end
|
376
|
+
end
|
377
|
+
|
378
|
+
end # 'context "when initialised (#{ backend })" do'
|
379
|
+
|
380
|
+
end # 'describe...'
|