sinatra 0.3.3 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of sinatra might be problematic. Click here for more details.

Files changed (82) hide show
  1. data/AUTHORS +40 -0
  2. data/CHANGES +189 -0
  3. data/README.rdoc +146 -117
  4. data/Rakefile +33 -10
  5. data/{test → compat}/app_test.rb +11 -10
  6. data/{test → compat}/application_test.rb +10 -5
  7. data/compat/builder_test.rb +101 -0
  8. data/{test → compat}/custom_error_test.rb +0 -0
  9. data/compat/erb_test.rb +136 -0
  10. data/{test → compat}/events_test.rb +16 -3
  11. data/compat/filter_test.rb +30 -0
  12. data/compat/haml_test.rb +233 -0
  13. data/compat/helper.rb +30 -0
  14. data/compat/mapped_error_test.rb +72 -0
  15. data/{test → compat}/pipeline_test.rb +9 -4
  16. data/{test → compat}/public/foo.xml +0 -0
  17. data/compat/sass_test.rb +57 -0
  18. data/{test → compat}/sessions_test.rb +0 -0
  19. data/{test → compat}/streaming_test.rb +4 -1
  20. data/{test → compat}/sym_params_test.rb +0 -0
  21. data/{test → compat}/template_test.rb +0 -0
  22. data/{test → compat}/use_in_file_templates_test.rb +0 -0
  23. data/{test → compat}/views/foo.builder +0 -0
  24. data/{test → compat}/views/foo.erb +0 -0
  25. data/{test → compat}/views/foo.haml +0 -0
  26. data/{test → compat}/views/foo.sass +0 -0
  27. data/{test → compat}/views/foo_layout.erb +0 -0
  28. data/{test → compat}/views/foo_layout.haml +0 -0
  29. data/{test → compat}/views/layout_test/foo.builder +0 -0
  30. data/{test → compat}/views/layout_test/foo.erb +0 -0
  31. data/{test → compat}/views/layout_test/foo.haml +0 -0
  32. data/{test → compat}/views/layout_test/foo.sass +0 -0
  33. data/{test → compat}/views/layout_test/layout.builder +0 -0
  34. data/{test → compat}/views/layout_test/layout.erb +0 -0
  35. data/{test → compat}/views/layout_test/layout.haml +0 -0
  36. data/{test → compat}/views/layout_test/layout.sass +0 -0
  37. data/{test → compat}/views/no_layout/no_layout.builder +0 -0
  38. data/{test → compat}/views/no_layout/no_layout.haml +0 -0
  39. data/lib/sinatra.rb +6 -1484
  40. data/lib/sinatra/base.rb +838 -0
  41. data/lib/sinatra/compat.rb +239 -0
  42. data/{images → lib/sinatra/images}/404.png +0 -0
  43. data/{images → lib/sinatra/images}/500.png +0 -0
  44. data/lib/sinatra/main.rb +48 -0
  45. data/lib/sinatra/test.rb +114 -0
  46. data/lib/sinatra/test/bacon.rb +17 -0
  47. data/lib/sinatra/test/rspec.rb +7 -8
  48. data/lib/sinatra/test/spec.rb +3 -4
  49. data/lib/sinatra/test/unit.rb +3 -5
  50. data/sinatra.gemspec +68 -35
  51. data/test/base_test.rb +68 -0
  52. data/test/builder_test.rb +50 -87
  53. data/test/data/reload_app_file.rb +3 -0
  54. data/test/erb_test.rb +38 -124
  55. data/test/filter_test.rb +27 -22
  56. data/test/haml_test.rb +51 -216
  57. data/test/helper.rb +22 -6
  58. data/test/helpers_test.rb +361 -0
  59. data/test/mapped_error_test.rb +137 -49
  60. data/test/middleware_test.rb +58 -0
  61. data/test/options_test.rb +97 -0
  62. data/test/reload_test.rb +61 -0
  63. data/test/request_test.rb +18 -0
  64. data/test/result_test.rb +88 -0
  65. data/test/routing_test.rb +391 -0
  66. data/test/sass_test.rb +27 -48
  67. data/test/sinatra_test.rb +13 -0
  68. data/test/static_test.rb +57 -0
  69. data/test/templates_test.rb +88 -0
  70. data/test/views/hello.builder +1 -0
  71. data/test/views/hello.erb +1 -0
  72. data/test/views/hello.haml +1 -0
  73. data/test/views/hello.sass +2 -0
  74. data/test/views/hello.test +1 -0
  75. data/test/views/layout2.builder +3 -0
  76. data/test/views/layout2.erb +2 -0
  77. data/test/views/layout2.haml +2 -0
  78. data/test/views/layout2.test +1 -0
  79. metadata +80 -48
  80. data/ChangeLog +0 -96
  81. data/lib/sinatra/test/methods.rb +0 -76
  82. data/test/event_context_test.rb +0 -15
