rtomayko-rack-cache 0.3.0 → 0.3.9

Sign up to get free protection for your applications and to get access to all the features.
data/test/cache_test.rb CHANGED
@@ -12,22 +12,25 @@ describe 'Rack::Cache::new' do
12
12
  Rack::Cache.new(@app).
13
13
  should.respond_to :call
14
14
  end
15
+
15
16
  it 'takes an options Hash' do
16
17
  lambda { Rack::Cache.new(@app, {}) }.
17
18
  should.not.raise(ArgumentError)
18
19
  end
20
+
19
21
  it 'sets options provided in the options Hash' do
20
22
  object = Rack::Cache.new(@app, :foo => 'bar', 'foo.bar' => 'bling')
21
23
  object.options['foo.bar'].should.equal 'bling'
22
24
  object.options['rack-cache.foo'].should.equal 'bar'
23
25
  end
26
+
24
27
  it 'takes a block; executes it during initialization' do
25
28
  state, block_scope = 'not invoked', nil
26
29
  object =
27
30
  Rack::Cache.new @app do
28
31
  block_scope = self
29
32
  state = 'invoked'
30
- should.respond_to :on
33
+ should.respond_to :set
31
34
  end
32
35
  state.should.equal 'invoked'
33
36
  object.should.be block_scope
