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:
- 8890 Bytes
1 | require "#{File.dirname(__FILE__)}/spec_setup" |
2 | require 'rack/cache/metastore' |
3 | |
4 | describe_shared 'A Rack::Cache::MetaStore Implementation' do |
5 | before do |
6 | @request = mock_request('/', {}) |
7 | @response = mock_response(200, {}, ['hello world']) |
8 | @entity_store = nil |
9 | end |
10 | after do |
11 | @store = nil |
12 | @entity_store = nil |
13 | end |
14 | |
15 | # Low-level implementation methods =========================================== |
16 | |
17 | it 'writes a list of negotation tuples with #write' do |
18 | lambda { @store.write('/test', [[{}, {}]]) }.should.not.raise |
19 | end |
20 | |
21 | it 'reads a list of negotation tuples with #read' do |
22 | @store.write('/test', [[{},{}],[{},{}]]) |
23 | tuples = @store.read('/test') |
24 | tuples.should.equal [ [{},{}], [{},{}] ] |
25 | end |
26 | |
27 | it 'reads an empty list with #read when nothing cached at key' do |
28 | @store.read('/nothing').should.be.empty |
29 | end |
30 | |
31 | it 'removes entries for key with #purge' do |
32 | @store.write('/test', [[{},{}]]) |
33 | @store.read('/test').should.not.be.empty |
34 | |
35 | @store.purge('/test') |
36 | @store.read('/test').should.be.empty |
37 | end |
38 | |
39 | it 'succeeds when purging non-existing entries' do |
40 | @store.read('/test').should.be.empty |
41 | @store.purge('/test') |
42 | end |
43 | |
44 | it 'returns nil from #purge' do |
45 | @store.write('/test', [[{},{}]]) |
46 | @store.purge('/test').should.be nil |
47 | @store.read('/test').should.equal [] |
48 | end |
49 | |
50 | %w[/test http://example.com:8080/ /test?x=y /test?x=y&p=q].each do |key| |
51 | it "can read and write key: '#{key}'" do |
52 | lambda { @store.write(key, [[{},{}]]) }.should.not.raise |
53 | @store.read(key).should.equal [[{},{}]] |
54 | end |
55 | end |
56 | |
57 | it "can read and write fairly large keys" do |
58 | key = "b" * 4096 |
59 | lambda { @store.write(key, [[{},{}]]) }.should.not.raise |
60 | @store.read(key).should.equal [[{},{}]] |
61 | end |
62 | |
63 | it "allows custom cache keys from block" do |
64 | request = mock_request('/test', {}) |
65 | request.env['rack-cache.cache_key'] = |
66 | lambda { |request| request.path_info.reverse } |
67 | @store.cache_key(request).should == 'tset/' |
68 | end |
69 | |
70 | it "allows custom cache keys from class" do |
71 | request = mock_request('/test', {}) |
72 | request.env['rack-cache.cache_key'] = Class.new do |
73 | def self.call(request); request.path_info.reverse end |
74 | end |
75 | @store.cache_key(request).should == 'tset/' |
76 | end |
77 | |
78 | # Abstract methods =========================================================== |
79 | |
80 | # Stores an entry for the given request args, returns a url encoded cache key |
81 | # for the request. |
82 | define_method :store_simple_entry do |*request_args| |
83 | path, headers = request_args |
84 | @request = mock_request(path || '/test', headers || {}) |
85 | @response = mock_response(200, {'Cache-Control' => 'max-age=420'}, ['test']) |
86 | body = @response.body |
87 | cache_key = @store.store(@request, @response, @entity_store) |
88 | @response.body.should.not.be body |
89 | cache_key |
90 | end |
91 | |
92 | it 'stores a cache entry' do |
93 | cache_key = store_simple_entry |
94 | @store.read(cache_key).should.not.be.empty |
95 | end |
96 | |
97 | it 'sets the X-Content-Digest response header before storing' do |
98 | cache_key = store_simple_entry |
99 | req, res = @store.read(cache_key).first |
100 | res['X-Content-Digest'].should.equal 'a94a8fe5ccb19ba61c4c0873d391e987982fbbd3' |
101 | end |
102 | |
103 | it 'finds a stored entry with #lookup' do |
104 | store_simple_entry |
105 | response = @store.lookup(@request, @entity_store) |
106 | response.should.not.be.nil |
107 | response.should.be.kind_of Rack::Cache::Response |
108 | end |
109 | |
110 | it 'does not find an entry with #lookup when none exists' do |
111 | req = mock_request('/test', {'HTTP_FOO' => 'Foo', 'HTTP_BAR' => 'Bar'}) |
112 | @store.lookup(req, @entity_store).should.be.nil |
113 | end |
114 | |
115 | it "canonizes urls for cache keys" do |
116 | store_simple_entry(path='/test?x=y&p=q') |
117 | |
118 | hits_req = mock_request(path, {}) |
119 | miss_req = mock_request('/test?p=x', {}) |
120 | |
121 | @store.lookup(hits_req, @entity_store).should.not.be.nil |
122 | @store.lookup(miss_req, @entity_store).should.be.nil |
123 | end |
124 | |
125 | it 'does not find an entry with #lookup when the body does not exist' do |
126 | store_simple_entry |
127 | @response.headers['X-Content-Digest'].should.not.be.nil |
128 | @entity_store.purge(@response.headers['X-Content-Digest']) |
129 | @store.lookup(@request, @entity_store).should.be.nil |
130 | end |
131 | |
132 | it 'restores response headers properly with #lookup' do |
133 | store_simple_entry |
134 | response = @store.lookup(@request, @entity_store) |
135 | response.headers. |
136 | should.equal @response.headers.merge('Content-Length' => '4') |
137 | end |
138 | |
139 | it 'restores response body from entity store with #lookup' do |
140 | store_simple_entry |
141 | response = @store.lookup(@request, @entity_store) |
142 | body = '' ; response.body.each {|p| body << p} |
143 | body.should.equal 'test' |
144 | end |
145 | |
146 | it 'invalidates meta and entity store entries with #invalidate' do |
147 | store_simple_entry |
148 | @store.invalidate(@request, @entity_store) |
149 | response = @store.lookup(@request, @entity_store) |
150 | response.should.be.kind_of Rack::Cache::Response |
151 | response.should.not.be.fresh |
152 | end |
153 | |
154 | it 'succeeds quietly when #invalidate called with no matching entries' do |
155 | req = mock_request('/test', {}) |
156 | @store.invalidate(req, @entity_store) |
157 | @store.lookup(@request, @entity_store).should.be.nil |
158 | end |
159 | |
160 | # Vary ======================================================================= |
161 | |
162 | it 'does not return entries that Vary with #lookup' do |
163 | req1 = mock_request('/test', {'HTTP_FOO' => 'Foo', 'HTTP_BAR' => 'Bar'}) |
164 | req2 = mock_request('/test', {'HTTP_FOO' => 'Bling', 'HTTP_BAR' => 'Bam'}) |
165 | res = mock_response(200, {'Vary' => 'Foo Bar'}, ['test']) |
166 | @store.store(req1, res, @entity_store) |
167 | |
168 | @store.lookup(req2, @entity_store).should.be.nil |
169 | end |
170 | |
171 | it 'stores multiple responses for each Vary combination' do |
172 | req1 = mock_request('/test', {'HTTP_FOO' => 'Foo', 'HTTP_BAR' => 'Bar'}) |
173 | res1 = mock_response(200, {'Vary' => 'Foo Bar'}, ['test 1']) |
174 | key = @store.store(req1, res1, @entity_store) |
175 | |
176 | req2 = mock_request('/test', {'HTTP_FOO' => 'Bling', 'HTTP_BAR' => 'Bam'}) |
177 | res2 = mock_response(200, {'Vary' => 'Foo Bar'}, ['test 2']) |
178 | @store.store(req2, res2, @entity_store) |
179 | |
180 | req3 = mock_request('/test', {'HTTP_FOO' => 'Baz', 'HTTP_BAR' => 'Boom'}) |
181 | res3 = mock_response(200, {'Vary' => 'Foo Bar'}, ['test 3']) |
182 | @store.store(req3, res3, @entity_store) |
183 | |
184 | slurp(@store.lookup(req3, @entity_store).body).should.equal 'test 3' |
185 | slurp(@store.lookup(req1, @entity_store).body).should.equal 'test 1' |
186 | slurp(@store.lookup(req2, @entity_store).body).should.equal 'test 2' |
187 | |
188 | @store.read(key).length.should.equal 3 |
189 | end |
190 | |
191 | it 'overwrites non-varying responses with #store' do |
192 | req1 = mock_request('/test', {'HTTP_FOO' => 'Foo', 'HTTP_BAR' => 'Bar'}) |
193 | res1 = mock_response(200, {'Vary' => 'Foo Bar'}, ['test 1']) |
194 | key = @store.store(req1, res1, @entity_store) |
195 | slurp(@store.lookup(req1, @entity_store).body).should.equal 'test 1' |
196 | |
197 | req2 = mock_request('/test', {'HTTP_FOO' => 'Bling', 'HTTP_BAR' => 'Bam'}) |
198 | res2 = mock_response(200, {'Vary' => 'Foo Bar'}, ['test 2']) |
199 | @store.store(req2, res2, @entity_store) |
200 | slurp(@store.lookup(req2, @entity_store).body).should.equal 'test 2' |
201 | |
202 | req3 = mock_request('/test', {'HTTP_FOO' => 'Foo', 'HTTP_BAR' => 'Bar'}) |
203 | res3 = mock_response(200, {'Vary' => 'Foo Bar'}, ['test 3']) |
204 | @store.store(req3, res3, @entity_store) |
205 | slurp(@store.lookup(req1, @entity_store).body).should.equal 'test 3' |
206 | |
207 | @store.read(key).length.should.equal 2 |
208 | end |
209 | |
210 | # Helper Methods ============================================================= |
211 | |
212 | define_method :mock_request do |uri,opts| |
213 | env = Rack::MockRequest.env_for(uri, opts || {}) |
214 | Rack::Cache::Request.new(env) |
215 | end |
216 | |
217 | define_method :mock_response do |status,headers,body| |
218 | headers ||= {} |
219 | body = Array(body).compact |
220 | Rack::Cache::Response.new(status, headers, body) |
221 | end |
222 | |
223 | define_method :slurp do |body| |
224 | buf = '' |
225 | body.each {|part| buf << part } |
226 | buf |
227 | end |
228 | end |
229 | |
230 | |
231 | describe 'Rack::Cache::MetaStore' do |
232 | describe 'Heap' do |
233 | it_should_behave_like 'A Rack::Cache::MetaStore Implementation' |
234 | before do |
235 | @store = Rack::Cache::MetaStore::Heap.new |
236 | @entity_store = Rack::Cache::EntityStore::Heap.new |
237 | end |
238 | end |
239 | |
240 | describe 'Disk' do |
241 | it_should_behave_like 'A Rack::Cache::MetaStore Implementation' |
242 | before do |
243 | @temp_dir = create_temp_directory |
244 | @store = Rack::Cache::MetaStore::Disk.new("#{@temp_dir}/meta") |
245 | @entity_store = Rack::Cache::EntityStore::Disk.new("#{@temp_dir}/entity") |
246 | end |
247 | after do |
248 | remove_entry_secure @temp_dir |
249 | end |
250 | end |
251 | |
252 | need_memcached 'metastore tests' do |
253 | describe 'MemCache' do |
254 | it_should_behave_like 'A Rack::Cache::MetaStore Implementation' |
255 | before :each do |
256 | @temp_dir = create_temp_directory |
257 | $memcached.flush |
258 | @store = Rack::Cache::MetaStore::MemCache.new($memcached) |
259 | @entity_store = Rack::Cache::EntityStore::Heap.new |
260 | end |
261 | end |
262 | end |
263 | end |