@@ -1,30 +1,35 @@
1
1
  require File.dirname(__FILE__) + '/helper'
2
2
 
3
- context "before filters" do
3
+ describe "Filters" do
4
+ it "executes filters in the order defined" do
5
+ count = 0
6
+ mock_app do
7
+ get('/') { 'Hello World' }
8
+ before {
9
+ assert_equal 0, count
10
+ count = 1
11
+ }
12
+ before {
13
+ assert_equal 1, count
14
+ count = 2
15
+ }
16
+ end
4
17
 
5
- setup do
6
- Sinatra.application = nil
7
- @app = Sinatra.application
18
+ get '/'
19
+ assert ok?
20
+ assert_equal 2, count
21
+ assert_equal 'Hello World', body
8
22
  end
9
23
 
10
- specify "should be executed in the order defined" do
11
- invoked = 0x0
12
- @app.before { invoked = 0x01 }
13
- @app.before { invoked |= 0x02 }
14
- @app.get('/') { 'Hello World' }
15
- get_it '/'
16
- should.be.ok
17
- body.should.be == 'Hello World'
18
- invoked.should.be == 0x03
19
- end
24
+ it "allows filters to modify the request" do
25
+ mock_app {
26
+ get('/foo') { 'foo' }
27
+ get('/bar') { 'bar' }
28
+ before { request.path_info = '/bar' }
29
+ }
20
30
 
21
- specify "should be capable of modifying the request" do
22
- @app.get('/foo') { 'foo' }
23
- @app.get('/bar') { 'bar' }
24
- @app.before { request.path_info = '/bar' }
25
- get_it '/foo'
26
- should.be.ok
27
- body.should.be == 'bar'
31
+ get '/foo'
32
+ assert ok?
33
+ assert_equal 'bar', body
28
34
  end
29
-
30
35
  end
@@ -1,233 +1,68 @@
1
1
  require File.dirname(__FILE__) + '/helper'
2
2
 
3
- context "Haml" do
4
-
5
- setup do
6
- Sinatra.application = nil
3
+ describe "HAML Templates" do
4
+ def haml_app(&block)
5
+ mock_app {
6
+ set :views, File.dirname(__FILE__) + '/views'
7
+ get '/', &block
8
+ }
9
+ get '/'
7
10
  end
8
11
 
9
- context "without layouts" do
10
-
11
- setup do
12
- Sinatra.application = nil
13
- end
14
-
15
- specify "should render" do
16
-
17
- get '/no_layout' do
18
- haml '== #{1+1}'
19
- end
20
-
21
- get_it '/no_layout'
22
- should.be.ok
23
- body.should == "2\n"
24
-
25
- end
12
+ it 'renders inline HAML strings' do
13
+ haml_app { haml '%h1 Hiya' }
14
+ assert ok?
15
+ assert_equal "<h1>Hiya</h1>\n", body
26
16
  end
27
17
 
28
- context "with layouts" do
29
-
30
- setup do
31
- Sinatra.application = nil
32
- end
33
-
34
- specify "can be inline" do
35
-
36
- layout do
37
- '== This is #{yield}!'
38
- end
39
-
40
- get '/lay' do
41
- haml 'Blake'
42
- end
43
-
44
- get_it '/lay'
45
- should.be.ok
46
- body.should.equal "This is Blake\n!\n"
47
-
48
- end
49
-
50
- specify "can use named layouts" do
51
-
52
- layout :pretty do
53
- '%h1== #{yield}'
54
- end
55
-
56
- get '/pretty' do
57
- haml 'Foo', :layout => :pretty
58
- end
59
-
60
- get '/not_pretty' do
61
- haml 'Bar'
62
- end
63
-
64
- get_it '/pretty'
65
- body.should.equal "<h1>Foo</h1>\n"
66
-
67
- get_it '/not_pretty'
68
- body.should.equal "Bar\n"
69
-
70
- end
71
-
72
- specify "can be read from a file if they're not inlined" do
73
-
74
- get '/foo' do
75
- @title = 'Welcome to the Hello Program'
76
- haml 'Blake', :layout => :foo_layout,
77
- :views_directory => File.dirname(__FILE__) + "/views"
78
- end
79
-
80
- get_it '/foo'
81
- body.should.equal "Welcome to the Hello Program\nHi Blake\n"
82
-
83
- end
84
-
85
- specify "can be read from file and layout from text" do
86
- get '/foo' do
87
- haml 'Test', :layout => '== Foo #{yield}'
88
- end
89
-
90
- get_it '/foo'
91
-
92
- body.should.equal "Foo Test\n"
93
- end
94
-
18
+ it 'renders .haml files in views path' do
19
+ haml_app { haml :hello }
20
+ assert ok?
21
+ assert_equal "<h1>Hello From Haml</h1>\n", body
95
22
  end