@@ -0,0 +1,139 @@
1
+ require "#{File.dirname(__FILE__)}/spec_setup"
2
+ require 'rack/cache/cachecontrol'
3
+
4
+ describe 'Rack::Cache::CacheControl' do
5
+ it 'takes no args and initializes with an empty set of values' do
6
+ cache_control = Rack::Cache::CacheControl.new
7
+ cache_control.should.be.empty
8
+ cache_control.to_s.should.equal ''
9
+ end
10
+
11
+ it 'takes a String and parses it into a Hash when created' do
12
+ cache_control = Rack::Cache::CacheControl.new('max-age=600, foo')
13
+ cache_control['max-age'].should.equal '600'
14
+ cache_control['foo'].should.be true
15
+ end
16
+
17
+ it 'takes a String with a single name=value pair' do
18
+ cache_control = Rack::Cache::CacheControl.new('max-age=600')
19
+ cache_control['max-age'].should.equal '600'
20
+ end
21
+
22
+ it 'takes a String with multiple name=value pairs' do
23
+ cache_control = Rack::Cache::CacheControl.new('max-age=600, max-stale=300, min-fresh=570')
24
+ cache_control['max-age'].should.equal '600'
25
+ cache_control['max-stale'].should.equal '300'
26
+ cache_control['min-fresh'].should.equal '570'
27
+ end
28
+
29
+ it 'takes a String with a single flag value' do
30
+ cache_control = Rack::Cache::CacheControl.new('no-cache')
31
+ cache_control.should.include 'no-cache'
32
+ cache_control['no-cache'].should.be true
33
+ end
34
+
35
+ it 'takes a String with a bunch of all kinds of stuff' do
36
+ cache_control =
37
+ Rack::Cache::CacheControl.new('max-age=600,must-revalidate,min-fresh=3000,foo=bar,baz')
38
+ cache_control['max-age'].should.equal '600'
39
+ cache_control['must-revalidate'].should.be true
40
+ cache_control['min-fresh'].should.equal '3000'
41
+ cache_control['foo'].should.equal 'bar'
42
+ cache_control['baz'].should.be true
43
+ end
44
+
45
+ it 'strips leading and trailing spaces from header value' do
46
+ cache_control = Rack::Cache::CacheControl.new(' public, max-age = 600 ')
47
+ cache_control.should.include 'public'
48
+ cache_control.should.include 'max-age'
49
+ cache_control['max-age'].should.equal '600'
50
+ end
51
+
52
+ it 'removes all directives with #clear' do
53
+ cache_control = Rack::Cache::CacheControl.new('max-age=600, must-revalidate')
54
+ cache_control.clear
55
+ cache_control.should.be.empty
56
+ end
57
+
58
+ it 'converts self into header String with #to_s' do
59
+ cache_control = Rack::Cache::CacheControl.new
60
+ cache_control['public'] = true
61
+ cache_control['max-age'] = '600'
62
+ cache_control.to_s.split(', ').sort.should.equal ['max-age=600', 'public']
63
+ end
64
+
65
+ it 'sorts alphabetically with boolean directives before value directives' do
66
+ cache_control = Rack::Cache::CacheControl.new('foo=bar, z, x, y, bling=baz, zoom=zib, b, a')
67
+ cache_control.to_s.should.equal 'a, b, x, y, z, bling=baz, foo=bar, zoom=zib'
68
+ end
69
+
70
+ it 'responds to #max_age with an integer when max-age directive present' do
71
+ cache_control = Rack::Cache::CacheControl.new('public, max-age=600')
72
+ cache_control.max_age.should.equal 600
73
+ end
74
+
75
+ it 'responds to #max_age with nil when no max-age directive present' do
76
+ cache_control = Rack::Cache::CacheControl.new('public')
77
+ cache_control.max_age.should.be nil
78
+ end
79
+
80
+ it 'responds to #shared_max_age with an integer when s-maxage directive present' do
81
+ cache_control = Rack::Cache::CacheControl.new('public, s-maxage=600')
82
+ cache_control.shared_max_age.should.equal 600
83
+ end
84
+
85
+ it 'responds to #shared_max_age with nil when no s-maxage directive present' do
86
+ cache_control = Rack::Cache::CacheControl.new('public')
87
+ cache_control.shared_max_age.should.be nil
88
+ end
89
+
90
+ it 'responds to #public? truthfully when public directive present' do
91
+ cache_control = Rack::Cache::CacheControl.new('public')
92
+ cache_control.should.be.public
93
+ end
94
+
95
+ it 'responds to #public? non-truthfully when no public directive present' do
96
+ cache_control = Rack::Cache::CacheControl.new('private')
97
+ cache_control.should.not.be.public
98
+ end
99
+
100
+ it 'responds to #private? truthfully when private directive present' do
101
+ cache_control = Rack::Cache::CacheControl.new('private')
102
+ cache_control.should.be.private
103
+ end
104
+
105
+ it 'responds to #private? non-truthfully when no private directive present' do
106
+ cache_control = Rack::Cache::CacheControl.new('public')
107
+ cache_control.should.not.be.private
108
+ end
109
+
110
+ it 'responds to #no_cache? truthfully when no-cache directive present' do
111
+ cache_control = Rack::Cache::CacheControl.new('no-cache')
112
+ cache_control.should.be.no_cache
113
+ end
114
+
115
+ it 'responds to #no_cache? non-truthfully when no no-cache directive present' do
116
+ cache_control = Rack::Cache::CacheControl.new('max-age=600')
117
+ cache_control.should.not.be.no_cache
118
+ end
119
+
120
+ it 'responds to #must_revalidate? truthfully when must-revalidate directive present' do
121
+ cache_control = Rack::Cache::CacheControl.new('must-revalidate')
122
+ cache_control.should.be.must_revalidate
123
+ end
124
+
125
+ it 'responds to #must_revalidate? non-truthfully when no must-revalidate directive present' do
126
+ cache_control = Rack::Cache::CacheControl.new('max-age=600')
127
+ cache_control.should.not.be.no_cache
128
+ end
129
+
130
+ it 'responds to #proxy_revalidate? truthfully when proxy-revalidate directive present' do
131
+ cache_control = Rack::Cache::CacheControl.new('proxy-revalidate')
132
+ cache_control.should.be.proxy_revalidate
133
+ end
134
+
135
+ it 'responds to #proxy_revalidate? non-truthfully when no proxy-revalidate directive present' do
136
+ cache_control = Rack::Cache::CacheControl.new('max-age=600')
137
+ cache_control.should.not.be.no_cache
138
+ end
139
+ end
data/test/context_test.rb CHANGED
@@ -11,21 +11,31 @@ describe 'Rack::Cache::Context' do
11
11
 
12
12
  app.should.be.called
13
13
  response.should.be.ok
14
- cache.should.a.performed :pass
14
+ cache.trace.should.include :pass
15
15
  response.headers.should.not.include 'Age'
16
16
  end
17
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
+
18
30
  it 'does not cache with Authorization request header and non public response' do
19
- respond_with 200, 'Etag' => '"FOO"'
31
+ respond_with 200, 'ETag' => '"FOO"'
20
32
  get '/', 'HTTP_AUTHORIZATION' => 'basic foobarbaz'
21
33
 
22
34
  app.should.be.called
23
35
  response.should.be.ok
