sinatra-sinatra 0.8.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. data/AUTHORS +40 -0
  2. data/CHANGES +174 -0
  3. data/LICENSE +22 -0
  4. data/README.rdoc +545 -0
  5. data/Rakefile +180 -0
  6. data/compat/app_test.rb +300 -0
  7. data/compat/application_test.rb +334 -0
  8. data/compat/builder_test.rb +101 -0
  9. data/compat/custom_error_test.rb +62 -0
  10. data/compat/erb_test.rb +136 -0
  11. data/compat/events_test.rb +78 -0
  12. data/compat/filter_test.rb +30 -0
  13. data/compat/haml_test.rb +233 -0
  14. data/compat/helper.rb +30 -0
  15. data/compat/mapped_error_test.rb +72 -0
  16. data/compat/pipeline_test.rb +71 -0
  17. data/compat/public/foo.xml +1 -0
  18. data/compat/sass_test.rb +57 -0
  19. data/compat/sessions_test.rb +39 -0
  20. data/compat/streaming_test.rb +121 -0
  21. data/compat/sym_params_test.rb +19 -0
  22. data/compat/template_test.rb +30 -0
  23. data/compat/use_in_file_templates_test.rb +47 -0
  24. data/compat/views/foo.builder +1 -0
  25. data/compat/views/foo.erb +1 -0
  26. data/compat/views/foo.haml +1 -0
  27. data/compat/views/foo.sass +2 -0
  28. data/compat/views/foo_layout.erb +2 -0
  29. data/compat/views/foo_layout.haml +2 -0
  30. data/compat/views/layout_test/foo.builder +1 -0
  31. data/compat/views/layout_test/foo.erb +1 -0
  32. data/compat/views/layout_test/foo.haml +1 -0
  33. data/compat/views/layout_test/foo.sass +2 -0
  34. data/compat/views/layout_test/layout.builder +3 -0
  35. data/compat/views/layout_test/layout.erb +1 -0
  36. data/compat/views/layout_test/layout.haml +1 -0
  37. data/compat/views/layout_test/layout.sass +2 -0
  38. data/compat/views/no_layout/no_layout.builder +1 -0
  39. data/compat/views/no_layout/no_layout.haml +1 -0
  40. data/lib/sinatra/base.rb +818 -0
  41. data/lib/sinatra/compat.rb +239 -0
  42. data/lib/sinatra/images/404.png +0 -0
  43. data/lib/sinatra/images/500.png +0 -0
  44. data/lib/sinatra/main.rb +48 -0
  45. data/lib/sinatra/test/bacon.rb +17 -0
  46. data/lib/sinatra/test/rspec.rb +9 -0
  47. data/lib/sinatra/test/spec.rb +9 -0
  48. data/lib/sinatra/test/unit.rb +11 -0
  49. data/lib/sinatra/test.rb +109 -0
  50. data/lib/sinatra.rb +4 -0
  51. data/sinatra.gemspec +109 -0
  52. data/test/base_test.rb +68 -0
  53. data/test/builder_test.rb +64 -0
  54. data/test/data/reload_app_file.rb +3 -0
  55. data/test/erb_test.rb +50 -0
  56. data/test/filter_test.rb +35 -0
  57. data/test/haml_test.rb +68 -0
  58. data/test/helper.rb +20 -0
  59. data/test/helpers_test.rb +361 -0
  60. data/test/mapped_error_test.rb +160 -0
  61. data/test/middleware_test.rb +58 -0
  62. data/test/options_test.rb +97 -0
  63. data/test/reload_test.rb +61 -0
  64. data/test/request_test.rb +9 -0
  65. data/test/result_test.rb +88 -0
  66. data/test/routing_test.rb +334 -0
  67. data/test/sass_test.rb +36 -0
  68. data/test/sinatra_test.rb +13 -0
  69. data/test/static_test.rb +57 -0
  70. data/test/templates_test.rb +88 -0
  71. data/test/views/hello.builder +1 -0
  72. data/test/views/hello.erb +1 -0
  73. data/test/views/hello.haml +1 -0
  74. data/test/views/hello.sass +2 -0
  75. data/test/views/hello.test +1 -0
  76. data/test/views/layout2.builder +3 -0
  77. data/test/views/layout2.erb +2 -0
  78. data/test/views/layout2.haml +2 -0
  79. data/test/views/layout2.test +1 -0
  80. metadata +161 -0
