test/routing_test.rb in darkhelmet-sinatra-0.9.1.1 vs test/routing_test.rb in darkhelmet-sinatra-0.10.1
- old
+ new
@@ -3,11 +3,27 @@
# Helper method for easy route pattern matching testing
def route_def(pattern)
mock_app { get(pattern) { } }
end
-describe "Routing" do
+class RegexpLookAlike
+ class MatchData
+ def captures
+ ["this", "is", "a", "test"]
+ end
+ end
+
+ def match(string)
+ ::RegexpLookAlike::MatchData.new if string == "/this/is/a/test/"
+ end
+
+ def keys
+ ["one", "two", "three", "four"]
+ end
+end
+
+class RoutingTest < Test::Unit::TestCase
%w[get put post delete].each do |verb|
it "defines #{verb.upcase} request handlers with #{verb}" do
mock_app {
send verb, '/hello' do
'Hello World'
@@ -42,10 +58,34 @@
}
get '/bar'
assert_equal 404, status
end
+ it "404s and sets X-Cascade header when no route satisfies the request" do
+ mock_app {
+ get('/foo') { }
+ }
+ get '/bar'
+ assert_equal 404, status
+ assert_equal 'pass', response.headers['X-Cascade']
+ end
+
+ it "overrides the content-type in error handlers" do
+ mock_app {
+ before { content_type 'text/plain' }
+ error Sinatra::NotFound do
+ content_type "text/html"
+ "<h1>Not Found</h1>"
+ end
+ }
+
+ get '/foo'
+ assert_equal 404, status
+ assert_equal 'text/html', response["Content-Type"]
+ assert_equal "<h1>Not Found</h1>", response.body
+ end
+
it 'takes multiple definitions of a route' do
mock_app {
user_agent(/Foo/)
get '/foo' do
'foo'
@@ -241,21 +281,11 @@
get '/testme?bar[foo]=baz'
assert_equal 'well, alright', body
end
it "supports deeply nested params" do
- input = {
- 'browser[chrome][engine][name]' => 'V8',
- 'browser[chrome][engine][version]' => '1.0',
- 'browser[firefox][engine][name]' => 'spidermonkey',
- 'browser[firefox][engine][version]' => '1.7.0',
- 'emacs[map][goto-line]' => 'M-g g',
- 'emacs[version]' => '22.3.1',
- 'paste[name]' => 'hello world',
- 'paste[syntax]' => 'ruby'
- }
- expected = {
+ expected_params = {
"emacs" => {
"map" => { "goto-line" => "M-g g" },
"version" => "22.3.1"
},
"browser" => {
@@ -264,15 +294,15 @@
},
"paste" => {"name"=>"hello world", "syntax"=>"ruby"}
}
mock_app {
get '/foo' do
- assert_equal expected, params
+ assert_equal expected_params, params
'looks good'
end
}
- get "/foo?#{build_query(input)}"
+ get '/foo', expected_params
assert ok?
assert_equal 'looks good', body
end
it "preserves non-nested params" do
@@ -350,13 +380,30 @@
get '/foorooomma/baf'
assert ok?
assert_equal 'right on', body
end
+ it 'supports regular expression look-alike routes' do
+ mock_app {
+ get(RegexpLookAlike.new) do
+ assert_equal 'this', params[:one]
+ assert_equal 'is', params[:two]
+ assert_equal 'a', params[:three]
+ assert_equal 'test', params[:four]
+ 'right on'
+ end
+ }
+
+ get '/this/is/a/test/'
+ assert ok?
+ assert_equal 'right on', body
+ end
+
it 'raises a TypeError when pattern is not a String or Regexp' do
- @app = mock_app
- assert_raise(TypeError) { @app.get(42){} }
+ assert_raise(TypeError) {
+ mock_app { get(42){} }
+ }
end
it "returns response immediately on halt" do
mock_app {
get '/' do
@@ -422,10 +469,42 @@
get '/bar'
assert not_found?
end
+ it "transitions to 404 and sets X-Cascade header when passed and no subsequent route matches" do
+ mock_app {
+ get '/:foo' do
+ pass
+ 'Hello Foo'
+ end
+
+ get '/bar' do
+ 'Hello Bar'
+ end
+ }
+
+ get '/foo'
+ assert not_found?
+ assert_equal 'pass', response.headers['X-Cascade']
+ end
+
+ it "uses optional block passed to pass as route block if no other route is found" do
+ mock_app {
+ get "/" do
+ pass do
+ "this"
+ end
+ "not this"
+ end
+ }
+
+ get "/"
+ assert ok?
+ assert "this", body
+ end
+
it "passes when matching condition returns false" do
mock_app {
condition { params[:foo] == 'bar' }
get '/:foo' do
'Hello World'
@@ -477,11 +556,11 @@
end
}
get '/foo'
assert not_found?
- get '/foo', :env => { 'HTTP_HOST' => 'example.com' }
+ get '/foo', {}, { 'HTTP_HOST' => 'example.com' }
assert_equal 200, status
assert_equal 'Hello World', body
end
it "passes to the next route when user_agent does not match" do
@@ -492,11 +571,11 @@
end
}
get '/foo'
assert not_found?
- get '/foo', :env => { 'HTTP_USER_AGENT' => 'Foo Bar' }
+ get '/foo', {}, { 'HTTP_USER_AGENT' => 'Foo Bar' }
assert_equal 200, status
assert_equal 'Hello World', body
end
it "makes captures in user agent pattern available in params[:agent]" do
@@ -504,11 +583,11 @@
user_agent(/Foo (.*)/)
get '/foo' do
'Hello ' + params[:agent].first
end
}
- get '/foo', :env => { 'HTTP_USER_AGENT' => 'Foo Bar' }
+ get '/foo', {}, { 'HTTP_USER_AGENT' => 'Foo Bar' }
assert_equal 200, status
assert_equal 'Hello Bar', body
end
it "filters by accept header" do
@@ -516,16 +595,16 @@
get '/', :provides => :xml do
request.env['HTTP_ACCEPT']
end
}
- get '/', :env => { :accept => 'application/xml' }
+ get '/', {}, { 'HTTP_ACCEPT' => 'application/xml' }
assert ok?
assert_equal 'application/xml', body
assert_equal 'application/xml', response.headers['Content-Type']
- get '/', :env => { :accept => 'text/html' }
+ get '/', {}, { :accept => 'text/html' }
assert !ok?
end
it "allows multiple mime types for accept header" do
types = ['image/jpeg', 'image/pjpeg']
@@ -535,11 +614,11 @@
request.env['HTTP_ACCEPT']
end
}
types.each do |type|
- get '/', :env => { :accept => type }
+ get '/', {}, { 'HTTP_ACCEPT' => type }
assert ok?
assert_equal type, body
assert_equal type, response.headers['Content-Type']
end
end
@@ -653,10 +732,44 @@
get '/a/b'
assert ok?
assert_equal 'ab', body
end
+ it 'allows custom route-conditions to be set via route options' do
+ protector = Module.new {
+ def protect(*args)
+ condition {
+ unless authorize(params["user"], params["password"])
+ halt 403, "go away"
+ end
+ }
+ end
+ }
+
+ mock_app {
+ register protector
+
+ helpers do
+ def authorize(username, password)
+ username == "foo" && password == "bar"
+ end
+ end
+
+ get "/", :protect => true do
+ "hey"
+ end
+ }
+
+ get "/"
+ assert forbidden?
+ assert_equal "go away", body
+
+ get "/", :user => "foo", :password => "bar"
+ assert ok?
+ assert_equal "hey", body
+ end
+
# NOTE Block params behaves differently under 1.8 and 1.9. Under 1.8, block
# param arity is lax: declaring a mismatched number of block params results
# in a warning. Under 1.9, block param arity is strict: mismatched block
# arity raises an ArgumentError.
@@ -706,7 +819,42 @@
silence_warnings { get '/a/b/c' }
assert ok?
assert_equal 'quux', body
end
+ end
+
+ it "matches routes defined in superclasses" do
+ base = Class.new(Sinatra::Base)
+ base.get('/foo') { 'foo in baseclass' }
+
+ mock_app(base) {
+ get('/bar') { 'bar in subclass' }
+ }
+
+ get '/foo'
+ assert ok?
+ assert_equal 'foo in baseclass', body
+
+ get '/bar'
+ assert ok?
+ assert_equal 'bar in subclass', body
+ end
+
+ it "matches routes in subclasses before superclasses" do
+ base = Class.new(Sinatra::Base)
+ base.get('/foo') { 'foo in baseclass' }
+ base.get('/bar') { 'bar in baseclass' }
+
+ mock_app(base) {
+ get('/foo') { 'foo in subclass' }
+ }
+
+ get '/foo'
+ assert ok?
+ assert_equal 'foo in subclass', body
+
+ get '/bar'
+ assert ok?
+ assert_equal 'bar in baseclass', body
end
end