24
36
  response.headers['Cache-Control'].should.equal 'private'
25
- cache.should.a.performed :miss
26
- cache.should.a.performed :fetch
27
- cache.should.a.not.performed :store
28
- cache.should.a.performed :deliver
37
+ cache.trace.should.include :miss
38
+ cache.trace.should.not.include :store
29
39
  response.headers.should.not.include 'Age'
30
40
  end
31
41
 
@@ -35,24 +45,21 @@ describe 'Rack::Cache::Context' do
35
45
 
36
46
  app.should.be.called
37
47
  response.should.be.ok
38
- cache.should.a.performed :miss
39
- cache.should.a.performed :fetch
40
- cache.should.a.performed :store
48
+ cache.trace.should.include :miss
49
+ cache.trace.should.include :store
41
50
  response.headers.should.include 'Age'
42
51
  response.headers['Cache-Control'].should.equal 'public'
43
52
  end
44
53
 
45
54
  it 'does not cache with Cookie header and non public response' do
46
- respond_with 200, 'Etag' => '"FOO"'
55
+ respond_with 200, 'ETag' => '"FOO"'
47
56
  get '/', 'HTTP_COOKIE' => 'foo=bar'
48
57
 
49
58
  app.should.be.called
50
59
  response.should.be.ok
51
60
  response.headers['Cache-Control'].should.equal 'private'
52
- cache.should.a.performed :miss
53
- cache.should.a.performed :fetch
54
- cache.should.a.not.performed :store
55
- cache.should.a.performed :deliver
61
+ cache.trace.should.include :miss
62
+ cache.trace.should.not.include :store
56
63
  response.headers.should.not.include 'Age'
57
64
  end
58
65
 
@@ -62,9 +69,8 @@ describe 'Rack::Cache::Context' do
62
69
 
63
70
  response.should.be.ok
64
71
  app.should.be.called
65
- cache.should.a.performed :miss
66
- cache.should.a.performed :fetch
67
- cache.should.a.performed :deliver
72
+ cache.trace.should.include :miss
73
+ cache.trace.should.not.include :store
68
74
  response.headers.should.not.include 'Age'
69
75
  response.headers['Cache-Control'].should.equal 'private'
70
76
  end
@@ -85,14 +91,14 @@ describe 'Rack::Cache::Context' do
85
91
  response.headers.should.not.include 'Content-Length'
86
92
  response.headers.should.not.include 'Content-Type'
87
93
  response.body.should.empty
88
- cache.should.a.performed :miss
89
- cache.should.a.performed :store
94
+ cache.trace.should.include :miss
95
+ cache.trace.should.include :store
90
96
  end
91
97
 
92
98
  it 'responds with 304 when If-None-Match matches ETag' do
93
99
  respond_with do |req,res|
94
100
  res.status = 200
95
- res['Etag'] = '12345'
101
+ res['ETag'] = '12345'
96
102
  res['Content-Type'] = 'text/plain'
97
103
  res.body = ['Hello World']
98
104
  end
@@ -103,28 +109,78 @@ describe 'Rack::Cache::Context' do
103
109
  response.status.should.equal 304
104
110
  response.headers.should.not.include 'Content-Length'
105
111
  response.headers.should.not.include 'Content-Type'
106
- response.headers.should.include 'Etag'
112
+ response.headers.should.include 'ETag'
107
113
  response.body.should.empty
108
- cache.should.a.performed :miss
109
- cache.should.a.performed :store
114
+ cache.trace.should.include :miss
115
+ cache.trace.should.include :store
110
116
  end
111
117
 
112
- it 'caches requests when Cache-Control request header set to no-cache' do
118
+ it 'stores responses when no-cache request directive present' do
113
119
  respond_with 200, 'Expires' => (Time.now + 5).httpdate
114
- get '/', 'HTTP_CACHE_CONTROL' => 'no-cache'
115
120
 
121
+ get '/', 'HTTP_CACHE_CONTROL' => 'no-cache'
116
122
  response.should.be.ok
117
- cache.should.a.performed :store
123
+ cache.trace.should.include :store
118
124
  response.headers.should.include 'Age'
119
125
  end
