Changesets can be listed by changeset number.
The Git repository is here.
- Revision:
- 373
- Log:
Initial import of Radiant 0.9.1, which is now packaged as a gem. This is an
import of the tagged 0.9.1 source checked out from GitHub, which isn't quite
the same as the gem distribution - but it doesn't seem to be available in an
archived form and the installed gem already has modifications, so this is
the closest I can get.
- Author:
- rool
- Date:
- Mon Mar 21 13:40:05 +0000 2011
- Size:
- 22287 Bytes
1 | require "#{File.dirname(__FILE__)}/spec_setup" |
2 | require 'rack/cache/context' |
3 | |
4 | describe 'Rack::Cache::Context' do |
5 | before(:each) { setup_cache_context } |
6 | after(:each) { teardown_cache_context } |
7 | |
8 | it 'passes on non-GET/HEAD requests' do |
9 | respond_with 200 |
10 | post '/' |
11 | |
12 | app.should.be.called |
13 | response.should.be.ok |
14 | cache.trace.should.include :pass |
15 | response.headers.should.not.include 'Age' |
16 | end |
17 | |
18 | %w[post put delete].each do |request_method| |
19 | it "invalidates on #{request_method} requests" do |
20 | respond_with 200 |
21 | request request_method, '/' |
22 | |
23 | app.should.be.called |
24 | response.should.be.ok |
25 | cache.trace.should.include :invalidate |
26 | cache.trace.should.include :pass |
27 | end |
28 | end |
29 | |
30 | it 'does not cache with Authorization request header and non public response' do |
31 | respond_with 200, 'ETag' => '"FOO"' |
32 | get '/', 'HTTP_AUTHORIZATION' => 'basic foobarbaz' |
33 | |
34 | app.should.be.called |
35 | response.should.be.ok |
36 | response.headers['Cache-Control'].should.equal 'private' |
37 | cache.trace.should.include :miss |
38 | cache.trace.should.not.include :store |
39 | response.headers.should.not.include 'Age' |
40 | end |
41 | |
42 | it 'does cache with Authorization request header and public response' do |
43 | respond_with 200, 'Cache-Control' => 'public', 'ETag' => '"FOO"' |
44 | get '/', 'HTTP_AUTHORIZATION' => 'basic foobarbaz' |
45 | |
46 | app.should.be.called |
47 | response.should.be.ok |
48 | cache.trace.should.include :miss |
49 | cache.trace.should.include :store |
50 | response.headers.should.include 'Age' |
51 | response.headers['Cache-Control'].should.equal 'public' |
52 | end |
53 | |
54 | it 'does not cache with Cookie header and non public response' do |
55 | respond_with 200, 'ETag' => '"FOO"' |
56 | get '/', 'HTTP_COOKIE' => 'foo=bar' |
57 | |
58 | app.should.be.called |
59 | response.should.be.ok |
60 | response.headers['Cache-Control'].should.equal 'private' |
61 | cache.trace.should.include :miss |
62 | cache.trace.should.not.include :store |
63 | response.headers.should.not.include 'Age' |
64 | end |
65 | |
66 | it 'does not cache requests with a Cookie header' do |
67 | respond_with 200 |
68 | get '/', 'HTTP_COOKIE' => 'foo=bar' |
69 | |
70 | response.should.be.ok |
71 | app.should.be.called |
72 | cache.trace.should.include :miss |
73 | cache.trace.should.not.include :store |
74 | response.headers.should.not.include 'Age' |
75 | response.headers['Cache-Control'].should.equal 'private' |
76 | end |
77 | |
78 | it 'responds with 304 when If-Modified-Since matches Last-Modified' do |
79 | timestamp = Time.now.httpdate |
80 | respond_with do |req,res| |
81 | res.status = 200 |
82 | res['Last-Modified'] = timestamp |
83 | res['Content-Type'] = 'text/plain' |
84 | res.body = ['Hello World'] |
85 | end |
86 | |
87 | get '/', |
88 | 'HTTP_IF_MODIFIED_SINCE' => timestamp |
89 | app.should.be.called |
90 | response.status.should.equal 304 |
91 | response.headers.should.not.include 'Content-Length' |
92 | response.headers.should.not.include 'Content-Type' |
93 | response.body.should.empty |
94 | cache.trace.should.include :miss |
95 | cache.trace.should.include :store |
96 | end |
97 | |
98 | it 'responds with 304 when If-None-Match matches ETag' do |
99 | respond_with do |req,res| |
100 | res.status = 200 |
101 | res['ETag'] = '12345' |
102 | res['Content-Type'] = 'text/plain' |
103 | res.body = ['Hello World'] |
104 | end |
105 | |
106 | get '/', |
107 | 'HTTP_IF_NONE_MATCH' => '12345' |
108 | app.should.be.called |
109 | response.status.should.equal 304 |
110 | response.headers.should.not.include 'Content-Length' |
111 | response.headers.should.not.include 'Content-Type' |
112 | response.headers.should.include 'ETag' |
113 | response.body.should.empty |
114 | cache.trace.should.include :miss |
115 | cache.trace.should.include :store |
116 | end |
117 | |
118 | it 'stores responses when no-cache request directive present' do |
119 | respond_with 200, 'Expires' => (Time.now + 5).httpdate |
120 | |
121 | get '/', 'HTTP_CACHE_CONTROL' => 'no-cache' |
122 | response.should.be.ok |
123 | cache.trace.should.include :store |
124 | response.headers.should.include 'Age' |
125 | end |
126 | |
127 | it 'reloads responses when cache hits but no-cache request directive present' do |
128 | count = 0 |
129 | respond_with 200, 'Cache-Control' => 'max-age=10000' do |req,res| |
130 | count+= 1 |
131 | res.body = (count == 1) ? ['Hello World'] : ['Goodbye World'] |
132 | end |
133 | |
134 | get '/' |
135 | response.should.be.ok |
136 | response.body.should.equal 'Hello World' |
137 | cache.trace.should.include :store |
138 | |
139 | get '/' |
140 | response.should.be.ok |
141 | response.body.should.equal 'Hello World' |
142 | cache.trace.should.include :fresh |
143 | |
144 | get '/', 'HTTP_CACHE_CONTROL' => 'no-cache' |
145 | response.should.be.ok |
146 | response.body.should.equal 'Goodbye World' |
147 | cache.trace.should.include :reload |
148 | cache.trace.should.include :store |
149 | end |
150 | |
151 | it 'does not reload responses when allow_reload is set false' do |
152 | count = 0 |
153 | respond_with 200, 'Cache-Control' => 'max-age=10000' do |req,res| |
154 | count+= 1 |
155 | res.body = (count == 1) ? ['Hello World'] : ['Goodbye World'] |
156 | end |
157 | |
158 | get '/' |
159 | response.should.be.ok |
160 | response.body.should.equal 'Hello World' |
161 | cache.trace.should.include :store |
162 | |
163 | get '/' |
164 | response.should.be.ok |
165 | response.body.should.equal 'Hello World' |
166 | cache.trace.should.include :fresh |
167 | |
168 | get '/', |
169 | 'rack-cache.allow_reload' => false, |
170 | 'HTTP_CACHE_CONTROL' => 'no-cache' |
171 | response.should.be.ok |
172 | response.body.should.equal 'Hello World' |
173 | cache.trace.should.not.include :reload |
174 | end |
175 | |
176 | it 'revalidates fresh cache entry when max-age request directive is exceeded' do |
177 | count = 0 |
178 | respond_with do |req,res| |
179 | count+= 1 |
180 | res['Cache-Control'] = 'max-age=10000' |
181 | res['ETag'] = count.to_s |
182 | res.body = (count == 1) ? ['Hello World'] : ['Goodbye World'] |
183 | end |
184 | |
185 | get '/' |
186 | response.should.be.ok |
187 | response.body.should.equal 'Hello World' |
188 | cache.trace.should.include :store |
189 | |
190 | get '/' |
191 | response.should.be.ok |
192 | response.body.should.equal 'Hello World' |
193 | cache.trace.should.include :fresh |
194 | |
195 | get '/', 'HTTP_CACHE_CONTROL' => 'max-age=0' |
196 | response.should.be.ok |
197 | response.body.should.equal 'Goodbye World' |
198 | cache.trace.should.include :stale |
199 | cache.trace.should.include :invalid |
200 | cache.trace.should.include :store |
201 | end |
202 | |
203 | it 'does not revalidate fresh cache entry when enable_revalidate option is set false' do |
204 | count = 0 |
205 | respond_with do |req,res| |
206 | count+= 1 |
207 | res['Cache-Control'] = 'max-age=10000' |
208 | res['ETag'] = count.to_s |
209 | res.body = (count == 1) ? ['Hello World'] : ['Goodbye World'] |
210 | end |
211 | |
212 | get '/' |
213 | response.should.be.ok |
214 | response.body.should.equal 'Hello World' |
215 | cache.trace.should.include :store |
216 | |
217 | get '/' |
218 | response.should.be.ok |
219 | response.body.should.equal 'Hello World' |
220 | cache.trace.should.include :fresh |
221 | |
222 | get '/', |
223 | 'rack-cache.allow_revalidate' => false, |
224 | 'HTTP_CACHE_CONTROL' => 'max-age=0' |
225 | response.should.be.ok |
226 | response.body.should.equal 'Hello World' |
227 | cache.trace.should.not.include :stale |
228 | cache.trace.should.not.include :invalid |
229 | cache.trace.should.include :fresh |
230 | end |
231 | it 'fetches response from backend when cache misses' do |
232 | respond_with 200, 'Expires' => (Time.now + 5).httpdate |
233 | get '/' |
234 | |
235 | response.should.be.ok |
236 | cache.trace.should.include :miss |
237 | response.headers.should.include 'Age' |
238 | end |
239 | |
240 | [(201..202),(204..206),(303..305),(400..403),(405..409),(411..417),(500..505)].each do |range| |
241 | range.each do |response_code| |
242 | it "does not cache #{response_code} responses" do |
243 | respond_with response_code, 'Expires' => (Time.now + 5).httpdate |
244 | get '/' |
245 | |
246 | cache.trace.should.not.include :store |
247 | response.status.should.equal response_code |
248 | response.headers.should.not.include 'Age' |
249 | end |
250 | end |
251 | end |
252 | |
253 | it "does not cache responses with explicit no-store directive" do |
254 | respond_with 200, |
255 | 'Expires' => (Time.now + 5).httpdate, |
256 | 'Cache-Control' => 'no-store' |
257 | get '/' |
258 | |
259 | response.should.be.ok |
260 | cache.trace.should.not.include :store |
261 | response.headers.should.not.include 'Age' |
262 | end |
263 | |
264 | it 'does not cache responses without freshness information or a validator' do |
265 | respond_with 200 |
266 | get '/' |
267 | |
268 | response.should.be.ok |
269 | cache.trace.should.not.include :store |
270 | end |
271 | |
272 | it "caches responses with explicit no-cache directive" do |
273 | respond_with 200, |
274 | 'Expires' => (Time.now + 5).httpdate, |
275 | 'Cache-Control' => 'no-cache' |
276 | get '/' |
277 | |
278 | response.should.be.ok |
279 | cache.trace.should.include :store |
280 | response.headers.should.include 'Age' |
281 | end |
282 | |
283 | it 'caches responses with an Expiration header' do |
284 | respond_with 200, 'Expires' => (Time.now + 5).httpdate |
285 | get '/' |
286 | |
287 | response.should.be.ok |
288 | response.body.should.equal 'Hello World' |
289 | response.headers.should.include 'Date' |
290 | response['Age'].should.not.be.nil |
291 | response['X-Content-Digest'].should.not.be.nil |
292 | cache.trace.should.include :miss |
293 | cache.trace.should.include :store |
294 | cache.metastore.to_hash.keys.length.should.equal 1 |
295 | end |
296 | |
297 | it 'caches responses with a max-age directive' do |
298 | respond_with 200, 'Cache-Control' => 'max-age=5' |
299 | get '/' |
300 | |
301 | response.should.be.ok |
302 | response.body.should.equal 'Hello World' |
303 | response.headers.should.include 'Date' |
304 | response['Age'].should.not.be.nil |
305 | response['X-Content-Digest'].should.not.be.nil |
306 | cache.trace.should.include :miss |
307 | cache.trace.should.include :store |
308 | cache.metastore.to_hash.keys.length.should.equal 1 |
309 | end |
310 | |
311 | it 'caches responses with a s-maxage directive' do |
312 | respond_with 200, 'Cache-Control' => 's-maxage=5' |
313 | get '/' |
314 | |
315 | response.should.be.ok |
316 | response.body.should.equal 'Hello World' |
317 | response.headers.should.include 'Date' |
318 | response['Age'].should.not.be.nil |
319 | response['X-Content-Digest'].should.not.be.nil |
320 | cache.trace.should.include :miss |
321 | cache.trace.should.include :store |
322 | cache.metastore.to_hash.keys.length.should.equal 1 |
323 | end |
324 | |
325 | it 'caches responses with a Last-Modified validator but no freshness information' do |
326 | respond_with 200, 'Last-Modified' => Time.now.httpdate |
327 | get '/' |
328 | |
329 | response.should.be.ok |
330 | response.body.should.equal 'Hello World' |
331 | cache.trace.should.include :miss |
332 | cache.trace.should.include :store |
333 | end |
334 | |
335 | it 'caches responses with an ETag validator but no freshness information' do |
336 | respond_with 200, 'ETag' => '"123456"' |
337 | get '/' |
338 | |
339 | response.should.be.ok |
340 | response.body.should.equal 'Hello World' |
341 | cache.trace.should.include :miss |
342 | cache.trace.should.include :store |
343 | end |
344 | |
345 | it 'hits cached response with Expires header' do |
346 | respond_with 200, |
347 | 'Date' => (Time.now - 5).httpdate, |
348 | 'Expires' => (Time.now + 5).httpdate |
349 | |
350 | get '/' |
351 | app.should.be.called |
352 | response.should.be.ok |
353 | response.headers.should.include 'Date' |
354 | cache.trace.should.include :miss |
355 | cache.trace.should.include :store |
356 | response.body.should.equal 'Hello World' |
357 | |
358 | get '/' |
359 | response.should.be.ok |
360 | app.should.not.be.called |
361 | response['Date'].should.equal responses.first['Date'] |
362 | response['Age'].to_i.should.satisfy { |age| age > 0 } |
363 | response['X-Content-Digest'].should.not.be.nil |
364 | cache.trace.should.include :fresh |
365 | cache.trace.should.not.include :store |
366 | response.body.should.equal 'Hello World' |
367 | end |
368 | |
369 | it 'hits cached response with max-age directive' do |
370 | respond_with 200, |
371 | 'Date' => (Time.now - 5).httpdate, |
372 | 'Cache-Control' => 'max-age=10' |
373 | |
374 | get '/' |
375 | app.should.be.called |
376 | response.should.be.ok |
377 | response.headers.should.include 'Date' |
378 | cache.trace.should.include :miss |
379 | cache.trace.should.include :store |
380 | response.body.should.equal 'Hello World' |
381 | |
382 | get '/' |
383 | response.should.be.ok |
384 | app.should.not.be.called |
385 | response['Date'].should.equal responses.first['Date'] |
386 | response['Age'].to_i.should.satisfy { |age| age > 0 } |
387 | response['X-Content-Digest'].should.not.be.nil |
388 | cache.trace.should.include :fresh |
389 | cache.trace.should.not.include :store |
390 | response.body.should.equal 'Hello World' |
391 | end |
392 | |
393 | it 'hits cached response with s-maxage directive' do |
394 | respond_with 200, |
395 | 'Date' => (Time.now - 5).httpdate, |
396 | 'Cache-Control' => 's-maxage=10, max-age=0' |
397 | |
398 | get '/' |
399 | app.should.be.called |
400 | response.should.be.ok |
401 | response.headers.should.include 'Date' |
402 | cache.trace.should.include :miss |
403 | cache.trace.should.include :store |
404 | response.body.should.equal 'Hello World' |
405 | |
406 | get '/' |
407 | response.should.be.ok |
408 | app.should.not.be.called |
409 | response['Date'].should.equal responses.first['Date'] |
410 | response['Age'].to_i.should.satisfy { |age| age > 0 } |
411 | response['X-Content-Digest'].should.not.be.nil |
412 | cache.trace.should.include :fresh |
413 | cache.trace.should.not.include :store |
414 | response.body.should.equal 'Hello World' |
415 | end |
416 | |
417 | it 'assigns default_ttl when response has no freshness information' do |
418 | respond_with 200 |
419 | |
420 | get '/', 'rack-cache.default_ttl' => 10 |
421 | app.should.be.called |
422 | response.should.be.ok |
423 | cache.trace.should.include :miss |
424 | cache.trace.should.include :store |
425 | response.body.should.equal 'Hello World' |
426 | response['Cache-Control'].should.include 's-maxage=10' |
427 | |
428 | get '/', 'rack-cache.default_ttl' => 10 |
429 | response.should.be.ok |
430 | app.should.not.be.called |
431 | cache.trace.should.include :fresh |
432 | cache.trace.should.not.include :store |
433 | response.body.should.equal 'Hello World' |
434 | end |
435 | |
436 | it 'does not assign default_ttl when response has must-revalidate directive' do |
437 | respond_with 200, |
438 | 'Cache-Control' => 'must-revalidate' |
439 | |
440 | get '/', 'rack-cache.default_ttl' => 10 |
441 | app.should.be.called |
442 | response.should.be.ok |
443 | cache.trace.should.include :miss |
444 | cache.trace.should.not.include :store |
445 | response['Cache-Control'].should.not.include 's-maxage' |
446 | response.body.should.equal 'Hello World' |
447 | end |
448 | |
449 | it 'fetches full response when cache stale and no validators present' do |
450 | respond_with 200, 'Expires' => (Time.now + 5).httpdate |
451 | |
452 | # build initial request |
453 | get '/' |
454 | app.should.be.called |
455 | response.should.be.ok |
456 | response.headers.should.include 'Date' |
457 | response.headers.should.include 'X-Content-Digest' |
458 | response.headers.should.include 'Age' |
459 | cache.trace.should.include :miss |
460 | cache.trace.should.include :store |
461 | response.body.should.equal 'Hello World' |
462 | |
463 | # go in and play around with the cached metadata directly ... |
464 | cache.metastore.to_hash.values.length.should.equal 1 |
465 | cache.metastore.to_hash.values.first.first[1]['Expires'] = Time.now.httpdate |
466 | |
467 | # build subsequent request; should be found but miss due to freshness |
468 | get '/' |
469 | app.should.be.called |
470 | response.should.be.ok |
471 | response['Age'].to_i.should.equal 0 |
472 | response.headers.should.include 'X-Content-Digest' |
473 | cache.trace.should.include :stale |
474 | cache.trace.should.not.include :fresh |
475 | cache.trace.should.not.include :miss |
476 | cache.trace.should.include :store |
477 | response.body.should.equal 'Hello World' |
478 | end |
479 | |
480 | it 'validates cached responses with Last-Modified and no freshness information' do |
481 | timestamp = Time.now.httpdate |
482 | respond_with do |req,res| |
483 | res['Last-Modified'] = timestamp |
484 | if req.env['HTTP_IF_MODIFIED_SINCE'] == timestamp |
485 | res.status = 304 |
486 | res.body = [] |
487 | end |
488 | end |
489 | |
490 | # build initial request |
491 | get '/' |
492 | app.should.be.called |
493 | response.should.be.ok |
494 | response.headers.should.include 'Last-Modified' |
495 | response.headers.should.include 'X-Content-Digest' |
496 | response.body.should.equal 'Hello World' |
497 | cache.trace.should.include :miss |
498 | cache.trace.should.include :store |
499 | cache.trace.should.not.include :stale |
500 | |
501 | # build subsequent request; should be found but miss due to freshness |
502 | get '/' |
503 | app.should.be.called |
504 | response.should.be.ok |
505 | response.headers.should.include 'Last-Modified' |
506 | response.headers.should.include 'X-Content-Digest' |
507 | response['Age'].to_i.should.equal 0 |
508 | response.body.should.equal 'Hello World' |
509 | cache.trace.should.include :stale |
510 | cache.trace.should.include :valid |
511 | cache.trace.should.include :store |
512 | cache.trace.should.not.include :miss |
513 | end |
514 | |
515 | it 'validates cached responses with ETag and no freshness information' do |
516 | timestamp = Time.now.httpdate |
517 | respond_with do |req,res| |
518 | res['ETAG'] = '"12345"' |
519 | if req.env['HTTP_IF_NONE_MATCH'] == res['Etag'] |
520 | res.status = 304 |
521 | res.body = [] |
522 | end |
523 | end |
524 | |
525 | # build initial request |
526 | get '/' |
527 | app.should.be.called |
528 | response.should.be.ok |
529 | response.headers.should.include 'ETag' |
530 | response.headers.should.include 'X-Content-Digest' |
531 | response.body.should.equal 'Hello World' |
532 | cache.trace.should.include :miss |
533 | cache.trace.should.include :store |
534 | |
535 | # build subsequent request; should be found but miss due to freshness |
536 | get '/' |
537 | app.should.be.called |
538 | response.should.be.ok |
539 | response.headers.should.include 'ETag' |
540 | response.headers.should.include 'X-Content-Digest' |
541 | response['Age'].to_i.should.equal 0 |
542 | response.body.should.equal 'Hello World' |
543 | cache.trace.should.include :stale |
544 | cache.trace.should.include :valid |
545 | cache.trace.should.include :store |
546 | cache.trace.should.not.include :miss |
547 | end |
548 | |
549 | it 'replaces cached responses when validation results in non-304 response' do |
550 | timestamp = Time.now.httpdate |
551 | count = 0 |
552 | respond_with do |req,res| |
553 | res['Last-Modified'] = timestamp |
554 | case (count+=1) |
555 | when 1 ; res.body = ['first response'] |
556 | when 2 ; res.body = ['second response'] |
557 | when 3 |
558 | res.body = [] |
559 | res.status = 304 |
560 | end |
561 | end |
562 | |
563 | # first request should fetch from backend and store in cache |
564 | get '/' |
565 | response.status.should.equal 200 |
566 | response.body.should.equal 'first response' |
567 | |
568 | # second request is validated, is invalid, and replaces cached entry |
569 | get '/' |
570 | response.status.should.equal 200 |
571 | response.body.should.equal 'second response' |
572 | |
573 | # third respone is validated, valid, and returns cached entry |
574 | get '/' |
575 | response.status.should.equal 200 |
576 | response.body.should.equal 'second response' |
577 | |
578 | count.should.equal 3 |
579 | end |
580 | |
581 | it 'passes HEAD requests through directly on pass' do |
582 | respond_with do |req,res| |
583 | res.status = 200 |
584 | res.body = [] |
585 | req.request_method.should.equal 'HEAD' |
586 | end |
587 | |
588 | head '/', 'HTTP_EXPECT' => 'something ...' |
589 | app.should.be.called |
590 | response.body.should.equal '' |
591 | end |
592 | |
593 | it 'uses cache to respond to HEAD requests when fresh' do |
594 | respond_with do |req,res| |
595 | res['Cache-Control'] = 'max-age=10' |
596 | res.body = ['Hello World'] |
597 | req.request_method.should.not.equal 'HEAD' |
598 | end |
599 | |
600 | get '/' |
601 | app.should.be.called |
602 | response.status.should.equal 200 |
603 | response.body.should.equal 'Hello World' |
604 | |
605 | head '/' |
606 | app.should.not.be.called |
607 | response.status.should.equal 200 |
608 | response.body.should.equal '' |
609 | response['Content-Length'].should.equal 'Hello World'.length.to_s |
610 | end |
611 | |
612 | it 'invalidates cached responses on POST' do |
613 | respond_with do |req,res| |
614 | if req.request_method == 'GET' |
615 | res.status = 200 |
616 | res['Cache-Control'] = 'public, max-age=500' |
617 | res.body = ['Hello World'] |
618 | elsif req.request_method == 'POST' |
619 | res.status = 303 |
620 | res['Location'] = '/' |
621 | res.headers.delete('Cache-Control') |
622 | res.body = [] |
623 | end |
624 | end |
625 | |
626 | # build initial request to enter into the cache |
627 | get '/' |
628 | app.should.be.called |
629 | response.should.be.ok |
630 | response.body.should.equal 'Hello World' |
631 | cache.trace.should.include :miss |
632 | cache.trace.should.include :store |
633 | |
634 | # make sure it is valid |
635 | get '/' |
636 | app.should.not.called |
637 | response.should.be.ok |
638 | response.body.should.equal 'Hello World' |
639 | cache.trace.should.include :fresh |
640 | |
641 | # now POST to same URL |
642 | post '/' |
643 | app.should.be.called |
644 | response.should.be.redirect |
645 | response['Location'].should.equal '/' |
646 | cache.trace.should.include :invalidate |
647 | cache.trace.should.include :pass |
648 | response.body.should.equal '' |
649 | |
650 | # now make sure it was actually invalidated |
651 | get '/' |
652 | app.should.be.called |
653 | response.should.be.ok |
654 | response.body.should.equal 'Hello World' |
655 | cache.trace.should.include :stale |
656 | cache.trace.should.include :invalid |
657 | cache.trace.should.include :store |
658 | end |
659 | |
660 | describe 'with responses that include a Vary header' do |
661 | before(:each) do |
662 | count = 0 |
663 | respond_with 200 do |req,res| |
664 | res['Vary'] = 'Accept User-Agent Foo' |
665 | res['Cache-Control'] = 'max-age=10' |
666 | res['X-Response-Count'] = (count+=1).to_s |
667 | res.body = [req.env['HTTP_USER_AGENT']] |
668 | end |
669 | end |
670 | |
671 | it 'serves from cache when headers match' do |
672 | get '/', |
673 | 'HTTP_ACCEPT' => 'text/html', |
674 | 'HTTP_USER_AGENT' => 'Bob/1.0' |
675 | response.should.be.ok |
676 | response.body.should.equal 'Bob/1.0' |
677 | cache.trace.should.include :miss |
678 | cache.trace.should.include :store |
679 | |
680 | get '/', |
681 | 'HTTP_ACCEPT' => 'text/html', |
682 | 'HTTP_USER_AGENT' => 'Bob/1.0' |
683 | response.should.be.ok |
684 | response.body.should.equal 'Bob/1.0' |
685 | cache.trace.should.include :fresh |
686 | cache.trace.should.not.include :store |
687 | response.headers.should.include 'X-Content-Digest' |
688 | end |
689 | |
690 | it 'stores multiple responses when headers differ' do |
691 | get '/', |
692 | 'HTTP_ACCEPT' => 'text/html', |
693 | 'HTTP_USER_AGENT' => 'Bob/1.0' |
694 | response.should.be.ok |
695 | response.body.should.equal 'Bob/1.0' |
696 | response['X-Response-Count'].should.equal '1' |
697 | |
698 | get '/', |
699 | 'HTTP_ACCEPT' => 'text/html', |
700 | 'HTTP_USER_AGENT' => 'Bob/2.0' |
701 | cache.trace.should.include :miss |
702 | cache.trace.should.include :store |
703 | response.body.should.equal 'Bob/2.0' |
704 | response['X-Response-Count'].should.equal '2' |
705 | |
706 | get '/', |
707 | 'HTTP_ACCEPT' => 'text/html', |
708 | 'HTTP_USER_AGENT' => 'Bob/1.0' |
709 | cache.trace.should.include :fresh |
710 | response.body.should.equal 'Bob/1.0' |
711 | response['X-Response-Count'].should.equal '1' |
712 | |
713 | get '/', |
714 | 'HTTP_ACCEPT' => 'text/html', |
715 | 'HTTP_USER_AGENT' => 'Bob/2.0' |
716 | cache.trace.should.include :fresh |
717 | response.body.should.equal 'Bob/2.0' |
718 | response['X-Response-Count'].should.equal '2' |
719 | |
720 | get '/', |
721 | 'HTTP_USER_AGENT' => 'Bob/2.0' |
722 | cache.trace.should.include :miss |
723 | response.body.should.equal 'Bob/2.0' |
724 | response['X-Response-Count'].should.equal '3' |
725 | end |
726 | end |
727 | end |