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