120
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 'revalidates fresh cache entry when max-age request directive is exceeded' do
152
+ count = 0
153
+ respond_with do |req,res|
154
+ count+= 1
155
+ res['Cache-Control'] = 'max-age=10000'
156
+ res['ETag'] = count.to_s
157
+ res.body = (count == 1) ? ['Hello World'] : ['Goodbye World']
158
+ end
159
+
160
+ get '/'
161
+ response.should.be.ok
162
+ response.body.should.equal 'Hello World'
163
+ cache.trace.should.include :store
164
+
165
+ get '/'
166
+ response.should.be.ok
167
+ response.body.should.equal 'Hello World'
168
+ cache.trace.should.include :fresh
169
+
170
+ get '/', 'HTTP_CACHE_CONTROL' => 'max-age=0'
171
+ response.should.be.ok
172
+ response.body.should.equal 'Goodbye World'
173
+ cache.trace.should.include :stale
174
+ cache.trace.should.include :invalid
175
+ cache.trace.should.include :store
176
+ end
177
+
121
178
  it 'fetches response from backend when cache misses' do
122
179
  respond_with 200, 'Expires' => (Time.now + 5).httpdate
123
180
  get '/'
124
181
 
125
182
  response.should.be.ok
126
- cache.should.a.performed :miss
127
- cache.should.a.performed :fetch
183
+ cache.trace.should.include :miss
128
184
  response.headers.should.include 'Age'
129
185
  end
130
186
 
@@ -134,7 +190,7 @@ describe 'Rack::Cache::Context' do
134
190
  respond_with response_code, 'Expires' => (Time.now + 5).httpdate
135
191
  get '/'
136
192
 
137
- cache.should.a.not.performed :store
193
+ cache.trace.should.not.include :store
138
194
  response.status.should.equal response_code
139
195
  response.headers.should.not.include 'Age'
140
196
  end
@@ -148,7 +204,7 @@ describe 'Rack::Cache::Context' do
148
204
  get '/'
149
205
 
150
206
  response.should.be.ok
151
- cache.should.a.not.performed :store
207
+ cache.trace.should.not.include :store
152
208
  response.headers.should.not.include 'Age'
153
209
  end
154
210
 
@@ -157,7 +213,7 @@ describe 'Rack::Cache::Context' do
157
213
  get '/'
158
214
 
159
215
  response.should.be.ok
160
- cache.should.a.not.performed :store
216
+ cache.trace.should.not.include :store
161
217
  end
162
218
 
163
219
  it "caches responses with explicit no-cache directive" do
@@ -167,7 +223,7 @@ describe 'Rack::Cache::Context' do
167
223
  get '/'
168
224
 
169
225
  response.should.be.ok
170
- cache.should.a.performed :store
226
+ cache.trace.should.include :store
171
227
  response.headers.should.include 'Age'
172
228
  end
173
229
 
@@ -180,8 +236,8 @@ describe 'Rack::Cache::Context' do
180
236
  response.headers.should.include 'Date'
181
237
  response['Age'].should.not.be.nil
182
238
  response['X-Content-Digest'].should.not.be.nil
183
- cache.should.a.performed :miss
184
- cache.should.a.performed :store
239
+ cache.trace.should.include :miss
240
+ cache.trace.should.include :store
185
241
  cache.metastore.to_hash.keys.length.should.equal 1
186
242
  end
187
243
 
@@ -194,8 +250,8 @@ describe 'Rack::Cache::Context' do
194
250
  response.headers.should.include 'Date'
195
251
  response['Age'].should.not.be.nil
196
252
  response['X-Content-Digest'].should.not.be.nil
197
- cache.should.a.performed :miss
198
- cache.should.a.performed :store
253
+ cache.trace.should.include :miss
254
+ cache.trace.should.include :store
199
255
  cache.metastore.to_hash.keys.length.should.equal 1
200
256
  end
201
257
 
@@ -208,8 +264,8 @@ describe 'Rack::Cache::Context' do
208
264
  response.headers.should.include 'Date'
209
265
  response['Age'].should.not.be.nil
210
266
  response['X-Content-Digest'].should.not.be.nil
211
- cache.should.a.performed :miss
212
- cache.should.a.performed :store
267
+ cache.trace.should.include :miss
268
+ cache.trace.should.include :store
213
269
  cache.metastore.to_hash.keys.length.should.equal 1
214
270
  end
215
271
 
@@ -219,18 +275,18 @@ describe 'Rack::Cache::Context' do
219
275
 
220
276
  response.should.be.ok
