cached_resource 7.2.0 → 9.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,594 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe CachedResource do
4
-
5
- def read_from_cache(key)
6
- Thing.send(:cache_read, key)
7
- end
8
-
9
- before(:each) do
10
- class Thing < ActiveResource::Base
11
- self.site = "http://api.thing.com"
12
- cached_resource
13
- end
14
-
15
- class NotTheThing < ActiveResource::Base
16
- self.site = "http://api.notthething.com"
17
- cached_resource
18
- end
19
-
20
- @thing = {:thing => {:id => 1, :name => "Ada"}}
21
- @thing_collection = [{:id => 1, :name => "Ada"}, {:id => 2, :name => "Ada", :major => 'CS'}]
22
- @thing_collection2 = [{:id => 2, :name => "Ada", :major => 'CS'}]
23
- @other_thing = {:thing => {:id => 1, :name => "Ari"}}
24
- @thing2 = {:thing => {:id => 2, :name => "Joe"}}
25
- @other_thing2 = {:thing => {:id => 2, :name => "Jeb"}}
26
- @thing3 = {:thing => {:id => 3, :name => "Stu"}}
27
- @string_thing = {:thing => {:id => "fded", :name => "Lev"}}
28
- @other_string_thing = {:thing => {:id => "fded", :name => "Lon"}}
29
- @date_thing = {:thing => {:id => 4, :created_at => DateTime.new(2020)}}
30
- @thing_json = @thing.to_json
31
- @other_thing_json = @other_thing.to_json
32
- @string_thing_json = @string_thing.to_json
33
- @other_string_thing_json = @other_string_thing.to_json
34
- @date_thing_json = @date_thing.to_json
35
- @nil_thing = nil.to_json
36
- @empty_array_thing = [].to_json
37
- @not_the_thing = {:not_the_thing => {:id => 1, :name => "Not"}}
38
- @not_the_thing_json = @not_the_thing.to_json
39
- end
40
-
41
- after(:each) do
42
- Thing.cached_resource.cache.clear
43
- Object.send(:remove_const, :Thing)
44
- NotTheThing.cached_resource.cache.clear
45
- Object.send(:remove_const, :NotTheThing)
46
- end
47
-
48
- describe "when enabled" do
49
- before(:each) do
50
- # it's on by default, but lets call the method
51
- # to make sure it works
52
- Thing.cached_resource.cache.clear
53
- Thing.cached_resource.on!
54
- NotTheThing.cached_resource.cache.clear
55
- NotTheThing.cached_resource.on!
56
-
57
- ActiveResource::HttpMock.reset!
58
- ActiveResource::HttpMock.respond_to do |mock|
59
- mock.get "/things/1.json", {}, @thing_json
60
- mock.get "/things/1.json?foo=bar", {}, @thing_json
61
- mock.get "/things/fded.json", {}, @string_thing_json
62
- mock.get "/things.json?name=42", {}, @nil_thing, 404
63
- mock.get "/things.json?name=43", {}, @empty_array_thing
64
- mock.get "/things/4.json", {}, @date_thing_json
65
- mock.get "/not_the_things/1.json", {}, @not_the_thing_json
66
- end
67
- end
68
-
69
- it "should cache a response" do
70
- result = Thing.find(1)
71
- read_from_cache("thing/1").should == result
72
- end
73
-
74
- it "shouldn't cache nil response" do
75
- Thing.find(:all, :params => { :name => '42' })
76
- read_from_cache("thing/all/name/42").should == nil
77
- end
78
-
79
- it "shouldn't cache [] response" do
80
- Thing.find(:all, :params => { :name => '43' })
81
- read_from_cache("thing/all/name/43").should == nil
82
- end
83
-
84
- it "should cache a response for a string primary key" do
85
- result = Thing.find("fded")
86
- read_from_cache("thing/fded").should == result
87
- end
88
-
89
- it "should cache without whitespace in keys" do
90
- result = Thing.find(1, :from => 'path', :params => { :foo => 'bar' })
91
- read_from_cache('thing/1/{:from=>"path",:params=>{:foo=>"bar"}}').should == result
92
- end
93
-
94
- it "should empty the cache when clear_cache is called" do
95
- result = Thing.find(1)
96
- Thing.clear_cache
97
- read_from_cache("thing/1").should == nil
98
- end
99
-
100
- it "should not empty the cache of NotTheThing when clear_cache is called on the Thing" do
101
- result1 = Thing.find(1)
102
- result2 = NotTheThing.find(1)
103
- Thing.clear_cache
104
- NotTheThing.send(:cache_read, 'notthething/1').should == result2
105
- end
106
-
107
- it "should empty all the cache when clear_cache is called on the Thing with :all option set" do
108
- result1 = Thing.find(1)
109
- result2 = NotTheThing.find(1)
110
- Thing.clear_cache(all: true)
111
- NotTheThing.send(:cache_read, 'notthething/1').should == nil
112
- end
113
-
114
- it "should cache a response with the same persistence" do
115
- result1 = Thing.find(1)
116
- result1.persisted?.should be true
117
- result2 = Thing.find(1)
118
- result1.persisted?.should == result2.persisted?
119
- end
120
-
121
- it "should read a response when the request is made again" do
122
- # make a request
123
- Thing.find(1)
124
- # make the same request
125
- Thing.find(1)
126
- # only one request should have happened
127
- ActiveResource::HttpMock.requests.length.should == 1
128
- end
129
-
130
- it "should read a response when the request is made again for a string primary key" do
131
- # make a request
132
- Thing.find("fded")
133
- # make the same request
134
- Thing.find("fded")
135
- # only one request should have happened
136
- ActiveResource::HttpMock.requests.length.should == 1
137
- end
138
-
139
- it "should remake a request when reloaded" do
140
- # make a request
141
- Thing.find(1)
142
- # make the same request, but reload it
143
- Thing.find(1, :reload => true)
144
- # we should get two requests
145
- ActiveResource::HttpMock.requests.length.should == 2
146
- end
147
-
148
- it "should remake a request when reloaded for a string primary key" do
149
- # make a request
150
- Thing.find("fded")
151
- # make the same request, but reload it
152
- Thing.find("fded", :reload => true)
153
- # we should get two requests
154
- ActiveResource::HttpMock.requests.length.should == 2
155
- end
156
-
157
- it "should rewrite the cache when the request is reloaded" do
158
- # make a request
159
- Thing.find(1)
160
- # get the cached result of the request
161
- old_result = read_from_cache("thing/1")
162
-
163
- # change the response
164
- ActiveResource::HttpMock.reset!
165
- ActiveResource::HttpMock.respond_to do |mock|
166
- mock.get "/things/1.json", {}, @other_thing_json
167
- end
168
-
169
- Thing.find(1, :reload => true)
170
- new_result = read_from_cache("thing/1")
171
- # since active resources are equal if and only if they
172
- # are the same object or an instance of the same class,
173
- # not new?, and have the same id.
174
- new_result.name.should_not == old_result.name
175
- end
176
-
177
- it "should remake the request when the ttl expires" do
178
- # set cache time to live to 1 second
179
- Thing.cached_resource.ttl = 1
180
- # make a request
181
- Thing.find(1)
182
- # wait for the cache to expire
183
- sleep(1.5)
184
- # make the same request
185
- Thing.find(1)
186
- ActiveResource::HttpMock.requests.length.should == 2
187
- end
188
-
189
- it "should not return a frozen object on first request" do
190
- result1 = Thing.find(1)
191
- result1.should_not be_frozen
192
- end
193
-
194
- it "should not return frozen object on a subsequent request" do
195
- result1 = Thing.find(1)
196
- result2 = Thing.find(1)
197
- result2.should_not be_frozen
198
- end
199
-
200
- it "should not freeze first requested object on a subsequent request" do
201
- result1 = Thing.find(1)
202
- result2 = Thing.find(1)
203
- result1.should_not be_frozen
204
- end
205
-
206
- describe "when ActiveSupport.parse_json_times is enabled" do
207
- before(:all) do
208
- Time.zone = 'UTC'
209
- ActiveSupport.parse_json_times = true
210
- end
211
-
212
- it "should convert date times to objects when reading from cache" do
213
- Thing.find(4)
214
-
215
- read_from_cache("thing/4").created_at.should == @date_thing[:thing][:created_at]
216
- end
217
- end
218
-
219
- shared_examples "collection_return_type" do
220
- if ActiveResource::VERSION::MAJOR >= 4
221
- it "should return an ActiveResource::Collection" do
222
- cached = read_from_cache("thing/all")
223
- cached.should be_instance_of(ActiveResource::Collection)
224
- end
225
-
226
- it "should return a chainable instance of the collection_parser" do
227
- Thing.cached_resource.cache.clear
228
- class CustomCollection < ActiveResource::Collection; end
229
- Thing.collection_parser = CustomCollection
230
-
231
- ActiveResource::HttpMock.respond_to do |mock|
232
- mock.get "/things.json?name=ada", {}, @thing_collection.to_json
233
- mock.get "/things.json?major=CS&name=ada", {}, @thing_collection2.to_json
234
- end
235
-
236
- non_cached = Thing.where(name: 'ada')
237
- non_cached.original_params.should == { 'name' => 'ada' }
238
- non_cached.map(&:id).should == @thing_collection.map { |h| h[:id]}
239
-
240
- cached = read_from_cache('thing/all/{:params=>{:name=>"ada"}}')
241
- cached.should be_instance_of(CustomCollection)
242
- cached.original_params.should == { 'name' => 'ada' }
243
- cached.resource_class.should == Thing
244
- cached.map(&:id).should == @thing_collection.map { |h| h[:id]}
245
-
246
- if ActiveResource::VERSION::MAJOR < 5
247
- non_cached = cached.resource_class.where(cached.original_params.merge(major: 'CS'))
248
- else
249
- non_cached = cached.where(major: 'CS')
250
- end
251
-
252
- non_cached.original_params.should == { 'name' => 'ada', 'major' => 'CS' }
253
- non_cached.resource_class.should == Thing
254
- non_cached.map(&:id).should == @thing_collection2.map { |h| h[:id]}
255
- cached = read_from_cache('thing/all/{:params=>{"name"=>"ada",:major=>"cs"}}')
256
- cached.original_params.should == { 'name' => 'ada', 'major' => 'CS' }
257
- cached.resource_class.should == Thing
258
- cached.map(&:id).should == @thing_collection2.map { |h| h[:id]}
259
- end
260
- else
261
- it "should return an Array" do
262
- cached = read_from_cache("thing/all")
263
- cached.should be_instance_of(Array)
264
- end
265
- end
266
- end
267
-
268
- shared_examples "collection_freezing" do
269
- it "should not return a frozen collection on first request" do
270
- Thing.cached_resource.cache.clear
271
- collection1 = Thing.all
272
- collection1.should_not be_frozen
273
- end
274
-
275
- it "should not return a frozen collection on a subsequent request" do
276
- Thing.cached_resource.cache.clear
277
- collection1 = Thing.all
278
- collection2 = Thing.all
279
- collection2.should_not be_frozen
280
- end
281
-
282
- it "should not freeze first requested collection on a subsequent request" do
283
- Thing.cached_resource.cache.clear
284
- result1 = Thing.all
285
- result2 = Thing.all
286
- result1.should_not be_frozen
287
- end
288
-
289
- it "should not return frozen members on first request" do
290
- Thing.cached_resource.cache.clear
291
- collection1 = Thing.all
292
- collection1.first.should_not be_frozen
293
- end
294
-
295
- it "should not return frozen members on a subsequent request" do
296
- Thing.cached_resource.cache.clear
297
- collection1 = Thing.all
298
- collection2 = Thing.all
299
- collection2.first.should_not be_frozen
300
- end
301
-
302
- it "should not freeze members on a subsequent request" do
303
- Thing.cached_resource.cache.clear
304
- collection1 = Thing.all
305
- member1 = Thing.find(1)
306
- collection1.first.should_not be_frozen
307
- end
308
-
309
- end
310
-
311
- shared_examples "collection_cache_clearing" do
312
- it "should empty the cache when clear_cache is called" do
313
- Thing.clear_cache
314
- read_from_cache("thing/all").should == nil
315
- read_from_cache("thing/1").should == nil
316
- end
317
-
318
- end
319
-
320
- describe "when collection synchronize is enabled" do
321
- before(:each) do
322
- Thing.cached_resource.cache.clear
323
- Thing.cached_resource.collection_synchronize = true
324
-
325
- ActiveResource::HttpMock.reset!
326
- ActiveResource::HttpMock.respond_to do |mock|
327
- mock.get "/things/1.json", {}, @thing_json
328
- mock.get "/things/fded.json", {}, @string_thing_json
329
- mock.get "/things.json", {}, [@thing[:thing], @string_thing[:thing]].to_json(:root => :thing)
330
- end
331
-
332
- # make a request for all things
333
- Thing.all
334
- end
335
-
336
- include_examples "collection_return_type"
337
-
338
- include_examples "collection_freezing"
339
-
340
- it "should write cache entries for its members" do
341
- result = Thing.find(1)
342
- string_result = Thing.find("fded")
343
- # only the all request should have been made
344
- ActiveResource::HttpMock.requests.length.should == 1
345
- # the result should be cached with the appropriate key
346
- read_from_cache("thing/1").should == result
347
- read_from_cache("thing/fded").should == string_result
348
- end
349
-
350
- include_examples "collection_cache_clearing"
351
-
352
- it "should rewrite cache entries for its members when reloaded" do
353
- # get the soon to be stale result so that we have a cache entry
354
- old_result = Thing.find(1)
355
- old_string_result = Thing.find("fded")
356
- # change the server
357
- ActiveResource::HttpMock.respond_to do |mock|
358
- mock.get "/things/1.json", {}, @other_thing_json
359
- mock.get "/things/fded.json", {}, @other_string_thing_json
360
- mock.get "/things.json", {}, [@other_thing[:thing], @other_string_thing[:thing]].to_json(:root => :thing)
361
- end
362
- # reload the collection
363
- Thing.all(:reload => true)
364
- # get the updated result, read from the cache
365
- result = Thing.find(1)
366
- read_from_cache("thing/all")[0].should == result
367
- read_from_cache("thing/all")[0].name.should == result.name
368
- string_result = Thing.find("fded")
369
- read_from_cache("thing/all")[1].should == string_result
370
- read_from_cache("thing/all")[1].name.should == string_result.name
371
- end
372
-
373
- it "should update the collection when an individual request is reloaded" do
374
- # change the server
375
- ActiveResource::HttpMock.respond_to do |mock|
376
- mock.get "/things/1.json", {}, @other_thing_json
377
- mock.get "/things/fded.json", {}, @other_string_thing_json
378
- mock.get "/things.json", {}, [@other_thing[:thing], @other_string_thing[:thing]].to_json(:root => :thing)
379
- end
380
-
381
- # reload the individual
382
- result = Thing.find(1, :reload => true)
383
- read_from_cache("thing/all")[0].should == result
384
- read_from_cache("thing/all")[0].name.should == result.name
385
- string_result = Thing.find("fded", :reload => true)
386
- read_from_cache("thing/all")[1].should == string_result
387
- read_from_cache("thing/all")[1].name.should == string_result.name
388
- end
389
-
390
- it "should update both the collection and the member cache entries when a subset of the collection is retrieved" do
391
- # create cache entries for 1 and all
392
- old_individual = Thing.find(1)
393
- old_collection = Thing.all
394
-
395
- # change the server
396
- ActiveResource::HttpMock.respond_to do |mock|
397
- mock.get "/things.json?name=Ari", {}, [@other_thing[:thing]].to_json(:root => :thing)
398
- mock.get "/things.json?name=Lon", {}, [@other_string_thing[:thing]].to_json(:root => :thing)
399
- end
400
-
401
- # make a request for a subset of the "mother" collection
402
- result = Thing.find(:all, :params => {:name => "Ari"})
403
- # the collection should be updated to reflect the server change
404
- read_from_cache("thing/all")[0].should == result[0]
405
- read_from_cache("thing/all")[0].name.should == result[0].name
406
- # the individual should be updated to reflect the server change
407
- read_from_cache("thing/1").should == result[0]
408
- read_from_cache("thing/1").name.should == result[0].name
409
-
410
- # make a request for a subset of the "mother" collection
411
- result = Thing.find(:all, :params => {:name => "Lon"})
412
- # the collection should be updated to reflect the server change
413
- read_from_cache("thing/all")[1].should == result[0]
414
- read_from_cache("thing/all")[1].name.should == result[0].name
415
- # the individual should be updated to reflect the server change
416
- read_from_cache("thing/fded").should == result[0]
417
- read_from_cache("thing/fded").name.should == result[0].name
418
- end
419
-
420
- it "should maintain the order of the collection when updating it" do
421
- # change the server to return a longer collection
422
- ActiveResource::HttpMock.respond_to do |mock|
423
- mock.get "/things.json", {}, [@thing[:thing], @thing3[:thing], @thing2[:thing], @string_thing[:thing]].to_json(:root => :thing)
424
- end
425
-
426
- # create cache entry for the collection (we reload because in before block we make an all request)
427
- old_collection = Thing.all(:reload => true)
428
-
429
- # change the server's response for the thing with id 2
430
- ActiveResource::HttpMock.respond_to do |mock|
431
- mock.get "/things/2.json", {}, @other_thing2.to_json(:root => :thing)
432
- mock.get "/things/fded.json", {}, @other_string_thing.to_json(:root => :thing)
433
- end
434
-
435
- # get thing 2, thereby updating the collection
436
- result = Thing.find(2, :reload => true)
437
- # get the updated collection from the cache
438
- updated_collection = Thing.all
439
- # name should have changed to "Jeb"
440
- updated_collection[2].name.should == result.name
441
- # the updated collection should have the elements in the same order
442
- old_collection.each_with_index do |thing, i|
443
- updated_collection[i].id.should == thing.id
444
- end
445
-
446
- # get string thing, thereby updating the collection
447
- string_result = Thing.find("fded", :reload => true)
448
- # get the updated collection from the cache
449
- updated_collection = Thing.all
450
- # name should have changed to "Lon"
451
- updated_collection[3].name.should == string_result.name
452
- # the updated collection should have the elements in the same order
453
- old_collection.each_with_index do |thing, i|
454
- updated_collection[i].id.should == thing.id
455
- end
456
- end
457
- end
458
-
459
- describe "when collection synchronize is disabled" do
460
- before(:each) do
461
- Thing.cached_resource.cache.clear
462
- Thing.cached_resource.collection_synchronize = false
463
-
464
- ActiveResource::HttpMock.reset!
465
- ActiveResource::HttpMock.respond_to do |mock|
466
- mock.get "/things/1.json", {}, @thing_json
467
- mock.get "/things/fded.json", {}, @string_thing_json
468
- mock.get "/things.json", {}, [@thing[:thing], @string_thing[:thing]].to_json(:root => :thing)
469
- end
470
-
471
- # make a request for all things
472
- Thing.all
473
- end
474
-
475
- include_examples "collection_return_type"
476
-
477
- include_examples "collection_freezing"
478
-
479
- it "should not write cache entries for its members" do
480
- result = Thing.find(1)
481
- result = Thing.find("fded")
482
- # both the all in the before each and this request should have been made
483
- ActiveResource::HttpMock.requests.length.should == 3
484
- end
485
-
486
- include_examples "collection_cache_clearing"
487
-
488
- it "should not update the collection when an individual request is reloaded" do
489
- # change the server
490
- ActiveResource::HttpMock.respond_to do |mock|
491
- mock.get "/things/1.json", {}, @other_thing_json
492
- mock.get "/things/fded.json", {}, @other_string_thing_json
493
- mock.get "/things.json", {}, [@other_thing[:thing], @other_string_thing[:thing]].to_json(:root => :thing)
494
- end
495
-
496
- # reload the individual
497
- result = Thing.find(1, :reload => true)
498
- # the ids are the same, but the names should be different
499
- read_from_cache("thing/all")[0].name.should_not == result.name
500
-
501
- # reload the individual
502
- string_result = Thing.find("fded", :reload => true)
503
- # the ids are the same, but the names should be different
504
- read_from_cache("thing/all")[1].name.should_not == string_result.name
505
- end
506
- end
507
-
508
- describe "when ttl randomization is enabled" do
509
- before(:each) do
510
- @ttl = 1
511
- Thing.cached_resource.ttl = @ttl
512
- Thing.cached_resource.ttl_randomization = true
513
- Thing.cached_resource.send(:sample_range, 1..2, @ttl)
514
- # next ttl 1.72032449344216
515
- end
516
-
517
- it "should generate a random ttl" do
518
- Thing.cached_resource.cache.should_receive(:write)
519
- Thing.cached_resource.cache.stub(:write) do |key, value, options|
520
- # we know the ttl should not be the same as the set ttl
521
- options[:expires_in].should_not == @ttl
522
- end
523
-
524
- Thing.find(1)
525
- end
526
-
527
- end
528
- end
529
-
530
- describe "when disabled" do
531
- before(:each) do
532
- Thing.cached_resource.cache.clear
533
- Thing.cached_resource.off!
534
-
535
- ActiveResource::HttpMock.reset!
536
- ActiveResource::HttpMock.respond_to do |mock|
537
- mock.get "/things/1.json", {}, @thing_json
538
- mock.get "/things/fded.json", {}, @string_thing_json
539
- end
540
- end
541
-
542
- it "should not cache a response" do
543
- result = Thing.find(1)
544
- read_from_cache("thing/1").should be_nil
545
- end
546
-
547
- it "should always remake the request" do
548
- Thing.find(1)
549
- ActiveResource::HttpMock.requests.length.should == 1
550
- Thing.find(1)
551
- ActiveResource::HttpMock.requests.length.should == 2
552
- end
553
-
554
- it "should always remake the request for a string primary key" do
555
- Thing.find("fded")
556
- ActiveResource::HttpMock.requests.length.should == 1
557
- Thing.find("fded")
558
- ActiveResource::HttpMock.requests.length.should == 2
559
- end
560
- end
561
-
562
- describe "when cache_collections is disabled" do
563
- before(:each) do
564
- Thing.cached_resource.cache.clear
565
- Thing.cached_resource.cache_collections = false
566
-
567
- ActiveResource::HttpMock.reset!
568
- ActiveResource::HttpMock.respond_to do |mock|
569
- mock.get "/things.json", {}, [@thing[:thing],@string_thing[:thing]].to_json(:root => :thing)
570
- mock.get "/things/1.json", {}, @thing_json
571
- mock.get "/things/fded.json", {}, @string_thing_json
572
- end
573
- end
574
-
575
- it "should cache a response" do
576
- result = Thing.find(1)
577
- read_from_cache("thing/1").should == result
578
- end
579
-
580
- it "should not remake a single request" do
581
- result = Thing.find(1)
582
- ActiveResource::HttpMock.requests.length.should == 1
583
- result = Thing.find(1)
584
- ActiveResource::HttpMock.requests.length.should == 1
585
- end
586
-
587
- it "should always remake the request for collections" do
588
- Thing.all
589
- ActiveResource::HttpMock.requests.length.should == 1
590
- Thing.all
591
- ActiveResource::HttpMock.requests.length.should == 2
592
- end
593
- end
594
- end