memcached_store 0.12.8 → 1.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.
@@ -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