1
0
Fork 0
mirror of https://github.com/sinatra/sinatra synced 2023-03-27 23:18:01 -04:00
sinatra/test/routing_test.rb
Markus Prinz 12eced8414 Escape '.', '+', '$' and parens in literal paths [#124]
Since Sinatra turns literal paths into a regular expression,
we need to escape dots and pluses, as they have special
meanings inside a regex, but not ?, since we need that
for optional parameters.
2009-01-23 01:49:27 -08:00

448 lines
9.4 KiB
Ruby

require File.dirname(__FILE__) + '/helper'
# Helper method for easy route pattern matching testing
def route_def(pattern)
mock_app { get(pattern) { } }
end
describe "Routing" do
%w[get put post delete head].each do |verb|
it "defines #{verb.upcase} request handlers with #{verb}" do
mock_app {
send verb, '/hello' do
'Hello World'
end
}
request = Rack::MockRequest.new(@app)
response = request.request(verb.upcase, '/hello', {})
assert response.ok?
assert_equal 'Hello World', response.body
end
end
it "404s when no route satisfies the request" do
mock_app {
get('/foo') { }
}
get '/bar'
assert_equal 404, status
end
it "exposes params with indifferent hash" do
mock_app {
get '/:foo' do
assert_equal 'bar', params['foo']
assert_equal 'bar', params[:foo]
'well, alright'
end
}
get '/bar'
assert_equal 'well, alright', body
end
it "merges named params and query string params in params" do
mock_app {
get '/:foo' do
assert_equal 'bar', params['foo']
assert_equal 'biz', params['baz']
end
}
get '/bar?baz=biz'
assert ok?
end
it "supports named params like /hello/:person" do
mock_app {
get '/hello/:person' do
"Hello #{params['person']}"
end
}
get '/hello/Frank'
assert_equal 'Hello Frank', body
end
it "supports optional named params like /?:foo?/?:bar?" do
mock_app {
get '/?:foo?/?:bar?' do
"foo=#{params[:foo]};bar=#{params[:bar]}"
end
}
get '/hello/world'
assert ok?
assert_equal "foo=hello;bar=world", body
get '/hello'
assert ok?
assert_equal "foo=hello;bar=", body
get '/'
assert ok?
assert_equal "foo=;bar=", body
end
it "supports single splat params like /*" do
mock_app {
get '/*' do
assert params['splat'].kind_of?(Array)
params['splat'].join "\n"
end
}
get '/foo'
assert_equal "foo", body
get '/foo/bar/baz'
assert_equal "foo/bar/baz", body
end
it "supports mixing multiple splat params like /*/foo/*/*" do
mock_app {
get '/*/foo/*/*' do
assert params['splat'].kind_of?(Array)
params['splat'].join "\n"
end
}
get '/bar/foo/bling/baz/boom'
assert_equal "bar\nbling\nbaz/boom", body
get '/bar/foo/baz'
assert not_found?
end
it "supports mixing named and splat params like /:foo/*" do
mock_app {
get '/:foo/*' do
assert_equal 'foo', params['foo']
assert_equal ['bar/baz'], params['splat']
end
}
get '/foo/bar/baz'
assert ok?
end
it "literally matches . in paths" do
route_def '/test.bar'
get '/test.bar'
assert ok?
get 'test0bar'
assert not_found?
end
it "literally matches $ in paths" do
route_def '/test$/'
get '/test$/'
assert ok?
end
it "literally matches + in paths" do
route_def '/te+st/'
get '/te+st/'
assert ok?
get '/teeeeeeest/'
assert not_found?
end
it "literally matches () in paths" do
route_def '/test(bar)/'
get '/test(bar)/'
assert ok?
end
it "supports basic nested params" do
mock_app {
get '/hi' do
params["person"]["name"]
end
}
get "/hi?person[name]=John+Doe"
assert ok?
assert_equal "John Doe", body
end
it "exposes nested params with indifferent hash" do
mock_app {
get '/testme' do
assert_equal 'baz', params['bar']['foo']
assert_equal 'baz', params['bar'][:foo]
'well, alright'
end
}
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 = {
"emacs" => {
"map" => { "goto-line" => "M-g g" },
"version" => "22.3.1"
},
"browser" => {
"firefox" => {"engine" => {"name"=>"spidermonkey", "version"=>"1.7.0"}},
"chrome" => {"engine" => {"name"=>"V8", "version"=>"1.0"}}
},
"paste" => {"name"=>"hello world", "syntax"=>"ruby"}
}
mock_app {
get '/foo' do
assert_equal expected, params
'looks good'
end
}
get "/foo?#{build_query(input)}"
assert ok?
assert_equal 'looks good', body
end
it "preserves non-nested params" do
mock_app {
get '/foo' do
assert_equal "2", params["article_id"]
assert_equal "awesome", params['comment']['body']
assert_nil params['comment[body]']
'looks good'
end
}
get '/foo?article_id=2&comment[body]=awesome'
assert ok?
assert_equal 'looks good', body
end
it "supports paths that include spaces" do
mock_app {
get '/path with spaces' do
'looks good'
end
}
get '/path%20with%20spaces'
assert ok?
assert_equal 'looks good', body
end
it "URL decodes named parameters and splats" do
mock_app {
get '/:foo/*' do
assert_equal 'hello world', params['foo']
assert_equal ['how are you'], params['splat']
nil
end
}
get '/hello%20world/how%20are%20you'
assert ok?
end
it 'supports regular expressions' do
mock_app {
get(/^\/foo...\/bar$/) do
'Hello World'
end
}
get '/foooom/bar'
assert ok?
assert_equal 'Hello World', body
end
it 'makes regular expression captures available in params[:captures]' do
mock_app {
get(/^\/fo(.*)\/ba(.*)/) do
assert_equal ['orooomma', 'f'], params[:captures]
'right on'
end
}
get '/foorooomma/baf'
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){} }
end
it "returns response immediately on halt" do
mock_app {
get '/' do
halt 'Hello World'
'Boo-hoo World'
end
}
get '/'
assert ok?
assert_equal 'Hello World', body
end
it "transitions to the next matching route on pass" do
mock_app {
get '/:foo' do
pass
'Hello Foo'
end
get '/*' do
assert !params.include?('foo')
'Hello World'
end
}
get '/bar'
assert ok?
assert_equal 'Hello World', body
end
it "transitions to 404 when passed and no subsequent route matches" do
mock_app {
get '/:foo' do
pass
'Hello Foo'
end
}
get '/bar'
assert not_found?
end
it "passes when matching condition returns false" do
mock_app {
condition { params[:foo] == 'bar' }
get '/:foo' do
'Hello World'
end
}
get '/bar'
assert ok?
assert_equal 'Hello World', body
get '/foo'
assert not_found?
end
it "does not pass when matching condition returns nil" do
mock_app {
condition { nil }
get '/:foo' do
'Hello World'
end
}
get '/bar'
assert ok?
assert_equal 'Hello World', body
end
it "passes to next route when condition calls pass explicitly" do
mock_app {
condition { pass unless params[:foo] == 'bar' }
get '/:foo' do
'Hello World'
end
}
get '/bar'
assert ok?
assert_equal 'Hello World', body
get '/foo'
assert not_found?
end
it "passes to the next route when host_name does not match" do
mock_app {
host_name 'example.com'
get '/foo' do
'Hello World'
end
}
get '/foo'
assert not_found?
get '/foo', :env => { '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
mock_app {
user_agent(/Foo/)
get '/foo' do
'Hello World'
end
}
get '/foo'
assert not_found?
get '/foo', :env => { '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
mock_app {
user_agent(/Foo (.*)/)
get '/foo' do
'Hello ' + params[:agent].first
end
}
get '/foo', :env => { 'HTTP_USER_AGENT' => 'Foo Bar' }
assert_equal 200, status
assert_equal 'Hello Bar', body
end
it "filters by accept header" do
mock_app {
get '/', :provides => :xml do
request.env['HTTP_ACCEPT']
end
}
get '/', :env => { :accept => 'application/xml' }
assert ok?
assert_equal 'application/xml', body
assert_equal 'application/xml', response.headers['Content-Type']
get '/', :env => { :accept => 'text/html' }
assert !ok?
end
it "allows multiple mime types for accept header" do
types = ['image/jpeg', 'image/pjpeg']
mock_app {
get '/', :provides => types do
request.env['HTTP_ACCEPT']
end
}
types.each do |type|
get '/', :env => { :accept => type }
assert ok?
assert_equal type, body
assert_equal type, response.headers['Content-Type']
end
end
end