rack-cache 0.3.0 → 0.4
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.
Potentially problematic release.
This version of rack-cache might be problematic. Click here for more details.
- data/CHANGES +43 -0
- data/README +18 -9
- data/Rakefile +1 -14
- data/TODO +13 -14
- data/doc/configuration.markdown +7 -153
- data/doc/faq.markdown +8 -0
- data/doc/index.markdown +7 -9
- data/example/sinatra/app.rb +25 -0
- data/example/sinatra/views/index.erb +44 -0
- data/lib/rack/cache.rb +5 -11
- data/lib/rack/cache/cachecontrol.rb +193 -0
- data/lib/rack/cache/context.rb +190 -52
- data/lib/rack/cache/entitystore.rb +10 -4
- data/lib/rack/cache/key.rb +52 -0
- data/lib/rack/cache/metastore.rb +52 -16
- data/lib/rack/cache/options.rb +60 -39
- data/lib/rack/cache/request.rb +11 -15
- data/lib/rack/cache/response.rb +221 -30
- data/lib/rack/cache/storage.rb +1 -2
- data/rack-cache.gemspec +9 -15
- data/test/cache_test.rb +9 -6
- data/test/cachecontrol_test.rb +139 -0
- data/test/context_test.rb +251 -169
- data/test/entitystore_test.rb +12 -11
- data/test/key_test.rb +50 -0
- data/test/metastore_test.rb +57 -14
- data/test/options_test.rb +11 -0
- data/test/request_test.rb +19 -0
- data/test/response_test.rb +164 -23
- data/test/spec_setup.rb +7 -0
- metadata +12 -20
- data/doc/events.dot +0 -27
- data/lib/rack/cache/config.rb +0 -65
- data/lib/rack/cache/config/busters.rb +0 -16
- data/lib/rack/cache/config/default.rb +0 -133
- data/lib/rack/cache/config/no-cache.rb +0 -13
- data/lib/rack/cache/core.rb +0 -299
- data/lib/rack/cache/headers.rb +0 -325
- data/lib/rack/utils/environment_headers.rb +0 -78
- data/test/config_test.rb +0 -66
- data/test/core_test.rb +0 -84
- data/test/environment_headers_test.rb +0 -69
- data/test/headers_test.rb +0 -298
- data/test/logging_test.rb +0 -45
data/lib/rack/cache/storage.rb
CHANGED
@@ -3,11 +3,11 @@ require 'rack/cache/metastore'
|
|
3
3
|
require 'rack/cache/entitystore'
|
4
4
|
|
5
5
|
module Rack::Cache
|
6
|
+
|
6
7
|
# Maintains a collection of MetaStore and EntityStore instances keyed by
|
7
8
|
# URI. A single instance of this class can be used across a single process
|
8
9
|
# to ensure that only a single instance of a backing store is created per
|
9
10
|
# unique storage URI.
|
10
|
-
|
11
11
|
class Storage
|
12
12
|
def initialize
|
13
13
|
@metastores = {}
|
@@ -22,7 +22,6 @@ module Rack::Cache
|
|
22
22
|
@entitystores[uri.to_s] ||= create_store(EntityStore, uri)
|
23
23
|
end
|
24
24
|
|
25
|
-
# Clear store instances.
|
26
25
|
def clear
|
27
26
|
@metastores.clear
|
28
27
|
@entitystores.clear
|
data/rack-cache.gemspec
CHANGED
@@ -3,8 +3,8 @@ Gem::Specification.new do |s|
|
|
3
3
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
4
4
|
|
5
5
|
s.name = 'rack-cache'
|
6
|
-
s.version = '0.
|
7
|
-
s.date = '
|
6
|
+
s.version = '0.4'
|
7
|
+
s.date = '2009-03-16'
|
8
8
|
|
9
9
|
s.description = "HTTP Caching for Rack"
|
10
10
|
s.summary = "HTTP Caching for Rack"
|
@@ -20,7 +20,6 @@ Gem::Specification.new do |s|
|
|
20
20
|
Rakefile
|
21
21
|
TODO
|
22
22
|
doc/configuration.markdown
|
23
|
-
doc/events.dot
|
24
23
|
doc/faq.markdown
|
25
24
|
doc/index.markdown
|
26
25
|
doc/layout.html.erb
|
@@ -28,33 +27,28 @@ Gem::Specification.new do |s|
|
|
28
27
|
doc/rack-cache.css
|
29
28
|
doc/server.ru
|
30
29
|
doc/storage.markdown
|
30
|
+
example/sinatra/app.rb
|
31
|
+
example/sinatra/views/index.erb
|
31
32
|
lib/rack/cache.rb
|
32
|
-
lib/rack/cache/
|
33
|
-
lib/rack/cache/config/busters.rb
|
34
|
-
lib/rack/cache/config/default.rb
|
35
|
-
lib/rack/cache/config/no-cache.rb
|
33
|
+
lib/rack/cache/cachecontrol.rb
|
36
34
|
lib/rack/cache/context.rb
|
37
|
-
lib/rack/cache/core.rb
|
38
35
|
lib/rack/cache/entitystore.rb
|
39
|
-
lib/rack/cache/
|
36
|
+
lib/rack/cache/key.rb
|
40
37
|
lib/rack/cache/metastore.rb
|
41
38
|
lib/rack/cache/options.rb
|
42
39
|
lib/rack/cache/request.rb
|
43
40
|
lib/rack/cache/response.rb
|
44
41
|
lib/rack/cache/storage.rb
|
45
|
-
lib/rack/utils/environment_headers.rb
|
46
42
|
rack-cache.gemspec
|
47
43
|
test/cache_test.rb
|
48
|
-
test/
|
44
|
+
test/cachecontrol_test.rb
|
49
45
|
test/context_test.rb
|
50
|
-
test/core_test.rb
|
51
46
|
test/entitystore_test.rb
|
52
|
-
test/
|
53
|
-
test/headers_test.rb
|
54
|
-
test/logging_test.rb
|
47
|
+
test/key_test.rb
|
55
48
|
test/metastore_test.rb
|
56
49
|
test/options_test.rb
|
57
50
|
test/pony.jpg
|
51
|
+
test/request_test.rb
|
58
52
|
test/response_test.rb
|
59
53
|
test/spec_setup.rb
|
60
54
|
test/storage_test.rb
|
data/test/cache_test.rb
CHANGED
@@ -12,24 +12,27 @@ 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
|
-
state,
|
26
|
-
|
27
|
-
Rack::Cache.new @app do
|
28
|
-
|
28
|
+
state, object = 'not invoked', nil
|
29
|
+
instance =
|
30
|
+
Rack::Cache.new @app do |cache|
|
31
|
+
object = cache
|
29
32
|
state = 'invoked'
|
30
|
-
should.respond_to :
|
33
|
+
cache.should.respond_to :set
|
31
34
|
end
|
32
35
|
state.should.equal 'invoked'
|
33
|
-
object.should.be
|
36
|
+
object.should.be instance
|
34
37
|
end
|
35
38
|
end
|
@@ -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.
|
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, '
|
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.
|
26
|
-
cache.should.
|
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.
|
39
|
-
cache.should.
|
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, '
|
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.
|
53
|
-
cache.should.
|
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.
|
66
|
-
cache.should.
|
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.
|
89
|
-
cache.should.
|
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['
|
101
|
+
res['ETag'] = '12345'
|
96
102
|
res['Content-Type'] = 'text/plain'
|
97
103
|
res.body = ['Hello World']
|
98
104
|
end
|
@@ -103,28 +109,131 @@ 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 '
|
112
|
+
response.headers.should.include 'ETag'
|
107
113
|
response.body.should.empty
|
108
|
-
cache.should.
|
109
|
-
cache.should.
|
114
|
+
cache.trace.should.include :miss
|
115
|
+
cache.trace.should.include :store
|
110
116
|
end
|
111
117
|
|
112
|
-
it '
|
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.
|
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 '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
|
121
231
|
it 'fetches response from backend when cache misses' do
|
122
232
|
respond_with 200, 'Expires' => (Time.now + 5).httpdate
|
123
233
|
get '/'
|
124
234
|
|
125
235
|
response.should.be.ok
|
126
|
-
cache.should.
|
127
|
-
cache.should.a.performed :fetch
|
236
|
+
cache.trace.should.include :miss
|
128
237
|
response.headers.should.include 'Age'
|
129
238
|
end
|
130
239
|
|
@@ -134,7 +243,7 @@ describe 'Rack::Cache::Context' do
|
|
134
243
|
respond_with response_code, 'Expires' => (Time.now + 5).httpdate
|
135
244
|
get '/'
|
136
245
|
|
137
|
-
cache.should.
|
246
|
+
cache.trace.should.not.include :store
|
138
247
|
response.status.should.equal response_code
|
139
248
|
response.headers.should.not.include 'Age'
|
140
249
|
end
|
@@ -148,7 +257,7 @@ describe 'Rack::Cache::Context' do
|
|
148
257
|
get '/'
|
149
258
|
|
150
259
|
response.should.be.ok
|
151
|
-
cache.should.
|
260
|
+
cache.trace.should.not.include :store
|
152
261
|
response.headers.should.not.include 'Age'
|
153
262
|
end
|
154
263
|
|
@@ -157,7 +266,7 @@ describe 'Rack::Cache::Context' do
|
|
157
266
|
get '/'
|
158
267
|
|
159
268
|
response.should.be.ok
|
160
|
-
cache.should.
|
269
|
+
cache.trace.should.not.include :store
|
161
270
|
end
|
162
271
|
|
163
272
|
it "caches responses with explicit no-cache directive" do
|
@@ -167,7 +276,7 @@ describe 'Rack::Cache::Context' do
|
|
167
276
|
get '/'
|
168
277
|
|
169
278
|
response.should.be.ok
|
170
|
-
cache.should.
|
279
|
+
cache.trace.should.include :store
|
171
280
|
response.headers.should.include 'Age'
|
172
281
|
end
|
173
282
|
|
@@ -180,8 +289,8 @@ describe 'Rack::Cache::Context' do
|
|
180
289
|
response.headers.should.include 'Date'
|
181
290
|
response['Age'].should.not.be.nil
|
182
291
|
response['X-Content-Digest'].should.not.be.nil
|
183
|
-
cache.should.
|
184
|
-
cache.should.
|
292
|
+
cache.trace.should.include :miss
|
293
|
+
cache.trace.should.include :store
|
185
294
|
cache.metastore.to_hash.keys.length.should.equal 1
|
186
295
|
end
|
187
296
|
|
@@ -194,8 +303,8 @@ describe 'Rack::Cache::Context' do
|
|
194
303
|
response.headers.should.include 'Date'
|
195
304
|
response['Age'].should.not.be.nil
|
196
305
|
response['X-Content-Digest'].should.not.be.nil
|
197
|
-
cache.should.
|
198
|
-
cache.should.
|
306
|
+
cache.trace.should.include :miss
|
307
|
+
cache.trace.should.include :store
|
199
308
|
cache.metastore.to_hash.keys.length.should.equal 1
|
200
309
|
end
|
201
310
|
|
@@ -208,8 +317,8 @@ describe 'Rack::Cache::Context' do
|
|
208
317
|
response.headers.should.include 'Date'
|
209
318
|
response['Age'].should.not.be.nil
|
210
319
|
response['X-Content-Digest'].should.not.be.nil
|
211
|
-
cache.should.
|
212
|
-
cache.should.
|
320
|
+
cache.trace.should.include :miss
|
321
|
+
cache.trace.should.include :store
|
213
322
|
cache.metastore.to_hash.keys.length.should.equal 1
|
214
323
|
end
|
215
324
|
|
@@ -219,18 +328,18 @@ describe 'Rack::Cache::Context' do
|
|
219
328
|
|
220
329
|
response.should.be.ok
|
221
330
|
response.body.should.equal 'Hello World'
|
222
|
-
cache.should.
|
223
|
-
cache.should.
|
331
|
+
cache.trace.should.include :miss
|
332
|
+
cache.trace.should.include :store
|
224
333
|
end
|
225
334
|
|
226
335
|
it 'caches responses with an ETag validator but no freshness information' do
|
227
|
-
respond_with 200, '
|
336
|
+
respond_with 200, 'ETag' => '"123456"'
|
228
337
|
get '/'
|
229
338
|
|
230
339
|
response.should.be.ok
|
231
340
|
response.body.should.equal 'Hello World'
|
232
|
-
cache.should.
|
233
|
-
cache.should.
|
341
|
+
cache.trace.should.include :miss
|
342
|
+
cache.trace.should.include :store
|
234
343
|
end
|
235
344
|
|
236
345
|
it 'hits cached response with Expires header' do
|
@@ -242,8 +351,8 @@ describe 'Rack::Cache::Context' do
|
|
242
351
|
app.should.be.called
|
243
352
|
response.should.be.ok
|
244
353
|
response.headers.should.include 'Date'
|
245
|
-
cache.should.
|
246
|
-
cache.should.
|
354
|
+
cache.trace.should.include :miss
|
355
|
+
cache.trace.should.include :store
|
247
356
|
response.body.should.equal 'Hello World'
|
248
357
|
|
249
358
|
get '/'
|
@@ -252,8 +361,8 @@ describe 'Rack::Cache::Context' do
|
|
252
361
|
response['Date'].should.equal responses.first['Date']
|
253
362
|
response['Age'].to_i.should.satisfy { |age| age > 0 }
|
254
363
|
response['X-Content-Digest'].should.not.be.nil
|
255
|
-
cache.should.
|
256
|
-
cache.should.
|
364
|
+
cache.trace.should.include :fresh
|
365
|
+
cache.trace.should.not.include :store
|
257
366
|
response.body.should.equal 'Hello World'
|
258
367
|
end
|
259
368
|
|
@@ -266,8 +375,8 @@ describe 'Rack::Cache::Context' do
|
|
266
375
|
app.should.be.called
|
267
376
|
response.should.be.ok
|
268
377
|
response.headers.should.include 'Date'
|
269
|
-
cache.should.
|
270
|
-
cache.should.
|
378
|
+
cache.trace.should.include :miss
|
379
|
+
cache.trace.should.include :store
|
271
380
|
response.body.should.equal 'Hello World'
|
272
381
|
|
273
382
|
get '/'
|
@@ -276,8 +385,8 @@ describe 'Rack::Cache::Context' do
|
|
276
385
|
response['Date'].should.equal responses.first['Date']
|
277
386
|
response['Age'].to_i.should.satisfy { |age| age > 0 }
|
278
387
|
response['X-Content-Digest'].should.not.be.nil
|
279
|
-
cache.should.
|
280
|
-
cache.should.
|
388
|
+
cache.trace.should.include :fresh
|
389
|
+
cache.trace.should.not.include :store
|
281
390
|
response.body.should.equal 'Hello World'
|
282
391
|
end
|
283
392
|
|
@@ -290,8 +399,8 @@ describe 'Rack::Cache::Context' do
|
|
290
399
|
app.should.be.called
|
291
400
|
response.should.be.ok
|
292
401
|
response.headers.should.include 'Date'
|
293
|
-
cache.should.
|
294
|
-
cache.should.
|
402
|
+
cache.trace.should.include :miss
|
403
|
+
cache.trace.should.include :store
|
295
404
|
response.body.should.equal 'Hello World'
|
296
405
|
|
297
406
|
get '/'
|
@@ -300,8 +409,8 @@ describe 'Rack::Cache::Context' do
|
|
300
409
|
response['Date'].should.equal responses.first['Date']
|
301
410
|
response['Age'].to_i.should.satisfy { |age| age > 0 }
|
302
411
|
response['X-Content-Digest'].should.not.be.nil
|
303
|
-
cache.should.
|
304
|
-
cache.should.
|
412
|
+
cache.trace.should.include :fresh
|
413
|
+
cache.trace.should.not.include :store
|
305
414
|
response.body.should.equal 'Hello World'
|
306
415
|
end
|
307
416
|
|
@@ -311,16 +420,16 @@ describe 'Rack::Cache::Context' do
|
|
311
420
|
get '/', 'rack-cache.default_ttl' => 10
|
312
421
|
app.should.be.called
|
313
422
|
response.should.be.ok
|
314
|
-
cache.should.
|
315
|
-
cache.should.
|
423
|
+
cache.trace.should.include :miss
|
424
|
+
cache.trace.should.include :store
|
316
425
|
response.body.should.equal 'Hello World'
|
317
426
|
response['Cache-Control'].should.include 's-maxage=10'
|
318
427
|
|
319
428
|
get '/', 'rack-cache.default_ttl' => 10
|
320
429
|
response.should.be.ok
|
321
430
|
app.should.not.be.called
|
322
|
-
cache.should.
|
323
|
-
cache.should.
|
431
|
+
cache.trace.should.include :fresh
|
432
|
+
cache.trace.should.not.include :store
|
324
433
|
response.body.should.equal 'Hello World'
|
325
434
|
end
|
326
435
|
|
@@ -331,8 +440,8 @@ describe 'Rack::Cache::Context' do
|
|
331
440
|
get '/', 'rack-cache.default_ttl' => 10
|
332
441
|
app.should.be.called
|
333
442
|
response.should.be.ok
|
334
|
-
cache.should.
|
335
|
-
cache.should.
|
443
|
+
cache.trace.should.include :miss
|
444
|
+
cache.trace.should.not.include :store
|
336
445
|
response['Cache-Control'].should.not.include 's-maxage'
|
337
446
|
response.body.should.equal 'Hello World'
|
338
447
|
end
|
@@ -347,8 +456,8 @@ describe 'Rack::Cache::Context' do
|
|
347
456
|
response.headers.should.include 'Date'
|
348
457
|
response.headers.should.include 'X-Content-Digest'
|
349
458
|
response.headers.should.include 'Age'
|
350
|
-
cache.should.
|
351
|
-
cache.should.
|
459
|
+
cache.trace.should.include :miss
|
460
|
+
cache.trace.should.include :store
|
352
461
|
response.body.should.equal 'Hello World'
|
353
462
|
|
354
463
|
# go in and play around with the cached metadata directly ...
|
@@ -361,10 +470,10 @@ describe 'Rack::Cache::Context' do
|
|
361
470
|
response.should.be.ok
|
362
471
|
response['Age'].to_i.should.equal 0
|
363
472
|
response.headers.should.include 'X-Content-Digest'
|
364
|
-
cache.should.
|
365
|
-
cache.should.
|
366
|
-
cache.should.
|
367
|
-
cache.should.
|
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
|
368
477
|
response.body.should.equal 'Hello World'
|
369
478
|
end
|
370
479
|
|
@@ -385,8 +494,9 @@ describe 'Rack::Cache::Context' do
|
|
385
494
|
response.headers.should.include 'Last-Modified'
|
386
495
|
response.headers.should.include 'X-Content-Digest'
|
387
496
|
response.body.should.equal 'Hello World'
|
388
|
-
cache.should.
|
389
|
-
cache.should.
|
497
|
+
cache.trace.should.include :miss
|
498
|
+
cache.trace.should.include :store
|
499
|
+
cache.trace.should.not.include :stale
|
390
500
|
|
391
501
|
# build subsequent request; should be found but miss due to freshness
|
392
502
|
get '/'
|
@@ -395,11 +505,11 @@ describe 'Rack::Cache::Context' do
|
|
395
505
|
response.headers.should.include 'Last-Modified'
|
396
506
|
response.headers.should.include 'X-Content-Digest'
|
397
507
|
response['Age'].to_i.should.equal 0
|
398
|
-
response['X-Origin-Status'].should.equal '304'
|
399
508
|
response.body.should.equal 'Hello World'
|
400
|
-
cache.should.
|
401
|
-
cache.should.
|
402
|
-
cache.should.
|
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
|
403
513
|
end
|
404
514
|
|
405
515
|
it 'validates cached responses with ETag and no freshness information' do
|
@@ -416,24 +526,24 @@ describe 'Rack::Cache::Context' do
|
|
416
526
|
get '/'
|
417
527
|
app.should.be.called
|
418
528
|
response.should.be.ok
|
419
|
-
response.headers.should.include '
|
529
|
+
response.headers.should.include 'ETag'
|
420
530
|
response.headers.should.include 'X-Content-Digest'
|
421
531
|
response.body.should.equal 'Hello World'
|
422
|
-
cache.should.
|
423
|
-
cache.should.
|
532
|
+
cache.trace.should.include :miss
|
533
|
+
cache.trace.should.include :store
|
424
534
|
|
425
535
|
# build subsequent request; should be found but miss due to freshness
|
426
536
|
get '/'
|
427
537
|
app.should.be.called
|
428
538
|
response.should.be.ok
|
429
|
-
response.headers.should.include '
|
539
|
+
response.headers.should.include 'ETag'
|
430
540
|
response.headers.should.include 'X-Content-Digest'
|
431
541
|
response['Age'].to_i.should.equal 0
|
432
|
-
response['X-Origin-Status'].should.equal '304'
|
433
542
|
response.body.should.equal 'Hello World'
|
434
|
-
cache.should.
|
435
|
-
cache.should.
|
436
|
-
cache.should.
|
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
|
437
547
|
end
|
438
548
|
|
439
549
|
it 'replaces cached responses when validation results in non-304 response' do
|
@@ -442,8 +552,8 @@ describe 'Rack::Cache::Context' do
|
|
442
552
|
respond_with do |req,res|
|
443
553
|
res['Last-Modified'] = timestamp
|
444
554
|
case (count+=1)
|
445
|
-
when 1 ; res.body = 'first response'
|
446
|
-
when 2 ; res.body = 'second response'
|
555
|
+
when 1 ; res.body = ['first response']
|
556
|
+
when 2 ; res.body = ['second response']
|
447
557
|
when 3
|
448
558
|
res.body = []
|
449
559
|
res.status = 304
|
@@ -475,11 +585,7 @@ describe 'Rack::Cache::Context' do
|
|
475
585
|
req.request_method.should.equal 'HEAD'
|
476
586
|
end
|
477
587
|
|
478
|
-
|
479
|
-
on(:receive) { pass! }
|
480
|
-
end
|
481
|
-
|
482
|
-
head '/'
|
588
|
+
head '/', 'HTTP_EXPECT' => 'something ...'
|
483
589
|
app.should.be.called
|
484
590
|
response.body.should.equal ''
|
485
591
|
end
|
@@ -503,6 +609,53 @@ describe 'Rack::Cache::Context' do
|
|
503
609
|
response['Content-Length'].should.equal 'Hello World'.length.to_s
|
504
610
|
end
|
505
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
|
506
659
|
|
507
660
|
describe 'with responses that include a Vary header' do
|
508
661
|
before(:each) do
|
@@ -511,7 +664,7 @@ describe 'Rack::Cache::Context' do
|
|
511
664
|
res['Vary'] = 'Accept User-Agent Foo'
|
512
665
|
res['Cache-Control'] = 'max-age=10'
|
513
666
|
res['X-Response-Count'] = (count+=1).to_s
|
514
|
-
res.body = req.env['HTTP_USER_AGENT']
|
667
|
+
res.body = [req.env['HTTP_USER_AGENT']]
|
515
668
|
end
|
516
669
|
end
|
517
670
|
|
@@ -521,16 +674,16 @@ describe 'Rack::Cache::Context' do
|
|
521
674
|
'HTTP_USER_AGENT' => 'Bob/1.0'
|
522
675
|
response.should.be.ok
|
523
676
|
response.body.should.equal 'Bob/1.0'
|
524
|
-
cache.should.
|
525
|
-
cache.should.
|
677
|
+
cache.trace.should.include :miss
|
678
|
+
cache.trace.should.include :store
|
526
679
|
|
527
680
|
get '/',
|
528
681
|
'HTTP_ACCEPT' => 'text/html',
|
529
682
|
'HTTP_USER_AGENT' => 'Bob/1.0'
|
530
683
|
response.should.be.ok
|
531
684
|
response.body.should.equal 'Bob/1.0'
|
532
|
-
cache.should.
|
533
|
-
cache.should.
|
685
|
+
cache.trace.should.include :fresh
|
686
|
+
cache.trace.should.not.include :store
|
534
687
|
response.headers.should.include 'X-Content-Digest'
|
535
688
|
end
|
536
689
|
|
@@ -545,101 +698,30 @@ describe 'Rack::Cache::Context' do
|
|
545
698
|
get '/',
|
546
699
|
'HTTP_ACCEPT' => 'text/html',
|
547
700
|
'HTTP_USER_AGENT' => 'Bob/2.0'
|
548
|
-
cache.should.
|
549
|
-
cache.should.
|
701
|
+
cache.trace.should.include :miss
|
702
|
+
cache.trace.should.include :store
|
550
703
|
response.body.should.equal 'Bob/2.0'
|
551
704
|
response['X-Response-Count'].should.equal '2'
|
552
705
|
|
553
706
|
get '/',
|
554
707
|
'HTTP_ACCEPT' => 'text/html',
|
555
708
|
'HTTP_USER_AGENT' => 'Bob/1.0'
|
556
|
-
cache.should.
|
709
|
+
cache.trace.should.include :fresh
|
557
710
|
response.body.should.equal 'Bob/1.0'
|
558
711
|
response['X-Response-Count'].should.equal '1'
|
559
712
|
|
560
713
|
get '/',
|
561
714
|
'HTTP_ACCEPT' => 'text/html',
|
562
715
|
'HTTP_USER_AGENT' => 'Bob/2.0'
|
563
|
-
cache.should.
|
716
|
+
cache.trace.should.include :fresh
|
564
717
|
response.body.should.equal 'Bob/2.0'
|
565
718
|
response['X-Response-Count'].should.equal '2'
|
566
719
|
|
567
720
|
get '/',
|
568
721
|
'HTTP_USER_AGENT' => 'Bob/2.0'
|
569
|
-
cache.should.
|
722
|
+
cache.trace.should.include :miss
|
570
723
|
response.body.should.equal 'Bob/2.0'
|
571
724
|
response['X-Response-Count'].should.equal '3'
|
572
725
|
end
|
573
726
|
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
727
|
end
|