sinatra 1.4.8 → 2.0.0.beta1

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 (124) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +77 -47
  3. data/CONTRIBUTING.md +1 -1
  4. data/Gemfile +37 -49
  5. data/MAINTENANCE.md +42 -0
  6. data/README.de.md +5 -5
  7. data/README.es.md +5 -5
  8. data/README.fr.md +9 -9
  9. data/README.hu.md +3 -3
  10. data/README.ja.md +19 -8
  11. data/README.ko.md +8 -8
  12. data/README.md +90 -61
  13. data/README.pt-br.md +3 -3
  14. data/README.pt-pt.md +2 -2
  15. data/README.ru.md +42 -26
  16. data/README.zh.md +8 -8
  17. data/Rakefile +0 -6
  18. data/SECURITY.md +35 -0
  19. data/lib/sinatra/base.rb +113 -161
  20. data/lib/sinatra/main.rb +1 -0
  21. data/lib/sinatra/show_exceptions.rb +8 -8
  22. data/lib/sinatra/version.rb +1 -1
  23. data/sinatra.gemspec +7 -4
  24. metadata +34 -168
  25. data/lib/sinatra/ext.rb +0 -17
  26. data/test/asciidoctor_test.rb +0 -72
  27. data/test/base_test.rb +0 -167
  28. data/test/builder_test.rb +0 -91
  29. data/test/coffee_test.rb +0 -96
  30. data/test/compile_test.rb +0 -183
  31. data/test/contest.rb +0 -91
  32. data/test/creole_test.rb +0 -65
  33. data/test/delegator_test.rb +0 -160
  34. data/test/encoding_test.rb +0 -20
  35. data/test/erb_test.rb +0 -116
  36. data/test/extensions_test.rb +0 -98
  37. data/test/filter_test.rb +0 -487
  38. data/test/haml_test.rb +0 -109
  39. data/test/helper.rb +0 -132
  40. data/test/helpers_test.rb +0 -1917
  41. data/test/integration/app.rb +0 -79
  42. data/test/integration_helper.rb +0 -236
  43. data/test/integration_test.rb +0 -104
  44. data/test/less_test.rb +0 -69
  45. data/test/liquid_test.rb +0 -77
  46. data/test/mapped_error_test.rb +0 -285
  47. data/test/markaby_test.rb +0 -80
  48. data/test/markdown_test.rb +0 -85
  49. data/test/mediawiki_test.rb +0 -68
  50. data/test/middleware_test.rb +0 -68
  51. data/test/nokogiri_test.rb +0 -67
  52. data/test/public/favicon.ico +0 -0
  53. data/test/public/hello+world.txt +0 -1
  54. data/test/rabl_test.rb +0 -89
  55. data/test/rack_test.rb +0 -45
  56. data/test/radius_test.rb +0 -59
  57. data/test/rdoc_test.rb +0 -66
  58. data/test/readme_test.rb +0 -130
  59. data/test/request_test.rb +0 -100
  60. data/test/response_test.rb +0 -63
  61. data/test/result_test.rb +0 -76
  62. data/test/route_added_hook_test.rb +0 -59
  63. data/test/routing_test.rb +0 -1456
  64. data/test/sass_test.rb +0 -115
  65. data/test/scss_test.rb +0 -88
  66. data/test/server_test.rb +0 -56
  67. data/test/settings_test.rb +0 -582
  68. data/test/sinatra_test.rb +0 -12
  69. data/test/slim_test.rb +0 -102
  70. data/test/static_test.rb +0 -266
  71. data/test/streaming_test.rb +0 -149
  72. data/test/stylus_test.rb +0 -90
  73. data/test/templates_test.rb +0 -382
  74. data/test/textile_test.rb +0 -65
  75. data/test/views/a/in_a.str +0 -1
  76. data/test/views/ascii.erb +0 -2
  77. data/test/views/b/in_b.str +0 -1
  78. data/test/views/calc.html.erb +0 -1
  79. data/test/views/error.builder +0 -3
  80. data/test/views/error.erb +0 -3
  81. data/test/views/error.haml +0 -3
  82. data/test/views/error.sass +0 -2
  83. data/test/views/explicitly_nested.str +0 -1
  84. data/test/views/foo/hello.test +0 -1
  85. data/test/views/hello.asciidoc +0 -1
  86. data/test/views/hello.builder +0 -1
  87. data/test/views/hello.coffee +0 -1
  88. data/test/views/hello.creole +0 -1
  89. data/test/views/hello.erb +0 -1
  90. data/test/views/hello.haml +0 -1
  91. data/test/views/hello.less +0 -5
  92. data/test/views/hello.liquid +0 -1
  93. data/test/views/hello.mab +0 -1
  94. data/test/views/hello.md +0 -1
  95. data/test/views/hello.mediawiki +0 -1
  96. data/test/views/hello.nokogiri +0 -1
  97. data/test/views/hello.rabl +0 -2
  98. data/test/views/hello.radius +0 -1
  99. data/test/views/hello.rdoc +0 -1
  100. data/test/views/hello.sass +0 -2
  101. data/test/views/hello.scss +0 -3
  102. data/test/views/hello.slim +0 -1
  103. data/test/views/hello.str +0 -1
  104. data/test/views/hello.styl +0 -2
  105. data/test/views/hello.test +0 -1
  106. data/test/views/hello.textile +0 -1
  107. data/test/views/hello.wlang +0 -1
  108. data/test/views/hello.yajl +0 -1
  109. data/test/views/layout2.builder +0 -3
  110. data/test/views/layout2.erb +0 -2
  111. data/test/views/layout2.haml +0 -2
  112. data/test/views/layout2.liquid +0 -2
  113. data/test/views/layout2.mab +0 -2
  114. data/test/views/layout2.nokogiri +0 -3
  115. data/test/views/layout2.rabl +0 -3
  116. data/test/views/layout2.radius +0 -2
  117. data/test/views/layout2.slim +0 -3
  118. data/test/views/layout2.str +0 -2
  119. data/test/views/layout2.test +0 -1
  120. data/test/views/layout2.wlang +0 -2
  121. data/test/views/nested.str +0 -1
  122. data/test/views/utf8.erb +0 -2
  123. data/test/wlang_test.rb +0 -87
  124. data/test/yajl_test.rb +0 -86
