1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00
rails--rails/actionpack/test/controller/routing_test.rb
eileencodes 92209356c3 Path parameters should default to UTF8
This commit changes the behavior such the path_params now default to
UTF8 just like regular parameters. This also changes the behavior such
that if a path parameter contains invalid UTF8 it returns a 400 bad
request. Previously the behavior was to encode the path params as binary
but that's not the same as query params.

So this commit makes path params behave the same as query params.

It's important to test with a path that's encoded as binary because
that's how paths are encoded from the socket. The test that was altered
was changed to make the behavior for bad encoding the same as query
params. We want to treat path params the same as query params. The params
in the test are invalid UTF8 so they should return a bad request.

Fixes #29669

*Eileen M. Uchitelle, Aaron Patterson, & Tsukuru Tanimichi*
2017-08-01 13:28:57 -04:00

2108 lines
74 KiB
Ruby

# frozen_string_literal: true
require "abstract_unit"
require "controller/fake_controllers"
require "active_support/core_ext/object/with_options"
require "active_support/core_ext/object/json"
class MilestonesController < ActionController::Base
def index() head :ok end
alias_method :show, :index
end
# See RFC 3986, section 3.3 for allowed path characters.
class UriReservedCharactersRoutingTest < ActiveSupport::TestCase
include RoutingTestHelpers
def setup
@set = ActionDispatch::Routing::RouteSet.new
@set.draw do
ActiveSupport::Deprecation.silence do
get ":controller/:action/:variable/*additional"
end
end
safe, unsafe = %w(: @ & = + $ , ;), %w(^ ? # [ ])
hex = unsafe.map { |char| "%" + char.unpack("H2").first.upcase }
@segment = "#{safe.join}#{unsafe.join}".freeze
@escaped = "#{safe.join}#{hex.join}".freeze
end
def test_route_generation_escapes_unsafe_path_characters
assert_equal "/content/act#{@escaped}ion/var#{@escaped}iable/add#{@escaped}itional-1/add#{@escaped}itional-2",
url_for(@set,
controller: "content",
action: "act#{@segment}ion",
variable: "var#{@segment}iable",
additional: ["add#{@segment}itional-1", "add#{@segment}itional-2"])
end
def test_route_recognition_unescapes_path_components
options = { controller: "content",
action: "act#{@segment}ion",
variable: "var#{@segment}iable",
additional: "add#{@segment}itional-1/add#{@segment}itional-2" }
assert_equal options, @set.recognize_path("/content/act#{@escaped}ion/var#{@escaped}iable/add#{@escaped}itional-1/add#{@escaped}itional-2")
end
def test_route_generation_allows_passing_non_string_values_to_generated_helper
assert_equal "/content/action/variable/1/2",
url_for(@set,
controller: "content",
action: "action",
variable: "variable",
additional: [1, 2])
end
end
class MockController
def self.build(helpers, additional_options = {})
Class.new do
define_method :url_options do
options = super()
options[:protocol] ||= "http"
options[:host] ||= "test.host"
options.merge(additional_options)
end
include helpers
end
end
end
class LegacyRouteSetTests < ActiveSupport::TestCase
include RoutingTestHelpers
include ActionDispatch::RoutingVerbs
attr_reader :rs
attr_accessor :controller
alias :routes :rs
def setup
@rs = make_set
@response = nil
end
def test_symbols_with_dashes
rs.draw do
get "/:artist/:song-omg", to: lambda { |env|
resp = ActiveSupport::JSON.encode ActionDispatch::Request.new(env).path_parameters
[200, {}, [resp]]
}
end
hash = ActiveSupport::JSON.decode get(URI("http://example.org/journey/faithfully-omg"))
assert_equal({ "artist" => "journey", "song" => "faithfully" }, hash)
end
def test_id_encoding
rs.draw do
get "/journey/:id", to: lambda { |env|
param = ActionDispatch::Request.new(env).path_parameters
resp = ActiveSupport::JSON.encode param
[200, {}, [resp]]
}
end
# The encoding of the URL in production is *binary*, so we add a
# .b here.
hash = ActiveSupport::JSON.decode get(URI("http://example.org/journey/%E5%A4%AA%E9%83%8E".b))
assert_equal({ "id" => "太郎" }, hash)
assert_equal ::Encoding::UTF_8, hash["id"].encoding
end
def test_id_with_dash
rs.draw do
get "/journey/:id", to: lambda { |env|
resp = ActiveSupport::JSON.encode ActionDispatch::Request.new(env).path_parameters
[200, {}, [resp]]
}
end
hash = ActiveSupport::JSON.decode get(URI("http://example.org/journey/faithfully-omg"))
assert_equal({ "id" => "faithfully-omg" }, hash)
end
def test_dash_with_custom_regexp
rs.draw do
get "/:artist/:song-omg", constraints: { song: /\d+/ }, to: lambda { |env|
resp = ActiveSupport::JSON.encode ActionDispatch::Request.new(env).path_parameters
[200, {}, [resp]]
}
end
hash = ActiveSupport::JSON.decode get(URI("http://example.org/journey/123-omg"))
assert_equal({ "artist" => "journey", "song" => "123" }, hash)
assert_equal "Not Found", get(URI("http://example.org/journey/faithfully-omg"))
end
def test_pre_dash
rs.draw do
get "/:artist/omg-:song", to: lambda { |env|
resp = ActiveSupport::JSON.encode ActionDispatch::Request.new(env).path_parameters
[200, {}, [resp]]
}
end
hash = ActiveSupport::JSON.decode get(URI("http://example.org/journey/omg-faithfully"))
assert_equal({ "artist" => "journey", "song" => "faithfully" }, hash)
end
def test_pre_dash_with_custom_regexp
rs.draw do
get "/:artist/omg-:song", constraints: { song: /\d+/ }, to: lambda { |env|
resp = ActiveSupport::JSON.encode ActionDispatch::Request.new(env).path_parameters
[200, {}, [resp]]
}
end
hash = ActiveSupport::JSON.decode get(URI("http://example.org/journey/omg-123"))
assert_equal({ "artist" => "journey", "song" => "123" }, hash)
assert_equal "Not Found", get(URI("http://example.org/journey/omg-faithfully"))
end
def test_star_paths_are_greedy
rs.draw do
get "/*path", to: lambda { |env|
x = env["action_dispatch.request.path_parameters"][:path]
[200, {}, [x]]
}, format: false
end
u = URI("http://example.org/foo/bar.html")
assert_equal u.path.sub(/^\//, ""), get(u)
end
def test_star_paths_are_greedy_but_not_too_much
rs.draw do
get "/*path", to: lambda { |env|
x = ActiveSupport::JSON.encode env["action_dispatch.request.path_parameters"]
[200, {}, [x]]
}
end
expected = { "path" => "foo/bar", "format" => "html" }
u = URI("http://example.org/foo/bar.html")
assert_equal expected, ActiveSupport::JSON.decode(get(u))
end
def test_optional_star_paths_are_greedy
rs.draw do
get "/(*filters)", to: lambda { |env|
x = env["action_dispatch.request.path_parameters"][:filters]
[200, {}, [x]]
}, format: false
end
u = URI("http://example.org/ne_27.065938,-80.6092/sw_25.489856,-82.542794")
assert_equal u.path.sub(/^\//, ""), get(u)
end
def test_optional_star_paths_are_greedy_but_not_too_much
rs.draw do
get "/(*filters)", to: lambda { |env|
x = ActiveSupport::JSON.encode env["action_dispatch.request.path_parameters"]
[200, {}, [x]]
}
end
expected = { "filters" => "ne_27.065938,-80.6092/sw_25.489856,-82",
"format" => "542794" }
u = URI("http://example.org/ne_27.065938,-80.6092/sw_25.489856,-82.542794")
assert_equal expected, ActiveSupport::JSON.decode(get(u))
end
def test_regexp_precidence
rs.draw do
get "/whois/:domain", constraints: {
domain: /\w+\.[\w\.]+/ },
to: lambda { |env| [200, {}, %w{regexp}] }
get "/whois/:id", to: lambda { |env| [200, {}, %w{id}] }
end
assert_equal "regexp", get(URI("http://example.org/whois/example.org"))
assert_equal "id", get(URI("http://example.org/whois/123"))
end
def test_class_and_lambda_constraints
subdomain = Class.new {
def matches?(request)
request.subdomain.present? && request.subdomain != "clients"
end
}
rs.draw do
get "/", constraints: subdomain.new,
to: lambda { |env| [200, {}, %w{default}] }
get "/", constraints: { subdomain: "clients" },
to: lambda { |env| [200, {}, %w{clients}] }
end
assert_equal "default", get(URI("http://www.example.org/"))
assert_equal "clients", get(URI("http://clients.example.org/"))
end
def test_lambda_constraints
rs.draw do
get "/", constraints: lambda { |req|
req.subdomain.present? && req.subdomain != "clients" },
to: lambda { |env| [200, {}, %w{default}] }
get "/", constraints: lambda { |req|
req.subdomain.present? && req.subdomain == "clients" },
to: lambda { |env| [200, {}, %w{clients}] }
end
assert_equal "default", get(URI("http://www.example.org/"))
assert_equal "clients", get(URI("http://clients.example.org/"))
end
def test_scoped_lambda
scope_called = false
rs.draw do
scope "/foo", constraints: lambda { |req| scope_called = true } do
get "/", to: lambda { |env| [200, {}, %w{default}] }
end
end
assert_equal "default", get(URI("http://www.example.org/foo/"))
assert scope_called, "scope constraint should be called"
end
def test_scoped_lambda_with_get_lambda
inner_called = false
rs.draw do
scope "/foo", constraints: lambda { |req| flunk "should not be called" } do
get "/", constraints: lambda { |req| inner_called = true },
to: lambda { |env| [200, {}, %w{default}] }
end
end
assert_equal "default", get(URI("http://www.example.org/foo/"))
assert inner_called, "inner constraint should be called"
end
def test_empty_string_match
rs.draw do
get "/:username", constraints: { username: /[^\/]+/ },
to: lambda { |e| [200, {}, ["foo"]] }
end
assert_equal "Not Found", get(URI("http://example.org/"))
assert_equal "foo", get(URI("http://example.org/hello"))
end
def test_non_greedy_glob_regexp
params = nil
rs.draw do
get "/posts/:id(/*filters)", constraints: { filters: /.+?/ },
to: lambda { |e|
params = e["action_dispatch.request.path_parameters"]
[200, {}, ["foo"]]
}
end
assert_equal "foo", get(URI("http://example.org/posts/1/foo.js"))
assert_equal({ id: "1", filters: "foo", format: "js" }, params)
end
def test_specific_controller_action_failure
rs.draw do
mount lambda {} => "/foo"
end
assert_raises(ActionController::UrlGenerationError) do
url_for(rs, controller: "omg", action: "lol")
end
end
def test_default_setup
rs.draw { ActiveSupport::Deprecation.silence { get "/:controller(/:action(/:id))" } }
assert_equal({ controller: "content", action: "index" }, rs.recognize_path("/content"))
assert_equal({ controller: "content", action: "list" }, rs.recognize_path("/content/list"))
assert_equal({ controller: "content", action: "show", id: "10" }, rs.recognize_path("/content/show/10"))
assert_equal({ controller: "admin/user", action: "show", id: "10" }, rs.recognize_path("/admin/user/show/10"))
assert_equal "/admin/user/show/10", url_for(rs, controller: "admin/user", action: "show", id: 10)
get URI("http://test.host/admin/user/list/10")
assert_equal({ controller: "admin/user", action: "list", id: "10" },
controller.request.path_parameters)
assert_equal "/admin/user/show", controller.url_for(action: "show", only_path: true)
assert_equal "/admin/user/list/10", controller.url_for(only_path: true)
assert_equal "/admin/stuff", controller.url_for(controller: "stuff", only_path: true)
assert_equal "/stuff", controller.url_for(controller: "/stuff", only_path: true)
end
def test_route_with_colon_first
rs.draw do
ActiveSupport::Deprecation.silence do
get "/:controller/:action/:id", action: "index", id: nil
end
get ":url", controller: "content", action: "translate"
end
assert_equal({ controller: "content", action: "translate", url: "example" }, rs.recognize_path("/example"))
end
def test_route_with_regexp_for_action
rs.draw { ActiveSupport::Deprecation.silence { get "/:controller/:action", action: /auth[-|_].+/ } }
assert_equal({ action: "auth_google", controller: "content" }, rs.recognize_path("/content/auth_google"))
assert_equal({ action: "auth-facebook", controller: "content" }, rs.recognize_path("/content/auth-facebook"))
assert_equal "/content/auth_google", url_for(rs, controller: "content", action: "auth_google")
assert_equal "/content/auth-facebook", url_for(rs, controller: "content", action: "auth-facebook")
end
def test_route_with_regexp_for_controller
rs.draw do
ActiveSupport::Deprecation.silence do
get ":controller/:admintoken(/:action(/:id))", controller: /admin\/.+/
get "/:controller(/:action(/:id))"
end
end
assert_equal({ controller: "admin/user", admintoken: "foo", action: "index" },
rs.recognize_path("/admin/user/foo"))
assert_equal({ controller: "content", action: "foo" },
rs.recognize_path("/content/foo"))
assert_equal "/admin/user/foo", url_for(rs, controller: "admin/user", admintoken: "foo", action: "index")
assert_equal "/content/foo", url_for(rs, controller: "content", action: "foo")
end
def test_route_with_regexp_and_captures_for_controller
rs.draw do
ActiveSupport::Deprecation.silence do
get "/:controller(/:action(/:id))", controller: /admin\/(accounts|users)/
end
end
assert_equal({ controller: "admin/accounts", action: "index" }, rs.recognize_path("/admin/accounts"))
assert_equal({ controller: "admin/users", action: "index" }, rs.recognize_path("/admin/users"))
assert_raise(ActionController::RoutingError) { rs.recognize_path("/admin/products") }
end
def test_route_with_regexp_and_dot
rs.draw do
ActiveSupport::Deprecation.silence do
get ":controller/:action/:file",
controller: /admin|user/,
action: /upload|download/,
defaults: { file: nil },
constraints: { file: %r{[^/]+(\.[^/]+)?} }
end
end
# Without a file extension
assert_equal "/user/download/file",
url_for(rs, controller: "user", action: "download", file: "file")
assert_equal({ controller: "user", action: "download", file: "file" },
rs.recognize_path("/user/download/file"))
# Now, let's try a file with an extension, really a dot (.)
assert_equal "/user/download/file.jpg",
url_for(rs, controller: "user", action: "download", file: "file.jpg")
assert_equal({ controller: "user", action: "download", file: "file.jpg" },
rs.recognize_path("/user/download/file.jpg"))
end
def test_basic_named_route
rs.draw do
root to: "content#list", as: "home"
end
assert_equal("http://test.host/", setup_for_named_route.send(:home_url))
end
def test_named_route_with_option
rs.draw do
get "page/:title" => "content#show_page", :as => "page"
end
assert_equal("http://test.host/page/new%20stuff",
setup_for_named_route.send(:page_url, title: "new stuff"))
end
def test_named_route_with_default
rs.draw do
get "page/:title" => "content#show_page", :title => "AboutPage", :as => "page"
end
assert_equal("http://test.host/page/AboutRails",
setup_for_named_route.send(:page_url, title: "AboutRails"))
end
def test_named_route_with_path_prefix
rs.draw do
scope "my" do
get "page" => "content#show_page", :as => "page"
end
end
assert_equal("http://test.host/my/page",
setup_for_named_route.send(:page_url))
end
def test_named_route_with_blank_path_prefix
rs.draw do
scope "" do
get "page" => "content#show_page", :as => "page"
end
end
assert_equal("http://test.host/page",
setup_for_named_route.send(:page_url))
end
def test_named_route_with_nested_controller
rs.draw do
get "admin/user" => "admin/user#index", :as => "users"
end
assert_equal("http://test.host/admin/user",
setup_for_named_route.send(:users_url))
end
def test_optimised_named_route_with_host
rs.draw do
get "page" => "content#show_page", :as => "pages", :host => "foo.com"
end
routes = setup_for_named_route
assert_equal "http://foo.com/page", routes.pages_url
end
def setup_for_named_route(options = {})
MockController.build(rs.url_helpers, options).new
end
def test_named_route_without_hash
rs.draw do
ActiveSupport::Deprecation.silence do
get ":controller/:action/:id", as: "normal"
end
end
end
def test_named_route_root
rs.draw do
root to: "hello#index"
end
routes = setup_for_named_route
assert_equal("http://test.host/", routes.send(:root_url))
assert_equal("/", routes.send(:root_path))
end
def test_named_route_root_without_hash
rs.draw do
root "hello#index"
end
routes = setup_for_named_route
assert_equal("http://test.host/", routes.send(:root_url))
assert_equal("/", routes.send(:root_path))
end
def test_named_route_root_with_hash
rs.draw do
root "hello#index", as: :index
end
routes = setup_for_named_route
assert_equal("http://test.host/", routes.send(:index_url))
assert_equal("/", routes.send(:index_path))
end
def test_root_without_path_raises_argument_error
assert_raises ArgumentError do
rs.draw { root nil }
end
end
def test_named_route_root_with_trailing_slash
rs.draw do
root "hello#index"
end
routes = setup_for_named_route(trailing_slash: true)
assert_equal("http://test.host/", routes.send(:root_url))
assert_equal("http://test.host/?foo=bar", routes.send(:root_url, foo: :bar))
end
def test_named_route_with_regexps
rs.draw do
get "page/:year/:month/:day/:title" => "page#show", :as => "article",
:year => /\d+/, :month => /\d+/, :day => /\d+/
ActiveSupport::Deprecation.silence do
get ":controller/:action/:id"
end
end
routes = setup_for_named_route
assert_equal "http://test.host/page/2005/6/10/hi",
routes.send(:article_url, title: "hi", day: 10, year: 2005, month: 6)
end
def test_changing_controller
rs.draw { ActiveSupport::Deprecation.silence { get ":controller/:action/:id" } }
get URI("http://test.host/admin/user/index/10")
assert_equal "/admin/stuff/show/10",
controller.url_for(controller: "stuff", action: "show", id: 10, only_path: true)
end
def test_paths_escaped
rs.draw do
get "file/*path" => "content#show_file", :as => "path"
ActiveSupport::Deprecation.silence do
get ":controller/:action/:id"
end
end
# No + to space in URI escaping, only for query params.
results = rs.recognize_path "/file/hello+world/how+are+you%3F"
assert results, "Recognition should have succeeded"
assert_equal "hello+world/how+are+you?", results[:path]
# Use %20 for space instead.
results = rs.recognize_path "/file/hello%20world/how%20are%20you%3F"
assert results, "Recognition should have succeeded"
assert_equal "hello world/how are you?", results[:path]
end
def test_paths_slashes_unescaped_with_ordered_parameters
rs.draw do
get "/file/*path" => "content#index", :as => "path"
end
# No / to %2F in URI, only for query params.
assert_equal("/file/hello/world", setup_for_named_route.send(:path_path, ["hello", "world"]))
end
def test_non_controllers_cannot_be_matched
rs.draw do
ActiveSupport::Deprecation.silence do
get ":controller/:action/:id"
end
end
assert_raise(ActionController::RoutingError) { rs.recognize_path("/not_a/show/10") }
end
def test_should_list_options_diff_when_routing_constraints_dont_match
rs.draw do
get "post/:id" => "post#show", :constraints => { id: /\d+/ }, :as => "post"
end
assert_raise(ActionController::UrlGenerationError) do
url_for(rs, controller: "post", action: "show", bad_param: "foo", use_route: "post")
end
end
def test_dynamic_path_allowed
rs.draw do
get "*path" => "content#show_file"
end
assert_equal "/pages/boo",
url_for(rs, controller: "content", action: "show_file", path: %w(pages boo))
end
def test_dynamic_recall_paths_allowed
rs.draw do
get "*path" => "content#show_file"
end
get URI("http://test.host/pages/boo")
assert_equal({ controller: "content", action: "show_file", path: "pages/boo" },
controller.request.path_parameters)
assert_equal "/pages/boo",
controller.url_for(only_path: true)
end
def test_backwards
rs.draw do
ActiveSupport::Deprecation.silence do
get "page/:id(/:action)" => "pages#show"
get ":controller(/:action(/:id))"
end
end
get URI("http://test.host/pages/show")
assert_equal "/page/20", controller.url_for(id: 20, only_path: true)
assert_equal "/page/20", url_for(rs, controller: "pages", id: 20, action: "show")
assert_equal "/pages/boo", url_for(rs, controller: "pages", action: "boo")
end
def test_route_with_integer_default
rs.draw do
get "page(/:id)" => "content#show_page", :id => 1
ActiveSupport::Deprecation.silence do
get ":controller/:action/:id"
end
end
assert_equal "/page", url_for(rs, controller: "content", action: "show_page")
assert_equal "/page", url_for(rs, controller: "content", action: "show_page", id: 1)
assert_equal "/page", url_for(rs, controller: "content", action: "show_page", id: "1")
assert_equal "/page/10", url_for(rs, controller: "content", action: "show_page", id: 10)
assert_equal({ controller: "content", action: "show_page", id: 1 }, rs.recognize_path("/page"))
assert_equal({ controller: "content", action: "show_page", id: "1" }, rs.recognize_path("/page/1"))
assert_equal({ controller: "content", action: "show_page", id: "10" }, rs.recognize_path("/page/10"))
end
# For newer revision
def test_route_with_text_default
rs.draw do
get "page/:id" => "content#show_page", :id => 1
ActiveSupport::Deprecation.silence do
get ":controller/:action/:id"
end
end
assert_equal "/page/foo", url_for(rs, controller: "content", action: "show_page", id: "foo")
assert_equal({ controller: "content", action: "show_page", id: "foo" }, rs.recognize_path("/page/foo"))
token = "\321\202\320\265\320\272\321\201\321\202".dup # 'text' in Russian
token.force_encoding(Encoding::BINARY)
escaped_token = CGI::escape(token)
assert_equal "/page/" + escaped_token, url_for(rs, controller: "content", action: "show_page", id: token)
assert_equal({ controller: "content", action: "show_page", id: token }, rs.recognize_path("/page/#{escaped_token}"))
end
def test_action_expiry
rs.draw { ActiveSupport::Deprecation.silence { get ":controller(/:action(/:id))" } }
get URI("http://test.host/content/show")
assert_equal "/content", controller.url_for(controller: "content", only_path: true)
end
def test_requirement_should_prevent_optional_id
rs.draw do
get "post/:id" => "post#show", :constraints => { id: /\d+/ }, :as => "post"
end
assert_equal "/post/10", url_for(rs, controller: "post", action: "show", id: 10)
assert_raise(ActionController::UrlGenerationError) do
url_for(rs, controller: "post", action: "show")
end
end
def test_both_requirement_and_optional
rs.draw do
get("test(/:year)" => "post#show", :as => "blog",
:defaults => { year: nil },
:constraints => { year: /\d{4}/ }
)
ActiveSupport::Deprecation.silence do
get ":controller/:action/:id"
end
end
assert_equal "/test", url_for(rs, controller: "post", action: "show")
assert_equal "/test", url_for(rs, controller: "post", action: "show", year: nil)
assert_equal("http://test.host/test", setup_for_named_route.send(:blog_url))
end
def test_set_to_nil_forgets
rs.draw do
get "pages(/:year(/:month(/:day)))" => "content#list_pages", :month => nil, :day => nil
ActiveSupport::Deprecation.silence do
get ":controller/:action/:id"
end
end
assert_equal "/pages/2005",
url_for(rs, controller: "content", action: "list_pages", year: 2005)
assert_equal "/pages/2005/6",
url_for(rs, controller: "content", action: "list_pages", year: 2005, month: 6)
assert_equal "/pages/2005/6/12",
url_for(rs, controller: "content", action: "list_pages", year: 2005, month: 6, day: 12)
get URI("http://test.host/pages/2005/6/12")
assert_equal({ controller: "content", action: "list_pages", year: "2005", month: "6", day: "12" },
controller.request.path_parameters)
assert_equal "/pages/2005/6/4",
controller.url_for(day: 4, only_path: true)
assert_equal "/pages/2005/6",
controller.url_for(day: nil, only_path: true)
assert_equal "/pages/2005",
controller.url_for(day: nil, month: nil, only_path: true)
end
def test_root_url_generation_with_controller_and_action
rs.draw do
root to: "content#index"
end
assert_equal "/", url_for(rs, controller: "content", action: "index")
assert_equal "/", url_for(rs, controller: "content")
end
def test_named_root_url_generation_with_controller_and_action
rs.draw do
root to: "content#index", as: "home"
end
assert_equal "/", url_for(rs, controller: "content", action: "index")
assert_equal "/", url_for(rs, controller: "content")
assert_equal("http://test.host/", setup_for_named_route.send(:home_url))
end
def test_named_route_method
rs.draw do
get "categories" => "content#categories", :as => "categories"
ActiveSupport::Deprecation.silence do
get ":controller(/:action(/:id))"
end
end
assert_equal "/categories", url_for(rs, controller: "content", action: "categories")
assert_equal "/content/hi", url_for(rs, controller: "content", action: "hi")
end
def test_named_routes_array
test_named_route_method
assert_equal [:categories], rs.named_routes.names
end
def test_nil_defaults
rs.draw do
get "journal" => "content#list_journal",
:date => nil, :user_id => nil
ActiveSupport::Deprecation.silence do
get ":controller/:action/:id"
end
end
assert_equal "/journal", url_for(rs,
controller: "content",
action: "list_journal",
date: nil,
user_id: nil)
end
def setup_request_method_routes_for(method)
rs.draw do
match "/match" => "books##{method}", :via => method.to_sym
end
end
%w(GET PATCH POST PUT DELETE).each do |request_method|
define_method("test_request_method_recognized_with_#{request_method}") do
setup_request_method_routes_for(request_method.downcase)
params = rs.recognize_path("/match", method: request_method)
assert_equal request_method.downcase, params[:action]
end
end
def test_recognize_array_of_methods
rs.draw do
match "/match" => "books#get_or_post", :via => [:get, :post]
put "/match" => "books#not_get_or_post"
end
params = rs.recognize_path("/match", method: :post)
assert_equal "get_or_post", params[:action]
params = rs.recognize_path("/match", method: :put)
assert_equal "not_get_or_post", params[:action]
end
def test_subpath_recognized
rs.draw do
ActiveSupport::Deprecation.silence do
get "/books/:id/edit" => "subpath_books#edit"
get "/items/:id/:action" => "subpath_books"
get "/posts/new/:action" => "subpath_books"
get "/posts/:id" => "subpath_books#show"
end
end
hash = rs.recognize_path "/books/17/edit"
assert_not_nil hash
assert_equal %w(subpath_books 17 edit), [hash[:controller], hash[:id], hash[:action]]
hash = rs.recognize_path "/items/3/complete"
assert_not_nil hash
assert_equal %w(subpath_books 3 complete), [hash[:controller], hash[:id], hash[:action]]
hash = rs.recognize_path "/posts/new/preview"
assert_not_nil hash
assert_equal %w(subpath_books preview), [hash[:controller], hash[:action]]
hash = rs.recognize_path "/posts/7"
assert_not_nil hash
assert_equal %w(subpath_books show 7), [hash[:controller], hash[:action], hash[:id]]
end
def test_subpath_generated
rs.draw do
ActiveSupport::Deprecation.silence do
get "/books/:id/edit" => "subpath_books#edit"
get "/items/:id/:action" => "subpath_books"
get "/posts/new/:action" => "subpath_books"
end
end
assert_equal "/books/7/edit", url_for(rs, controller: "subpath_books", id: 7, action: "edit")
assert_equal "/items/15/complete", url_for(rs, controller: "subpath_books", id: 15, action: "complete")
assert_equal "/posts/new/preview", url_for(rs, controller: "subpath_books", action: "preview")
end
def test_failed_constraints_raises_exception_with_violated_constraints
rs.draw do
get "foos/:id" => "foos#show", :as => "foo_with_requirement", :constraints => { id: /\d+/ }
end
assert_raise(ActionController::UrlGenerationError) do
setup_for_named_route.send(:foo_with_requirement_url, "I am Against the constraints")
end
end
def test_routes_changed_correctly_after_clear
rs = ::ActionDispatch::Routing::RouteSet.new
rs.draw do
get "ca" => "ca#aa"
get "cb" => "cb#ab"
get "cc" => "cc#ac"
ActiveSupport::Deprecation.silence do
get ":controller/:action/:id"
get ":controller/:action/:id.:format"
end
end
hash = rs.recognize_path "/cc"
assert_not_nil hash
assert_equal %w(cc ac), [hash[:controller], hash[:action]]
rs.draw do
get "cb" => "cb#ab"
get "cc" => "cc#ac"
ActiveSupport::Deprecation.silence do
get ":controller/:action/:id"
get ":controller/:action/:id.:format"
end
end
hash = rs.recognize_path "/cc"
assert_not_nil hash
assert_equal %w(cc ac), [hash[:controller], hash[:action]]
end
end
class RouteSetTest < ActiveSupport::TestCase
include RoutingTestHelpers
include ActionDispatch::RoutingVerbs
attr_reader :set
alias :routes :set
attr_accessor :controller
def setup
super
@set = make_set
end
def request
@request ||= ActionController::TestRequest.new
end
def default_route_set
@default_route_set ||= begin
set = ActionDispatch::Routing::RouteSet.new
set.draw do
ActiveSupport::Deprecation.silence do
get "/:controller(/:action(/:id))"
end
end
set
end
end
def test_generate_extras
set.draw { ActiveSupport::Deprecation.silence { get ":controller/(:action(/:id))" } }
path, extras = set.generate_extras(controller: "foo", action: "bar", id: 15, this: "hello", that: "world")
assert_equal "/foo/bar/15", path
assert_equal %w(that this), extras.map(&:to_s).sort
end
def test_extra_keys
set.draw { ActiveSupport::Deprecation.silence { get ":controller/:action/:id" } }
extras = set.extra_keys(controller: "foo", action: "bar", id: 15, this: "hello", that: "world")
assert_equal %w(that this), extras.map(&:to_s).sort
end
def test_generate_extras_not_first
set.draw do
ActiveSupport::Deprecation.silence do
get ":controller/:action/:id.:format"
get ":controller/:action/:id"
end
end
path, extras = set.generate_extras(controller: "foo", action: "bar", id: 15, this: "hello", that: "world")
assert_equal "/foo/bar/15", path
assert_equal %w(that this), extras.map(&:to_s).sort
end
def test_generate_not_first
set.draw do
ActiveSupport::Deprecation.silence do
get ":controller/:action/:id.:format"
get ":controller/:action/:id"
end
end
assert_equal "/foo/bar/15?this=hello",
url_for(set, controller: "foo", action: "bar", id: 15, this: "hello")
end
def test_extra_keys_not_first
set.draw do
ActiveSupport::Deprecation.silence do
get ":controller/:action/:id.:format"
get ":controller/:action/:id"
end
end
extras = set.extra_keys(controller: "foo", action: "bar", id: 15, this: "hello", that: "world")
assert_equal %w(that this), extras.map(&:to_s).sort
end
def test_draw
assert_equal 0, set.routes.size
set.draw do
get "/hello/world" => "a#b"
end
assert_equal 1, set.routes.size
end
def test_draw_symbol_controller_name
assert_equal 0, set.routes.size
set.draw do
get "/users/index" => "users#index"
end
set.recognize_path("/users/index", method: :get)
assert_equal 1, set.routes.size
end
def test_named_draw
assert_equal 0, set.routes.size
set.draw do
get "/hello/world" => "a#b", :as => "hello"
end
assert_equal 1, set.routes.size
assert_equal set.routes.first, set.named_routes[:hello]
end
def test_duplicate_named_route_raises_rather_than_pick_precedence
assert_raise ArgumentError do
set.draw do
get "/hello/world" => "a#b", :as => "hello"
get "/hello" => "a#b", :as => "hello"
end
end
end
def setup_named_route_test
set.draw do
get "/people(/:id)" => "people#show", :as => "show"
get "/people" => "people#index", :as => "index"
get "/people/go/:foo/:bar/joe(/:id)" => "people#multi", :as => "multi"
get "/admin/users" => "admin/users#index", :as => "users"
end
get URI("http://test.host/people")
controller
end
def test_named_route_url_method
controller = setup_named_route_test
assert_equal "http://test.host/people/5", controller.send(:show_url, id: 5)
assert_equal "/people/5", controller.send(:show_path, id: 5)
assert_equal "http://test.host/people", controller.send(:index_url)
assert_equal "/people", controller.send(:index_path)
assert_equal "http://test.host/admin/users", controller.send(:users_url)
assert_equal "/admin/users", controller.send(:users_path)
end
def test_named_route_url_method_with_anchor
controller = setup_named_route_test
assert_equal "http://test.host/people/5#location", controller.send(:show_url, id: 5, anchor: "location")
assert_equal "/people/5#location", controller.send(:show_path, id: 5, anchor: "location")
assert_equal "http://test.host/people#location", controller.send(:index_url, anchor: "location")
assert_equal "/people#location", controller.send(:index_path, anchor: "location")
assert_equal "http://test.host/admin/users#location", controller.send(:users_url, anchor: "location")
assert_equal "/admin/users#location", controller.send(:users_path, anchor: "location")
assert_equal "http://test.host/people/go/7/hello/joe/5#location",
controller.send(:multi_url, 7, "hello", 5, anchor: "location")
assert_equal "http://test.host/people/go/7/hello/joe/5?baz=bar#location",
controller.send(:multi_url, 7, "hello", 5, baz: "bar", anchor: "location")
assert_equal "http://test.host/people?baz=bar#location",
controller.send(:index_url, baz: "bar", anchor: "location")
assert_equal "http://test.host/people", controller.send(:index_url, anchor: nil)
assert_equal "http://test.host/people", controller.send(:index_url, anchor: false)
end
def test_named_route_url_method_with_port
controller = setup_named_route_test
assert_equal "http://test.host:8080/people/5", controller.send(:show_url, 5, port: 8080)
end
def test_named_route_url_method_with_host
controller = setup_named_route_test
assert_equal "http://some.example.com/people/5", controller.send(:show_url, 5, host: "some.example.com")
end
def test_named_route_url_method_with_protocol
controller = setup_named_route_test
assert_equal "https://test.host/people/5", controller.send(:show_url, 5, protocol: "https")
end
def test_named_route_url_method_with_ordered_parameters
controller = setup_named_route_test
assert_equal "http://test.host/people/go/7/hello/joe/5",
controller.send(:multi_url, 7, "hello", 5)
end
def test_named_route_url_method_with_ordered_parameters_and_hash
controller = setup_named_route_test
assert_equal "http://test.host/people/go/7/hello/joe/5?baz=bar",
controller.send(:multi_url, 7, "hello", 5, baz: "bar")
end
def test_named_route_url_method_with_ordered_parameters_and_empty_hash
controller = setup_named_route_test
assert_equal "http://test.host/people/go/7/hello/joe/5",
controller.send(:multi_url, 7, "hello", 5, {})
end
def test_named_route_url_method_with_no_positional_arguments
controller = setup_named_route_test
assert_equal "http://test.host/people?baz=bar",
controller.send(:index_url, baz: "bar")
end
def test_draw_default_route
set.draw do
ActiveSupport::Deprecation.silence do
get ":controller/:action/:id"
end
end
assert_equal 1, set.routes.size
assert_equal "/users/show/10", url_for(set, controller: "users", action: "show", id: 10)
assert_equal "/users/index/10", url_for(set, controller: "users", id: 10)
assert_equal({ controller: "users", action: "index", id: "10" }, set.recognize_path("/users/index/10"))
assert_equal({ controller: "users", action: "index", id: "10" }, set.recognize_path("/users/index/10/"))
end
def test_route_with_parameter_shell
set.draw do
get "page/:id" => "pages#show", :id => /\d+/
ActiveSupport::Deprecation.silence do
get "/:controller(/:action(/:id))"
end
end
assert_equal({ controller: "pages", action: "index" }, request_path_params("/pages"))
assert_equal({ controller: "pages", action: "index" }, request_path_params("/pages/index"))
assert_equal({ controller: "pages", action: "list" }, request_path_params("/pages/list"))
assert_equal({ controller: "pages", action: "show", id: "10" }, request_path_params("/pages/show/10"))
assert_equal({ controller: "pages", action: "show", id: "10" }, request_path_params("/page/10"))
end
def test_route_constraints_on_request_object_with_anchors_are_valid
assert_nothing_raised do
set.draw do
get "page/:id" => "pages#show", :constraints => { host: /^foo$/ }
end
end
end
def test_route_constraints_with_anchor_chars_are_invalid
assert_raise ArgumentError do
set.draw do
get "page/:id" => "pages#show", :id => /^\d+/
end
end
assert_raise ArgumentError do
set.draw do
get "page/:id" => "pages#show", :id => /\A\d+/
end
end
assert_raise ArgumentError do
set.draw do
get "page/:id" => "pages#show", :id => /\d+$/
end
end
assert_raise ArgumentError do
set.draw do
get "page/:id" => "pages#show", :id => /\d+\Z/
end
end
assert_raise ArgumentError do
set.draw do
get "page/:id" => "pages#show", :id => /\d+\z/
end
end
end
def test_route_constraints_with_options_method_condition_is_valid
assert_nothing_raised do
set.draw do
match "valid/route" => "pages#show", :via => :options
end
end
end
def test_route_error_with_missing_controller
set.draw do
get "/people" => "missing#index"
end
assert_raises(ActionController::RoutingError) { request_path_params "/people" }
end
def test_recognize_with_encoded_id_and_regex
set.draw do
get "page/:id" => "pages#show", :id => /[a-zA-Z0-9\+]+/
end
assert_equal({ controller: "pages", action: "show", id: "10" }, request_path_params("/page/10"))
assert_equal({ controller: "pages", action: "show", id: "hello+world" }, request_path_params("/page/hello+world"))
end
def test_recognize_with_http_methods
set.draw do
get "/people" => "people#index", :as => "people"
post "/people" => "people#create"
get "/people/:id" => "people#show", :as => "person"
put "/people/:id" => "people#update"
patch "/people/:id" => "people#update"
delete "/people/:id" => "people#destroy"
end
params = request_path_params("/people", method: :get)
assert_equal("index", params[:action])
params = request_path_params("/people", method: :post)
assert_equal("create", params[:action])
params = request_path_params("/people/5", method: :put)
assert_equal("update", params[:action])
params = request_path_params("/people/5", method: :patch)
assert_equal("update", params[:action])
assert_raise(ActionController::UnknownHttpMethod) {
request_path_params("/people", method: :bacon)
}
params = request_path_params("/people/5", method: :get)
assert_equal("show", params[:action])
assert_equal("5", params[:id])
params = request_path_params("/people/5", method: :put)
assert_equal("update", params[:action])
assert_equal("5", params[:id])
params = request_path_params("/people/5", method: :patch)
assert_equal("update", params[:action])
assert_equal("5", params[:id])
params = request_path_params("/people/5", method: :delete)
assert_equal("destroy", params[:action])
assert_equal("5", params[:id])
assert_raise(ActionController::RoutingError) {
request_path_params("/people/5", method: :post)
}
end
def test_recognize_with_alias_in_conditions
set.draw do
match "/people" => "people#index", :as => "people", :via => :get
root to: "people#index"
end
params = request_path_params("/people", method: :get)
assert_equal("people", params[:controller])
assert_equal("index", params[:action])
params = request_path_params("/", method: :get)
assert_equal("people", params[:controller])
assert_equal("index", params[:action])
end
def test_typo_recognition
set.draw do
get "articles/:year/:month/:day/:title" => "articles#permalink",
:year => /\d{4}/, :day => /\d{1,2}/, :month => /\d{1,2}/
end
params = request_path_params("/articles/2005/11/05/a-very-interesting-article", method: :get)
assert_equal("permalink", params[:action])
assert_equal("2005", params[:year])
assert_equal("11", params[:month])
assert_equal("05", params[:day])
assert_equal("a-very-interesting-article", params[:title])
end
def test_routing_traversal_does_not_load_extra_classes
assert !Object.const_defined?("Profiler__"), "Profiler should not be loaded"
set.draw do
get "/profile" => "profile#index"
end
request_path_params("/profile") rescue nil
assert !Object.const_defined?("Profiler__"), "Profiler should not be loaded"
end
def test_recognize_with_conditions_and_format
set.draw do
get "people/:id" => "people#show", :as => "person"
put "people/:id" => "people#update"
patch "people/:id" => "people#update"
get "people/:id(.:format)" => "people#show"
end
params = request_path_params("/people/5", method: :get)
assert_equal("show", params[:action])
assert_equal("5", params[:id])
params = request_path_params("/people/5", method: :put)
assert_equal("update", params[:action])
params = request_path_params("/people/5", method: :patch)
assert_equal("update", params[:action])
params = request_path_params("/people/5.png", method: :get)
assert_equal("show", params[:action])
assert_equal("5", params[:id])
assert_equal("png", params[:format])
end
def test_generate_with_default_action
set.draw do
get "/people", controller: "people", action: "index"
get "/people/list", controller: "people", action: "list"
end
url = url_for(set, controller: "people", action: "list")
assert_equal "/people/list", url
end
def test_root_map
set.draw { root to: "people#index" }
params = request_path_params("", method: :get)
assert_equal("people", params[:controller])
assert_equal("index", params[:action])
end
def test_namespace
set.draw do
namespace "api" do
get "inventory" => "products#inventory"
end
end
params = request_path_params("/api/inventory", method: :get)
assert_equal("api/products", params[:controller])
assert_equal("inventory", params[:action])
end
def test_namespaced_root_map
set.draw do
namespace "api" do
root to: "products#index"
end
end
params = request_path_params("/api", method: :get)
assert_equal("api/products", params[:controller])
assert_equal("index", params[:action])
end
def test_namespace_with_path_prefix
set.draw do
scope module: "api", path: "prefix" do
get "inventory" => "products#inventory"
end
end
params = request_path_params("/prefix/inventory", method: :get)
assert_equal("api/products", params[:controller])
assert_equal("inventory", params[:action])
end
def test_namespace_with_blank_path_prefix
set.draw do
scope module: "api", path: "" do
get "inventory" => "products#inventory"
end
end
params = request_path_params("/inventory", method: :get)
assert_equal("api/products", params[:controller])
assert_equal("inventory", params[:action])
end
def test_id_is_sticky_when_it_ought_to_be
@set = make_set false
set.draw do
ActiveSupport::Deprecation.silence do
get ":controller/:id/:action"
end
end
get URI("http://test.host/people/7/show")
assert_equal "/people/7/destroy", controller.url_for(action: "destroy", only_path: true)
end
def test_use_static_path_when_possible
@set = make_set false
set.draw do
get "about" => "welcome#about"
ActiveSupport::Deprecation.silence do
get ":controller/:id/:action"
end
end
get URI("http://test.host/welcom/get/7")
assert_equal "/about", controller.url_for(controller: "welcome",
action: "about",
only_path: true)
end
def test_generate
set.draw { ActiveSupport::Deprecation.silence { get ":controller/:action/:id" } }
args = { controller: "foo", action: "bar", id: "7", x: "y" }
assert_equal "/foo/bar/7?x=y", url_for(set, args)
assert_equal ["/foo/bar/7", [:x]], set.generate_extras(args)
assert_equal [:x], set.extra_keys(args)
end
def test_generate_with_path_prefix
set.draw do
scope "my" do
ActiveSupport::Deprecation.silence do
get ":controller(/:action(/:id))"
end
end
end
args = { controller: "foo", action: "bar", id: "7", x: "y" }
assert_equal "/my/foo/bar/7?x=y", url_for(set, args)
end
def test_generate_with_blank_path_prefix
set.draw do
scope "" do
ActiveSupport::Deprecation.silence do
get ":controller(/:action(/:id))"
end
end
end
args = { controller: "foo", action: "bar", id: "7", x: "y" }
assert_equal "/foo/bar/7?x=y", url_for(set, args)
end
def test_named_routes_are_never_relative_to_modules
@set = make_set false
set.draw do
ActiveSupport::Deprecation.silence do
get "/connection/manage(/:action)" => "connection/manage#index"
get "/connection/connection" => "connection/connection#index"
get "/connection" => "connection#index", :as => "family_connection"
end
end
assert_equal({ controller: "connection/manage",
action: "index", }, request_path_params("/connection/manage"))
url = controller.url_for(controller: "connection", only_path: true)
assert_equal "/connection/connection", url
url = controller.url_for(use_route: "family_connection",
controller: "connection", only_path: true)
assert_equal "/connection", url
end
def test_action_left_off_when_id_is_recalled
@set = make_set false
set.draw do
ActiveSupport::Deprecation.silence do
get ":controller(/:action(/:id))"
end
end
get URI("http://test.host/books/show/10")
assert_equal "/books", controller.url_for(controller: "books",
only_path: true,
action: "index")
end
def test_query_params_will_be_shown_when_recalled
@set = make_set false
set.draw do
get "show_weblog/:parameter" => "weblog#show"
ActiveSupport::Deprecation.silence do
get ":controller(/:action(/:id))"
end
end
get URI("http://test.host/weblog/show/1")
assert_equal "/weblog/edit?parameter=1", controller.url_for(
action: "edit", parameter: 1, only_path: true)
end
def test_format_is_not_inherit
set.draw do
get "/posts(.:format)" => "posts#index"
end
get URI("http://test.host/posts.xml")
assert_equal({ controller: "posts", action: "index", format: "xml" },
controller.request.path_parameters)
assert_equal "/posts", controller.url_for(
controller: "posts", only_path: true)
assert_equal "/posts.xml", controller.url_for(
controller: "posts", format: "xml", only_path: true)
end
def test_expiry_determination_should_consider_values_with_to_param
@set = make_set false
set.draw { ActiveSupport::Deprecation.silence { get "projects/:project_id/:controller/:action" } }
get URI("http://test.host/projects/1/weblog/show")
assert_equal(
{ controller: "weblog", action: "show", project_id: "1" },
controller.request.path_parameters)
assert_equal "/projects/1/weblog/show",
controller.url_for(action: "show", project_id: 1, only_path: true)
end
def test_named_route_in_nested_resource
set.draw do
resources :projects do
member do
get "milestones" => "milestones#index", :as => "milestones"
end
end
end
params = set.recognize_path("/projects/1/milestones", method: :get)
assert_equal("milestones", params[:controller])
assert_equal("index", params[:action])
end
def test_setting_root_in_namespace_using_symbol
assert_nothing_raised do
set.draw do
namespace :admin do
root to: "home#index"
end
end
end
end
def test_setting_root_in_namespace_using_string
assert_nothing_raised do
set.draw do
namespace "admin" do
root to: "home#index"
end
end
end
end
def test_route_constraints_with_unsupported_regexp_options_must_error
assert_raise ArgumentError do
set.draw do
get "page/:name" => "pages#show",
:constraints => { name: /(david|jamis)/m }
end
end
end
def test_route_constraints_with_supported_options_must_not_error
assert_nothing_raised do
set.draw do
get "page/:name" => "pages#show",
:constraints => { name: /(david|jamis)/i }
end
end
assert_nothing_raised do
set.draw do
get "page/:name" => "pages#show",
:constraints => { name: / # Desperately overcommented regexp
( #Either
david #The Creator
| #Or
jamis #The Deployer
)/x }
end
end
end
def test_route_with_subdomain_and_constraints_must_receive_params
name_param = nil
set.draw do
get "page/:name" => "pages#show", :constraints => lambda { |request|
name_param = request.params[:name]
return true
}
end
assert_equal({ controller: "pages", action: "show", name: "mypage" },
set.recognize_path("http://subdomain.example.org/page/mypage"))
assert_equal(name_param, "mypage")
end
def test_route_requirement_recognize_with_ignore_case
set.draw do
get "page/:name" => "pages#show",
:constraints => { name: /(david|jamis)/i }
end
assert_equal({ controller: "pages", action: "show", name: "jamis" }, set.recognize_path("/page/jamis"))
assert_raise ActionController::RoutingError do
set.recognize_path("/page/davidjamis")
end
assert_equal({ controller: "pages", action: "show", name: "DAVID" }, set.recognize_path("/page/DAVID"))
end
def test_route_requirement_generate_with_ignore_case
set.draw do
get "page/:name" => "pages#show",
:constraints => { name: /(david|jamis)/i }
end
url = url_for(set, controller: "pages", action: "show", name: "david")
assert_equal "/page/david", url
assert_raise(ActionController::UrlGenerationError) do
url_for(set, controller: "pages", action: "show", name: "davidjamis")
end
url = url_for(set, controller: "pages", action: "show", name: "JAMIS")
assert_equal "/page/JAMIS", url
end
def test_route_requirement_recognize_with_extended_syntax
set.draw do
get "page/:name" => "pages#show",
:constraints => { name: / # Desperately overcommented regexp
( #Either
david #The Creator
| #Or
jamis #The Deployer
)/x }
end
assert_equal({ controller: "pages", action: "show", name: "jamis" }, set.recognize_path("/page/jamis"))
assert_equal({ controller: "pages", action: "show", name: "david" }, set.recognize_path("/page/david"))
assert_raise ActionController::RoutingError do
set.recognize_path("/page/david #The Creator")
end
assert_raise ActionController::RoutingError do
set.recognize_path("/page/David")
end
end
def test_route_requirement_with_xi_modifiers
set.draw do
get "page/:name" => "pages#show",
:constraints => { name: / # Desperately overcommented regexp
( #Either
david #The Creator
| #Or
jamis #The Deployer
)/xi }
end
assert_equal({ controller: "pages", action: "show", name: "JAMIS" },
set.recognize_path("/page/JAMIS"))
assert_equal "/page/JAMIS",
url_for(set, controller: "pages", action: "show", name: "JAMIS")
end
def test_routes_with_symbols
set.draw do
get "unnamed", controller: :pages, action: :show, name: :as_symbol
get "named" , controller: :pages, action: :show, name: :as_symbol, as: :named
end
assert_equal({ controller: "pages", action: "show", name: :as_symbol }, set.recognize_path("/unnamed"))
assert_equal({ controller: "pages", action: "show", name: :as_symbol }, set.recognize_path("/named"))
end
def test_regexp_chunk_should_add_question_mark_for_optionals
set.draw do
get "/" => "foo#index"
get "/hello" => "bar#index"
end
assert_equal "/", url_for(set, controller: "foo")
assert_equal "/hello", url_for(set, controller: "bar")
assert_equal({ controller: "foo", action: "index" }, set.recognize_path("/"))
assert_equal({ controller: "bar", action: "index" }, set.recognize_path("/hello"))
end
def test_assign_route_options_with_anchor_chars
set.draw do
ActiveSupport::Deprecation.silence do
get "/cars/:action/:person/:car/", controller: "cars"
end
end
assert_equal "/cars/buy/1/2", url_for(set, controller: "cars", action: "buy", person: "1", car: "2")
assert_equal({ controller: "cars", action: "buy", person: "1", car: "2" }, set.recognize_path("/cars/buy/1/2"))
end
def test_segmentation_of_dot_path
set.draw do
ActiveSupport::Deprecation.silence do
get "/books/:action.rss", controller: "books"
end
end
assert_equal "/books/list.rss", url_for(set, controller: "books", action: "list")
assert_equal({ controller: "books", action: "list" }, set.recognize_path("/books/list.rss"))
end
def test_segmentation_of_dynamic_dot_path
set.draw do
ActiveSupport::Deprecation.silence do
get "/books(/:action(.:format))", controller: "books"
end
end
assert_equal "/books/list.rss", url_for(set, controller: "books", action: "list", format: "rss")
assert_equal "/books/list.xml", url_for(set, controller: "books", action: "list", format: "xml")
assert_equal "/books/list", url_for(set, controller: "books", action: "list")
assert_equal "/books", url_for(set, controller: "books", action: "index")
assert_equal({ controller: "books", action: "list", format: "rss" }, set.recognize_path("/books/list.rss"))
assert_equal({ controller: "books", action: "list", format: "xml" }, set.recognize_path("/books/list.xml"))
assert_equal({ controller: "books", action: "list" }, set.recognize_path("/books/list"))
assert_equal({ controller: "books", action: "index" }, set.recognize_path("/books"))
end
def test_slashes_are_implied
set.draw { ActiveSupport::Deprecation.silence { get("/:controller(/:action(/:id))") } }
assert_equal "/content", url_for(set, controller: "content", action: "index")
assert_equal "/content/list", url_for(set, controller: "content", action: "list")
assert_equal "/content/show/1", url_for(set, controller: "content", action: "show", id: "1")
assert_equal({ controller: "content", action: "index" }, set.recognize_path("/content"))
assert_equal({ controller: "content", action: "index" }, set.recognize_path("/content/index"))
assert_equal({ controller: "content", action: "list" }, set.recognize_path("/content/list"))
assert_equal({ controller: "content", action: "show", id: "1" }, set.recognize_path("/content/show/1"))
end
def test_default_route_recognition
expected = { controller: "pages", action: "show", id: "10" }
assert_equal expected, default_route_set.recognize_path("/pages/show/10")
assert_equal expected, default_route_set.recognize_path("/pages/show/10/")
expected[:id] = "jamis"
assert_equal expected, default_route_set.recognize_path("/pages/show/jamis/")
expected.delete :id
assert_equal expected, default_route_set.recognize_path("/pages/show")
assert_equal expected, default_route_set.recognize_path("/pages/show/")
expected[:action] = "index"
assert_equal expected, default_route_set.recognize_path("/pages/")
assert_equal expected, default_route_set.recognize_path("/pages")
assert_raise(ActionController::RoutingError) { default_route_set.recognize_path("/") }
assert_raise(ActionController::RoutingError) { default_route_set.recognize_path("/pages/how/goood/it/is/to/be/free") }
end
def test_default_route_should_omit_default_action
assert_equal "/accounts", url_for(default_route_set, controller: "accounts", action: "index")
end
def test_default_route_should_include_default_action_when_id_present
assert_equal "/accounts/index/20", url_for(default_route_set, controller: "accounts", action: "index", id: "20")
end
def test_default_route_should_work_with_action_but_no_id
assert_equal "/accounts/list_all", url_for(default_route_set, controller: "accounts", action: "list_all")
end
def test_default_route_should_uri_escape_pluses
expected = { controller: "pages", action: "show", id: "hello world" }
assert_equal expected, default_route_set.recognize_path("/pages/show/hello%20world")
assert_equal "/pages/show/hello%20world", url_for(default_route_set, expected)
expected[:id] = "hello+world"
assert_equal expected, default_route_set.recognize_path("/pages/show/hello+world")
assert_equal expected, default_route_set.recognize_path("/pages/show/hello%2Bworld")
assert_equal "/pages/show/hello+world", url_for(default_route_set, expected)
end
def test_build_empty_query_string
assert_uri_equal "/foo", url_for(default_route_set, controller: "foo")
end
def test_build_query_string_with_nil_value
assert_uri_equal "/foo", url_for(default_route_set, controller: "foo", x: nil)
end
def test_simple_build_query_string
assert_uri_equal "/foo?x=1&y=2", url_for(default_route_set, controller: "foo", x: "1", y: "2")
end
def test_convert_ints_build_query_string
assert_uri_equal "/foo?x=1&y=2", url_for(default_route_set, controller: "foo", x: 1, y: 2)
end
def test_escape_spaces_build_query_string
assert_uri_equal "/foo?x=hello+world&y=goodbye+world", url_for(default_route_set, controller: "foo", x: "hello world", y: "goodbye world")
end
def test_expand_array_build_query_string
assert_uri_equal "/foo?x%5B%5D=1&x%5B%5D=2", url_for(default_route_set, controller: "foo", x: [1, 2])
end
def test_escape_spaces_build_query_string_selected_keys
assert_uri_equal "/foo?x=hello+world", url_for(default_route_set, controller: "foo", x: "hello world")
end
def test_generate_with_default_params
set.draw do
get "dummy/page/:page" => "dummy#show"
get "dummy/dots/page.:page" => "dummy#dots"
get "ibocorp(/:page)" => "ibocorp#show",
:constraints => { page: /\d+/ },
:defaults => { page: 1 }
ActiveSupport::Deprecation.silence do
get ":controller/:action/:id"
end
end
assert_equal "/ibocorp", url_for(set, controller: "ibocorp", action: "show", page: 1)
end
include ActionDispatch::RoutingVerbs
alias :routes :set
def test_generate_with_optional_params_recalls_last_request
@set = make_set false
set.draw do
get "blog/", controller: "blog", action: "index"
get "blog(/:year(/:month(/:day)))",
controller: "blog",
action: "show_date",
constraints: { year: /(19|20)\d\d/, month: /[01]?\d/, day: /[0-3]?\d/ },
day: nil, month: nil
get "blog/show/:id", controller: "blog", action: "show", id: /\d+/
ActiveSupport::Deprecation.silence do
get "blog/:controller/:action(/:id)"
end
get "*anything", controller: "blog", action: "unknown_request"
end
recognize_path = ->(path) {
get(URI("http://example.org" + path))
controller.request.path_parameters
}
assert_equal({ controller: "blog", action: "index" }, recognize_path.("/blog"))
assert_equal({ controller: "blog", action: "show", id: "123" }, recognize_path.("/blog/show/123"))
assert_equal({ controller: "blog", action: "show_date", year: "2004", day: nil, month: nil }, recognize_path.("/blog/2004"))
assert_equal({ controller: "blog", action: "show_date", year: "2004", month: "12", day: nil }, recognize_path.("/blog/2004/12"))
assert_equal({ controller: "blog", action: "show_date", year: "2004", month: "12", day: "25" }, recognize_path.("/blog/2004/12/25"))
assert_equal({ controller: "articles", action: "edit", id: "123" }, recognize_path.("/blog/articles/edit/123"))
assert_equal({ controller: "articles", action: "show_stats" }, recognize_path.("/blog/articles/show_stats"))
assert_equal({ controller: "blog", action: "unknown_request", anything: "blog/wibble" }, recognize_path.("/blog/wibble"))
assert_equal({ controller: "blog", action: "unknown_request", anything: "junk" }, recognize_path.("/junk"))
get URI("http://example.org/blog/2006/07/28")
assert_equal({ controller: "blog", action: "show_date", year: "2006", month: "07", day: "28" }, controller.request.path_parameters)
assert_equal("/blog/2006/07/25", controller.url_for(day: 25, only_path: true))
assert_equal("/blog/2005", controller.url_for(year: 2005, only_path: true))
assert_equal("/blog/show/123", controller.url_for(action: "show" , id: 123, only_path: true))
assert_equal("/blog/2006", controller.url_for(year: 2006, only_path: true))
assert_equal("/blog/2006", controller.url_for(year: 2006, month: nil, only_path: true))
end
private
def assert_uri_equal(expected, actual)
assert_equal(sort_query_string_params(expected), sort_query_string_params(actual))
end
def sort_query_string_params(uri)
path, qs = uri.split("?")
qs = qs.split("&").sort.join("&") if qs
qs ? "#{path}?#{qs}" : path
end
end
class RackMountIntegrationTests < ActiveSupport::TestCase
include RoutingTestHelpers
Model = Struct.new(:to_param)
Mapping = lambda {
namespace :admin do
resources :users, :posts
end
namespace "api" do
root to: "users#index"
end
get "/blog(/:year(/:month(/:day)))" => "posts#show_date",
:constraints => {
year: /(19|20)\d\d/,
month: /[01]?\d/,
day: /[0-3]?\d/
},
:day => nil,
:month => nil
get "archive/:year", controller: "archive", action: "index",
defaults: { year: nil },
constraints: { year: /\d{4}/ },
as: "blog"
resources :people
get "legacy/people" => "people#index", :legacy => "true"
get "symbols", controller: :symbols, action: :show, name: :as_symbol
get "id_default(/:id)" => "foo#id_default", :id => 1
match "get_or_post" => "foo#get_or_post", :via => [:get, :post]
get "optional/:optional" => "posts#index"
get "projects/:project_id" => "project#index", :as => "project"
get "clients" => "projects#index"
get "ignorecase/geocode/:postalcode" => "geocode#show", :postalcode => /hx\d\d-\d[a-z]{2}/i
get "extended/geocode/:postalcode" => "geocode#show", :constraints => {
postalcode: /# Postcode format
\d{5} #Prefix
(-\d{4})? #Suffix
/x
}, :as => "geocode"
get "news(.:format)" => "news#index"
ActiveSupport::Deprecation.silence do
get "comment/:id(/:action)" => "comments#show"
get "ws/:controller(/:action(/:id))", ws: true
get "account(/:action)" => "account#subscription"
get "pages/:page_id/:controller(/:action(/:id))"
get ":controller/ping", action: "ping"
end
get "こんにちは/世界", controller: "news", action: "index"
ActiveSupport::Deprecation.silence do
match ":controller(/:action(/:id))(.:format)", via: :all
end
root to: "news#index"
}
attr_reader :routes
attr_reader :controller
def setup
@routes = ActionDispatch::Routing::RouteSet.new
@routes.draw(&Mapping)
end
def test_recognize_path
assert_equal({ controller: "admin/users", action: "index" }, @routes.recognize_path("/admin/users", method: :get))
assert_equal({ controller: "admin/users", action: "create" }, @routes.recognize_path("/admin/users", method: :post))
assert_equal({ controller: "admin/users", action: "new" }, @routes.recognize_path("/admin/users/new", method: :get))
assert_equal({ controller: "admin/users", action: "show", id: "1" }, @routes.recognize_path("/admin/users/1", method: :get))
assert_equal({ controller: "admin/users", action: "update", id: "1" }, @routes.recognize_path("/admin/users/1", method: :put))
assert_equal({ controller: "admin/users", action: "destroy", id: "1" }, @routes.recognize_path("/admin/users/1", method: :delete))
assert_equal({ controller: "admin/users", action: "edit", id: "1" }, @routes.recognize_path("/admin/users/1/edit", method: :get))
assert_equal({ controller: "admin/posts", action: "index" }, @routes.recognize_path("/admin/posts", method: :get))
assert_equal({ controller: "admin/posts", action: "new" }, @routes.recognize_path("/admin/posts/new", method: :get))
assert_equal({ controller: "api/users", action: "index" }, @routes.recognize_path("/api", method: :get))
assert_equal({ controller: "api/users", action: "index" }, @routes.recognize_path("/api/", method: :get))
assert_equal({ controller: "posts", action: "show_date", year: "2009", month: nil, day: nil }, @routes.recognize_path("/blog/2009", method: :get))
assert_equal({ controller: "posts", action: "show_date", year: "2009", month: "01", day: nil }, @routes.recognize_path("/blog/2009/01", method: :get))
assert_equal({ controller: "posts", action: "show_date", year: "2009", month: "01", day: "01" }, @routes.recognize_path("/blog/2009/01/01", method: :get))
assert_equal({ controller: "archive", action: "index", year: "2010" }, @routes.recognize_path("/archive/2010"))
assert_equal({ controller: "archive", action: "index" }, @routes.recognize_path("/archive"))
assert_equal({ controller: "people", action: "index" }, @routes.recognize_path("/people", method: :get))
assert_equal({ controller: "people", action: "index", format: "xml" }, @routes.recognize_path("/people.xml", method: :get))
assert_equal({ controller: "people", action: "create" }, @routes.recognize_path("/people", method: :post))
assert_equal({ controller: "people", action: "new" }, @routes.recognize_path("/people/new", method: :get))
assert_equal({ controller: "people", action: "show", id: "1" }, @routes.recognize_path("/people/1", method: :get))
assert_equal({ controller: "people", action: "show", id: "1", format: "xml" }, @routes.recognize_path("/people/1.xml", method: :get))
assert_equal({ controller: "people", action: "update", id: "1" }, @routes.recognize_path("/people/1", method: :put))
assert_equal({ controller: "people", action: "destroy", id: "1" }, @routes.recognize_path("/people/1", method: :delete))
assert_equal({ controller: "people", action: "edit", id: "1" }, @routes.recognize_path("/people/1/edit", method: :get))
assert_equal({ controller: "people", action: "edit", id: "1", format: "xml" }, @routes.recognize_path("/people/1/edit.xml", method: :get))
assert_equal({ controller: "symbols", action: "show", name: :as_symbol }, @routes.recognize_path("/symbols"))
assert_equal({ controller: "foo", action: "id_default", id: "1" }, @routes.recognize_path("/id_default/1"))
assert_equal({ controller: "foo", action: "id_default", id: "2" }, @routes.recognize_path("/id_default/2"))
assert_equal({ controller: "foo", action: "id_default", id: 1 }, @routes.recognize_path("/id_default"))
assert_equal({ controller: "foo", action: "get_or_post" }, @routes.recognize_path("/get_or_post", method: :get))
assert_equal({ controller: "foo", action: "get_or_post" }, @routes.recognize_path("/get_or_post", method: :post))
assert_raise(ActionController::RoutingError) { @routes.recognize_path("/get_or_post", method: :put) }
assert_raise(ActionController::RoutingError) { @routes.recognize_path("/get_or_post", method: :delete) }
assert_equal({ controller: "posts", action: "index", optional: "bar" }, @routes.recognize_path("/optional/bar"))
assert_raise(ActionController::RoutingError) { @routes.recognize_path("/optional") }
assert_equal({ controller: "posts", action: "show", id: "1", ws: true }, @routes.recognize_path("/ws/posts/show/1", method: :get))
assert_equal({ controller: "posts", action: "list", ws: true }, @routes.recognize_path("/ws/posts/list", method: :get))
assert_equal({ controller: "posts", action: "index", ws: true }, @routes.recognize_path("/ws/posts", method: :get))
assert_equal({ controller: "account", action: "subscription" }, @routes.recognize_path("/account", method: :get))
assert_equal({ controller: "account", action: "subscription" }, @routes.recognize_path("/account/subscription", method: :get))
assert_equal({ controller: "account", action: "billing" }, @routes.recognize_path("/account/billing", method: :get))
assert_equal({ page_id: "1", controller: "notes", action: "index" }, @routes.recognize_path("/pages/1/notes", method: :get))
assert_equal({ page_id: "1", controller: "notes", action: "list" }, @routes.recognize_path("/pages/1/notes/list", method: :get))
assert_equal({ page_id: "1", controller: "notes", action: "show", id: "2" }, @routes.recognize_path("/pages/1/notes/show/2", method: :get))
assert_equal({ controller: "posts", action: "ping" }, @routes.recognize_path("/posts/ping", method: :get))
assert_equal({ controller: "posts", action: "index" }, @routes.recognize_path("/posts", method: :get))
assert_equal({ controller: "posts", action: "index" }, @routes.recognize_path("/posts/index", method: :get))
assert_equal({ controller: "posts", action: "show" }, @routes.recognize_path("/posts/show", method: :get))
assert_equal({ controller: "posts", action: "show", id: "1" }, @routes.recognize_path("/posts/show/1", method: :get))
assert_equal({ controller: "posts", action: "create" }, @routes.recognize_path("/posts/create", method: :post))
assert_equal({ controller: "geocode", action: "show", postalcode: "hx12-1az" }, @routes.recognize_path("/ignorecase/geocode/hx12-1az"))
assert_equal({ controller: "geocode", action: "show", postalcode: "hx12-1AZ" }, @routes.recognize_path("/ignorecase/geocode/hx12-1AZ"))
assert_equal({ controller: "geocode", action: "show", postalcode: "12345-1234" }, @routes.recognize_path("/extended/geocode/12345-1234"))
assert_equal({ controller: "geocode", action: "show", postalcode: "12345" }, @routes.recognize_path("/extended/geocode/12345"))
assert_equal({ controller: "news", action: "index" }, @routes.recognize_path("/", method: :get))
assert_equal({ controller: "news", action: "index", format: "rss" }, @routes.recognize_path("/news.rss", method: :get))
assert_raise(ActionController::RoutingError) { @routes.recognize_path("/none", method: :get) }
end
def test_generate_extras
assert_equal ["/people", []], @routes.generate_extras(controller: "people")
assert_equal ["/people", [:foo]], @routes.generate_extras(controller: "people", foo: "bar")
assert_equal ["/people", []], @routes.generate_extras(controller: "people", action: "index")
assert_equal ["/people", [:foo]], @routes.generate_extras(controller: "people", action: "index", foo: "bar")
assert_equal ["/people/new", []], @routes.generate_extras(controller: "people", action: "new")
assert_equal ["/people/new", [:foo]], @routes.generate_extras(controller: "people", action: "new", foo: "bar")
assert_equal ["/people/1", []], @routes.generate_extras(controller: "people", action: "show", id: "1")
assert_equal ["/people/1", [:bar, :foo]], sort_extras!(@routes.generate_extras(controller: "people", action: "show", id: "1", foo: "2", bar: "3"))
assert_equal ["/people", [:person]], @routes.generate_extras(controller: "people", action: "create", person: { first_name: "Josh", last_name: "Peek" })
assert_equal ["/people", [:people]], @routes.generate_extras(controller: "people", action: "create", people: ["Josh", "Dave"])
assert_equal ["/posts/show/1", []], @routes.generate_extras(controller: "posts", action: "show", id: "1")
assert_equal ["/posts/show/1", [:bar, :foo]], sort_extras!(@routes.generate_extras(controller: "posts", action: "show", id: "1", foo: "2", bar: "3"))
assert_equal ["/posts", []], @routes.generate_extras(controller: "posts", action: "index")
assert_equal ["/posts", [:foo]], @routes.generate_extras(controller: "posts", action: "index", foo: "bar")
end
def test_extras
params = { controller: "people" }
assert_equal [], @routes.extra_keys(params)
assert_equal({ controller: "people", action: "index" }, params)
params = { controller: "people", foo: "bar" }
assert_equal [:foo], @routes.extra_keys(params)
assert_equal({ controller: "people", action: "index", foo: "bar" }, params)
params = { controller: "people", action: "create", person: { name: "Josh" } }
assert_equal [:person], @routes.extra_keys(params)
assert_equal({ controller: "people", action: "create", person: { name: "Josh" } }, params)
end
def test_unicode_path
assert_equal({ controller: "news", action: "index" }, @routes.recognize_path(URI.parser.escape("こんにちは/世界"), method: :get))
end
def test_downcased_unicode_path
assert_equal({ controller: "news", action: "index" }, @routes.recognize_path(URI.parser.escape("こんにちは/世界").downcase, method: :get))
end
private
def sort_extras!(extras)
if extras.length == 2
extras[1].sort! { |a, b| a.to_s <=> b.to_s }
end
extras
end
end