@@ -0,0 +1,3 @@
1
+ $reload_count += 1
2
+
3
+ $reload_app.get('/') { 'Hello from reload file' }
data/test/erb_test.rb ADDED
@@ -0,0 +1,50 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
3
+ describe "ERB Templates" do
4
+ def erb_app(&block)
5
+ mock_app {
6
+ set :views, File.dirname(__FILE__) + '/views'
7
+ get '/', &block
8
+ }
9
+ get '/'
10
+ end
11
+
12
+ it 'renders inline ERB strings' do
13
+ erb_app { erb '<%= 1 + 1 %>' }
14
+ assert ok?
15
+ assert_equal '2', body
16
+ end
17
+
18
+ it 'renders .erb files in views path' do
19
+ erb_app { erb :hello }
20
+ assert ok?
21
+ assert_equal "Hello World\n", body
22
+ end
23
+
24
+ it 'takes a :locals option' do
25
+ erb_app {
26
+ locals = {:foo => 'Bar'}
27
+ erb '<%= foo %>', :locals => locals
28
+ }
29
+ assert ok?
30
+ assert_equal 'Bar', body
31
+ end
32
+
33
+ it "renders with inline layouts" do
34
+ mock_app {
35
+ layout { 'THIS. IS. <%= yield.upcase %>!' }
36
+ get('/') { erb 'Sparta' }
37
+ }
38
+ get '/'
39
+ assert ok?
40
+ assert_equal 'THIS. IS. SPARTA!', body
41
+ end
42
+
43
+ it "renders with file layouts" do
44
+ erb_app {
45
+ erb 'Hello World', :layout => :layout2
46
+ }
47
+ assert ok?
48
+ assert_equal "ERB Layout!\nHello World\n", body
49
+ end
50
+ end
@@ -0,0 +1,35 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
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
+ fail 'count != 0' if count != 0
10
+ count = 1
11
+ }
12
+ before {
13
+ fail 'count != 1' if count != 1
14
+ count = 2
15
+ }
16
+ end
17
+
18
+ get '/'
19
+ assert ok?
20
+ assert_equal 2, count
21
+ assert_equal 'Hello World', body
22
+ end
23
+
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
+ }
30
+
31
+ get '/foo'
32
+ assert ok?
33
+ assert_equal 'bar', body
34
+ end
35
+ end
data/test/haml_test.rb ADDED
@@ -0,0 +1,68 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
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 '/'
10
+ end
11
+
12
+ it 'renders inline HAML strings' do
13
+ haml_app { haml '%h1 Hiya' }
14
+ assert ok?
15
+ assert_equal "<h1>Hiya</h1>\n", body
16
+ end
17
+
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
22
+ end
23
+
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
32
+ end
33
+
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
41
+
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
48
+
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
56
+
57
+ it "passes default HAML options to the Haml engine" do
58
+ mock_app {
59
+ set :haml, {:format => :html5}
60
+ get '/' do
61
+ haml "!!!\n%h1 Hello World"
62
+ end
63
+ }
64
+ get '/'
65
+ assert ok?
66
+ assert_equal "<!DOCTYPE html>\n<h1>Hello World</h1>\n", body
67
+ end
68
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,20 @@
1
+ begin
2
+ require 'test/spec'
3
+ rescue LoadError
4
+ require 'rubygems'
5
+ require 'test/spec'
6
+ end
7
+
8
+ $:.unshift File.dirname(File.dirname(__FILE__)) + '/lib'
9
+ require 'sinatra/base'
10
+ require 'sinatra/test'
11
+ require 'sinatra/test/spec'
12
+
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
@@ -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
+ fail "session != {}" unless 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
@@ -0,0 +1,160 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
3
+ describe 'Exception Mappings' do
4
+ class FooError < RuntimeError
5
+ end
6
+
7
+ it 'invokes handlers registered with ::error when raised' do
8
+ mock_app {
9
+ set :raise_errors, false
10
+ error(FooError) { 'Foo!' }
11
+ get '/' do
12
+ raise FooError
13
+ end
14
+ }
15
+ get '/'
16
+ assert_equal 500, status
17
+ assert_equal 'Foo!', body
18
+ end
19
+
20
+ it 'uses the Exception handler if no matching handler found' do
21
+ mock_app {
22
+ set :raise_errors, false
23
+ error(Exception) { 'Exception!' }
24
+ get '/' do
25
+ raise FooError
26
+ end
27
+ }
28
+ get '/'
29
+ assert_equal 500, status
30
+ assert_equal 'Exception!', body
31
+ end
32
+
33
+ it "sets env['sinatra.error'] to the rescued exception" do
34
+ mock_app {
35
+ set :raise_errors, false
36
+ error(FooError) {
37
+ fail "!env.include?('sinatra.error')" if !env.include?('sinatra.error')
38
+ fail unless env['sinatra.error'].kind_of?(FooError)
39
+ 'looks good'
40
+ }
41
+ get '/' do
42
+ raise FooError
43
+ end
44
+ }
45
+ get '/'
46
+ assert_equal 'looks good', body
47
+ end
48
+
49
+ it 'dumps errors to rack.errors when dump_errors is enabled' do
50
+ mock_app {
51
+ set :raise_errors, false
52
+ set :dump_errors, true
53
+ get('/') { raise FooError, 'BOOM!' }
54
+ }
55
+
56
+ get '/'
57
+ assert_equal 500, status
58
+ assert @response.errors =~ /FooError - BOOM!:/
59
+ end
60
+
61
+ it "raises without calling the handler when the raise_errors options is set" do
62
+ mock_app {
63
+ set :raise_errors, true
64
+ error(FooError) { "she's not there." }
65
+ get '/' do
66
+ raise FooError
67
+ end
68
+ }
69
+ assert_raise(FooError) { get '/' }
70
+ end
71
+
72
+ it "never raises Sinatra::NotFound beyond the application" do
73
+ mock_app {
74
+ set :raise_errors, true
75
+ get '/' do
76
+ raise Sinatra::NotFound
77
+ end
78
+ }
79
+ assert_nothing_raised { get '/' }
80
+ assert_equal 404, status
81
+ end
82
+
83
+ class FooNotFound < Sinatra::NotFound
84
+ end
85
+
86
+ it "cascades for subclasses of Sinatra::NotFound" do
87
+ mock_app {
88
+ set :raise_errors, true
89
+ error(FooNotFound) { "foo! not found." }
90
+ get '/' do
91
+ raise FooNotFound
92
+ end
93
+ }
94
+ assert_nothing_raised { get '/' }
95
+ assert_equal 404, status
96
+ assert_equal 'foo! not found.', body
97
+ end
98
+
99
+ it 'has a not_found method for backwards compatibility' do
100
+ mock_app {
101
+ not_found do
102
+ "Lost, are we?"
103
+ end
104
+ }
105
+
106
+ get '/test'
107
+ assert_equal 404, status
108
+ assert_equal "Lost, are we?", body
109
+ end
110
+ end
111
+
112
+ describe 'Custom Error Pages' do
113
+ it 'allows numeric status code mappings to be registered with ::error' do
114
+ mock_app {
115
+ set :raise_errors, false
116
+ error(500) { 'Foo!' }
117
+ get '/' do
118
+ [500, {}, 'Internal Foo Error']
119
+ end
120
+ }
121
+ get '/'
122
+ assert_equal 500, status
123
+ assert_equal 'Foo!', body
124
+ end
125
+
126
+ it 'allows ranges of status code mappings to be registered with :error' do
127
+ mock_app {
128
+ set :raise_errors, false
129
+ error(500..550) { "Error: #{response.status}" }
130
+ get '/' do
131
+ [507, {}, 'A very special error']
132
+ end
133
+ }
134
+ get '/'
135
+ assert_equal 507, status
136
+ assert_equal 'Error: 507', body
137
+ end
138
+
139
+ class FooError < RuntimeError
140
+ end
141
+
142
+ it 'runs after exception mappings and overwrites body' do
143
+ mock_app {
144
+ set :raise_errors, false
145
+ error FooError do
146
+ response.status = 502
147
+ 'from exception mapping'
148
+ end
149
+ error(500) { 'from 500 handler' }
150
+ error(502) { 'from custom error page' }
151
+
152
+ get '/' do
153
+ raise FooError
154
+ end
155
+ }
156
+ get '/'
157
+ assert_equal 502, status
158
+ assert_equal 'from custom error page', body
159
+ end
160
+ end