96
23
 
97
- context "Templates (in general)" do
98
-
99
- setup do
100
- Sinatra.application = nil
101
- end
102
-
103
- specify "are read from files if Symbols" do
104
-
105
- get '/from_file' do
106
- @name = 'Alena'
107
- haml :foo, :views_directory => File.dirname(__FILE__) + "/views"
108
- end
109
-
110
- get_it '/from_file'
111
-
112
- body.should.equal "You rock Alena!\n"
113
-
114
- end
115
-
116
- specify "use layout.ext by default if available" do
117
-
118
- get '/' do
119
- haml :foo, :views_directory => File.dirname(__FILE__) + "/views/layout_test"
120
- end
121
-
122
- get_it '/'
123
- should.be.ok
124
- body.should.equal "x This is foo!\n x\n"
125
-
126
- end
127
-
128
- specify "renders without layout" do
129
-
130
- get '/' do
131
- haml :no_layout, :views_directory => File.dirname(__FILE__) + "/views/no_layout"
132
- end
133
-
134
- get_it '/'
135
- should.be.ok
136
- body.should.equal "<h1>No Layout!</h1>\n"
137
-
138
- end
139
-
140
- specify "can render with no layout" do
141
- layout do
142
- "X\n= yield\nX"
143
- end
144
-
145
- get '/' do
146
- haml 'blake', :layout => false
147
- end
148
-
149
- get_it '/'
150
-
151
- body.should.equal "blake\n"
152
- end
153
-
154
- specify "raises error if template not found" do
155
- get '/' do
156
- haml :not_found
157
- end
158
-
159
- lambda { get_it '/' }.should.raise(Errno::ENOENT)
160
- end
161
-
162
- specify "use layout.ext by default if available" do
163
-
164
- template :foo do
165
- 'asdf'
166
- end
167
-
168
- get '/' do
169
- haml :foo, :layout => false,
170
- :views_directory => File.dirname(__FILE__) + "/views/layout_test"
171
- end
172
-
173
- get_it '/'
174
- should.be.ok
175
- body.should.equal "asdf\n"
176
-
177
- end
178
-
24
+ it "renders with inline layouts" do
25
+ mock_app {
26
+ layout { %q(%h1= 'THIS. IS. ' + yield.upcase) }
27
+ get('/') { haml '%em Sparta' }
28
+ }
29
+ get '/'
30
+ assert ok?
31
+ assert_equal "<h1>THIS. IS. <EM>SPARTA</EM></h1>\n", body
179
32
  end
180
33
 
181
- describe 'Options passed to the HAML interpreter' do
182
- setup do
183
- Sinatra.application = nil
184
- end
185
-
186
- specify 'are empty be default' do
187
-
188
- get '/' do
189
- haml 'foo'
190
- end
191
-
192
- Haml::Engine.expects(:new).with('foo', {}).returns(stub(:render => 'foo'))
193
-
194
- get_it '/'
195
- should.be.ok
196
-
197
- end
198
-
199
- specify 'can be configured by passing :options to haml' do
200
-
201
- get '/' do
202
- haml 'foo', :options => {:format => :html4}
203
- end
204
-
205
- Haml::Engine.expects(:new).with('foo', {:format => :html4}).returns(stub(:render => 'foo'))
206
-
207
- get_it '/'
208
- should.be.ok
209
-
210
- end
34
+ it "renders with file layouts" do
35
+ haml_app {
36
+ haml 'Hello World', :layout => :layout2
37
+ }
38
+ assert ok?
39
+ assert_equal "<h1>HAML Layout!</h1>\n<p>Hello World</p>\n", body
40
+ end
211
41
 
