# 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.unpack1("H2").upcase }

    @segment = "#{safe.join}#{unsafe.join}"
    @escaped = "#{safe.join}#{hex.join}"
  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.delete_prefix("/"), 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.delete_prefix("/"), 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_precedence
    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-twitter", controller: "content" }, rs.recognize_path("/content/auth-twitter"))

    assert_equal "/content/auth_google", url_for(rs, controller: "content", action: "auth_google")
    assert_equal "/content/auth-twitter", url_for(rs, controller: "content", action: "auth-twitter")
  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.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.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.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.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.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.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.root_url)
    assert_equal("/", routes.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.root_url)
    assert_equal("/", routes.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.index_url)
    assert_equal("/", routes.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.root_url)
    assert_equal("http://test.host/?foo=bar", routes.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.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.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" # '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.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.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.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.show_url(id: 5)
    assert_equal "/people/5", controller.show_path(id: 5)

    assert_equal "http://test.host/people", controller.index_url
    assert_equal "/people", controller.index_path

    assert_equal "http://test.host/admin/users", controller.users_url
    assert_equal "/admin/users", controller.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.show_url(id: 5, anchor: "location")
    assert_equal "/people/5#location", controller.show_path(id: 5, anchor: "location")

    assert_equal "http://test.host/people#location", controller.index_url(anchor: "location")
    assert_equal "/people#location", controller.index_path(anchor: "location")

    assert_equal "http://test.host/admin/users#location", controller.users_url(anchor: "location")
    assert_equal "/admin/users#location", controller.users_path(anchor: "location")

    assert_equal "http://test.host/people/go/7/hello/joe/5#location",
      controller.multi_url(7, "hello", 5, anchor: "location")

    assert_equal "http://test.host/people/go/7/hello/joe/5?baz=bar#location",
      controller.multi_url(7, "hello", 5, baz: "bar", anchor: "location")

    assert_equal "http://test.host/people?baz=bar#location",
      controller.index_url(baz: "bar", anchor: "location")

    assert_equal "http://test.host/people", controller.index_url(anchor: nil)
    assert_equal "http://test.host/people", controller.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.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.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.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.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.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.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.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_not Object.const_defined?("Profiler__"), "Profiler should not be loaded"
    set.draw do
      get "/profile" => "profile#index"
    end

    request_path_params("/profile") rescue nil

    assert_not 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" }, params)

    params = { controller: "people", foo: "bar" }
    assert_equal [:foo], @routes.extra_keys(params)
    assert_equal({ controller: "people", 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)

    params = { controller: "people", action: "create", domain: { foo: "Josh" } }
    assert_equal [:domain], @routes.extra_keys(params)
    assert_equal({ controller: "people", action: "create", domain: { foo: "Josh" } }, params)
  end

  def test_unicode_path
    assert_equal({ controller: "news", action: "index" }, @routes.recognize_path(URI::DEFAULT_PARSER.escape("こんにちは/世界"), method: :get))
  end

  def test_downcased_unicode_path
    assert_equal({ controller: "news", action: "index" }, @routes.recognize_path(URI::DEFAULT_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