221
277
  response.body.should.equal 'Hello World'
222
- cache.should.a.performed :miss
223
- cache.should.a.performed :store
278
+ cache.trace.should.include :miss
279
+ cache.trace.should.include :store
224
280
  end
225
281
 
226
282
  it 'caches responses with an ETag validator but no freshness information' do
227
- respond_with 200, 'Etag' => '"123456"'
283
+ respond_with 200, 'ETag' => '"123456"'
228
284
  get '/'
229
285
 
230
286
  response.should.be.ok
231
287
  response.body.should.equal 'Hello World'
232
- cache.should.a.performed :miss
233
- cache.should.a.performed :store
288
+ cache.trace.should.include :miss
289
+ cache.trace.should.include :store
234
290
  end
235
291
 
236
292
  it 'hits cached response with Expires header' do
@@ -242,8 +298,8 @@ describe 'Rack::Cache::Context' do
242
298
  app.should.be.called
243
299
  response.should.be.ok
244
300
  response.headers.should.include 'Date'
245
- cache.should.a.performed :miss
246
- cache.should.a.performed :store
301
+ cache.trace.should.include :miss
302
+ cache.trace.should.include :store
247
303
  response.body.should.equal 'Hello World'
248
304
 
249
305
  get '/'
@@ -252,8 +308,8 @@ describe 'Rack::Cache::Context' do
252
308
  response['Date'].should.equal responses.first['Date']
253
309
  response['Age'].to_i.should.satisfy { |age| age > 0 }
254
310
  response['X-Content-Digest'].should.not.be.nil
255
- cache.should.a.performed :hit
256
- cache.should.a.not.performed :fetch
311
+ cache.trace.should.include :fresh
312
+ cache.trace.should.not.include :store
257
313
  response.body.should.equal 'Hello World'
258
314
  end
259
315
 
@@ -266,8 +322,8 @@ describe 'Rack::Cache::Context' do
266
322
  app.should.be.called
267
323
  response.should.be.ok
268
324
  response.headers.should.include 'Date'
269
- cache.should.a.performed :miss
270
- cache.should.a.performed :store
325
+ cache.trace.should.include :miss
326
+ cache.trace.should.include :store
271
327
  response.body.should.equal 'Hello World'
272
328
 
273
329
  get '/'
@@ -276,8 +332,8 @@ describe 'Rack::Cache::Context' do
276
332
  response['Date'].should.equal responses.first['Date']
277
333
  response['Age'].to_i.should.satisfy { |age| age > 0 }
278
334
  response['X-Content-Digest'].should.not.be.nil
279
- cache.should.a.performed :hit
280
- cache.should.a.not.performed :fetch
335
+ cache.trace.should.include :fresh
336
+ cache.trace.should.not.include :store
281
337
  response.body.should.equal 'Hello World'
282
338
  end
283
339
 
@@ -290,8 +346,8 @@ describe 'Rack::Cache::Context' do
290
346
  app.should.be.called
291
347
  response.should.be.ok
292
348
  response.headers.should.include 'Date'
293
- cache.should.a.performed :miss
294
- cache.should.a.performed :store
349
+ cache.trace.should.include :miss
350
+ cache.trace.should.include :store
295
351
  response.body.should.equal 'Hello World'
296
352
 
297
353
  get '/'
@@ -300,8 +356,8 @@ describe 'Rack::Cache::Context' do
300
356
  response['Date'].should.equal responses.first['Date']
301
357
  response['Age'].to_i.should.satisfy { |age| age > 0 }
302
358
  response['X-Content-Digest'].should.not.be.nil
303
- cache.should.a.performed :hit
304
- cache.should.a.not.performed :fetch
359
+ cache.trace.should.include :fresh
360
+ cache.trace.should.not.include :store
305
361
  response.body.should.equal 'Hello World'
306
362
  end
307
363
 
@@ -311,16 +367,16 @@ describe 'Rack::Cache::Context' do
311
367
  get '/', 'rack-cache.default_ttl' => 10
312
368
  app.should.be.called
313
369
  response.should.be.ok
314
- cache.should.a.performed :miss
315
- cache.should.a.performed :store
370
+ cache.trace.should.include :miss
371
+ cache.trace.should.include :store
316
372
  response.body.should.equal 'Hello World'
317
373
  response['Cache-Control'].should.include 's-maxage=10'
318
374
 
