memcached_store 0.12.8 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,601 +0,0 @@
1
- require 'test_helper'
2
- require 'logger'
3
-
4
- class TestMemcachedStore < ActiveSupport::TestCase
5
- setup do
6
- @cache = ActiveSupport::Cache.lookup_store(:memcached_store, expires_in: 60, support_cas: true)
7
- @cache.clear
8
-
9
- # Enable ActiveSupport notifications. Can be disabled in Rails 5.
10
- Thread.current[:instrument_cache_store] = true
11
- end
12
-
13
- def test_write_not_found
14
- expect_not_found
15
- assert_equal false, @cache.write('not_exist', 1)
16
- end
17
-
18
- def test_fetch_not_found
19
- expect_not_found
20
- assert_equal nil, @cache.fetch('not_exist')
21
- end
22
-
23
- def test_should_read_and_write_strings
24
- assert @cache.write('foo', 'bar')
25
- assert_equal 'bar', @cache.read('foo')
26
- end
27
-
28
- def test_should_overwrite
29
- @cache.write('foo', 'bar')
30
- @cache.write('foo', 'baz')
31
- assert_equal 'baz', @cache.read('foo')
32
- end
33
-
34
- def test_fetch_without_cache_miss
35
- @cache.write('foo', 'bar')
36
- @cache.expects(:write).never
37
- assert_equal 'bar', @cache.fetch('foo') { 'baz' }
38
- end
39
-
40
- def test_fetch_with_cache_miss
41
- @cache.expects(:write).with('foo', 'baz', @cache.options)
42
- assert_equal 'baz', @cache.fetch('foo') { 'baz' }
43
- end
44
-
45
- def test_fetch_with_forced_cache_miss
46
- @cache.write('foo', 'bar')
47
- @cache.expects(:read).never
48
- @cache.expects(:write).with('foo', 'bar', @cache.options.merge(:force => true))
49
- @cache.fetch('foo', :force => true) { 'bar' }
50
- end
51
-
52
- def test_fetch_with_cached_nil
53
- @cache.write('foo', nil)
54
- @cache.expects(:write).never
55
- assert_nil @cache.fetch('foo') { 'baz' }
56
- end
57
-
58
- def test_cas
59
- @cache.write('foo', nil)
60
- assert @cache.cas('foo') {|value| assert_nil value; 'bar' }
61
- assert_equal 'bar', @cache.read('foo')
62
- end
63
-
64
- def test_cas_with_cache_miss
65
- refute @cache.cas('not_exist') {|value| flunk }
66
- end
67
-
68
- def test_cas_with_conflict
69
- @cache.write('foo', 'bar')
70
- refute @cache.cas('foo') {|value|
71
- @cache.write('foo', 'baz')
72
- 'biz'
73
- }
74
- assert_equal 'baz', @cache.read('foo')
75
- end
76
-
77
- def test_cas_multi_with_empty_set
78
- refute @cache.cas_multi() {|hash| flunk }
79
- end
80
-
81
- def test_cas_multi
82
- @cache.write('foo', 'bar')
83
- @cache.write('fud', 'biz')
84
- assert @cache.cas_multi('foo', 'fud') {|hash| assert_equal({"foo" => "bar", "fud" => "biz"}, hash); {"foo" => "baz", "fud" => "buz"} }
85
- assert_equal({"foo" => "baz", "fud" => "buz"}, @cache.read_multi('foo', 'fud'))
86
- end
87
-
88
- def test_cas_multi_with_altered_key
89
- @cache.write('foo', 'baz')
90
- assert @cache.cas_multi('foo') {|hash| {'fu' => 'baz'}}
91
- assert_nil @cache.read('fu')
92
- assert_equal 'baz', @cache.read('foo')
93
- end
94
-
95
- def test_cas_multi_with_cache_miss
96
- assert @cache.cas_multi('not_exist') {|hash| assert hash.empty?; {} }
97
- end
98
-
99
- def test_cas_multi_with_partial_miss
100
- @cache.write('foo', 'baz')
101
- assert @cache.cas_multi('foo', 'bar') {|hash| assert_equal({"foo" => "baz"}, hash); {} }
102
- assert_equal 'baz', @cache.read('foo')
103
- end
104
-
105
- def test_cas_multi_with_partial_update
106
- @cache.write('foo', 'bar')
107
- @cache.write('fud', 'biz')
108
- assert @cache.cas_multi('foo', 'fud') {|hash| assert_equal({"foo" => "bar", "fud" => "biz"}, hash); {"foo" => "baz"} }
109
- assert_equal({"foo" => "baz", "fud" => "biz"}, @cache.read_multi('foo', 'fud'))
110
- end
111
-
112
- def test_cas_multi_with_partial_conflict
113
- @cache.write('foo', 'bar')
114
- @cache.write('fud', 'biz')
115
- result = @cache.cas_multi('foo', 'fud') do |hash|
116
- assert_equal({"foo" => "bar", "fud" => "biz"}, hash)
117
- @cache.write('foo', 'bad')
118
- {"foo" => "baz", "fud" => "buz"}
119
- end
120
- assert result
121
- assert_equal({"foo" => "bad", "fud" => "buz"}, @cache.read_multi('foo', 'fud'))
122
- end
123
-
124
- def test_should_read_and_write_hash
125
- assert @cache.write('foo', {:a => "b"})
126
- assert_equal({:a => "b"}, @cache.read('foo'))
127
- end
128
-
129
- def test_should_read_and_write_integer
130
- assert @cache.write('foo', 1)
131
- assert_equal 1, @cache.read('foo')
132
- end
133
-
134
- def test_should_read_and_write_nil
135
- assert @cache.write('foo', nil)
136
- assert_equal nil, @cache.read('foo')
137
- end
138
-
139
- def test_should_read_and_write_false
140
- assert @cache.write('foo', false)
141
- assert_equal false, @cache.read('foo')
142
- end
143
-
144
- def test_read_multi
145
- @cache.write('foo', 'bar')
146
- @cache.write('fu', 'baz')
147
- @cache.write('fud', 'biz')
148
- assert_equal({"foo" => "bar", "fu" => "baz"}, @cache.read_multi('foo', 'fu'))
149
- end
150
-
151
- def test_read_multi_with_expires
152
- time = Time.now
153
- @cache.write('foo', 'bar', :expires_in => 10)
154
- @cache.write('fu', 'baz')
155
- @cache.write('fud', 'biz')
156
- Time.stubs(:now).returns(time + 11)
157
- assert_equal({"fu" => "baz"}, @cache.read_multi('foo', 'fu'))
158
- end
159
-
160
- def test_read_multi_not_found
161
- expect_not_found
162
- assert_equal({}, @cache.read_multi('foe', 'fue'))
163
- end
164
-
165
- def test_read_and_write_compressed_small_data
166
- @cache.write('foo', 'bar', :compress => true)
167
- assert_equal 'bar', @cache.read('foo')
168
- end
169
-
170
- def test_read_and_write_compressed_large_data
171
- @cache.write('foo', 'bar', :compress => true, :compress_threshold => 2)
172
- assert_equal 'bar', @cache.read('foo')
173
- end
174
-
175
- def test_read_and_write_compressed_nil
176
- @cache.write('foo', nil, :compress => true)
177
- assert_nil @cache.read('foo')
178
- end
179
-
180
- def test_cache_key
181
- obj = Object.new
182
- def obj.cache_key
183
- :foo
184
- end
185
- @cache.write(obj, "bar")
186
- assert_equal "bar", @cache.read("foo")
187
- end
188
-
189
- def test_param_as_cache_key
190
- obj = Object.new
191
- def obj.to_param
192
- "foo"
193
- end
194
- @cache.write(obj, "bar")
195
- assert_equal "bar", @cache.read("foo")
196
- end
197
-
198
- def test_array_as_cache_key
199
- @cache.write([:fu, "foo"], "bar")
200
- assert_equal "bar", @cache.read("fu/foo")
201
- end
202
-
203
- def test_hash_as_cache_key
204
- @cache.write({:foo => 1, :fu => 2}, "bar")
205
- assert_equal "bar", @cache.read("foo=1/fu=2")
206
- end
207
-
208
- def test_keys_are_case_sensitive
209
- @cache.write("foo", "bar")
210
- assert_nil @cache.read("FOO")
211
- end
212
-
213
- def test_exist
214
- @cache.write('foo', 'bar')
215
- assert_equal true, @cache.exist?('foo')
216
- assert_equal false, @cache.exist?('bar')
217
- end
218
-
219
- def test_nil_exist
220
- @cache.write('foo', nil)
221
- assert @cache.exist?('foo')
222
- end
223
-
224
- def test_delete
225
- @cache.write('foo', 'bar')
226
- assert @cache.exist?('foo')
227
- assert @cache.delete('foo')
228
- assert !@cache.exist?('foo')
229
-
230
- assert @cache.delete('foo')
231
- end
232
-
233
- def test_original_store_objects_should_not_be_immutable
234
- bar = 'bar'
235
- @cache.write('foo', bar)
236
- assert_nothing_raised { bar.gsub!(/.*/, 'baz') }
237
- end
238
-
239
- def test_expires_in
240
- time = Time.local(2008, 4, 24)
241
- Time.stubs(:now).returns(time)
242
-
243
- @cache.write('foo', 'bar')
244
- assert_equal 'bar', @cache.read('foo')
245
-
246
- Time.stubs(:now).returns(time + 30)
247
- assert_equal 'bar', @cache.read('foo')
248
-
249
- Time.stubs(:now).returns(time + 61)
250
- assert_nil @cache.read('foo')
251
- end
252
-
253
- def test_race_condition_protection
254
- time = Time.now
255
- @cache.write('foo', 'bar', :expires_in => 60)
256
- Time.stubs(:now).returns(time + 61)
257
- result = @cache.fetch('foo', :race_condition_ttl => 10) do
258
- assert_equal 'bar', @cache.read('foo')
259
- "baz"
260
- end
261
- assert_equal "baz", result
262
- end
263
-
264
- def test_race_condition_protection_is_limited
265
- time = Time.now
266
- @cache.write('foo', 'bar', :expires_in => 60)
267
- Time.stubs(:now).returns(time + 71)
268
- result = @cache.fetch('foo', :race_condition_ttl => 10) do
269
- assert_equal nil, @cache.read('foo')
270
- "baz"
271
- end
272
- assert_equal "baz", result
273
- end
274
-
275
- def test_race_condition_protection_is_safe
276
- time = Time.now
277
- @cache.write('foo', 'bar', :expires_in => 60)
278
- Time.stubs(:now).returns(time + 61)
279
- begin
280
- @cache.fetch('foo', :race_condition_ttl => 10) do
281
- assert_equal 'bar', @cache.read('foo')
282
- raise ArgumentError.new
283
- end
284
- rescue ArgumentError
285
- end
286
- assert_equal "bar", @cache.read('foo')
287
- Time.stubs(:now).returns(time + 91)
288
- assert_nil @cache.read('foo')
289
- end
290
-
291
- def test_crazy_key_characters
292
- crazy_key = "#/:*(<+=> )&$%@?;'\"\'`~-"
293
- assert @cache.write(crazy_key, "1", :raw => true)
294
- assert_equal "1", @cache.read(crazy_key)
295
- assert_equal "1", @cache.fetch(crazy_key)
296
- assert @cache.delete(crazy_key)
297
- assert_equal "2", @cache.fetch(crazy_key, :raw => true) { "2" }
298
- assert_equal 3, @cache.increment(crazy_key)
299
- assert_equal 2, @cache.decrement(crazy_key)
300
- end
301
-
302
- def test_really_long_keys
303
- key = ""
304
- 900.times{key << "x"}
305
- assert @cache.write(key, "bar")
306
- assert_equal "bar", @cache.read(key)
307
- assert_equal "bar", @cache.fetch(key)
308
- assert_nil @cache.read("#{key}x")
309
- assert_equal({key => "bar"}, @cache.read_multi(key))
310
- assert @cache.delete(key)
311
- end
312
-
313
- def test_increment
314
- @cache.write('foo', 1, :raw => true)
315
- assert_equal 1, @cache.read('foo').to_i
316
- assert_equal 2, @cache.increment('foo')
317
- assert_equal 2, @cache.read('foo').to_i
318
- assert_equal 3, @cache.increment('foo')
319
- assert_equal 3, @cache.read('foo').to_i
320
- assert_nil @cache.increment('bar')
321
- end
322
-
323
- def test_increment_not_found
324
- expect_not_found
325
- assert_equal nil, @cache.increment('not_exist')
326
- end
327
-
328
- def test_decrement
329
- @cache.write('foo', 3, :raw => true)
330
- assert_equal 3, @cache.read('foo').to_i
331
- assert_equal 2, @cache.decrement('foo')
332
- assert_equal 2, @cache.read('foo').to_i
333
- assert_equal 1, @cache.decrement('foo')
334
- assert_equal 1, @cache.read('foo').to_i
335
- assert_nil @cache.decrement('bar')
336
- end
337
-
338
- def test_decrement_not_found
339
- expect_not_found
340
- assert_equal nil, @cache.decrement('not_exist')
341
- end
342
-
343
- def test_common_utf8_values
344
- key = "\xC3\xBCmlaut".force_encoding(Encoding::UTF_8)
345
- assert @cache.write(key, "1", :raw => true)
346
- assert_equal "1", @cache.read(key)
347
- assert_equal "1", @cache.fetch(key)
348
- assert @cache.delete(key)
349
- assert_equal "2", @cache.fetch(key, :raw => true) { "2" }
350
- assert_equal 3, @cache.increment(key)
351
- assert_equal 2, @cache.decrement(key)
352
- end
353
-
354
- def test_retains_encoding
355
- key = "\xC3\xBCmlaut".force_encoding(Encoding::UTF_8)
356
- assert @cache.write(key, "1", :raw => true)
357
- assert_equal Encoding::UTF_8, key.encoding
358
- end
359
-
360
- def test_initialize_accepts_a_list_of_servers_in_options
361
- options = {servers: ["localhost:21211"]}
362
- cache = ActiveSupport::Cache.lookup_store(:memcached_store, options)
363
- assert_equal 21211, cache.instance_variable_get(:@data).servers.first.port
364
- end
365
-
366
- def test_multiple_servers
367
- options = {servers: ["localhost:21211", "localhost:11211"]}
368
- cache = ActiveSupport::Cache.lookup_store(:memcached_store, options)
369
- assert_equal [21211, 11211], cache.instance_variable_get(:@data).servers.map(&:port)
370
- end
371
-
372
- def test_namespace_without_servers
373
- options = {namespace: 'foo:'}
374
- cache = ActiveSupport::Cache.lookup_store(:memcached_store, options)
375
- client = cache.instance_variable_get(:@data)
376
- assert_equal [11211], client.servers.map(&:port)
377
- assert_equal "", client.prefix_key, "should not send the namespace to the client"
378
- assert_equal "foo::key", cache.send(:namespaced_key, "key", cache.options)
379
- end
380
-
381
- def test_reset
382
- client = @cache.instance_variable_get(:@data)
383
- client.expects(:reset).once
384
- @cache.reset
385
- end
386
-
387
- def test_write_to_read_only_memcached_store_should_not_write
388
- with_read_only(@cache) do
389
- assert @cache.write("walrus", "awesome"), "Writing to a disabled memcached
390
- store should return truthy to make clients not care"
391
-
392
- assert_nil @cache.read("walrus"), "Key should have nil value in disabled cache"
393
- end
394
- end
395
-
396
- def test_delete_with_read_only_memcached_store_should_not_delete
397
- assert @cache.write("walrus", "big")
398
-
399
- with_read_only(@cache) do
400
- assert @cache.delete("walrus"), "Should return truthy when deleted to not raise in client"
401
- end
402
-
403
- assert_equal "big", @cache.read("walrus"), "Cache entry should not have been deleted from read only client"
404
- end
405
-
406
- def test_cas_with_read_only_memcached_store_should_not_s
407
- called_block = false
408
- @cache.write('walrus', 'slimy')
409
-
410
- with_read_only(@cache) do
411
- assert(@cache.cas('walrus') { |value|
412
- assert_equal 'slimy', value
413
- called_block = true
414
- 'full'
415
- })
416
- end
417
-
418
- assert_equal 'slimy', @cache.read('walrus')
419
- assert called_block, "CAS with read only should have called the inner block with an assertion"
420
- end
421
-
422
- def test_cas_multi_with_read_only_memcached_store_should_not_s
423
- called_block = false
424
-
425
- @cache.write('walrus', 'cool')
426
- @cache.write('narwhal', 'horn')
427
-
428
- with_read_only(@cache) do
429
- assert(@cache.cas_multi('walrus', 'narwhal') {
430
- called_block = true
431
- { "walrus" => "not cool", "narwhal" => "not with horns" }
432
- })
433
- end
434
-
435
- assert_equal 'cool', @cache.read('walrus')
436
- assert_equal 'horn', @cache.read('narwhal')
437
- assert called_block, "CAS with read only should have called the inner block with an assertion"
438
- end
439
-
440
- def test_write_with_read_only_should_not_send_activesupport_notification
441
- assert_notifications(/cache/, 0) do
442
- with_read_only(@cache) do
443
- assert @cache.write("walrus", "bestest")
444
- end
445
- end
446
- end
447
-
448
- def test_delete_with_read_only_should_not_send_activesupport_notification
449
- assert_notifications(/cache/, 0) do
450
- with_read_only(@cache) do
451
- assert @cache.delete("walrus")
452
- end
453
- end
454
- end
455
-
456
- def test_fetch_with_expires_in_with_read_only_should_not_send_activesupport_notification
457
- expires_in = 10
458
- @cache.fetch("walrus", expires_in: expires_in) { "yo" }
459
-
460
- Timecop.travel(Time.now + expires_in + 1) do
461
- assert_notifications(/cache_write/, 0) do
462
- with_read_only(@cache) do
463
- @cache.fetch("walrus") { "no" }
464
- end
465
- end
466
- end
467
- end
468
-
469
- def test_fetch_with_expired_entry_with_read_only_should_return_nil_and_not_delete_from_cache
470
- expires_in = 10
471
- @cache.fetch("walrus", expires_in: expires_in) { "yo" }
472
-
473
- Timecop.travel(Time.now + expires_in + 1) do
474
- with_read_only(@cache) do
475
- value = @cache.fetch("walrus", expires_in: expires_in) { "no" }
476
-
477
- assert_equal "no", value
478
- refute @cache.fetch("walrus"), "Client should return nil for expired key"
479
- assert_equal "yo", @cache.instance_variable_get(:@data).get("walrus").value
480
- end
481
- end
482
- end
483
-
484
- def test_fetch_with_expired_entry_and_race_condition_ttl_with_read_only_should_return_nil_and_not_delete_from_cache
485
- expires_in = 10
486
- race_condition_ttl = 10
487
- @cache.fetch("walrus", expires_in: expires_in) { "yo" }
488
-
489
- Timecop.travel(Time.now + expires_in + 1) do
490
- with_read_only(@cache) do
491
- value = @cache.fetch("walrus", expires_in: expires_in, race_condition_ttl: race_condition_ttl) { "no" }
492
-
493
- assert_equal "no", value
494
- assert_equal "no", @cache.fetch("walrus") { "no" }
495
- refute @cache.fetch("walrus")
496
-
497
- assert_equal "yo", @cache.instance_variable_get(:@data).get("walrus").value
498
- end
499
- end
500
- end
501
-
502
- def test_read_with_expired_with_read_only_entry_should_return_nil_and_not_delete_from_cache
503
- expires_in = 10
504
- @cache.fetch("walrus", expires_in: expires_in) { "yo" }
505
-
506
- Timecop.travel(Time.now + expires_in + 1) do
507
- with_read_only(@cache) do
508
- refute @cache.read("walrus")
509
-
510
- assert_equal "yo", @cache.instance_variable_get(:@data).get("walrus").value
511
- end
512
- end
513
- end
514
-
515
- def test_read_multi_with_expired_entry_should_return_nil_and_not_delete_from_cache
516
- expires_in = 10
517
- @cache.fetch("walrus", expires_in: expires_in) { "yo" }
518
- @cache.fetch("narwhal", expires_in: expires_in) { "yiir" }
519
-
520
- Timecop.travel(Time.now + expires_in + 1) do
521
- with_read_only(@cache) do
522
- assert_predicate @cache.read_multi("walrus", "narwhal"), :empty?
523
-
524
- assert_equal "yo", @cache.instance_variable_get(:@data).get("walrus").value
525
- assert_equal "yiir", @cache.instance_variable_get(:@data).get("narwhal").value
526
- end
527
- end
528
- end
529
-
530
- def test_fetch_with_race_condition_ttl_with_read_only_should_not_send_activesupport_notification
531
- expires_in = 10
532
- race_condition_ttl = 10
533
- @cache.fetch("walrus", expires_in: expires_in) { "yo" }
534
-
535
- Timecop.travel(Time.now + expires_in + 1) do
536
- assert_notifications(/cache_write/, 0) do
537
- with_read_only(@cache) do
538
- @cache.fetch("walrus", expires_in: expires_in, race_condition_ttl: race_condition_ttl) { "no" }
539
- end
540
- end
541
- end
542
- end
543
-
544
- def test_cas_with_read_only_should_send_activesupport_notification
545
- @cache.write("walrus", "yes")
546
-
547
- with_read_only(@cache) do
548
- assert_notifications(/cache_cas/, 1) do
549
- assert(@cache.cas("walrus") { |value| "no" })
550
- end
551
- end
552
-
553
- assert_equal "yes", @cache.fetch("walrus")
554
- end
555
-
556
- def test_cas_multi_with_read_only_should_send_activesupport_notification
557
- @cache.write("walrus", "yes")
558
- @cache.write("narwhal", "yes")
559
-
560
- with_read_only(@cache) do
561
- assert_notifications(/cache_cas/, 1) do
562
- assert(@cache.cas_multi("walrus", "narwhal") { |*values|
563
- { "walrus" => "no", "narwhal" => "no" }
564
- })
565
- end
566
- end
567
-
568
- assert_equal "yes", @cache.fetch("walrus")
569
- assert_equal "yes", @cache.fetch("narwhal")
570
- end
571
-
572
- def test_logger_defaults_to_rails_logger
573
- assert_equal Rails.logger, @cache.logger
574
- end
575
-
576
- private
577
-
578
- def assert_notifications(pattern, num)
579
- count = 0
580
- subscriber = ActiveSupport::Notifications.subscribe(pattern) do |name, start, finish, id, payload|
581
- count += 1
582
- end
583
-
584
- yield
585
-
586
- assert_equal num, count, "Expected #{num} notifications for #{pattern}, but got #{count}"
587
- ensure
588
- ActiveSupport::Notifications.unsubscribe(subscriber)
589
- end
590
-
591
- def with_read_only(client)
592
- previous, client.read_only = client.read_only, true
593
- yield
594
- ensure
595
- client.read_only = previous
596
- end
597
-
598
- def expect_not_found
599
- @cache.instance_variable_get(:@data).expects(:check_return_code).raises(Memcached::NotFound)
600
- end
601
- end