rack-cache 1.2 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/test/options_test.rb DELETED
@@ -1,77 +0,0 @@
1
- require "#{File.dirname(__FILE__)}/spec_setup"
2
- require 'rack/cache/options'
3
-
4
- module Rack::Cache::Options
5
- option_accessor :foo
6
- end
7
-
8
- class MockOptions
9
- include Rack::Cache::Options
10
- def initialize
11
- @env = nil
12
- initialize_options
13
- end
14
- end
15
-
16
- describe 'Rack::Cache::Options' do
17
- before { @options = MockOptions.new }
18
-
19
- describe '#set' do
20
- it 'sets a Symbol option as rack-cache.symbol' do
21
- @options.set :bar, 'baz'
22
- @options.options['rack-cache.bar'].should.equal 'baz'
23
- end
24
- it 'sets a String option as string' do
25
- @options.set 'foo.bar', 'bling'
26
- @options.options['foo.bar'].should.equal 'bling'
27
- end
28
- it 'sets all key/value pairs when given a Hash' do
29
- @options.set :foo => 'bar', :bar => 'baz', 'foo.bar' => 'bling'
30
- @options.foo.should.equal 'bar'
31
- @options.options['rack-cache.bar'].should.equal 'baz'
32
- @options.options['foo.bar'].should.equal 'bling'
33
- end
34
- end
35
-
36
- it 'makes options declared with option_accessor available as attributes' do
37
- @options.set :foo, 'bar'
38
- @options.foo.should.equal 'bar'
39
- end
40
-
41
- it 'allows setting multiple options via assignment' do
42
- @options.options = { :foo => 'bar', :bar => 'baz', 'foo.bar' => 'bling' }
43
- @options.foo.should.equal 'bar'
44
- @options.options['foo.bar'].should.equal 'bling'
45
- @options.options['rack-cache.bar'].should.equal 'baz'
46
- end
47
-
48
- it "allows storing the value as a block" do
49
- block = Proc.new { "bar block" }
50
- @options.set(:foo, &block)
51
- @options.options['rack-cache.foo'].should.equal block
52
- end
53
-
54
- it 'allows the cache key generator to be configured' do
55
- @options.should.respond_to :cache_key
56
- @options.should.respond_to :cache_key=
57
- end
58
-
59
- it 'allows the meta store to be configured' do
60
- @options.should.respond_to :metastore
61
- @options.should.respond_to :metastore=
62
- @options.metastore.should.not.be.nil
63
- end
64
-
65
- it 'allows the entity store to be configured' do
66
- @options.should.respond_to :entitystore
67
- @options.should.respond_to :entitystore=
68
- @options.entitystore.should.not.be.nil
69
- end
70
-
71
- it 'allows log verbosity to be configured' do
72
- @options.should.respond_to :verbose
73
- @options.should.respond_to :verbose=
74
- @options.should.respond_to :verbose?
75
- @options.verbose.should.not.be.nil
76
- end
77
- end
data/test/pony.jpg DELETED
Binary file
data/test/request_test.rb DELETED
@@ -1,19 +0,0 @@
1
- require "#{File.dirname(__FILE__)}/spec_setup"
2
- require 'rack/cache/request'
3
-
4
- describe 'Rack::Cache::Request' do
5
- it 'is marked as no_cache when the Cache-Control header includes the no-cache directive' do
6
- request = Rack::Cache::Request.new('HTTP_CACHE_CONTROL' => 'public, no-cache')
7
- request.should.be.no_cache
8
- end
9
-
10
- it 'is marked as no_cache when request should not be loaded from cache' do
11
- request = Rack::Cache::Request.new('HTTP_PRAGMA' => 'no-cache')
12
- request.should.be.no_cache
13
- end
14
-
15
- it 'is not marked as no_cache when neither no-cache directive is specified' do
16
- request = Rack::Cache::Request.new('HTTP_CACHE_CONTROL' => 'public')
17
- request.should.not.be.no_cache
18
- end
19
- end
@@ -1,184 +0,0 @@
1
- require "#{File.dirname(__FILE__)}/spec_setup"
2
-
3
- describe 'Rack::Cache::Response' do
4
- before do
5
- @now = Time.httpdate(Time.now.httpdate)
6
- @one_hour_ago = Time.httpdate((Time.now - (60**2)).httpdate)
7
- @one_hour_later = Time.httpdate((Time.now + (60**2)).httpdate)
8
- @res = Rack::Cache::Response.new(200, {'Date' => @now.httpdate}, [])
9
- end
10
-
11
- after do
12
- @now, @res, @one_hour_ago = nil
13
- end
14
-
15
- it 'marks Rack tuples with string typed statuses as cacheable' do
16
- @res = Rack::Cache::Response.new('200',{'Date' => @now.httpdate},[])
17
- @res.headers['Expires'] = @one_hour_later.httpdate
18
- @res.should.be.cacheable
19
- end
20
-
21
- it 'responds to #to_a with a Rack response tuple' do
22
- @res.should.respond_to :to_a
23
- @res.to_a.should.equal [200, {'Date' => @now.httpdate}, []]
24
- end
25
-
26
- describe '#cache_control' do
27
- it 'handles multiple name=value pairs' do
28
- @res.headers['Cache-Control'] = 'max-age=600, max-stale=300, min-fresh=570'
29
- @res.cache_control['max-age'].should.equal '600'
30
- @res.cache_control['max-stale'].should.equal '300'
31
- @res.cache_control['min-fresh'].should.equal '570'
32
- end
33
- it 'removes the header when given an empty hash' do
34
- @res.headers['Cache-Control'] = 'max-age=600, must-revalidate'
35
- @res.cache_control['max-age'].should.equal '600'
36
- @res.cache_control = {}
37
- @res.headers.should.not.include 'Cache-Control'
38
- end
39
- end
40
-
41
- describe '#validateable?' do
42
- it 'is true when Last-Modified header present' do
43
- @res = Rack::Cache::Response.new(200, {'Last-Modified' => @one_hour_ago.httpdate}, [])
44
- @res.should.be.validateable
45
- end
46
- it 'is true when ETag header present' do
47
- @res = Rack::Cache::Response.new(200, {'ETag' => '"12345"'}, [])
48
- @res.should.be.validateable
49
- end
50
- it 'is false when no validator is present' do
51
- @res = Rack::Cache::Response.new(200, {}, [])
52
- @res.should.not.be.validateable
53
- end
54
- end
55
-
56
- describe '#date' do
57
- it 'uses the Date header if present' do
58
- @res = Rack::Cache::Response.new(200, {'Date' => @one_hour_ago.httpdate}, [])
59
- @res.date.should.equal @one_hour_ago
60
- end
61
- it 'uses the current time when no Date header present' do
62
- @res = Rack::Cache::Response.new(200, {}, [])
63
- @res.date.to_i.should.be.close Time.now.to_i, 1
64
- end
65
- it 'returns the correct date when the header is modified directly' do
66
- @res = Rack::Cache::Response.new(200, { 'Date' => @one_hour_ago.httpdate }, [])
67
- @res.date.should.equal @one_hour_ago
68
- @res.headers['Date'] = @now.httpdate
69
- @res.date.should.equal @now
70
- end
71
- end
72
-
73
- describe '#max_age' do
74
- it 'uses s-maxage cache control directive when present' do
75
- @res.headers['Cache-Control'] = 's-maxage=600, max-age=0'
76
- @res.max_age.should.equal 600
77
- end
78
- it 'falls back to max-age when no s-maxage directive present' do
79
- @res.headers['Cache-Control'] = 'max-age=600'
80
- @res.max_age.should.equal 600
81
- end
82
- it 'falls back to Expires when no max-age or s-maxage directive present' do
83
- @res.headers['Cache-Control'] = 'must-revalidate'
84
- @res.headers['Expires'] = @one_hour_later.httpdate
85
- @res.max_age.should.equal 60 ** 2
86
- end
87
- it 'gives a #max_age of nil when no freshness information available' do
88
- @res.max_age.should.be.nil
89
- end
90
- end
91
-
92
- describe '#private=' do
93
- it 'adds the private Cache-Control directive when set true' do
94
- @res.headers['Cache-Control'] = 'max-age=100'
95
- @res.private = true
96
- @res.headers['Cache-Control'].split(', ').sort.
97
- should.equal ['max-age=100', 'private']
98
- end
99
- it 'removes the public Cache-Control directive' do
100
- @res.headers['Cache-Control'] = 'public, max-age=100'
101
- @res.private = true
102
- @res.headers['Cache-Control'].split(', ').sort.
103
- should.equal ['max-age=100', 'private']
104
- end
105
- end
106
-
107
- describe '#expire!' do
108
- it 'sets the Age to be equal to the max-age' do
109
- @res.headers['Cache-Control'] = 'max-age=100'
110
- @res.expire!
111
- @res.headers['Age'].should.equal '100'
112
- end
113
- it 'sets the Age to be equal to the s-maxage when both max-age and s-maxage present' do
114
- @res.headers['Cache-Control'] = 'max-age=100, s-maxage=500'
115
- @res.expire!
116
- @res.headers['Age'].should.equal '500'
117
- end
118
- it 'does nothing when the response is already stale/expired' do
119
- @res.headers['Cache-Control'] = 'max-age=5, s-maxage=500'
120
- @res.headers['Age'] = '1000'
121
- @res.expire!
122
- @res.headers['Age'].should.equal '1000'
123
- end
124
- it 'does nothing when the response does not include freshness information' do
125
- @res.expire!
126
- @res.headers.should.not.include 'Age'
127
- end
128
- end
129
-
130
- describe '#ttl' do
131
- it 'is nil when no Expires or Cache-Control headers present' do
132
- @res.ttl.should.be.nil
133
- end
134
- it 'uses the Expires header when no max-age is present' do
135
- @res.headers['Expires'] = (@res.now + (60**2)).httpdate
136
- @res.ttl.should.be.close(60**2, 1)
137
- end
138
- it 'returns negative values when Expires is in part' do
139
- @res.ttl.should.be.nil
140
- @res.headers['Expires'] = @one_hour_ago.httpdate
141
- @res.ttl.should.be < 0
142
- end
143
- it 'uses the Cache-Control max-age value when present' do
144
- @res.headers['Cache-Control'] = 'max-age=60'
145
- @res.ttl.should.be.close(60, 1)
146
- end
147
- end
148
-
149
- describe '#vary' do
150
- it 'is nil when no Vary header is present' do
151
- @res.vary.should.be.nil
152
- end
153
- it 'returns the literal value of the Vary header' do
154
- @res.headers['Vary'] = 'Foo Bar Baz'
155
- @res.vary.should.equal 'Foo Bar Baz'
156
- end
157
- it 'can be checked for existence using the #vary? method' do
158
- @res.should.respond_to :vary?
159
- @res.should.not.vary
160
- @res.headers['Vary'] = '*'
161
- @res.should.vary
162
- end
163
- end
164
-
165
- describe '#vary_header_names' do
166
- it 'returns an empty Array when no Vary header is present' do
167
- @res.vary_header_names.should.be.empty
168
- end
169
- it 'parses a single header name value' do
170
- @res.headers['Vary'] = 'Accept-Language'
171
- @res.vary_header_names.should.equal ['Accept-Language']
172
- end
173
- it 'parses multiple header name values separated by spaces' do
174
- @res.headers['Vary'] = 'Accept-Language User-Agent X-Foo'
175
- @res.vary_header_names.should.equal \
176
- ['Accept-Language', 'User-Agent', 'X-Foo']
177
- end
178
- it 'parses multiple header name values separated by commas' do
179
- @res.headers['Vary'] = 'Accept-Language,User-Agent, X-Foo'
180
- @res.vary_header_names.should.equal \
181
- ['Accept-Language', 'User-Agent', 'X-Foo']
182
- end
183
- end
184
- end
data/test/spec_setup.rb DELETED
@@ -1,232 +0,0 @@
1
- require 'pp'
2
- require 'tmpdir'
3
- require 'stringio'
4
-
5
- [STDOUT, STDERR].each { |io| io.sync = true }
6
-
7
- begin
8
- require 'bacon'
9
- rescue LoadError => boom
10
- require 'rubygems' rescue nil
11
- require 'bacon'
12
- end
13
-
14
- # Set the MEMCACHED environment variable as follows to enable testing
15
- # of the MemCached meta and entity stores.
16
- ENV['MEMCACHED'] ||= 'localhost:11211'
17
- $memcached = nil
18
- $dalli = nil
19
-
20
- def have_memcached?(server=ENV['MEMCACHED'])
21
- return $memcached unless $memcached.nil?
22
-
23
- # silence warnings from memcached
24
- begin
25
- v, $VERBOSE = $VERBOSE, nil
26
- require 'memcached'
27
- ensure
28
- $VERBOSE = v
29
- end
30
-
31
- $memcached = Memcached.new(server)
32
- $memcached.set('ping', '')
33
- true
34
- rescue LoadError => boom
35
- warn "memcached library not available. related tests will be skipped."
36
- $memcached = false
37
- false
38
- rescue => boom
39
- warn "memcached not working. related tests will be skipped."
40
- $memcached = false
41
- false
42
- end
43
-
44
- have_memcached?
45
-
46
- def have_dalli?(server=ENV['MEMCACHED'])
47
- return $dalli unless $dalli.nil?
48
- require 'dalli'
49
- $dalli = Dalli::Client.new(server)
50
- $dalli.set('ping', '')
51
- true
52
- rescue LoadError => boom
53
- warn "dalli library not available. related tests will be skipped."
54
- $dalli = false
55
- false
56
- rescue => boom
57
- warn "dalli not working. related tests will be skipped."
58
- $dalli = false
59
- false
60
- end
61
-
62
- have_dalli?
63
-
64
- def need_dalli(forwhat)
65
- yield if have_dalli?
66
- end
67
-
68
- def need_memcached(forwhat)
69
- yield if have_memcached?
70
- end
71
-
72
- def need_java(forwhat)
73
- yield if RUBY_PLATFORM =~ /java/
74
- end
75
-
76
-
77
- # Setup the load path ..
78
- $LOAD_PATH.unshift File.dirname(File.dirname(__FILE__)) + '/lib'
79
- $LOAD_PATH.unshift File.dirname(__FILE__)
80
-
81
- require 'rack/cache'
82
-
83
- # Methods for constructing downstream applications / response
84
- # generators.
85
- module CacheContextHelpers
86
-
87
- # The Rack::Cache::Context instance used for the most recent
88
- # request.
89
- attr_reader :cache
90
-
91
- # An Array of Rack::Cache::Context instances used for each request, in
92
- # request order.
93
- attr_reader :caches
94
-
95
- # The Rack::Response instance result of the most recent request.
96
- attr_reader :response
97
-
98
- # An Array of Rack::Response instances for each request, in request order.
99
- attr_reader :responses
100
-
101
- # The backend application object.
102
- attr_reader :app
103
-
104
- def setup_cache_context
105
- # holds each Rack::Cache::Context
106
- @app = nil
107
-
108
- # each time a request is made, a clone of @cache_template is used
109
- # and appended to @caches.
110
- @cache_template = nil
111
- @cache = nil
112
- @caches = []
113
- @errors = StringIO.new
114
- @cache_config = nil
115
-
116
- @called = false
117
- @request = nil
118
- @response = nil
119
- @responses = []
120
-
121
- @storage = Rack::Cache::Storage.new
122
- end
123
-
124
- def teardown_cache_context
125
- @app, @cache_template, @cache, @caches, @called,
126
- @request, @response, @responses, @cache_config, @cache_prototype = nil
127
- end
128
-
129
- # A basic response with 200 status code and a tiny body.
130
- def respond_with(status=200, headers={}, body=['Hello World'], &bk)
131
- called = false
132
- @app =
133
- lambda do |env|
134
- called = true
135
- response = Rack::Response.new(body, status, headers)
136
- request = Rack::Request.new(env)
137
- bk.call(request, response) if bk
138
- response.finish
139
- end
140
- @app.meta_def(:called?) { called }
141
- @app.meta_def(:reset!) { called = false }
142
- @app
143
- end
144
-
145
- def cache_config(&block)
146
- @cache_config = block
147
- end
148
-
149
- def request(method, uri='/', opts={})
150
- opts = {
151
- 'rack.run_once' => true,
152
- 'rack.errors' => @errors,
153
- 'rack-cache.storage' => @storage
154
- }.merge(opts)
155
-
156
- fail 'response not specified (use respond_with)' if @app.nil?
157
- @app.reset! if @app.respond_to?(:reset!)
158
-
159
- @cache_prototype ||= Rack::Cache::Context.new(@app, &@cache_config)
160
- @cache = @cache_prototype.clone
161
- @caches << @cache
162
- @request = Rack::MockRequest.new(@cache)
163
- yield @cache if block_given?
164
- @response = @request.request(method.to_s.upcase, uri, opts)
165
- @responses << @response
166
- @response
167
- end
168
-
169
- def get(stem, env={}, &b)
170
- request(:get, stem, env, &b)
171
- end
172
-
173
- def head(stem, env={}, &b)
174
- request(:head, stem, env, &b)
175
- end
176
-
177
- def post(*args, &b)
178
- request(:post, *args, &b)
179
- end
180
- end
181
-
182
-
183
- module TestHelpers
184
- include FileUtils
185
- F = File
186
-
187
- @@temp_dir_count = 0
188
-
189
- def create_temp_directory
190
- @@temp_dir_count += 1
191
- path = F.join(Dir.tmpdir, "rack-cache-#{$$}-#{@@temp_dir_count}")
192
- mkdir_p path
193
- if block_given?
194
- yield path
195
- remove_entry_secure path
196
- end
197
- path
198
- end
199
-
200
- def create_temp_file(root, file, data='')
201
- path = F.join(root, file)
202
- mkdir_p F.dirname(path)
203
- F.open(path, 'w') { |io| io.write(data) }
204
- end
205
-
206
- end
207
-
208
- class Bacon::Context
209
- include TestHelpers
210
- include CacheContextHelpers
211
- end
212
-
213
- # Metaid == a few simple metaclass helper
214
- # (See http://whytheluckystiff.net/articles/seeingMetaclassesClearly.html.)
215
- class Object
216
- # The hidden singleton lurks behind everyone
217
- def metaclass; class << self; self; end; end
218
- def meta_eval(&blk); metaclass.instance_eval(&blk); end
219
- # Adds methods to a metaclass
220
- def meta_def name, &blk
221
- meta_eval { define_method name, &blk }
222
- end
223
- # Defines an instance method within a class
224
- def class_def name, &blk
225
- class_eval { define_method name, &blk }
226
- end
227
-
228
- # True when the Object is neither false or nil.
229
- def truthy?
230
- !!self
231
- end
232
- end
data/test/storage_test.rb DELETED
@@ -1,94 +0,0 @@
1
- require "#{File.dirname(__FILE__)}/spec_setup"
2
- require 'rack/cache/storage'
3
-
4
- describe 'Rack::Cache::Storage' do
5
- before do
6
- @storage = Rack::Cache::Storage.new
7
- end
8
-
9
- it "fails when an unknown URI scheme is provided" do
10
- lambda { @storage.resolve_metastore_uri('foo:/') }.should.raise
11
- end
12
- it "creates a new MetaStore for URI if none exists" do
13
- @storage.resolve_metastore_uri('heap:/').
14
- should.be.kind_of Rack::Cache::MetaStore
15
- end
16
- it "returns an existing MetaStore instance for URI that exists" do
17
- store = @storage.resolve_metastore_uri('heap:/')
18
- @storage.resolve_metastore_uri('heap:/').should.be.same_as store
19
- end
20
- it "creates a new EntityStore for URI if none exists" do
21
- @storage.resolve_entitystore_uri('heap:/').
22
- should.be.kind_of Rack::Cache::EntityStore
23
- end
24
- it "returns an existing EntityStore instance for URI that exists" do
25
- store = @storage.resolve_entitystore_uri('heap:/')
26
- @storage.resolve_entitystore_uri('heap:/').should.be.same_as store
27
- end
28
- it "clears all URI -> store mappings with #clear" do
29
- meta = @storage.resolve_metastore_uri('heap:/')
30
- entity = @storage.resolve_entitystore_uri('heap:/')
31
- @storage.clear
32
- @storage.resolve_metastore_uri('heap:/').should.not.be.same_as meta
33
- @storage.resolve_entitystore_uri('heap:/').should.not.be.same_as entity
34
- end
35
-
36
- describe 'Heap Store URIs' do
37
- %w[heap:/ mem:/].each do |uri|
38
- it "resolves #{uri} meta store URIs" do
39
- @storage.resolve_metastore_uri(uri).
40
- should.be.kind_of Rack::Cache::MetaStore
41
- end
42
- it "resolves #{uri} entity store URIs" do
43
- @storage.resolve_entitystore_uri(uri).
44
- should.be.kind_of Rack::Cache::EntityStore
45
- end
46
- end
47
- end
48
-
49
- describe 'Disk Store URIs' do
50
- before do
51
- @temp_dir = create_temp_directory
52
- end
53
- after do
54
- remove_entry_secure @temp_dir
55
- @temp_dir = nil
56
- end
57
-
58
- %w[file: disk:].each do |uri|
59
- it "resolves #{uri} meta store URIs" do
60
- @storage.resolve_metastore_uri(uri + @temp_dir).
61
- should.be.kind_of Rack::Cache::MetaStore
62
- end
63
- it "resolves #{uri} entity store URIs" do
64
- @storage.resolve_entitystore_uri(uri + @temp_dir).
65
- should.be.kind_of Rack::Cache::EntityStore
66
- end
67
- end
68
- end
69
-
70
- if have_memcached?
71
-
72
- describe 'MemCache Store URIs' do
73
- %w[memcache: memcached:].each do |scheme|
74
- it "resolves #{scheme} meta store URIs" do
75
- uri = scheme + '//' + ENV['MEMCACHED']
76
- @storage.resolve_metastore_uri(uri).
77
- should.be.kind_of Rack::Cache::MetaStore
78
- end
79
- it "resolves #{scheme} entity store URIs" do
80
- uri = scheme + '//' + ENV['MEMCACHED']
81
- @storage.resolve_entitystore_uri(uri).
82
- should.be.kind_of Rack::Cache::EntityStore
83
- end
84
- end
85
- it 'supports namespaces in memcached: URIs' do
86
- uri = "memcached://" + ENV['MEMCACHED'] + "/namespace"
87
- @storage.resolve_metastore_uri(uri).
88
- should.be.kind_of Rack::Cache::MetaStore
89
- end
90
- end
91
-
92
- end
93
-
94
- end