319
375
  get '/', 'rack-cache.default_ttl' => 10
320
376
  response.should.be.ok
321
377
  app.should.not.be.called
322
- cache.should.a.performed :hit
323
- cache.should.a.not.performed :fetch
378
+ cache.trace.should.include :fresh
379
+ cache.trace.should.not.include :store
324
380
  response.body.should.equal 'Hello World'
325
381
  end
326
382
 
@@ -331,8 +387,8 @@ describe 'Rack::Cache::Context' do
331
387
  get '/', 'rack-cache.default_ttl' => 10
332
388
  app.should.be.called
333
389
  response.should.be.ok
334
- cache.should.a.performed :miss
335
- cache.should.a.not.performed :store
390
+ cache.trace.should.include :miss
391
+ cache.trace.should.not.include :store
336
392
  response['Cache-Control'].should.not.include 's-maxage'
337
393
  response.body.should.equal 'Hello World'
338
394
  end
@@ -347,8 +403,8 @@ describe 'Rack::Cache::Context' do
347
403
  response.headers.should.include 'Date'
348
404
  response.headers.should.include 'X-Content-Digest'
349
405
  response.headers.should.include 'Age'
350
- cache.should.a.performed :miss
351
- cache.should.a.performed :store
406
+ cache.trace.should.include :miss
407
+ cache.trace.should.include :store
352
408
  response.body.should.equal 'Hello World'
353
409
 
354
410
  # go in and play around with the cached metadata directly ...
@@ -361,10 +417,10 @@ describe 'Rack::Cache::Context' do
361
417
  response.should.be.ok
362
418
  response['Age'].to_i.should.equal 0
363
419
  response.headers.should.include 'X-Content-Digest'
364
- cache.should.a.not.performed :hit
365
- cache.should.a.not.performed :miss
366
- cache.should.a.performed :fetch
367
- cache.should.a.performed :store
420
+ cache.trace.should.include :stale
421
+ cache.trace.should.not.include :fresh
422
+ cache.trace.should.not.include :miss
423
+ cache.trace.should.include :store
368
424
  response.body.should.equal 'Hello World'
369
425
  end
370
426
 
@@ -385,8 +441,9 @@ describe 'Rack::Cache::Context' do
385
441
  response.headers.should.include 'Last-Modified'
386
442
  response.headers.should.include 'X-Content-Digest'
387
443
  response.body.should.equal 'Hello World'
388
- cache.should.a.performed :miss
389
- cache.should.a.performed :store
444
+ cache.trace.should.include :miss
445
+ cache.trace.should.include :store
446
+ cache.trace.should.not.include :stale
390
447
 
391
448
  # build subsequent request; should be found but miss due to freshness
392
449
  get '/'
@@ -395,11 +452,11 @@ describe 'Rack::Cache::Context' do
395
452
  response.headers.should.include 'Last-Modified'
396
453
  response.headers.should.include 'X-Content-Digest'
397
454
  response['Age'].to_i.should.equal 0
398
- response['X-Origin-Status'].should.equal '304'
399
455
  response.body.should.equal 'Hello World'
400
- cache.should.a.not.performed :miss
401
- cache.should.a.performed :fetch
402
- cache.should.a.performed :store
456
+ cache.trace.should.include :stale
457
+ cache.trace.should.include :valid
458
+ cache.trace.should.include :store
459
+ cache.trace.should.not.include :miss
403
460
  end
404
461
 
405
462
  it 'validates cached responses with ETag and no freshness information' do
@@ -416,24 +473,24 @@ describe 'Rack::Cache::Context' do
416
473
  get '/'
417
474
  app.should.be.called
418
475
  response.should.be.ok
419
- response.headers.should.include 'Etag'
476
+ response.headers.should.include 'ETag'
420
477
  response.headers.should.include 'X-Content-Digest'
421
478
  response.body.should.equal 'Hello World'
422
- cache.should.a.performed :miss
423
- cache.should.a.performed :store
479
+ cache.trace.should.include :miss
480
+ cache.trace.should.include :store
424
481
 
425
482
  # build subsequent request; should be found but miss due to freshness
426
483
  get '/'
427
484
  app.should.be.called
428
485
  response.should.be.ok
429
- response.headers.should.include 'Etag'
486
+ response.headers.should.include 'ETag'
430
487
  response.headers.should.include 'X-Content-Digest'
