123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544 |
- # encoding: UTF-8
- require 'helper'
- module Journey
- class TestRouter < MiniTest::Unit::TestCase
- attr_reader :routes
- def setup
- @routes = Routes.new
- @router = Router.new(@routes, {})
- @formatter = Formatter.new(@routes)
- end
- def test_request_class_reader
- klass = Object.new
- router = Router.new(routes, :request_class => klass)
- assert_equal klass, router.request_class
- end
- class FakeRequestFeeler < Struct.new(:env, :called)
- def new env
- self.env = env
- self
- end
- def hello
- self.called = true
- 'world'
- end
- def path_info; env['PATH_INFO']; end
- def request_method; env['REQUEST_METHOD']; end
- def ip; env['REMOTE_ADDR']; end
- end
- def test_dashes
- klass = FakeRequestFeeler.new nil
- router = Router.new(routes, {})
- exp = Router::Strexp.new '/foo-bar-baz', {}, ['/.?']
- path = Path::Pattern.new exp
- routes.add_route nil, path, {}, {:id => nil}, {}
- env = rails_env 'PATH_INFO' => '/foo-bar-baz'
- called = false
- router.recognize(env) do |r, _, params|
- called = true
- end
- assert called
- end
- def test_unicode
- klass = FakeRequestFeeler.new nil
- router = Router.new(routes, {})
- #match the escaped version of /ほげ
- exp = Router::Strexp.new '/%E3%81%BB%E3%81%92', {}, ['/.?']
- path = Path::Pattern.new exp
- routes.add_route nil, path, {}, {:id => nil}, {}
- env = rails_env 'PATH_INFO' => '/%E3%81%BB%E3%81%92'
- called = false
- router.recognize(env) do |r, _, params|
- called = true
- end
- assert called
- end
- def test_request_class_and_requirements_success
- klass = FakeRequestFeeler.new nil
- router = Router.new(routes, {:request_class => klass })
- requirements = { :hello => /world/ }
- exp = Router::Strexp.new '/foo(/:id)', {}, ['/.?']
- path = Path::Pattern.new exp
- routes.add_route nil, path, requirements, {:id => nil}, {}
- env = rails_env 'PATH_INFO' => '/foo/10'
- router.recognize(env) do |r, _, params|
- assert_equal({:id => '10'}, params)
- end
- assert klass.called, 'hello should have been called'
- assert_equal env.env, klass.env
- end
- def test_request_class_and_requirements_fail
- klass = FakeRequestFeeler.new nil
- router = Router.new(routes, {:request_class => klass })
- requirements = { :hello => /mom/ }
- exp = Router::Strexp.new '/foo(/:id)', {}, ['/.?']
- path = Path::Pattern.new exp
- router.routes.add_route nil, path, requirements, {:id => nil}, {}
- env = rails_env 'PATH_INFO' => '/foo/10'
- router.recognize(env) do |r, _, params|
- flunk 'route should not be found'
- end
- assert klass.called, 'hello should have been called'
- assert_equal env.env, klass.env
- end
- class CustomPathRequest < Router::NullReq
- def path_info
- env['custom.path_info']
- end
- end
- def test_request_class_overrides_path_info
- router = Router.new(routes, {:request_class => CustomPathRequest })
- exp = Router::Strexp.new '/bar', {}, ['/.?']
- path = Path::Pattern.new exp
- routes.add_route nil, path, {}, {}, {}
- env = rails_env 'PATH_INFO' => '/foo', 'custom.path_info' => '/bar'
- recognized = false
- router.recognize(env) do |r, _, params|
- recognized = true
- end
- assert recognized, "route should have been recognized"
- end
- def test_regexp_first_precedence
- add_routes @router, [
- Router::Strexp.new("/whois/:domain", {:domain => /\w+\.[\w\.]+/}, ['/', '.', '?']),
- Router::Strexp.new("/whois/:id(.:format)", {}, ['/', '.', '?'])
- ]
- env = rails_env 'PATH_INFO' => '/whois/example.com'
- list = []
- @router.recognize(env) do |r, _, params|
- list << r
- end
- assert_equal 2, list.length
- r = list.first
- assert_equal '/whois/:domain', r.path.spec.to_s
- end
- def test_required_parts_verified_are_anchored
- add_routes @router, [
- Router::Strexp.new("/foo/:id", { :id => /\d/ }, ['/', '.', '?'], false)
- ]
- assert_raises(Router::RoutingError) do
- @formatter.generate(:path_info, nil, { :id => '10' }, { })
- end
- end
- def test_required_parts_are_verified_when_building
- add_routes @router, [
- Router::Strexp.new("/foo/:id", { :id => /\d+/ }, ['/', '.', '?'], false)
- ]
- path, _ = @formatter.generate(:path_info, nil, { :id => '10' }, { })
- assert_equal '/foo/10', path
- assert_raises(Router::RoutingError) do
- @formatter.generate(:path_info, nil, { :id => 'aa' }, { })
- end
- end
- def test_only_required_parts_are_verified
- add_routes @router, [
- Router::Strexp.new("/foo(/:id)", {:id => /\d/}, ['/', '.', '?'], false)
- ]
- path, _ = @formatter.generate(:path_info, nil, { :id => '10' }, { })
- assert_equal '/foo/10', path
- path, _ = @formatter.generate(:path_info, nil, { }, { })
- assert_equal '/foo', path
- path, _ = @formatter.generate(:path_info, nil, { :id => 'aa' }, { })
- assert_equal '/foo/aa', path
- end
- def test_X_Cascade
- add_routes @router, [ "/messages(.:format)" ]
- resp = @router.call({ 'REQUEST_METHOD' => 'GET', 'PATH_INFO' => '/lol' })
- assert_equal ['Not Found'], resp.last
- assert_equal 'pass', resp[1]['X-Cascade']
- assert_equal 404, resp.first
- end
- def test_clear_trailing_slash_from_script_name_on_root_unanchored_routes
- strexp = Router::Strexp.new("/", {}, ['/', '.', '?'], false)
- path = Path::Pattern.new strexp
- app = lambda { |env| [200, {}, ['success!']] }
- @router.routes.add_route(app, path, {}, {}, {})
- env = rack_env('SCRIPT_NAME' => '', 'PATH_INFO' => '/weblog')
- resp = @router.call(env)
- assert_equal ['success!'], resp.last
- assert_equal '', env['SCRIPT_NAME']
- assert_equal '/weblog', env['PATH_INFO']
- end
- def test_defaults_merge_correctly
- path = Path::Pattern.new '/foo(/:id)'
- @router.routes.add_route nil, path, {}, {:id => nil}, {}
- env = rails_env 'PATH_INFO' => '/foo/10'
- @router.recognize(env) do |r, _, params|
- assert_equal({:id => '10'}, params)
- end
- env = rails_env 'PATH_INFO' => '/foo'
- @router.recognize(env) do |r, _, params|
- assert_equal({:id => nil}, params)
- end
- end
- def test_recognize_with_unbound_regexp
- add_routes @router, [
- Router::Strexp.new("/foo", { }, ['/', '.', '?'], false)
- ]
- env = rails_env 'PATH_INFO' => '/foo/bar'
- @router.recognize(env) { |*_| }
- assert_equal '/foo', env.env['SCRIPT_NAME']
- assert_equal '/bar', env.env['PATH_INFO']
- end
- def test_bound_regexp_keeps_path_info
- add_routes @router, [
- Router::Strexp.new("/foo", { }, ['/', '.', '?'], true)
- ]
- env = rails_env 'PATH_INFO' => '/foo'
- before = env.env['SCRIPT_NAME']
- @router.recognize(env) { |*_| }
- assert_equal before, env.env['SCRIPT_NAME']
- assert_equal '/foo', env.env['PATH_INFO']
- end
- def test_path_not_found
- add_routes @router, [
- "/messages(.:format)",
- "/messages/new(.:format)",
- "/messages/:id/edit(.:format)",
- "/messages/:id(.:format)"
- ]
- env = rails_env 'PATH_INFO' => '/messages/unknown/path'
- yielded = false
- @router.recognize(env) do |*whatever|
- yielded = true
- end
- refute yielded
- end
- def test_required_part_in_recall
- add_routes @router, [ "/messages/:a/:b" ]
- path, _ = @formatter.generate(:path_info, nil, { :a => 'a' }, { :b => 'b' })
- assert_equal "/messages/a/b", path
- end
- def test_splat_in_recall
- add_routes @router, [ "/*path" ]
- path, _ = @formatter.generate(:path_info, nil, { }, { :path => 'b' })
- assert_equal "/b", path
- end
- def test_recall_should_be_used_when_scoring
- add_routes @router, [
- "/messages/:action(/:id(.:format))",
- "/messages/:id(.:format)"
- ]
- path, _ = @formatter.generate(:path_info, nil, { :id => 10 }, { :action => 'index' })
- assert_equal "/messages/index/10", path
- end
- def test_nil_path_parts_are_ignored
- path = Path::Pattern.new "/:controller(/:action(.:format))"
- @router.routes.add_route nil, path, {}, {}, {}
- params = { :controller => "tasks", :format => nil }
- extras = { :action => 'lol' }
- path, _ = @formatter.generate(:path_info, nil, params, extras)
- assert_equal '/tasks', path
- end
- def test_generate_slash
- params = [ [:controller, "tasks"],
- [:action, "show"] ]
- str = Router::Strexp.new("/", Hash[params], ['/', '.', '?'], true)
- path = Path::Pattern.new str
- @router.routes.add_route nil, path, {}, {}, {}
- path, _ = @formatter.generate(:path_info, nil, Hash[params], {})
- assert_equal '/', path
- end
- def test_generate_calls_param_proc
- path = Path::Pattern.new '/:controller(/:action)'
- @router.routes.add_route nil, path, {}, {}, {}
- parameterized = []
- params = [ [:controller, "tasks"],
- [:action, "show"] ]
- @formatter.generate(
- :path_info,
- nil,
- Hash[params],
- {},
- lambda { |k,v| parameterized << [k,v]; v })
- assert_equal params.map(&:to_s).sort, parameterized.map(&:to_s).sort
- end
- def test_generate_id
- path = Path::Pattern.new '/:controller(/:action)'
- @router.routes.add_route nil, path, {}, {}, {}
- path, params = @formatter.generate(
- :path_info, nil, {:id=>1, :controller=>"tasks", :action=>"show"}, {})
- assert_equal '/tasks/show', path
- assert_equal({:id => 1}, params)
- end
- def test_generate_escapes
- path = Path::Pattern.new '/:controller(/:action)'
- @router.routes.add_route nil, path, {}, {}, {}
- path, _ = @formatter.generate(:path_info,
- nil, { :controller => "tasks",
- :action => "a/b c+d",
- }, {})
- assert_equal '/tasks/a/b%20c+d', path
- end
- def test_generate_extra_params
- path = Path::Pattern.new '/:controller(/:action)'
- @router.routes.add_route nil, path, {}, {}, {}
- path, params = @formatter.generate(:path_info,
- nil, { :id => 1,
- :controller => "tasks",
- :action => "show",
- :relative_url_root => nil
- }, {})
- assert_equal '/tasks/show', path
- assert_equal({:id => 1, :relative_url_root => nil}, params)
- end
- def test_generate_uses_recall_if_needed
- path = Path::Pattern.new '/:controller(/:action(/:id))'
- @router.routes.add_route nil, path, {}, {}, {}
- path, params = @formatter.generate(:path_info,
- nil,
- {:controller =>"tasks", :id => 10},
- {:action =>"index"})
- assert_equal '/tasks/index/10', path
- assert_equal({}, params)
- end
- def test_generate_with_name
- path = Path::Pattern.new '/:controller(/:action)'
- @router.routes.add_route nil, path, {}, {}, {}
- path, params = @formatter.generate(:path_info,
- "tasks",
- {:controller=>"tasks"},
- {:controller=>"tasks", :action=>"index"})
- assert_equal '/tasks', path
- assert_equal({}, params)
- end
- {
- '/content' => { :controller => 'content' },
- '/content/list' => { :controller => 'content', :action => 'list' },
- '/content/show/10' => { :controller => 'content', :action => 'show', :id => "10" },
- }.each do |request_path, expected|
- define_method("test_recognize_#{expected.keys.map(&:to_s).join('_')}") do
- path = Path::Pattern.new "/:controller(/:action(/:id))"
- app = Object.new
- route = @router.routes.add_route(app, path, {}, {}, {})
- env = rails_env 'PATH_INFO' => request_path
- called = false
- @router.recognize(env) do |r, _, params|
- assert_equal route, r
- assert_equal(expected, params)
- called = true
- end
- assert called
- end
- end
- {
- :segment => ['/a%2Fb%20c+d/splat', { :segment => 'a/b c+d', :splat => 'splat' }],
- :splat => ['/segment/a/b%20c+d', { :segment => 'segment', :splat => 'a/b c+d' }]
- }.each do |name, (request_path, expected)|
- define_method("test_recognize_#{name}") do
- path = Path::Pattern.new '/:segment/*splat'
- app = Object.new
- route = @router.routes.add_route(app, path, {}, {}, {})
- env = rails_env 'PATH_INFO' => request_path
- called = false
- @router.recognize(env) do |r, _, params|
- assert_equal route, r
- assert_equal(expected, params)
- called = true
- end
- assert called
- end
- end
- def test_namespaced_controller
- strexp = Router::Strexp.new(
- "/:controller(/:action(/:id))",
- { :controller => /.+?/ },
- ["/", ".", "?"]
- )
- path = Path::Pattern.new strexp
- app = Object.new
- route = @router.routes.add_route(app, path, {}, {}, {})
- env = rails_env 'PATH_INFO' => '/admin/users/show/10'
- called = false
- expected = {
- :controller => 'admin/users',
- :action => 'show',
- :id => '10'
- }
- @router.recognize(env) do |r, _, params|
- assert_equal route, r
- assert_equal(expected, params)
- called = true
- end
- assert called
- end
- def test_recognize_literal
- path = Path::Pattern.new "/books(/:action(.:format))"
- app = Object.new
- route = @router.routes.add_route(app, path, {}, {:controller => 'books'})
- env = rails_env 'PATH_INFO' => '/books/list.rss'
- expected = { :controller => 'books', :action => 'list', :format => 'rss' }
- called = false
- @router.recognize(env) do |r, _, params|
- assert_equal route, r
- assert_equal(expected, params)
- called = true
- end
- assert called
- end
- def test_recognize_cares_about_verbs
- path = Path::Pattern.new "/books(/:action(.:format))"
- app = Object.new
- conditions = {
- :request_method => 'GET'
- }
- @router.routes.add_route(app, path, conditions, {})
- conditions = conditions.dup
- conditions[:request_method] = 'POST'
- post = @router.routes.add_route(app, path, conditions, {})
- env = rails_env 'PATH_INFO' => '/books/list.rss',
- "REQUEST_METHOD" => "POST"
- called = false
- @router.recognize(env) do |r, _, params|
- assert_equal post, r
- called = true
- end
- assert called
- end
- private
- def add_routes router, paths
- paths.each do |path|
- path = Path::Pattern.new path
- router.routes.add_route nil, path, {}, {}, {}
- end
- end
- RailsEnv = Struct.new(:env)
- def rails_env env
- RailsEnv.new rack_env env
- end
- def rack_env env
- {
- "rack.version" => [1, 1],
- "rack.input" => StringIO.new,
- "rack.errors" => StringIO.new,
- "rack.multithread" => true,
- "rack.multiprocess" => true,
- "rack.run_once" => false,
- "REQUEST_METHOD" => "GET",
- "SERVER_NAME" => "example.org",
- "SERVER_PORT" => "80",
- "QUERY_STRING" => "",
- "PATH_INFO" => "/content",
- "rack.url_scheme" => "http",
- "HTTPS" => "off",
- "SCRIPT_NAME" => "",
- "CONTENT_LENGTH" => "0"
- }.merge env
- end
- end
- end
|