@@ -1,109 +0,0 @@
1
- require File.expand_path('../helper', __FILE__)
2
-
3
- begin
4
- require 'haml'
5
-
6
- class HAMLTest < Minitest::Test
7
- def haml_app(&block)
8
- mock_app do
9
- set :views, File.dirname(__FILE__) + '/views'
10
- get('/', &block)
11
- end
12
- get '/'
13
- end
14
-
15
- it 'renders inline HAML strings' do
16
- haml_app { haml '%h1 Hiya' }
17
- assert ok?
18
- assert_equal "<h1>Hiya</h1>\n", body
19
- end
20
-
21
- it 'renders .haml files in views path' do
22
- haml_app { haml :hello }
23
- assert ok?
24
- assert_equal "<h1>Hello From Haml</h1>\n", body
25
- end
26
-
27
- it "renders with inline layouts" do
28
- mock_app do
29
- layout { %q(%h1= 'THIS. IS. ' + yield.upcase) }
30
- get('/') { haml '%em Sparta' }
31
- end
32
- get '/'
33
- assert ok?
34
- assert_equal "<h1>THIS. IS. <EM>SPARTA</EM></h1>\n", body
35
- end
36
-
37
- it "renders with file layouts" do
38
- haml_app { haml 'Hello World', :layout => :layout2 }
39
- assert ok?
40
- assert_equal "<h1>HAML Layout!</h1>\n<p>Hello World</p>\n", body
41
- end
42
-
43
- it "raises error if template not found" do
44
- mock_app { get('/') { haml :no_such_template } }
45
- assert_raises(Errno::ENOENT) { get('/') }
46
- end
47
-
48
- it "passes HAML options to the Haml engine" do
49
- mock_app {
50
- get('/') { haml "!!!\n%h1 Hello World", :format => :html5 }
51
- }
52
- get '/'
53
- assert ok?
54
- assert_equal "<!DOCTYPE html>\n<h1>Hello World</h1>\n", body
55
- end
56
-
57
- it "passes default HAML options to the Haml engine" do
58
- mock_app do
59
- set :haml, {:format => :html5}
60
- get('/') { haml "!!!\n%h1 Hello World" }
61
- end
62
- get '/'
63
- assert ok?
64
- assert_equal "<!DOCTYPE html>\n<h1>Hello World</h1>\n", body
65
- end
66
-
67
- it "merges the default HAML options with the overrides and passes them to the Haml engine" do
68
- mock_app do
69
- set :haml, {:format => :html5, :attr_wrapper => '"'} # default HAML attr are <tag attr='single-quoted'>
70
- get('/') { haml "!!!\n%h1{:class => :header} Hello World" }
71
- get('/html4') {
72
- haml "!!!\n%h1{:class => 'header'} Hello World", :format => :html4
73
- }
74
- end
75
- get '/'
76
- assert ok?
77
- assert_equal "<!DOCTYPE html>\n<h1 class=\"header\">Hello World</h1>\n", body
78
- get '/html4'
79
- assert ok?
80
- assert_match(/^<!DOCTYPE html PUBLIC (.*) HTML 4.01/, body)
81
- end
82
-
83
- it "is possible to pass locals" do
84
- haml_app { haml "= foo", :locals => { :foo => 'bar' }}
85
- assert_equal "bar\n", body
86
- end
87
-
88
- it "can render truly nested layouts by accepting a layout and a block with the contents" do
89
- mock_app do
90
- template(:main_outer_layout) { "%h1 Title\n= yield" }
91
- template(:an_inner_layout) { "%h2 Subtitle\n= yield" }
92
- template(:a_page) { "%p Contents." }
93
- get('/') do
94
- haml :main_outer_layout, :layout => false do
95
- haml :an_inner_layout do
96
- haml :a_page
97
- end
98
- end
99
- end
100
- end
101
- get '/'
102
- assert ok?
103
- assert_body "<h1>Title</h1>\n<h2>Subtitle</h2>\n<p>Contents.</p>\n"
104
- end
105
- end
106
-
107
- rescue LoadError
108
- warn "#{$!.to_s}: skipping haml tests"
109
- end
@@ -1,132 +0,0 @@
1
- ENV['RACK_ENV'] = 'test'
2
- Encoding.default_external = "UTF-8" if defined? Encoding
3
-
4
- RUBY_ENGINE = 'ruby' unless defined? RUBY_ENGINE
5
-
6
- begin
7
- require 'rack'
8
- rescue LoadError
9
- require 'rubygems'
10
- require 'rack'
11
- end
12
-
13
- testdir = File.dirname(__FILE__)
14
- $LOAD_PATH.unshift testdir unless $LOAD_PATH.include?(testdir)
15
-
16
- libdir = File.dirname(File.dirname(__FILE__)) + '/lib'
17
- $LOAD_PATH.unshift libdir unless $LOAD_PATH.include?(libdir)
18
-
19
- require 'minitest'
20
- require 'contest'
21
- require 'rack/test'
22
- require 'sinatra/base'
23
-
24
- class Sinatra::Base
25
- include Minitest::Assertions
26
- # Allow assertions in request context
27
- def assertions
28
- @assertions ||= 0
29
- end
30
-
31
- attr_writer :assertions
32
- end
33
-
34
- class Rack::Builder
35
- def include?(middleware)
36
- @ins.any? { |m| p m ; middleware === m }
37
- end
38
- end
39
-
40
- Sinatra::Base.set :environment, :test
41
-
42
- class Minitest::Test
43
- include Rack::Test::Methods
44
-
45
- class << self
46
- alias_method :it, :test
47
- alias_method :section, :context
48
- end
49
-
50
- def self.example(desc = nil, &block)
51
- @example_count = 0 unless instance_variable_defined? :@example_count
52
- @example_count += 1
53
- it(desc || "Example #{@example_count}", &block)
54
- end
55
-
56
- alias_method :response, :last_response
57
-
58
- setup do
59
- Sinatra::Base.set :environment, :test
60
- end
61
-
62
- # Sets up a Sinatra::Base subclass defined with the block
63
- # given. Used in setup or individual spec methods to establish
64
- # the application.
65
- def mock_app(base=Sinatra::Base, &block)
66
- @app = Sinatra.new(base, &block)
67
- end
68
-
69
- def app
70
- Rack::Lint.new(@app)
71
- end
72
-
73
- def body
74
- response.body.to_s
75
- end
76
-
77
- def assert_body(value)
78
- if value.respond_to? :to_str
79
- assert_equal value.lstrip.gsub(/\s*\n\s*/, ""), body.lstrip.gsub(/\s*\n\s*/, "")
80
- else
81
- assert_match value, body
82
- end
83
- end
84
-
85
- def assert_status(expected)
86
- assert_equal Integer(expected), Integer(status)
87
- end
88
-
89
- def assert_like(a,b)
90
- pattern = /id=['"][^"']*["']|\s+/
91
- assert_equal a.strip.gsub(pattern, ""), b.strip.gsub(pattern, "")
92
- end
93
-
94
- def assert_include(str, substr)
95
- assert str.include?(substr), "expected #{str.inspect} to include #{substr.inspect}"
96
- end
97
-
98
- def options(uri, params = {}, env = {}, &block)
99
- request(uri, env.merge(:method => "OPTIONS", :params => params), &block)
100
- end
101
-
102
- def patch(uri, params = {}, env = {}, &block)
103
- request(uri, env.merge(:method => "PATCH", :params => params), &block)
104
- end
105
-
106
- def link(uri, params = {}, env = {}, &block)
107
- request(uri, env.merge(:method => "LINK", :params => params), &block)
108
- end
109
-
110
- def unlink(uri, params = {}, env = {}, &block)
111
- request(uri, env.merge(:method => "UNLINK", :params => params), &block)
112
- end
113
-
114
- # Delegate other missing methods to response.
115
- def method_missing(name, *args, &block)
116
- if response && response.respond_to?(name)
117
- response.send(name, *args, &block)
118
- else
119
- super
120
- end
121
- rescue Rack::Test::Error
122
- super
123
- end
124
-
125
- # Do not output warnings for the duration of the block.
126
- def silence_warnings
127
- $VERBOSE, v = nil, $VERBOSE
128
- yield
129
- ensure
130
- $VERBOSE = v
131
- end
132
- end
@@ -1,1917 +0,0 @@
1
- require File.expand_path('../helper', __FILE__)
2
- require 'date'
3
- require 'json'
4
-
5
- class HelpersTest < Minitest::Test
6
- def test_default
7
- assert true
8
- end
9
-
10
- def status_app(code, &block)
11
- code += 2 if [204, 205, 304].include? code
12
- block ||= proc { }
13
- mock_app do
14
- get('/') do
15
- status code
16
- instance_eval(&block).inspect
17
- end
18
- end
19
- get '/'
20
- end
21
-
22
- describe 'status' do
23
- it 'sets the response status code' do
24
- status_app 207
25
- assert_equal 207, response.status
26
- end
27
- end
28
-
29
- describe 'not_found?' do
30
- it 'is true for status == 404' do
31
- status_app(404) { not_found? }
32
- assert_body 'true'
33
- end
34
-
35
- it 'is false for status gt 404' do
36
- status_app(405) { not_found? }
37
- assert_body 'false'
38
- end
39
-
40
- it 'is false for status lt 404' do
41
- status_app(403) { not_found? }
42
- assert_body 'false'
43
- end
44
- end
45
-
46
- describe 'informational?' do
47
- it 'is true for 1xx status' do
48
- status_app(100 + rand(100)) { informational? }
49
- assert_body 'true'
50
- end
51
-
52
- it 'is false for status > 199' do
53
- status_app(200 + rand(400)) { informational? }
54
- assert_body 'false'
55
- end
56
- end
57
-
58
- describe 'success?' do
59
- it 'is true for 2xx status' do
60
- status_app(200 + rand(100)) { success? }
61
- assert_body 'true'
62
- end
63
-
64
- it 'is false for status < 200' do
65
- status_app(100 + rand(100)) { success? }
66
- assert_body 'false'
67
- end
68
-
69
- it 'is false for status > 299' do
70
- status_app(300 + rand(300)) { success? }
71
- assert_body 'false'
72
- end
73
- end
74
-
75
- describe 'redirect?' do
76
- it 'is true for 3xx status' do
77
- status_app(300 + rand(100)) { redirect? }
78
- assert_body 'true'
79
- end
80
-
81
- it 'is false for status < 300' do
82
- status_app(200 + rand(100)) { redirect? }
83
- assert_body 'false'
84
- end
85
-
86
- it 'is false for status > 399' do
87
- status_app(400 + rand(200)) { redirect? }
88
- assert_body 'false'
89
- end
90
- end
91
-
92
- describe 'client_error?' do
93
- it 'is true for 4xx status' do
94
- status_app(400 + rand(100)) { client_error? }
95
- assert_body 'true'
96
- end
97
-
98
- it 'is false for status < 400' do
99
- status_app(200 + rand(200)) { client_error? }
100
- assert_body 'false'
101
- end
102
-
103
- it 'is false for status > 499' do
104
- status_app(500 + rand(100)) { client_error? }
105
- assert_body 'false'
106
- end
107
- end
108
-
109
- describe 'server_error?' do
110
- it 'is true for 5xx status' do
111
- status_app(500 + rand(100)) { server_error? }
112
- assert_body 'true'
113
- end
114
-
115
- it 'is false for status < 500' do
116
- status_app(200 + rand(300)) { server_error? }
117
- assert_body 'false'
118
- end
119
- end
120
-
121
- describe 'body' do
122
- it 'takes a block for deferred body generation' do
123
- mock_app do
124
- get('/') { body { 'Hello World' } }
125
- end
126
-
127
- get '/'
128
- assert_equal 'Hello World', body
129
- end
130
-
131
- it 'takes a String, Array, or other object responding to #each' do
132
- mock_app { get('/') { body 'Hello World' } }
133
-
134
- get '/'
135
- assert_equal 'Hello World', body
136
- end
137
-
138
- it 'can be used with other objects' do
139
- mock_app do
140
- get '/' do
141
- body :hello => 'from json'
142
- end
143
-
144
- after do
145
- if Hash === response.body
146
- body response.body[:hello]
147
- end
148
- end
149
- end
150
-
151
- get '/'
152
- assert_body 'from json'
153
- end
154
-
155
- it 'can be set in after filter' do
156
- mock_app do
157
- get('/') { body 'route' }
158
- after { body 'filter' }
159
- end
160
-
161
- get '/'
162
- assert_body 'filter'
163
- end
164
- end
165
-
166
- describe 'redirect' do
167
- it 'uses a 302 when only a path is given' do
168
- mock_app do
169
- get('/') do
170
- redirect '/foo'
171
- fail 'redirect should halt'
172
- end
173
- end
174
-
175
- get '/'
176
- assert_equal 302, status
177
- assert_equal '', body
178
- assert_equal 'http://example.org/foo', response['Location']
179
- end
180
-
181
- it 'uses the code given when specified' do
182
- mock_app do
183
- get('/') do
184
- redirect '/foo', 301
185
- fail 'redirect should halt'
186
- end
187
- end
188
-
189
- get '/'
190
- assert_equal 301, status
191
- assert_equal '', body
192
- assert_equal 'http://example.org/foo', response['Location']
193
- end
194
-
195
- it 'redirects back to request.referer when passed back' do
196
- mock_app { get('/try_redirect') { redirect back } }
197
-
198
- request = Rack::MockRequest.new(@app)
199
- response = request.get('/try_redirect', 'HTTP_REFERER' => '/foo')
200
- assert_equal 302, response.status
201
- assert_equal 'http://example.org/foo', response['Location']
202
- end
203
-
204
- it 'redirects using a non-standard HTTP port' do
205
- mock_app { get('/') { redirect '/foo' } }
206
-
207
- request = Rack::MockRequest.new(@app)
208
- response = request.get('/', 'SERVER_PORT' => '81')
209
- assert_equal 'http://example.org:81/foo', response['Location']
210
- end
211
-
212
- it 'redirects using a non-standard HTTPS port' do
213
- mock_app { get('/') { redirect '/foo' } }
214
-
215
- request = Rack::MockRequest.new(@app)
216
- response = request.get('/', 'SERVER_PORT' => '444')
217
- assert_equal 'http://example.org:444/foo', response['Location']
218
- end
219
-
220
- it 'uses 303 for post requests if request is HTTP 1.1' do
221
- mock_app { post('/') { redirect '/'} }
222
- post('/', {}, 'HTTP_VERSION' => 'HTTP/1.1')
223
- assert_equal 303, status
224
- assert_equal '', body
225
- assert_equal 'http://example.org/', response['Location']
226
- end
227
-
228
- it 'uses 302 for post requests if request is HTTP 1.0' do
229
- mock_app { post('/') { redirect '/'} }
230
- post('/', {}, 'HTTP_VERSION' => 'HTTP/1.0')
231
- assert_equal 302, status
232
- assert_equal '', body
233
- assert_equal 'http://example.org/', response['Location']
234
- end
235
-
236
- it 'works behind a reverse proxy' do
237
- mock_app { get('/') { redirect '/foo' } }
238
-
239
- request = Rack::MockRequest.new(@app)
240
- response = request.get('/', 'HTTP_X_FORWARDED_HOST' => 'example.com', 'SERVER_PORT' => '8080')
241
- assert_equal 'http://example.com/foo', response['Location']
242
- end
243
-
244
- it 'accepts absolute URIs' do
245
- mock_app do
246
- get('/') do
247
- redirect 'http://google.com'
248
- fail 'redirect should halt'
249
- end
250
- end
251
-
252
- get '/'
253
- assert_equal 302, status
254
- assert_equal '', body
255
- assert_equal 'http://google.com', response['Location']
256
- end
257
-
258
- it 'accepts absolute URIs with a different schema' do
259
- mock_app do
260
- get('/') do
261
- redirect 'mailto:jsmith@example.com'
262
- fail 'redirect should halt'
263
- end
264
- end
265
-
266
- get '/'
267
- assert_equal 302, status
268
- assert_equal '', body
269
- assert_equal 'mailto:jsmith@example.com', response['Location']
270
- end
271
-
272
- it 'accepts a URI object instead of a String' do
273
- mock_app do
274
- get('/') { redirect URI.parse('http://sinatrarb.com') }
275
- end
276
-
277
- get '/'
278
- assert_equal 302, status
279
- assert_equal '', body
280
- assert_equal 'http://sinatrarb.com', response['Location']
281
- end
282
- end
283
-
284
- describe 'error' do
285
- it 'sets a status code and halts' do
286
- mock_app do
287
- get('/') do
288
- error 501
289
- fail 'error should halt'
290
- end
291
- end
292
-
293
- get '/'
294
- assert_equal 501, status
295
- assert_equal '', body
296
- end
297
-
298
- it 'takes an optional body' do
299
- mock_app do
300
- get('/') do
301
- error 501, 'FAIL'
302
- fail 'error should halt'
303
- end
304
- end
305
-
306
- get '/'
307
- assert_equal 501, status
308
- assert_equal 'FAIL', body
309
- end
310
-
311
- it 'should not invoke error handler when setting status inside an error handler' do
312
- mock_app do
313
- disable :raise_errors
314
- not_found do
315
- body "not_found handler"
316
- status 404
317
- end
318
-
319
- error do
320
- body "error handler"
321
- status 404
322
- end
323
-
324
- get '/' do
325
- raise
326
- end
327
- end
328
-
329
- get '/'
330
- assert_equal 404, status
331
- assert_equal 'error handler', body
332
- end
333
-
334
- it 'should not reset the content-type to html for error handlers' do
335
- mock_app do
336
- disable :raise_errors
337
- before { content_type "application/json" }
338
- not_found { JSON.dump("error" => "Not Found") }
339
- end
340
-
341
- get '/'
342
- assert_equal 404, status
343
- assert_equal 'application/json', response.content_type
344
- end
345
-
346
- it 'should not invoke error handler when halting with 500 inside an error handler' do
347
- mock_app do
348
- disable :raise_errors
349
- not_found do
350
- body "not_found handler"
351
- halt 404
352
- end
353
-
354
- error do
355
- body "error handler"
356
- halt 404
357
- end
358
-
359
- get '/' do
360
- raise
361
- end
362
- end
363
-
364
- get '/'
365
- assert_equal 404, status
366
- assert_equal 'error handler', body
367
- end
368
-
369
- it 'should not invoke not_found handler when halting with 404 inside a not found handler' do
370
- mock_app do
371
- disable :raise_errors
372
-
373
- not_found do
374
- body "not_found handler"
375
- halt 500
376
- end
377
-
378
- error do
379
- body "error handler"
380
- halt 500
381
- end
382
- end
383
-
384
- get '/'
385
- assert_equal 500, status
386
- assert_equal 'not_found handler', body
387
- end
388
-
389
- it 'uses a 500 status code when first argument is a body' do
390
- mock_app do
391
- get('/') do
392
- error 'FAIL'
393
- fail 'error should halt'
394
- end
395
- end
396
-
397
- get '/'
398
- assert_equal 500, status
399
- assert_equal 'FAIL', body
400
- end
401
- end
402
-
403
- describe 'not_found' do
404
- it 'halts with a 404 status' do
405
- mock_app do
406
- get('/') do
407
- not_found
408
- fail 'not_found should halt'
409
- end
410
- end
411
-
412
- get '/'
413
- assert_equal 404, status
414
- assert_equal '', body
415
- end
416
-
417
- it 'does not set a X-Cascade header' do
418
- mock_app do
419
- get('/') do
420
- not_found
421
- fail 'not_found should halt'
422
- end
423
- end
424
-
425
- get '/'
426
- assert_equal 404, status
427
- assert_equal nil, response.headers['X-Cascade']
428
- end
429
- end
430
-
431
- describe 'headers' do
432
- it 'sets headers on the response object when given a Hash' do
433
- mock_app do
434
- get('/') do
435
- headers 'X-Foo' => 'bar', 'X-Baz' => 'bling'
436
- 'kthx'
437
- end
438
- end
439
-
440
- get '/'
441
- assert ok?
442
- assert_equal 'bar', response['X-Foo']
443
- assert_equal 'bling', response['X-Baz']
444
- assert_equal 'kthx', body
445
- end
446
-
447
- it 'returns the response headers hash when no hash provided' do
448
- mock_app do
449
- get('/') do
450
- headers['X-Foo'] = 'bar'
451
- 'kthx'
452
- end
453
- end
454
-
455
- get '/'
456
- assert ok?
457
- assert_equal 'bar', response['X-Foo']
458
- end
459
- end
460
-
461
- describe 'session' do
462
- it 'uses the existing rack.session' do
463
- mock_app do
464
- get('/') do
465
- session[:foo]
466
- end
467
- end
468
-
469
- get('/', {}, { 'rack.session' => { :foo => 'bar' } })
470
- assert_equal 'bar', body
471
- end
472
-
473
- it 'creates a new session when none provided' do
474
- mock_app do
475
- enable :sessions
476
-
477
- get('/') do
478
- assert session[:foo].nil?
479
- session[:foo] = 'bar'
480
- redirect '/hi'
481
- end
482
-
483
- get('/hi') do
484
- "hi #{session[:foo]}"
485
- end
486
- end
487
-
488
- get '/'
489
- follow_redirect!
490
- assert_equal 'hi bar', body
491
- end
492
-
493
- it 'inserts session middleware' do
494
- mock_app do
495
- enable :sessions
496
-
497
- get('/') do
498
- assert env['rack.session']
499
- assert env['rack.session.options']
500
- 'ok'
501
- end
502
- end
503
-
504
- get '/'
505
- assert_body 'ok'
506
- end
507
-
508
- it 'sets a default session secret' do
509
- mock_app do
510
- enable :sessions
511
-
512
- get('/') do
513
- secret = env['rack.session.options'][:secret]
514
- assert secret
515
- assert_equal secret, settings.session_secret
516
- 'ok'
517
- end
518
- end
519
-
520
- get '/'
521
- assert_body 'ok'
522
- end
523
-
524
- it 'allows disabling session secret' do
525
- mock_app do
526
- enable :sessions
527
- disable :session_secret
528
-
529
- get('/') do
530
- assert !env['rack.session.options'].include?(:session_secret)
531
- 'ok'
532
- end
533
- end
534
-
535
- # Silence warnings since Rack::Session::Cookie complains about the non-present session secret
536
- silence_warnings do
537
- get '/'
538
- end
539
- assert_body 'ok'
540
- end
541
-
542
- it 'accepts an options hash' do
543
- mock_app do
544
- set :sessions, :foo => :bar
545
-
546
- get('/') do
547
- assert_equal env['rack.session.options'][:foo], :bar
548
- 'ok'
549
- end
550
- end
551
-
552
- get '/'
553
- assert_body 'ok'
554
- end
555
- end
556
-
557
- describe 'mime_type' do
558
- include Sinatra::Helpers
559
-
560
- it "looks up mime types in Rack's MIME registry" do
561
- Rack::Mime::MIME_TYPES['.foo'] = 'application/foo'
562
- assert_equal 'application/foo', mime_type('foo')
563
- assert_equal 'application/foo', mime_type('.foo')
564
- assert_equal 'application/foo', mime_type(:foo)
565
- end
566
-
567
- it 'returns nil when given nil' do
568
- assert mime_type(nil).nil?
569
- end
570
-
571
- it 'returns nil when media type not registered' do
572
- assert mime_type(:bizzle).nil?
573
- end
574
-
575
- it 'returns the argument when given a media type string' do
576
- assert_equal 'text/plain', mime_type('text/plain')
577
- end
578
-
579
- it 'turns AcceptEntry into String' do
580
- type = mime_type(Sinatra::Request::AcceptEntry.new('text/plain'))
581
- assert_equal String, type.class
582
- assert_equal 'text/plain', type
583
- end
584
- end
585
-
586
- test 'Base.mime_type registers mime type' do
587
- mock_app do
588
- mime_type :foo, 'application/foo'
589
-
590
- get('/') do
591
- "foo is #{mime_type(:foo)}"
592
- end
593
- end
594
-
595
- get '/'
596
- assert_equal 'foo is application/foo', body
597
- end
598
-
599
- describe 'content_type' do
600
- it 'sets the Content-Type header' do
601
- mock_app do
602
- get('/') do
603
- content_type 'text/plain'
604
- 'Hello World'
605
- end
606
- end
607
-
608
- get '/'
609
- assert_equal 'text/plain;charset=utf-8', response['Content-Type']
610
- assert_equal 'Hello World', body
611
- end
612
-
613
- it 'takes media type parameters (like charset=)' do
614
- mock_app do
615
- get('/') do
616
- content_type 'text/html', :charset => 'latin1'
617
- "<h1>Hello, World</h1>"
618
- end
619
- end
620
-
621
- get '/'
622
- assert ok?
623
- assert_equal 'text/html;charset=latin1', response['Content-Type']
624
- assert_equal "<h1>Hello, World</h1>", body
625
- end
626
-
627
- it "looks up symbols in Rack's mime types dictionary" do
628
- Rack::Mime::MIME_TYPES['.foo'] = 'application/foo'
629
- mock_app do
630
- get('/foo.xml') do
631
- content_type :foo
632
- "I AM FOO"
633
- end
634
- end
635
-
636
- get '/foo.xml'
637
- assert ok?
638
- assert_equal 'application/foo', response['Content-Type']
639
- assert_equal 'I AM FOO', body
640
- end
641
-
642
- it 'fails when no mime type is registered for the argument provided' do
643
- mock_app do
644
- get('/foo.xml') do
645
- content_type :bizzle
646
- "I AM FOO"
647
- end
648
- end
649
-
650
- assert_raises(RuntimeError) { get '/foo.xml' }
651
- end
652
-
653
- it 'only sets default charset for specific mime types' do
654
- tests_ran = false
655
- mock_app do
656
- mime_type :foo, 'text/foo'
657
- mime_type :bar, 'application/bar'
658
- mime_type :baz, 'application/baz'
659
- add_charset << mime_type(:baz)
660
- get('/') do
661
- assert_equal content_type(:txt), 'text/plain;charset=utf-8'
662
- assert_equal content_type(:css), 'text/css;charset=utf-8'
663
- assert_equal content_type(:html), 'text/html;charset=utf-8'
664
- assert_equal content_type(:foo), 'text/foo;charset=utf-8'
665
- assert_equal content_type(:xml), 'application/xml;charset=utf-8'
666
- assert_equal content_type(:xhtml), 'application/xhtml+xml;charset=utf-8'
667
- assert_equal content_type(:js), 'application/javascript;charset=utf-8'
668
- assert_equal content_type(:json), 'application/json'
669
- assert_equal content_type(:bar), 'application/bar'
670
- assert_equal content_type(:png), 'image/png'
671
- assert_equal content_type(:baz), 'application/baz;charset=utf-8'
672
- tests_ran = true
673
- "done"
674
- end
675
- end
676
-
677
- get '/'
678
- assert tests_ran
679
- end
680
-
681
- it 'handles already present params' do
682
- mock_app do
683
- get('/') do
684
- content_type 'foo/bar;level=1', :charset => 'utf-8'
685
- 'ok'
686
- end
687
- end
688
-
689
- get '/'
690
- assert_equal 'foo/bar;level=1, charset=utf-8', response['Content-Type']
691
- end
692
-
693
- it 'does not add charset if present' do
694
- mock_app do
695
- get('/') do
696
- content_type 'text/plain;charset=utf-16'
697
- 'ok'
698
- end
699
- end
700
-
701
- get '/'
702
- assert_equal 'text/plain;charset=utf-16', response['Content-Type']
703
- end
704
-
705
- it 'properly encodes parameters with delimiter characters' do
706
- mock_app do
707
- before '/comma' do
708
- content_type 'image/png', :comment => 'Hello, world!'
709
- end
710
- before '/semicolon' do
711
- content_type 'image/png', :comment => 'semi;colon'
712
- end
713
- before '/quote' do
714
- content_type 'image/png', :comment => '"Whatever."'
715
- end
716
-
717
- get('*') { 'ok' }
718
- end
719
-
720
- get '/comma'
721
- assert_equal 'image/png;comment="Hello, world!"', response['Content-Type']
722
- get '/semicolon'
723
- assert_equal 'image/png;comment="semi;colon"', response['Content-Type']
724
- get '/quote'
725
- assert_equal 'image/png;comment="\"Whatever.\""', response['Content-Type']
726
- end
727
- end
728
-
729
- describe 'attachment' do
730
- def attachment_app(filename=nil)
731
- mock_app do
732
- get('/attachment') do
733
- attachment filename
734
- response.write("<sinatra></sinatra>")
735
- end
736
- end
737
- end
738
-
739
- it 'sets the Content-Type response header' do
740
- attachment_app('test.xml')
741
- get '/attachment'
742
- assert_equal 'application/xml;charset=utf-8', response['Content-Type']
743
- assert_equal '<sinatra></sinatra>', body
744
- end
745
-
746
- it 'sets the Content-Type response header without extname' do
747
- attachment_app('test')
748
- get '/attachment'
749
- assert_equal 'text/html;charset=utf-8', response['Content-Type']
750
- assert_equal '<sinatra></sinatra>', body
751
- end
752
-
753
- it 'sets the Content-Type response header with extname' do
754
- mock_app do
755
- get('/attachment') do
756
- content_type :atom
757
- attachment 'test.xml'
758
- response.write("<sinatra></sinatra>")
759
- end
760
- end
761
-
762
- get '/attachment'
763
- assert_equal 'application/atom+xml', response['Content-Type']
764
- assert_equal '<sinatra></sinatra>', body
765
- end
766
-
767
- end
768
-
769
- describe 'send_file' do
770
- setup do
771
- @file = File.dirname(__FILE__) + '/file.txt'
772
- File.open(@file, 'wb') { |io| io.write('Hello World') }
773
- end
774
-
775
- def teardown
776
- File.unlink @file
777
- @file = nil
778
- end
779
-
780
- def send_file_app(opts={})
781
- path = @file
782
- mock_app {
783
- get '/file.txt' do
784
- send_file path, opts
785
- end
786
- }
787
- end
788
-
789
- it "sends the contents of the file" do
790
- send_file_app
791
- get '/file.txt'
792
- assert ok?
793
- assert_equal 'Hello World', body
794
- end
795
-
796
- it 'sets the Content-Type response header if a mime-type can be located' do
797
- send_file_app
798
- get '/file.txt'
799
- assert_equal 'text/plain;charset=utf-8', response['Content-Type']
800
- end
801
-
802
- it 'sets the Content-Type response header if type option is set to a file extension' do
803
- send_file_app :type => 'html'
804
- get '/file.txt'
805
- assert_equal 'text/html;charset=utf-8', response['Content-Type']
806
- end
807
-
808
- it 'sets the Content-Type response header if type option is set to a mime type' do
809
- send_file_app :type => 'application/octet-stream'
810
- get '/file.txt'
811
- assert_equal 'application/octet-stream', response['Content-Type']
812
- end
813
-
814
- it 'sets the Content-Length response header' do
815
- send_file_app
816
- get '/file.txt'
817
- assert_equal 'Hello World'.length.to_s, response['Content-Length']
818
- end
819
-
820
- it 'sets the Last-Modified response header' do
821
- send_file_app
822
- get '/file.txt'
823
- assert_equal File.mtime(@file).httpdate, response['Last-Modified']
824
- end
825
-
826
- it 'allows passing in a different Last-Modified response header with :last_modified' do
827
- time = Time.now
828
- send_file_app :last_modified => time
829
- get '/file.txt'
830
- assert_equal time.httpdate, response['Last-Modified']
831
- end
832
-
833
- it "returns a 404 when not found" do
834
- mock_app {
835
- get('/') { send_file 'this-file-does-not-exist.txt' }
836
- }
837
- get '/'
838
- assert not_found?
839
- end
840
-
841
- it "does not set the Content-Disposition header by default" do
842
- send_file_app
843
- get '/file.txt'
844
- assert_nil response['Content-Disposition']
845
- end
846
-
847
- it "sets the Content-Disposition header when :disposition set to 'attachment'" do
848
- send_file_app :disposition => 'attachment'
849
- get '/file.txt'
850
- assert_equal 'attachment; filename="file.txt"', response['Content-Disposition']
851
- end
852
-
853
- it "does not set add a file name if filename is false" do
854
- send_file_app :disposition => 'inline', :filename => false
855
- get '/file.txt'
856
- assert_equal 'inline', response['Content-Disposition']
857
- end
858
-
859
- it "sets the Content-Disposition header when :disposition set to 'inline'" do
860
- send_file_app :disposition => 'inline'
861
- get '/file.txt'
862
- assert_equal 'inline; filename="file.txt"', response['Content-Disposition']
863
- end
864
-
865
- it "sets the Content-Disposition header when :filename provided" do
866
- send_file_app :filename => 'foo.txt'
867
- get '/file.txt'
868
- assert_equal 'attachment; filename="foo.txt"', response['Content-Disposition']
869
- end
870
-
871
- it 'allows setting a custom status code' do
872
- send_file_app :status => 201
873
- get '/file.txt'
874
- assert_status 201
875
- end
876
-
877
- it "is able to send files with unknown mime type" do
878
- @file = File.dirname(__FILE__) + '/file.foobar'
879
- File.open(@file, 'wb') { |io| io.write('Hello World') }
880
- send_file_app
881
- get '/file.txt'
882
- assert_equal 'application/octet-stream', response['Content-Type']
883
- end
884
-
885
- it "does not override Content-Type if already set and no explicit type is given" do
886
- path = @file
887
- mock_app do
888
- get('/') do
889
- content_type :png
890
- send_file path
891
- end
892
- end
893
- get '/'
894
- assert_equal 'image/png', response['Content-Type']
895
- end
896
-
897
- it "does override Content-Type even if already set, if explicit type is given" do
898
- path = @file
899
- mock_app do
900
- get('/') do
901
- content_type :png
902
- send_file path, :type => :gif
903
- end
904
- end
905
- get '/'
906
- assert_equal 'image/gif', response['Content-Type']
907
- end
908
-
909
- it 'can have :status option as a string' do
910
- path = @file
911
- mock_app do
912
- post '/' do
913
- send_file path, :status => '422'
914
- end
915
- end
916
- post '/'
917
- assert_equal response.status, 422
918
- end
919
- end
920
-
921
- describe 'cache_control' do
922
- setup do
923
- mock_app do
924
- get('/foo') do
925
- cache_control :public, :no_cache, :max_age => 60.0
926
- 'Hello World'
927
- end
928
-
929
- get('/bar') do
930
- cache_control :public, :no_cache
931
- 'Hello World'
932
- end
933
- end
934
- end
935
-
936
- it 'sets the Cache-Control header' do
937
- get '/foo'
938
- assert_equal ['public', 'no-cache', 'max-age=60'], response['Cache-Control'].split(', ')
939
- end
940
-
941
- it 'last argument does not have to be a hash' do
942
- get '/bar'
943
- assert_equal ['public', 'no-cache'], response['Cache-Control'].split(', ')
944
- end
945
- end
946
-
947
- describe 'expires' do
948
- setup do
949
- mock_app do
950
- get('/foo') do
951
- expires 60, :public, :no_cache
952
- 'Hello World'
953
- end
954
-
955
- get('/bar') { expires Time.now }
956
-
957
- get('/baz') { expires Time.at(0) }
958
-
959
- get('/blah') do
960
- obj = Object.new
961
- def obj.method_missing(*a, &b) 60.send(*a, &b) end
962
- def obj.is_a?(thing) 60.is_a?(thing) end
963
- expires obj, :public, :no_cache
964
- 'Hello World'
965
- end
966
-
967
- get('/boom') { expires '9999' }
968
- end
969
- end
970
-
971
- it 'sets the Cache-Control header' do
972
- get '/foo'
973
- assert_equal ['public', 'no-cache', 'max-age=60'], response['Cache-Control'].split(', ')
974
- end
975
-
976
- it 'sets the Expires header' do
977
- get '/foo'
978
- refute_nil response['Expires']
979
- end
980
-
981
- it 'allows passing Time.now objects' do
982
- get '/bar'
983
- refute_nil response['Expires']
984
- end
985
-
986
- it 'allows passing Time.at objects' do
987
- get '/baz'
988
- assert_equal 'Thu, 01 Jan 1970 00:00:00 GMT', response['Expires']
989
- end
990
-
991
- it 'accepts values pretending to be a Numeric (like ActiveSupport::Duration)' do
992
- get '/blah'
993
- assert_equal ['public', 'no-cache', 'max-age=60'], response['Cache-Control'].split(', ')
994
- end
995
-
996
- it 'fails when Time.parse raises an ArgumentError' do
997
- assert_raises(ArgumentError) { get '/boom' }
998
- end
999
- end
1000
-
1001
- describe 'last_modified' do
1002
- it 'ignores nil' do
1003
- mock_app { get('/') { last_modified nil; 200; } }
1004
-
1005
- get '/'
1006
- assert ! response['Last-Modified']
1007
- end
1008
-
1009
- it 'does not change a status other than 200' do
1010
- mock_app do
1011
- get('/') do
1012
- status 299
1013
- last_modified Time.at(0)
1014
- 'ok'
1015
- end
1016
- end
1017
-
1018
- get('/', {}, 'HTTP_IF_MODIFIED_SINCE' => 'Sun, 26 Sep 2030 23:43:52 GMT')
1019
- assert_status 299
1020
- assert_body 'ok'
1021
- end
1022
-
1023
- [Time.now, DateTime.now, Date.today, Time.now.to_i,
1024
- Struct.new(:to_time).new(Time.now) ].each do |last_modified_time|
1025
- describe "with #{last_modified_time.class.name}" do
1026
- setup do
1027
- mock_app do
1028
- get('/') do
1029
- last_modified last_modified_time
1030
- 'Boo!'
1031
- end
1032
- end
1033
- wrapper = Object.new.extend Sinatra::Helpers
1034
- @last_modified_time = wrapper.time_for last_modified_time
1035
- end
1036
-
1037
- # fixes strange missing test error when running complete test suite.
1038
- it("does not complain about missing tests") { }
1039
-
1040
- context "when there's no If-Modified-Since header" do
1041
- it 'sets the Last-Modified header to a valid RFC 2616 date value' do
1042
- get '/'
1043
- assert_equal @last_modified_time.httpdate, response['Last-Modified']
1044
- end
1045
-
1046
- it 'conditional GET misses and returns a body' do
1047
- get '/'
1048
- assert_equal 200, status
1049
- assert_equal 'Boo!', body
1050
- end
1051
- end
1052
-
1053
- context "when there's an invalid If-Modified-Since header" do
1054
- it 'sets the Last-Modified header to a valid RFC 2616 date value' do
1055
- get('/', {}, { 'HTTP_IF_MODIFIED_SINCE' => 'a really weird date' })
1056
- assert_equal @last_modified_time.httpdate, response['Last-Modified']
1057
- end
1058
-
1059
- it 'conditional GET misses and returns a body' do
1060
- get('/', {}, { 'HTTP_IF_MODIFIED_SINCE' => 'a really weird date' })
1061
- assert_equal 200, status
1062
- assert_equal 'Boo!', body
1063
- end
1064
- end
1065
-
1066
- context "when the resource has been modified since the If-Modified-Since header date" do
1067
- it 'sets the Last-Modified header to a valid RFC 2616 date value' do
1068
- get('/', {}, { 'HTTP_IF_MODIFIED_SINCE' => (@last_modified_time - 1).httpdate })
1069
- assert_equal @last_modified_time.httpdate, response['Last-Modified']
1070
- end
1071
-
1072
- it 'conditional GET misses and returns a body' do
1073
- get('/', {}, { 'HTTP_IF_MODIFIED_SINCE' => (@last_modified_time - 1).httpdate })
1074
- assert_equal 200, status
1075
- assert_equal 'Boo!', body
1076
- end
1077
-
1078
- it 'does not rely on string comparison' do
1079
- mock_app do
1080
- get('/compare') do
1081
- last_modified "Mon, 18 Oct 2010 20:57:11 GMT"
1082
- "foo"
1083
- end
1084
- end
1085
-
1086
- get('/compare', {}, { 'HTTP_IF_MODIFIED_SINCE' => 'Sun, 26 Sep 2010 23:43:52 GMT' })
1087
- assert_equal 200, status
1088
- assert_equal 'foo', body
1089
- get('/compare', {}, { 'HTTP_IF_MODIFIED_SINCE' => 'Sun, 26 Sep 2030 23:43:52 GMT' })
1090
- assert_equal 304, status
1091
- assert_equal '', body
1092
- end
1093
- end
1094
-
1095
- context "when the resource has been modified on the exact If-Modified-Since header date" do
1096
- it 'sets the Last-Modified header to a valid RFC 2616 date value' do
1097
- get('/', {}, { 'HTTP_IF_MODIFIED_SINCE' => @last_modified_time.httpdate })
1098
- assert_equal @last_modified_time.httpdate, response['Last-Modified']
1099
- end
1100
-
1101
- it 'conditional GET matches and halts' do
1102
- get( '/', {}, { 'HTTP_IF_MODIFIED_SINCE' => @last_modified_time.httpdate })
1103
- assert_equal 304, status
1104
- assert_equal '', body
1105
- end
1106
- end
1107
-
1108
- context "when the resource hasn't been modified since the If-Modified-Since header date" do
1109
- it 'sets the Last-Modified header to a valid RFC 2616 date value' do
1110
- get('/', {}, { 'HTTP_IF_MODIFIED_SINCE' => (@last_modified_time + 1).httpdate })
1111
- assert_equal @last_modified_time.httpdate, response['Last-Modified']
1112
- end
1113
-
1114
- it 'conditional GET matches and halts' do
1115
- get('/', {}, { 'HTTP_IF_MODIFIED_SINCE' => (@last_modified_time + 1).httpdate })
1116
- assert_equal 304, status
1117
- assert_equal '', body
1118
- end
1119
- end
1120
-
1121
- context "If-Unmodified-Since" do
1122
- it 'results in 200 if resource has not been modified' do
1123
- get('/', {}, { 'HTTP_IF_UNMODIFIED_SINCE' => 'Sun, 26 Sep 2030 23:43:52 GMT' })
1124
- assert_equal 200, status
1125
- assert_equal 'Boo!', body
1126
- end
1127
-
1128
- it 'results in 412 if resource has been modified' do
1129
- get('/', {}, { 'HTTP_IF_UNMODIFIED_SINCE' => Time.at(0).httpdate })
1130
- assert_equal 412, status
1131
- assert_equal '', body
1132
- end
1133
- end
1134
- end
1135
- end
1136
- end
1137
-
1138
- describe 'etag' do
1139
- context "safe requests" do
1140
- it 'returns 200 for normal requests' do
1141
- mock_app do
1142
- get('/') do
1143
- etag 'foo'
1144
- 'ok'
1145
- end
1146
- end
1147
-
1148
- get '/'
1149
- assert_status 200
1150
- assert_body 'ok'
1151
- end
1152
-
1153
- context "If-None-Match" do
1154
- it 'returns 304 when If-None-Match is *' do
1155
- mock_app do
1156
- get('/') do
1157
- etag 'foo'
1158
- 'ok'
1159
- end
1160
- end
1161
-
1162
- get('/', {}, 'HTTP_IF_NONE_MATCH' => '*')
1163
- assert_status 304
1164
- assert_body ''
1165
- end
1166
-
1167
- it 'returns 200 when If-None-Match is * for new resources' do
1168
- mock_app do
1169
- get('/') do
1170
- etag 'foo', :new_resource => true
1171
- 'ok'
1172
- end
1173
- end
1174
-
1175
- get('/', {}, 'HTTP_IF_NONE_MATCH' => '*')
1176
- assert_status 200
1177
- assert_body 'ok'
1178
- end
1179
-
1180
- it 'returns 304 when If-None-Match is * for existing resources' do
1181
- mock_app do
1182
- get('/') do
1183
- etag 'foo', :new_resource => false
1184
- 'ok'
1185
- end
1186
- end
1187
-
1188
- get('/', {}, 'HTTP_IF_NONE_MATCH' => '*')
1189
- assert_status 304
1190
- assert_body ''
1191
- end
1192
-
1193
- it 'returns 304 when If-None-Match is the etag' do
1194
- mock_app do
1195
- get('/') do
1196
- etag 'foo'
1197
- 'ok'
1198
- end
1199
- end
1200
-
1201
- get('/', {}, 'HTTP_IF_NONE_MATCH' => '"foo"')
1202
- assert_status 304
1203
- assert_body ''
1204
- end
1205
-
1206
- it 'returns 304 when If-None-Match includes the etag' do
1207
- mock_app do
1208
- get('/') do
1209
- etag 'foo'
1210
- 'ok'
1211
- end
1212
- end
1213
-
1214
- get('/', {}, 'HTTP_IF_NONE_MATCH' => '"bar", "foo"')
1215
- assert_status 304
1216
- assert_body ''
1217
- end
1218
-
1219
- it 'returns 200 when If-None-Match does not include the etag' do
1220
- mock_app do
1221
- get('/') do
1222
- etag 'foo'
1223
- 'ok'
1224
- end
1225
- end
1226
-
1227
- get('/', {}, 'HTTP_IF_NONE_MATCH' => '"bar"')
1228
- assert_status 200
1229
- assert_body 'ok'
1230
- end
1231
-
1232
- it 'ignores If-Modified-Since if If-None-Match does not match' do
1233
- mock_app do
1234
- get('/') do
1235
- etag 'foo'
1236
- last_modified Time.at(0)
1237
- 'ok'
1238
- end
1239
- end
1240
-
1241
- get('/', {}, 'HTTP_IF_NONE_MATCH' => '"bar"')
1242
- assert_status 200
1243
- assert_body 'ok'
1244
- end
1245
-
1246
- it 'does not change a status code other than 2xx or 304' do
1247
- mock_app do
1248
- get('/') do
1249
- status 499
1250
- etag 'foo'
1251
- 'ok'
1252
- end
1253
- end
1254
-
1255
- get('/', {}, 'HTTP_IF_NONE_MATCH' => '"foo"')
1256
- assert_status 499
1257
- assert_body 'ok'
1258
- end
1259
-
1260
- it 'does change 2xx status codes' do
1261
- mock_app do
1262
- get('/') do
1263
- status 299
1264
- etag 'foo'
1265
- 'ok'
1266
- end
1267
- end
1268
-
1269
- get('/', {}, 'HTTP_IF_NONE_MATCH' => '"foo"')
1270
- assert_status 304
1271
- assert_body ''
1272
- end
1273
-
1274
- it 'does not send a body on 304 status codes' do
1275
- mock_app do
1276
- get('/') do
1277
- status 304
1278
- etag 'foo'
1279
- 'ok'
1280
- end
1281
- end
1282
-
1283
- get('/', {}, 'HTTP_IF_NONE_MATCH' => '"foo"')
1284
- assert_status 304
1285
- assert_body ''
1286
- end
1287
- end
1288
-
1289
- context "If-Match" do
1290
- it 'returns 200 when If-Match is the etag' do
1291
- mock_app do
1292
- get('/') do
1293
- etag 'foo'
1294
- 'ok'
1295
- end
1296
- end
1297
-
1298
- get('/', {}, 'HTTP_IF_MATCH' => '"foo"')
1299
- assert_status 200
1300
- assert_body 'ok'
1301
- end
1302
-
1303
- it 'returns 200 when If-Match includes the etag' do
1304
- mock_app do
1305
- get('/') do
1306
- etag 'foo'
1307
- 'ok'
1308
- end
1309
- end
1310
-
1311
- get('/', {}, 'HTTP_IF_MATCH' => '"foo", "bar"')
1312
- assert_status 200
1313
- assert_body 'ok'
1314
- end
1315
-
1316
- it 'returns 200 when If-Match is *' do
1317
- mock_app do
1318
- get('/') do
1319
- etag 'foo'
1320
- 'ok'
1321
- end
1322
- end
1323
-
1324
- get('/', {}, 'HTTP_IF_MATCH' => '*')
1325
- assert_status 200
1326
- assert_body 'ok'
1327
- end
1328
-
1329
- it 'returns 412 when If-Match is * for new resources' do
1330
- mock_app do
1331
- get('/') do
1332
- etag 'foo', :new_resource => true
1333
- 'ok'
1334
- end
1335
- end
1336
-
1337
- get('/', {}, 'HTTP_IF_MATCH' => '*')
1338
- assert_status 412
1339
- assert_body ''
1340
- end
1341
-
1342
- it 'returns 200 when If-Match is * for existing resources' do
1343
- mock_app do
1344
- get('/') do
1345
- etag 'foo', :new_resource => false
1346
- 'ok'
1347
- end
1348
- end
1349
-
1350
- get('/', {}, 'HTTP_IF_MATCH' => '*')
1351
- assert_status 200
1352
- assert_body 'ok'
1353
- end
1354
-
1355
- it 'returns 412 when If-Match does not include the etag' do
1356
- mock_app do
1357
- get('/') do
1358
- etag 'foo'
1359
- 'ok'
1360
- end
1361
- end
1362
-
1363
- get('/', {}, 'HTTP_IF_MATCH' => '"bar"')
1364
- assert_status 412
1365
- assert_body ''
1366
- end
1367
- end
1368
- end
1369
-
1370
- context "idempotent requests" do
1371
- it 'returns 200 for normal requests' do
1372
- mock_app do
1373
- put('/') do
1374
- etag 'foo'
1375
- 'ok'
1376
- end
1377
- end
1378
-
1379
- put '/'
1380
- assert_status 200
1381
- assert_body 'ok'
1382
- end
1383
-
1384
- context "If-None-Match" do
1385
- it 'returns 412 when If-None-Match is *' do
1386
- mock_app do
1387
- put('/') do
1388
- etag 'foo'
1389
- 'ok'
1390
- end
1391
- end
1392
-
1393
- put('/', {}, 'HTTP_IF_NONE_MATCH' => '*')
1394
- assert_status 412
1395
- assert_body ''
1396
- end
1397
-
1398
- it 'returns 200 when If-None-Match is * for new resources' do
1399
- mock_app do
1400
- put('/') do
1401
- etag 'foo', :new_resource => true
1402
- 'ok'
1403
- end
1404
- end
1405
-
1406
- put('/', {}, 'HTTP_IF_NONE_MATCH' => '*')
1407
- assert_status 200
1408
- assert_body 'ok'
1409
- end
1410
-
1411
- it 'returns 412 when If-None-Match is * for existing resources' do
1412
- mock_app do
1413
- put('/') do
1414
- etag 'foo', :new_resource => false
1415
- 'ok'
1416
- end
1417
- end
1418
-
1419
- put('/', {}, 'HTTP_IF_NONE_MATCH' => '*')
1420
- assert_status 412
1421
- assert_body ''
1422
- end
1423
-
1424
- it 'returns 412 when If-None-Match is the etag' do
1425
- mock_app do
1426
- put '/' do
1427
- etag 'foo'
1428
- 'ok'
1429
- end
1430
- end
1431
-
1432
- put('/', {}, 'HTTP_IF_NONE_MATCH' => '"foo"')
1433
- assert_status 412
1434
- assert_body ''
1435
- end
1436
-
1437
- it 'returns 412 when If-None-Match includes the etag' do
1438
- mock_app do
1439
- put('/') do
1440
- etag 'foo'
1441
- 'ok'
1442
- end
1443
- end
1444
-
1445
- put('/', {}, 'HTTP_IF_NONE_MATCH' => '"bar", "foo"')
1446
- assert_status 412
1447
- assert_body ''
1448
- end
1449
-
1450
- it 'returns 200 when If-None-Match does not include the etag' do
1451
- mock_app do
1452
- put('/') do
1453
- etag 'foo'
1454
- 'ok'
1455
- end
1456
- end
1457
-
1458
- put('/', {}, 'HTTP_IF_NONE_MATCH' => '"bar"')
1459
- assert_status 200
1460
- assert_body 'ok'
1461
- end
1462
-
1463
- it 'ignores If-Modified-Since if If-None-Match does not match' do
1464
- mock_app do
1465
- put('/') do
1466
- etag 'foo'
1467
- last_modified Time.at(0)
1468
- 'ok'
1469
- end
1470
- end
1471
-
1472
- put('/', {}, 'HTTP_IF_NONE_MATCH' => '"bar"')
1473
- assert_status 200
1474
- assert_body 'ok'
1475
- end
1476
- end
1477
-
1478
- context "If-Match" do
1479
- it 'returns 200 when If-Match is the etag' do
1480
- mock_app do
1481
- put('/') do
1482
- etag 'foo'
1483
- 'ok'
1484
- end
1485
- end
1486
-
1487
- put('/', {}, 'HTTP_IF_MATCH' => '"foo"')
1488
- assert_status 200
1489
- assert_body 'ok'
1490
- end
1491
-
1492
- it 'returns 200 when If-Match includes the etag' do
1493
- mock_app do
1494
- put('/') do
1495
- etag 'foo'
1496
- 'ok'
1497
- end
1498
- end
1499
-
1500
- put('/', {}, 'HTTP_IF_MATCH' => '"foo", "bar"')
1501
- assert_status 200
1502
- assert_body 'ok'
1503
- end
1504
-
1505
- it 'returns 200 when If-Match is *' do
1506
- mock_app do
1507
- put('/') do
1508
- etag 'foo'
1509
- 'ok'
1510
- end
1511
- end
1512
-
1513
- put('/', {}, 'HTTP_IF_MATCH' => '*')
1514
- assert_status 200
1515
- assert_body 'ok'
1516
- end
1517
-
1518
- it 'returns 412 when If-Match is * for new resources' do
1519
- mock_app do
1520
- put('/') do
1521
- etag 'foo', :new_resource => true
1522
- 'ok'
1523
- end
1524
- end
1525
-
1526
- put('/', {}, 'HTTP_IF_MATCH' => '*')
1527
- assert_status 412
1528
- assert_body ''
1529
- end
1530
-
1531
- it 'returns 200 when If-Match is * for existing resources' do
1532
- mock_app do
1533
- put('/') do
1534
- etag 'foo', :new_resource => false
1535
- 'ok'
1536
- end
1537
- end
1538
-
1539
- put('/', {}, 'HTTP_IF_MATCH' => '*')
1540
- assert_status 200
1541
- assert_body 'ok'
1542
- end
1543
-
1544
- it 'returns 412 when If-Match does not include the etag' do
1545
- mock_app do
1546
- put('/') do
1547
- etag 'foo'
1548
- 'ok'
1549
- end
1550
- end
1551
-
1552
- put('/', {}, 'HTTP_IF_MATCH' => '"bar"')
1553
- assert_status 412
1554
- assert_body ''
1555
- end
1556
- end
1557
- end
1558
-
1559
- context "post requests" do
1560
- it 'returns 200 for normal requests' do
1561
- mock_app do
1562
- post('/') do
1563
- etag 'foo'
1564
- 'ok'
1565
- end
1566
- end
1567
-
1568
- post('/')
1569
- assert_status 200
1570
- assert_body 'ok'
1571
- end
1572
-
1573
- context "If-None-Match" do
1574
- it 'returns 200 when If-None-Match is *' do
1575
- mock_app do
1576
- post('/') do
1577
- etag 'foo'
1578
- 'ok'
1579
- end
1580
- end
1581
-
1582
- post('/', {}, 'HTTP_IF_NONE_MATCH' => '*')
1583
- assert_status 200
1584
- assert_body 'ok'
1585
- end
1586
-
1587
- it 'returns 200 when If-None-Match is * for new resources' do
1588
- mock_app do
1589
- post('/') do
1590
- etag 'foo', :new_resource => true
1591
- 'ok'
1592
- end
1593
- end
1594
-
1595
- post('/', {}, 'HTTP_IF_NONE_MATCH' => '*')
1596
- assert_status 200
1597
- assert_body 'ok'
1598
- end
1599
-
1600
- it 'returns 412 when If-None-Match is * for existing resources' do
1601
- mock_app do
1602
- post('/') do
1603
- etag 'foo', :new_resource => false
1604
- 'ok'
1605
- end
1606
- end
1607
-
1608
- post('/', {}, 'HTTP_IF_NONE_MATCH' => '*')
1609
- assert_status 412
1610
- assert_body ''
1611
- end
1612
-
1613
- it 'returns 412 when If-None-Match is the etag' do
1614
- mock_app do
1615
- post('/') do
1616
- etag 'foo'
1617
- 'ok'
1618
- end
1619
- end
1620
-
1621
- post('/', {}, 'HTTP_IF_NONE_MATCH' => '"foo"')
1622
- assert_status 412
1623
- assert_body ''
1624
- end
1625
-
1626
- it 'returns 412 when If-None-Match includes the etag' do
1627
- mock_app do
1628
- post('/') do
1629
- etag 'foo'
1630
- 'ok'
1631
- end
1632
- end
1633
-
1634
- post('/', {}, 'HTTP_IF_NONE_MATCH' => '"bar", "foo"')
1635
- assert_status 412
1636
- assert_body ''
1637
- end
1638
-
1639
- it 'returns 200 when If-None-Match does not include the etag' do
1640
- mock_app do
1641
- post('/') do
1642
- etag 'foo'
1643
- 'ok'
1644
- end
1645
- end
1646
-
1647
- post('/', {}, 'HTTP_IF_NONE_MATCH' => '"bar"')
1648
- assert_status 200
1649
- assert_body 'ok'
1650
- end
1651
-
1652
- it 'ignores If-Modified-Since if If-None-Match does not match' do
1653
- mock_app do
1654
- post('/') do
1655
- etag 'foo'
1656
- last_modified Time.at(0)
1657
- 'ok'
1658
- end
1659
- end
1660
-
1661
- post('/', {}, 'HTTP_IF_NONE_MATCH' => '"bar"')
1662
- assert_status 200
1663
- assert_body 'ok'
1664
- end
1665
- end
1666
-
1667
- context "If-Match" do
1668
- it 'returns 200 when If-Match is the etag' do
1669
- mock_app do
1670
- post('/') do
1671
- etag 'foo'
1672
- 'ok'
1673
- end
1674
- end
1675
-
1676
- post('/', {}, 'HTTP_IF_MATCH' => '"foo"')
1677
- assert_status 200
1678
- assert_body 'ok'
1679
- end
1680
-
1681
- it 'returns 200 when If-Match includes the etag' do
1682
- mock_app do
1683
- post('/') do
1684
- etag 'foo'
1685
- 'ok'
1686
- end
1687
- end
1688
-
1689
- post('/', {}, 'HTTP_IF_MATCH' => '"foo", "bar"')
1690
- assert_status 200
1691
- assert_body 'ok'
1692
- end
1693
-
1694
- it 'returns 412 when If-Match is *' do
1695
- mock_app do
1696
- post('/') do
1697
- etag 'foo'
1698
- 'ok'
1699
- end
1700
- end
1701
-
1702
- post('/', {}, 'HTTP_IF_MATCH' => '*')
1703
- assert_status 412
1704
- assert_body ''
1705
- end
1706
-
1707
- it 'returns 412 when If-Match is * for new resources' do
1708
- mock_app do
1709
- post('/') do
1710
- etag 'foo', :new_resource => true
1711
- 'ok'
1712
- end
1713
- end
1714
-
1715
- post('/', {}, 'HTTP_IF_MATCH' => '*')
1716
- assert_status 412
1717
- assert_body ''
1718
- end
1719
-
1720
- it 'returns 200 when If-Match is * for existing resources' do
1721
- mock_app do
1722
- post('/') do
1723
- etag 'foo', :new_resource => false
1724
- 'ok'
1725
- end
1726
- end
1727
-
1728
- post('/', {}, 'HTTP_IF_MATCH' => '*')
1729
- assert_status 200
1730
- assert_body 'ok'
1731
- end
1732
-
1733
- it 'returns 412 when If-Match does not include the etag' do
1734
- mock_app do
1735
- post('/') do
1736
- etag 'foo'
1737
- 'ok'
1738
- end
1739
- end
1740
-
1741
- post('/', {}, 'HTTP_IF_MATCH' => '"bar"')
1742
- assert_status 412
1743
- assert_body ''
1744
- end
1745
- end
1746
- end
1747
-
1748
- it 'uses a weak etag with the :weak option' do
1749
- mock_app do
1750
- get('/') do
1751
- etag 'FOO', :weak
1752
- "that's weak, dude."
1753
- end
1754
- end
1755
- get '/'
1756
- assert_equal 'W/"FOO"', response['ETag']
1757
- end
1758
-
1759
- it 'raises an ArgumentError for an invalid strength' do
1760
- mock_app do
1761
- get('/') do
1762
- etag 'FOO', :w00t
1763
- "that's weak, dude."
1764
- end
1765
- end
1766
- assert_raises(ArgumentError) { get('/') }
1767
- end
1768
- end
1769
-
1770
- describe 'back' do
1771
- it "makes redirecting back pretty" do
1772
- mock_app { get('/foo') { redirect back } }
1773
-
1774
- get('/foo', {}, 'HTTP_REFERER' => 'http://github.com')
1775
- assert redirect?
1776
- assert_equal "http://github.com", response.location
1777
- end
1778
- end
1779
-
1780
- describe 'uri' do
1781
- it 'generates absolute urls' do
1782
- mock_app { get('/') { uri }}
1783
- get '/'
1784
- assert_equal 'http://example.org/', body
1785
- end
1786
-
1787
- it 'includes path_info' do
1788
- mock_app { get('/:name') { uri }}
1789
- get '/foo'
1790
- assert_equal 'http://example.org/foo', body
1791
- end
1792
-
1793
- it 'allows passing an alternative to path_info' do
1794
- mock_app { get('/:name') { uri '/bar' }}
1795
- get '/foo'
1796
- assert_equal 'http://example.org/bar', body
1797
- end
1798
-
1799
- it 'includes script_name' do
1800
- mock_app { get('/:name') { uri '/bar' }}
1801
- get '/foo', {}, { "SCRIPT_NAME" => '/foo' }
1802
- assert_equal 'http://example.org/foo/bar', body
1803
- end
1804
-
1805
- it 'handles absolute URIs' do
1806
- mock_app { get('/') { uri 'http://google.com' }}
1807
- get '/'
1808
- assert_equal 'http://google.com', body
1809
- end
1810
-
1811
- it 'handles different protocols' do
1812
- mock_app { get('/') { uri 'mailto:jsmith@example.com' }}
1813
- get '/'
1814
- assert_equal 'mailto:jsmith@example.com', body
1815
- end
1816
-
1817
- it 'is aliased to #url' do
1818
- mock_app { get('/') { url }}
1819
- get '/'
1820
- assert_equal 'http://example.org/', body
1821
- end
1822
-
1823
- it 'is aliased to #to' do
1824
- mock_app { get('/') { to }}
1825
- get '/'
1826
- assert_equal 'http://example.org/', body
1827
- end
1828
- end
1829
-
1830
- describe 'logger' do
1831
- it 'logging works when logging is enabled' do
1832
- mock_app do
1833
- enable :logging
1834
- get('/') do
1835
- logger.info "Program started"
1836
- logger.warn "Nothing to do!"
1837
- end
1838
- end
1839
- io = StringIO.new
1840
- get '/', {}, 'rack.errors' => io
1841
- assert io.string.include?("INFO -- : Program started")
1842
- assert io.string.include?("WARN -- : Nothing to do")
1843
- end
1844
-
1845
- it 'logging works when logging is disable, but no output is produced' do
1846
- mock_app do
1847
- disable :logging
1848
- get('/') do
1849
- logger.info "Program started"
1850
- logger.warn "Nothing to do!"
1851
- end
1852
- end
1853
- io = StringIO.new
1854
- get '/', {}, 'rack.errors' => io
1855
- assert !io.string.include?("INFO -- : Program started")
1856
- assert !io.string.include?("WARN -- : Nothing to do")
1857
- end
1858
-
1859
- it 'does not create a logger when logging is set to nil' do
1860
- mock_app do
1861
- set :logging, nil
1862
- get('/') { logger.inspect }
1863
- end
1864
-
1865
- get '/'
1866
- assert_body 'nil'
1867
- end
1868
- end
1869
-
1870
- module ::HelperOne; def one; '1'; end; end
1871
- module ::HelperTwo; def two; '2'; end; end
1872
-
1873
- describe 'Adding new helpers' do
1874
- it 'takes a list of modules to mix into the app' do
1875
- mock_app do
1876
- helpers ::HelperOne, ::HelperTwo
1877
-
1878
- get('/one') { one }
1879
-
1880
- get('/two') { two }
1881
- end
1882
-
1883
- get '/one'
1884
- assert_equal '1', body
1885
-
1886
- get '/two'
1887
- assert_equal '2', body
1888
- end
1889
-
1890
- it 'takes a block to mix into the app' do
1891
- mock_app do
1892
- helpers do
1893
- def foo
1894
- 'foo'
1895
- end
1896
- end
1897
-
1898
- get('/') { foo }
1899
- end
1900
-
1901
- get '/'
1902
- assert_equal 'foo', body
1903
- end
1904
-
1905
- it 'evaluates the block in class context so that methods can be aliased' do
1906
- mock_app do
1907
- helpers { alias_method :h, :escape_html }
1908
-
1909
- get('/') { h('42 < 43') }
1910
- end
1911
-
1912
- get '/'
1913
- assert ok?
1914
- assert_equal '42 &lt; 43', body
1915
- end
1916
- end
1917
- end