431
488
  response['Age'].to_i.should.equal 0
432
- response['X-Origin-Status'].should.equal '304'
433
489
  response.body.should.equal 'Hello World'
434
- cache.should.a.not.performed :miss
435
- cache.should.a.performed :fetch
436
- cache.should.a.performed :store
490
+ cache.trace.should.include :stale
491
+ cache.trace.should.include :valid
492
+ cache.trace.should.include :store
493
+ cache.trace.should.not.include :miss
437
494
  end
438
495
 
439
496
  it 'replaces cached responses when validation results in non-304 response' do
@@ -442,8 +499,8 @@ describe 'Rack::Cache::Context' do
442
499
  respond_with do |req,res|
443
500
  res['Last-Modified'] = timestamp
444
501
  case (count+=1)
445
- when 1 ; res.body = 'first response'
446
- when 2 ; res.body = 'second response'
502
+ when 1 ; res.body = ['first response']
503
+ when 2 ; res.body = ['second response']
447
504
  when 3
448
505
  res.body = []
449
506
  res.status = 304
@@ -475,11 +532,7 @@ describe 'Rack::Cache::Context' do
475
532
  req.request_method.should.equal 'HEAD'
476
533
  end
477
534
 
478
- cache_config do
479
- on(:receive) { pass! }
480
- end
481
-
482
- head '/'
535
+ head '/', 'HTTP_EXPECT' => 'something ...'
483
536
  app.should.be.called
484
537
  response.body.should.equal ''
485
538
  end
@@ -503,6 +556,53 @@ describe 'Rack::Cache::Context' do
503
556
  response['Content-Length'].should.equal 'Hello World'.length.to_s
504
557
  end
505
558
 
559
+ it 'invalidates cached responses on POST' do
560
+ respond_with do |req,res|
561
+ if req.request_method == 'GET'
562
+ res.status = 200
563
+ res['Cache-Control'] = 'public, max-age=500'
564
+ res.body = ['Hello World']
565
+ elsif req.request_method == 'POST'
566
+ res.status = 303
567
+ res['Location'] = '/'
568
+ res.headers.delete('Cache-Control')
569
+ res.body = []
570
+ end
571
+ end
572
+
573
+ # build initial request to enter into the cache
574
+ get '/'
575
+ app.should.be.called
576
+ response.should.be.ok
577
+ response.body.should.equal 'Hello World'
578
+ cache.trace.should.include :miss
579
+ cache.trace.should.include :store
580
+
581
+ # make sure it is valid
582
+ get '/'
583
+ app.should.not.called
584
+ response.should.be.ok
585
+ response.body.should.equal 'Hello World'
586
+ cache.trace.should.include :fresh
587
+
588
+ # now POST to same URL
589
+ post '/'
590
+ app.should.be.called
591
+ response.should.be.redirect
592
+ response['Location'].should.equal '/'
593
+ cache.trace.should.include :invalidate
594
+ cache.trace.should.include :pass
595
+ response.body.should.equal ''
596
+
597
+ # now make sure it was actually invalidated
598
+ get '/'
599
+ app.should.be.called
600
+ response.should.be.ok
601
+ response.body.should.equal 'Hello World'
602
+ cache.trace.should.include :stale
603
+ cache.trace.should.include :invalid
604
+ cache.trace.should.include :store
605
+ end
506
606
 
507
607
  describe 'with responses that include a Vary header' do
508
608
  before(:each) do
@@ -511,7 +611,7 @@ describe 'Rack::Cache::Context' do
511
611
  res['Vary'] = 'Accept User-Agent Foo'
512
612
  res['Cache-Control'] = 'max-age=10'
513
613
  res['X-Response-Count'] = (count+=1).to_s
514
- res.body = req.env['HTTP_USER_AGENT']
614
+ res.body = [req.env['HTTP_USER_AGENT']]
515
615
  end
516
616
  end
517
617
 
@@ -521,16 +621,16 @@ describe 'Rack::Cache::Context' do
521
621
  'HTTP_USER_AGENT' => 'Bob/1.0'
522
622
  response.should.be.ok
523
623
  response.body.should.equal 'Bob/1.0'
524
- cache.should.a.performed :miss
525
- cache.should.a.performed :store
624
+ cache.trace.should.include :miss
625
+ cache.trace.should.include :store
526
626
 