212
- specify 'can be configured using set_option :haml' do
42
+ it "raises error if template not found" do
43
+ mock_app {
44
+ get('/') { haml :no_such_template }
45
+ }
46
+ assert_raise(Errno::ENOENT) { get('/') }
47
+ end
213
48
 
214
- configure do
215
- set_option :haml, :format => :html4,
216
- :escape_html => true
217
- end
49
+ it "passes HAML options to the Haml engine" do
50
+ haml_app {
51
+ haml "!!!\n%h1 Hello World", :options => {:format => :html5}
52
+ }
53
+ assert ok?
54
+ assert_equal "<!DOCTYPE html>\n<h1>Hello World</h1>\n", body
55
+ end
218
56
 
57
+ it "passes default HAML options to the Haml engine" do
58
+ mock_app {
59
+ set :haml, {:format => :html5}
219
60
  get '/' do
220
- haml 'foo'
61
+ haml "!!!\n%h1 Hello World"
221
62
  end
222
-
223
- Haml::Engine.expects(:new).with('foo', {:format => :html4,
224
- :escape_html => true}).returns(stub(:render => 'foo'))
225
-
226
- get_it '/'
227
- should.be.ok
228
-
229
- end
230
-
63
+ }
64
+ get '/'
65
+ assert ok?
66
+ assert_equal "<!DOCTYPE html>\n<h1>Hello World</h1>\n", body
231
67
  end
232
-
233
68
  end
@@ -1,9 +1,25 @@
1
- require 'rubygems'
2
- require 'mocha'
1
+ begin
2
+ require 'test/spec'
3
+ rescue LoadError
4
+ require 'rubygems'
5
+ require 'test/spec'
6
+ end
3
7
 
4
- gem 'rack', '~> 0.4.0'
8
+ $:.unshift File.dirname(File.dirname(__FILE__)) + '/lib'
9
+ require 'sinatra/base'
10
+ require 'sinatra/test'
11
+ require 'sinatra/test/spec'
5
12
 
6
- $:.unshift File.dirname(File.dirname(__FILE__)) + "/lib"
13
+ module Sinatra::Test
14
+ # Sets up a Sinatra::Base subclass defined with the block
15
+ # given. Used in setup or individual spec methods to establish
16
+ # the application.
17
+ def mock_app(base=Sinatra::Base, &block)
18
+ @app = Sinatra.new(base, &block)
19
+ end
20
+ end
7
21
 
