sinatra 1.3.0.e → 1.3.0.f

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.

@@ -6,6 +6,10 @@ end
6
6
  class FooNotFound < Sinatra::NotFound
7
7
  end
8
8
 
9
+ class FooSpecialError < RuntimeError
10
+ def code; 501 end
11
+ end
12
+
9
13
  class MappedErrorTest < Test::Unit::TestCase
10
14
  def test_default
11
15
  assert true
@@ -25,6 +29,15 @@ class MappedErrorTest < Test::Unit::TestCase
25
29
  assert_equal 'Foo!', body
26
30
  end
27
31
 
32
+ it 'passes the exception object to the error handler' do
33
+ mock_app do
34
+ set :raise_errors, false
35
+ error(FooError) { |e| assert_equal(FooError, e.class) }
36
+ get('/') { raise FooError }
37
+ end
38
+ get('/')
39
+ end
40
+
28
41
  it 'uses the Exception handler if no matching handler found' do
29
42
  mock_app {
30
43
  set :raise_errors, false
@@ -108,12 +121,7 @@ class MappedErrorTest < Test::Unit::TestCase
108
121
  end
109
122
 
110
123
  it "never raises Sinatra::NotFound beyond the application" do
111
- mock_app {
112
- set :raise_errors, true
113
- get '/' do
114
- raise Sinatra::NotFound
115
- end
116
- }
124
+ mock_app(Sinatra::Application) { get('/') { raise Sinatra::NotFound }}
117
125
  assert_nothing_raised { get '/' }
118
126
  assert_equal 404, status
119
127
  end
@@ -173,6 +181,17 @@ class MappedErrorTest < Test::Unit::TestCase
173
181
  get '/'
174
182
  assert_equal 'subclass', body
175
183
  end
184
+
185
+ it 'honors Exception#code if present' do
186
+ mock_app do
187
+ set :raise_errors, false
188
+ error(501) { 'Foo!' }
189
+ get('/') { raise FooSpecialError }
190
+ end
191
+ get '/'
192
+ assert_equal 501, status
193
+ assert_equal 'Foo!', body
194
+ end
176
195
  end
177
196
 
178
197
  describe 'Custom Error Pages' do
@@ -15,7 +15,7 @@ class NokogiriTest < Test::Unit::TestCase
15
15
  it 'renders inline Nokogiri strings' do
16
16
  nokogiri_app { nokogiri 'xml' }
17
17
  assert ok?
18
- assert_body %{<?xml version="1.0"?>\n}
18
+ assert_body %(<?xml version="1.0"?>\n)
19
19
  end
20
20
 
21
21
  it 'renders inline blocks' do
@@ -26,7 +26,7 @@ class NokogiriTest < Test::Unit::TestCase
26
26
  end
27
27
  end
28
28
  assert ok?
29
- assert_body "<?xml version=\"1.0\"?>\n<couple>Frank &amp; Mary</couple>\n"
29
+ assert_body %(<?xml version="1.0"?>\n<couple>Frank &amp; Mary</couple>\n)
30
30
  end
31
31
 
32
32
  it 'renders .nokogiri files in views path' do
@@ -35,7 +35,7 @@ class NokogiriTest < Test::Unit::TestCase
35
35
  nokogiri :hello
36
36
  end
37
37
  assert ok?
38
- assert_body %(<?xml version="1.0"?>\n<exclaim>You're my boy, Blue!</exclaim>\n)
38
+ assert_body "<?xml version=\"1.0\"?>\n<exclaim>You're my boy, Blue!</exclaim>\n"
39
39
  end
40
40
 
41
41
  it "renders with inline layouts" do
@@ -46,17 +46,16 @@ class NokogiriTest < Test::Unit::TestCase
46
46
  end
47
47
  get '/'
48
48
  assert ok?
49
- assert_body "<?xml version=\"1.0\"?>\n<layout>\n <em>Hello World</em>\n</layout>\n"
49
+ assert_body %(<?xml version="1.0"?>\n<layout>\n <em>Hello World</em>\n</layout>\n)
50
50
  end
51
51
 
52
52
  it "renders with file layouts" do
53
53
  next if Tilt::VERSION <= "1.1"
54
54
  nokogiri_app do
55
- @name = "Blue"
56
55
  nokogiri %(xml.em 'Hello World'), :layout => :layout2
57
56
  end
58
57
  assert ok?
59
- assert_body "<?xml version=\"1.0\"?>\n<layout>\n <em>Hello World</em>\n</layout>\n"
58
+ assert_body %(<?xml version="1.0"?>\n<layout>\n <em>Hello World</em>\n</layout>\n)
60
59
  end
61
60
 
62
61
  it "raises error if template not found" do
@@ -37,7 +37,16 @@ class ResponseTest < Test::Unit::TestCase
37
37
  @response.body = ['Hello', 'World!', '✈']
38
38
  status, headers, body = @response.finish
39
39
  assert_equal '14', headers['Content-Length']
40
- assert_equal @response.body, body.body
40
+ assert_equal @response.body, body
41
+ end
42
+
43
+ it 'does not call #to_ary or #inject on the body' do
44
+ object = Object.new
45
+ def object.inject(*) fail 'called' end
46
+ def object.to_ary(*) fail 'called' end
47
+ def object.each(*) end
48
+ @response.body = object
49
+ assert @response.finish
41
50
  end
42
51
 
43
52
  it 'does not nest a Sinatra::Response' do
data/test/result_test.rb CHANGED
@@ -54,12 +54,12 @@ class ResultTest < Test::Unit::TestCase
54
54
  it "sets status, headers, and body when result is a Rack response tuple" do
55
55
  mock_app {
56
56
  get '/' do
57
- [205, {'Content-Type' => 'foo/bar'}, 'Hello World']
57
+ [203, {'Content-Type' => 'foo/bar'}, 'Hello World']
58
58
  end
59
59
  }
60
60
 
61
61
  get '/'
62
- assert_equal 205, status
62
+ assert_equal 203, status
63
63
  assert_equal 'foo/bar', response['Content-Type']
64
64
  assert_equal 'Hello World', body
65
65
  end
data/test/routing_test.rb CHANGED
@@ -1080,4 +1080,17 @@ class RoutingTest < Test::Unit::TestCase
1080
1080
  assert ok?
1081
1081
  assert_body 'hello'
1082
1082
  end
1083
+
1084
+ it 'returns the route signature' do
1085
+ signature = list = nil
1086
+
1087
+ mock_app do
1088
+ signature = post('/') { }
1089
+ list = routes['POST']
1090
+ end
1091
+
1092
+ assert_equal Array, signature.class
1093
+ assert_equal 4, signature.length
1094
+ assert list.include?(signature)
1095
+ end
1083
1096
  end
data/test/server_test.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require File.expand_path('../helper', __FILE__)
2
+ require 'stringio'
2
3
 
3
4
  module Rack::Handler
4
5
  class Mock
@@ -25,11 +26,11 @@ class ServerTest < Test::Unit::TestCase
25
26
  set :bind, 'foo.local'
26
27
  set :port, 9001
27
28
  }
28
- $stdout = File.open('/dev/null', 'wb')
29
+ $stderr = StringIO.new
29
30
  end
30
31
 
31
32
  def teardown
32
- $stdout = STDOUT
33
+ $stderr = STDERR
33
34
  end
34
35
 
35
36
  it "locates the appropriate Rack handler and calls ::run" do
@@ -306,6 +306,29 @@ class SettingsTest < Test::Unit::TestCase
306
306
  get '/'
307
307
  assert body.include?("RuntimeError") && body.include?("settings_test.rb")
308
308
  end
309
+
310
+ it 'does not dump 404 errors' do
311
+ klass = Sinatra.new(Sinatra::Application)
312
+
313
+ mock_app(klass) {
314
+ enable :dump_errors
315
+ disable :raise_errors
316
+
317
+ error do
318
+ error = @env['rack.errors'].instance_variable_get(:@error)
319
+ error.rewind
320
+
321
+ error.read
322
+ end
323
+
324
+ get '/' do
325
+ raise Sinatra::NotFound
326
+ end
327
+ }
328
+
329
+ get '/'
330
+ assert !body.include?("NotFound") && !body.include?("settings_test.rb")
331
+ end
309
332
  end
310
333
 
311
334
  describe 'sessions' do
@@ -336,13 +359,13 @@ class SettingsTest < Test::Unit::TestCase
336
359
  assert ! @base.static?
337
360
  end
338
361
 
339
- it 'is enabled on Base when public is set and exists' do
362
+ it 'is enabled on Base when public_folder is set and exists' do
340
363
  @base.set :environment, :development
341
- @base.set :public, File.dirname(__FILE__)
364
+ @base.set :public_folder, File.dirname(__FILE__)
342
365
  assert @base.static?
343
366
  end
344
367
 
345
- it 'is enabled on Base when root is set and root/public exists' do
368
+ it 'is enabled on Base when root is set and root/public_folder exists' do
346
369
  @base.set :environment, :development
347
370
  @base.set :root, File.dirname(__FILE__)
348
371
  assert @base.static?
@@ -352,17 +375,36 @@ class SettingsTest < Test::Unit::TestCase
352
375
  assert ! @application.static?
353
376
  end
354
377
 
355
- it 'is enabled on Application when public is set and exists' do
378
+ it 'is enabled on Application when public_folder is set and exists' do
356
379
  @application.set :environment, :development
357
- @application.set :public, File.dirname(__FILE__)
380
+ @application.set :public_folder, File.dirname(__FILE__)
358
381
  assert @application.static?
359
382
  end
360
383
 
361
- it 'is enabled on Application when root is set and root/public exists' do
384
+ it 'is enabled on Application when root is set and root/public_folder exists' do
362
385
  @application.set :environment, :development
363
386
  @application.set :root, File.dirname(__FILE__)
364
387
  assert @application.static?
365
388
  end
389
+
390
+ it 'is possible to use Module#public' do
391
+ @base.send(:define_method, :foo) { }
392
+ @base.send(:private, :foo)
393
+ assert !@base.public_method_defined?(:foo)
394
+ @base.send(:public, :foo)
395
+ assert @base.public_method_defined?(:foo)
396
+ end
397
+
398
+ it 'is possible to use the keyword public in a sinatra app' do
399
+ app = Sinatra.new do
400
+ private
401
+ def priv; end
402
+ public
403
+ def pub; end
404
+ end
405
+ assert !app.public_method_defined?(:priv)
406
+ assert app.public_method_defined?(:pub)
407
+ end
366
408
  end
367
409
 
368
410
  describe 'bind' do
@@ -427,18 +469,18 @@ class SettingsTest < Test::Unit::TestCase
427
469
  end
428
470
  end
429
471
 
430
- describe 'public' do
472
+ describe 'public_folder' do
431
473
  it 'is nil if root is not set' do
432
- assert @base.public.nil?
433
- assert @application.public.nil?
474
+ assert @base.public_folder.nil?
475
+ assert @application.public_folder.nil?
434
476
  end
435
477
 
436
478
  it 'is set to root joined with public/' do
437
479
  @base.root = File.dirname(__FILE__)
438
- assert_equal File.dirname(__FILE__) + "/public", @base.public
480
+ assert_equal File.dirname(__FILE__) + "/public", @base.public_folder
439
481
 
440
482
  @application.root = File.dirname(__FILE__)
441
- assert_equal File.dirname(__FILE__) + "/public", @application.public
483
+ assert_equal File.dirname(__FILE__) + "/public", @application.public_folder
442
484
  end
443
485
  end
444
486
 
@@ -448,4 +490,49 @@ class SettingsTest < Test::Unit::TestCase
448
490
  assert ! @application.lock?
449
491
  end
450
492
  end
493
+
494
+ describe 'protection' do
495
+ class MiddlewareTracker < Rack::Builder
496
+ def self.track
497
+ Rack.send :remove_const, :Builder
498
+ Rack.const_set :Builder, MiddlewareTracker
499
+ MiddlewareTracker.used.clear
500
+ yield
501
+ ensure
502
+ Rack.send :remove_const, :Builder
503
+ Rack.const_set :Builder, MiddlewareTracker.superclass
504
+ end
505
+
506
+ def self.used
507
+ @used ||= []
508
+ end
509
+
510
+ def use(middleware, *)
511
+ MiddlewareTracker.used << middleware
512
+ super
513
+ end
514
+ end
515
+
516
+ it 'sets up Rack::Protection' do
517
+ MiddlewareTracker.track do
518
+ Sinatra::Base.new
519
+ assert_include MiddlewareTracker.used, Rack::Protection
520
+ end
521
+ end
522
+
523
+ it 'sets up Rack::Protection::PathTraversal' do
524
+ MiddlewareTracker.track do
525
+ Sinatra::Base.new
526
+ assert_include MiddlewareTracker.used, Rack::Protection::PathTraversal
527
+ end
528
+ end
529
+
530
+ it 'does not set up Rack::Protection::PathTraversal when disabling it' do
531
+ MiddlewareTracker.track do
532
+ Sinatra.new { set :protection, :except => :path_traversal }.new
533
+ assert_include MiddlewareTracker.used, Rack::Protection
534
+ assert !MiddlewareTracker.used.include?(Rack::Protection::PathTraversal)
535
+ end
536
+ end
537
+ end
451
538
  end
data/test/slim_test.rb CHANGED
@@ -52,44 +52,34 @@ class SlimTest < Test::Unit::TestCase
52
52
  HTML4_DOCTYPE = "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">"
53
53
 
54
54
  it "passes slim options to the slim engine" do
55
- mock_app {
56
- get '/' do
57
- slim "! doctype html\nh1 Hello World", :format => :html4
58
- end
59
- }
55
+ mock_app { get('/') { slim "x foo='bar'", :attr_wrapper => "'" }}
60
56
  get '/'
61
57
  assert ok?
62
- assert_equal "#{HTML4_DOCTYPE}<h1>Hello World</h1>", body
58
+ assert_body "<x foo='bar'></x>"
63
59
  end
64
60
 
65
61
  it "passes default slim options to the slim engine" do
66
- mock_app {
67
- set :slim, {:format => :html4}
68
- get '/' do
69
- slim "! doctype html\nh1 Hello World"
70
- end
71
- }
62
+ mock_app do
63
+ set :slim, :attr_wrapper => "'"
64
+ get('/') { slim "x foo='bar'" }
65
+ end
72
66
  get '/'
73
67
  assert ok?
74
- assert_equal "#{HTML4_DOCTYPE}<h1>Hello World</h1>", body
68
+ assert_body "<x foo='bar'></x>"
75
69
  end
76
70
 
77
71
  it "merges the default slim options with the overrides and passes them to the slim engine" do
78
- mock_app {
79
- set :slim, {:format => :html4}
80
- get '/' do
81
- slim "! doctype html\nh1.header Hello World"
82
- end
83
- get '/html5' do
84
- slim "! doctype html\nh1.header Hello World", :format => :html5
85
- end
86
- }
72
+ mock_app do
73
+ set :slim, :attr_wrapper => "'"
74
+ get('/') { slim "x foo='bar'" }
75
+ get('/other') { slim "x foo='bar'", :attr_wrapper => '"' }
76
+ end
87
77
  get '/'
88
78
  assert ok?
89
- assert_match(/^#{HTML4_DOCTYPE}/, body)
90
- get '/html5'
79
+ assert_body "<x foo='bar'></x>"
80
+ get '/other'
91
81
  assert ok?
92
- assert_equal "<!DOCTYPE html><h1 class=\"header\">Hello World</h1>", body
82
+ assert_body '<x foo="bar"></x>'
93
83
  end
94
84
  end
95
85
 
data/test/static_test.rb CHANGED
@@ -4,7 +4,7 @@ class StaticTest < Test::Unit::TestCase
4
4
  setup do
5
5
  mock_app {
6
6
  set :static, true
7
- set :public, File.dirname(__FILE__)
7
+ set :public_folder, File.dirname(__FILE__)
8
8
  }
9
9
  end
10
10
 
@@ -66,7 +66,7 @@ class StaticTest < Test::Unit::TestCase
66
66
  end
67
67
 
68
68
  it 'passes to the next handler when the public option is nil' do
69
- @app.set :public, nil
69
+ @app.set :public_folder, nil
70
70
  get "/#{File.basename(__FILE__)}"
71
71
  assert not_found?
72
72
  end
@@ -85,7 +85,7 @@ class StaticTest < Test::Unit::TestCase
85
85
  it '404s when .. path traverses outside of public directory' do
86
86
  mock_app {
87
87
  set :static, true
88
- set :public, File.dirname(__FILE__) + '/data'
88
+ set :public_folder, File.dirname(__FILE__) + '/data'
89
89
  }
90
90
  get "/../#{File.basename(__FILE__)}"
91
91
  assert not_found?
@@ -0,0 +1,100 @@
1
+ require File.expand_path('../helper', __FILE__)
2
+
3
+ class StreamingTest < Test::Unit::TestCase
4
+ Stream = Sinatra::Helpers::Stream
5
+
6
+ it 'returns the concatinated body' do
7
+ mock_app do
8
+ get '/' do
9
+ stream do |out|
10
+ out << "Hello" << " "
11
+ out << "World!"
12
+ end
13
+ end
14
+ end
15
+
16
+ get('/')
17
+ assert_body "Hello World!"
18
+ end
19
+
20
+ it 'always yields strings' do
21
+ stream = Stream.new { |out| out << :foo }
22
+ stream.each { |str| assert_equal 'foo', str }
23
+ end
24
+
25
+ it 'postpones body generation' do
26
+ step = 0
27
+
28
+ stream = Stream.new do |out|
29
+ 10.times do
30
+ out << step
31
+ step += 1
32
+ end
33
+ end
34
+
35
+ stream.each do |s|
36
+ assert_equal s, step.to_s
37
+ step += 1
38
+ end
39
+ end
40
+
41
+ it 'calls the callback after it is done' do
42
+ step = 0
43
+ final = 0
44
+ stream = Stream.new { |o| 10.times { step += 1 }}
45
+ stream.callback { final = step }
46
+ stream.each { |str| }
47
+ assert_equal 10, final
48
+ end
49
+
50
+ it 'does not trigger the callback if close is set to :keep_open' do
51
+ step = 0
52
+ final = 0
53
+ stream = Stream.new(Stream, :keep_open) { |o| 10.times { step += 1 } }
54
+ stream.callback { final = step }
55
+ stream.each { |str| }
56
+ assert_equal 0, final
57
+ end
58
+
59
+ class MockScheduler
60
+ def initialize(*) @schedule, @defer = [], [] end
61
+ def schedule(&block) @schedule << block end
62
+ def defer(&block) @defer << block end
63
+ def schedule!(*) @schedule.pop.call until @schedule.empty? end
64
+ def defer!(*) @defer.pop.call until @defer.empty? end
65
+ end
66
+
67
+ it 'allows dropping in another scheduler' do
68
+ scheduler = MockScheduler.new
69
+ processing = sending = done = false
70
+
71
+ stream = Stream.new(scheduler) do |out|
72
+ processing = true
73
+ out << :foo
74
+ end
75
+
76
+ stream.each { sending = true}
77
+ stream.callback { done = true }
78
+
79
+ scheduler.schedule!
80
+ assert !processing
81
+ assert !sending
82
+ assert !done
83
+
84
+ scheduler.defer!
85
+ assert processing
86
+ assert !sending
87
+ assert !done
88
+
89
+ scheduler.schedule!
90
+ assert sending
91
+ assert done
92
+ end
93
+
94
+ it 'schedules exceptions to be raised on the main thread/event loop/...' do
95
+ scheduler = MockScheduler.new
96
+ Stream.new(scheduler) { fail 'should be caught' }.each { }
97
+ scheduler.defer!
98
+ assert_raise(RuntimeError) { scheduler.schedule! }
99
+ end
100
+ end