527
627
  get '/',
528
628
  'HTTP_ACCEPT' => 'text/html',
529
629
  'HTTP_USER_AGENT' => 'Bob/1.0'
530
630
  response.should.be.ok
531
631
  response.body.should.equal 'Bob/1.0'
532
- cache.should.a.performed :hit
533
- cache.should.a.not.performed :fetch
632
+ cache.trace.should.include :fresh
633
+ cache.trace.should.not.include :store
534
634
  response.headers.should.include 'X-Content-Digest'
535
635
  end
536
636
 
@@ -545,101 +645,30 @@ describe 'Rack::Cache::Context' do
545
645
  get '/',
546
646
  'HTTP_ACCEPT' => 'text/html',
547
647
  'HTTP_USER_AGENT' => 'Bob/2.0'
548
- cache.should.a.performed :miss
549
- cache.should.a.performed :store
648
+ cache.trace.should.include :miss
649
+ cache.trace.should.include :store
550
650
  response.body.should.equal 'Bob/2.0'
551
651
  response['X-Response-Count'].should.equal '2'
552
652
 
553
653
  get '/',
554
654
  'HTTP_ACCEPT' => 'text/html',
555
655
  'HTTP_USER_AGENT' => 'Bob/1.0'
556
- cache.should.a.performed :hit
656
+ cache.trace.should.include :fresh
557
657
  response.body.should.equal 'Bob/1.0'
558
658
  response['X-Response-Count'].should.equal '1'
559
659
 
560
660
  get '/',
561
661
  'HTTP_ACCEPT' => 'text/html',
562
662
  'HTTP_USER_AGENT' => 'Bob/2.0'
563
- cache.should.a.performed :hit
663
+ cache.trace.should.include :fresh
564
664
  response.body.should.equal 'Bob/2.0'
565
665
  response['X-Response-Count'].should.equal '2'
566
666
 
567
667
  get '/',
568
668
  'HTTP_USER_AGENT' => 'Bob/2.0'
569
- cache.should.a.performed :miss
669
+ cache.trace.should.include :miss
570
670
  response.body.should.equal 'Bob/2.0'
571
671
  response['X-Response-Count'].should.equal '3'
572
672
  end
573
673
  end
574
-
575
- describe 'when transitioning to the error state' do
576
-
577
- setup { respond_with(200) }
578
-
579
- it 'creates a blank slate response object with 500 status with no args' do
580
- cache_config do
581
- on(:receive) { error! }
582
- end
583
- get '/'
584
- response.status.should.equal 500
585
- response.body.should.be.empty
586
- cache.should.a.performed :error
587
- end
588
-
589
- it 'sets the status code with one arg' do
590
- cache_config do
591
- on(:receive) { error! 505 }
592
- end
593
- get '/'
594
- response.status.should.equal 505
595
- end
596
-
597
- it 'sets the status and headers with args: status, Hash' do
598
- cache_config do
599
- on(:receive) { error! 504, 'Content-Type' => 'application/x-foo' }
600
- end
601
- get '/'
602
- response.status.should.equal 504
603
- response['Content-Type'].should.equal 'application/x-foo'
604
- response.body.should.be.empty
605
- end
606
-
607
- it 'sets the status and body with args: status, String' do
608
- cache_config do
609
- on(:receive) { error! 503, 'foo bar baz' }
610
- end
611
- get '/'
612
- response.status.should.equal 503
613
- response.body.should.equal 'foo bar baz'
614
- end
615
-
616
- it 'sets the status and body with args: status, Array' do
617
- cache_config do
618
- on(:receive) { error! 503, ['foo bar baz'] }
619
- end
620
- get '/'
621
- response.status.should.equal 503
622
- response.body.should.equal 'foo bar baz'
623
- end
624
-
625
- it 'fires the error event before finishing' do
626
- fired = false
627
- cache_config do
628
- on(:receive) { error! }
629
- on(:error) {
630
- fired = true
631
- response.status.should.equal 500
632
- response['Content-Type'] = 'application/x-foo'
633
- response.body = ['overridden response body']
634
- }
635
- end
636
- get '/'
637
- fired.should.be true
638
- response.status.should.equal 500
639
- response.body.should.equal 'overridden response body'
640
- response['Content-Type'].should.equal 'application/x-foo'
641
- end
642
-
643
- end
644
-
645
674
  end