8
- require 'sinatra'
9
- require 'sinatra/test/spec'
22
+ class Sinatra::Base
23
+ # Allow assertions in request context
24
+ include Test::Unit::Assertions
25
+ end
@@ -0,0 +1,361 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
3
+ describe 'Sinatra::Helpers' do
4
+ describe '#status' do
5
+ setup do
6
+ mock_app {
7
+ get '/' do
8
+ status 207
9
+ nil
10
+ end
11
+ }
12
+ end
13
+
14
+ it 'sets the response status code' do
15
+ get '/'
16
+ assert_equal 207, response.status
17
+ end
18
+ end
19
+
20
+ describe '#body' do
21
+ it 'takes a block for defered body generation' do
22
+ mock_app {
23
+ get '/' do
24
+ body { 'Hello World' }
25
+ end
26
+ }
27
+
28
+ get '/'
29
+ assert_equal 'Hello World', body
30
+ end
31
+
32
+ it 'takes a String, Array, or other object responding to #each' do
33
+ mock_app {
34
+ get '/' do
35
+ body 'Hello World'
36
+ end
37
+ }
38
+
39
+ get '/'
40
+ assert_equal 'Hello World', body
41
+ end
42
+ end
43
+
44
+ describe '#redirect' do
45
+ it 'uses a 302 when only a path is given' do
46
+ mock_app {
47
+ get '/' do
48
+ redirect '/foo'
49
+ fail 'redirect should halt'
50
+ end
51
+ }
52
+
53
+ get '/'
54
+ assert_equal 302, status
55
+ assert_equal '', body
56
+ assert_equal '/foo', response['Location']
57
+ end
58
+
59
+ it 'uses the code given when specified' do
60
+ mock_app {
61
+ get '/' do
62
+ redirect '/foo', 301
63
+ fail 'redirect should halt'
64
+ end
65
+ }
66
+
67
+ get '/'
68
+ assert_equal 301, status
69
+ assert_equal '', body
70
+ assert_equal '/foo', response['Location']
71
+ end
72
+ end
73
+
74
+ describe '#error' do
75
+ it 'sets a status code and halts' do
76
+ mock_app {
77
+ get '/' do
78
+ error 501
79
+ fail 'error should halt'
80
+ end
81
+ }
82
+
83
+ get '/'
84
+ assert_equal 501, status
85
+ assert_equal '', body
86
+ end
87
+
88
+ it 'takes an optional body' do
89
+ mock_app {
90
+ get '/' do
91
+ error 501, 'FAIL'
92
+ fail 'error should halt'
93
+ end
94
+ }
95
+
96
+ get '/'
97
+ assert_equal 501, status
98
+ assert_equal 'FAIL', body
99
+ end
100
+
101
+ it 'uses a 500 status code when first argument is a body' do
102
+ mock_app {
103
+ get '/' do
104
+ error 'FAIL'
105
+ fail 'error should halt'
106
+ end
107
+ }
108
+
109
+ get '/'
110
+ assert_equal 500, status
111
+ assert_equal 'FAIL', body
112
+ end
113
+ end
114
+
115
+ describe '#not_found' do
116
+ it 'halts with a 404 status' do
117
+ mock_app {
118
+ get '/' do
119
+ not_found
120
+ fail 'not_found should halt'
121
+ end
122
+ }
123
+
124
+ get '/'
125
+ assert_equal 404, status
126
+ assert_equal '', body
127
+ end
128
+ end
129
+
130
+ describe '#session' do
131
+ it 'uses the existing rack.session' do
132
+ mock_app {
133
+ get '/' do
134
+ session[:foo]
135
+ end
136
+ }
137
+
138
+ get '/', :env => { 'rack.session' => { :foo => 'bar' } }
139
+ assert_equal 'bar', body
140
+ end
141
+
142
+ it 'creates a new session when none provided' do
143
+ mock_app {
144
+ get '/' do
145
+ assert session.empty?
146
+ session[:foo] = 'bar'
147
+ 'Hi'
148
+ end
149
+ }
150
+
151
+ get '/'
152
+ assert_equal 'Hi', body
153
+ end
154
+ end
155
+
156
+ describe '#media_type' do
157
+ include Sinatra::Helpers
158
+ it "looks up media types in Rack's MIME registry" do
159
+ Rack::Mime::MIME_TYPES['.foo'] = 'application/foo'
160
+ assert_equal 'application/foo', media_type('foo')
161
+ assert_equal 'application/foo', media_type('.foo')
162
+ assert_equal 'application/foo', media_type(:foo)
163
+ end
164
+ it 'returns nil when given nil' do
165
+ assert media_type(nil).nil?
166
+ end
167
+ it 'returns nil when media type not registered' do
168
+ assert media_type(:bizzle).nil?
169
+ end
170
+ it 'returns the argument when given a media type string' do
171
+ assert_equal 'text/plain', media_type('text/plain')
172
+ end
173
+ end
174
+
175
+ describe '#content_type' do
176
+ it 'sets the Content-Type header' do
177
+ mock_app {
178
+ get '/' do
179
+ content_type 'text/plain'
180
+ 'Hello World'
181
+ end
182
+ }
183
+
184
+ get '/'
185
+ assert_equal 'text/plain', response['Content-Type']
186
+ assert_equal 'Hello World', body
187
+ end
188
+
189
+ it 'takes media type parameters (like charset=)' do
190
+ mock_app {
191
+ get '/' do
192
+ content_type 'text/html', :charset => 'utf-8'
193
+ "<h1>Hello, World</h1>"
194
+ end
195
+ }
196
+
197
+ get '/'
198
+ assert ok?
199
+ assert_equal 'text/html;charset=utf-8', response['Content-Type']
200
+ assert_equal "<h1>Hello, World</h1>", body
201
+ end
202
+
203
+ it "looks up symbols in Rack's mime types dictionary" do
204
+ Rack::Mime::MIME_TYPES['.foo'] = 'application/foo'
205
+ mock_app {
206
+ get '/foo.xml' do
207
+ content_type :foo
208
+ "I AM FOO"
209
+ end
210
+ }
211
+
212
+ get '/foo.xml'
213
+ assert ok?
214
+ assert_equal 'application/foo', response['Content-Type']
215
+ assert_equal 'I AM FOO', body
216
+ end
217
+
218
+ it 'fails when no mime type is registered for the argument provided' do
219
+ mock_app {
220
+ get '/foo.xml' do
221
+ content_type :bizzle
222
+ "I AM FOO"
223
+ end
224
+ }
225
+ assert_raise(RuntimeError) { get '/foo.xml' }
226
+ end
227
+ end
228
+
229
+ describe '#send_file' do
230
+ before {
231
+ @file = File.dirname(__FILE__) + '/file.txt'
232
+ File.open(@file, 'wb') { |io| io.write('Hello World') }
233
+ }
234
+ after {
235
+ File.unlink @file
236
+ @file = nil
237
+ }
238
+
239
+ def send_file_app
240
+ path = @file
241
+ mock_app {
242
+ get '/file.txt' do
243
+ send_file path
244
+ end
245
+ }
246
+ end
247
+
248
+ it "sends the contents of the file" do
249
+ send_file_app
250
+ get '/file.txt'
251
+ assert ok?
252
+ assert_equal 'Hello World', body
253
+ end
254
+
255
+ it 'sets the Content-Type response header if a mime-type can be located' do
256
+ send_file_app
257
+ get '/file.txt'
258
+ assert_equal 'text/plain', response['Content-Type']
259
+ end
260
+
261
+ it 'sets the Content-Length response header' do
262
+ send_file_app
263
+ get '/file.txt'
264
+ assert_equal 'Hello World'.length.to_s, response['Content-Length']
265
+ end
266
+
267
+ it 'sets the Last-Modified response header' do
268
+ send_file_app
269
+ get '/file.txt'
270
+ assert_equal File.mtime(@file).httpdate, response['Last-Modified']
271
+ end
272
+
273
+ it "returns a 404 when not found" do
274
+ mock_app {
275
+ get '/' do
276
+ send_file 'this-file-does-not-exist.txt'
277
+ end
278
+ }
279
+ get '/'
280
+ assert not_found?
281
+ end
282
+ end
283
+
284
+ describe '#last_modified' do
285
+ before do
286
+ now = Time.now
287
+ mock_app {
288
+ get '/' do
289
+ body { 'Hello World' }
290
+ last_modified now
291
+ 'Boo!'
292
+ end
293
+ }
294
+ @now = now
295
+ end
296
+
297
+ it 'sets the Last-Modified header to a valid RFC 2616 date value' do
298
+ get '/'
299
+ assert_equal @now.httpdate, response['Last-Modified']
300
+ end
301
+
302
+ it 'returns a body when conditional get misses' do
303
+ get '/'
304
+ assert_equal 200, status
305
+ assert_equal 'Boo!', body
306
+ end
307
+
308
+ it 'halts when a conditional GET matches' do
309
+ get '/', :env => { 'HTTP_IF_MODIFIED_SINCE' => @now.httpdate }
310
+ assert_equal 304, status
311
+ assert_equal '', body
312
+ end
313
+ end
314
+
315
+ describe '#etag' do
316
+ before do
317
+ mock_app {
318
+ get '/' do
319
+ body { 'Hello World' }
320
+ etag 'FOO'
321
+ 'Boo!'
322
+ end
323
+ }
324
+ end
325
+
326
+ it 'sets the ETag header' do
327
+ get '/'
328
+ assert_equal '"FOO"', response['ETag']
329
+ end
330
+
331
+ it 'returns a body when conditional get misses' do
332
+ get '/'
333
+ assert_equal 200, status
334
+ assert_equal 'Boo!', body
335
+ end
336
+
337
+ it 'halts when a conditional GET matches' do
338
+ get '/', :env => { 'HTTP_IF_NONE_MATCH' => '"FOO"' }
339
+ assert_equal 304, status
340
+ assert_equal '', body
341
+ end
342
+
343
+ it 'should handle multiple ETag values in If-None-Match header' do
344
+ get '/', :env => { 'HTTP_IF_NONE_MATCH' => '"BAR", *' }
345
+ assert_equal 304, status
346
+ assert_equal '', body
347
+ end
348
+
349
+ it 'uses a weak etag with the :weak option' do
350
+ mock_app {
351
+ get '/' do
352
+ etag 'FOO', :weak
353
+ "that's weak, dude."
354
+ end
355
+ }
356
+ get '/'
357
+ assert_equal 'W/"FOO"', response['ETag']
358
+ end
359
+
360
+ end
361
+ end