aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack/test
diff options
context:
space:
mode:
Diffstat (limited to 'actionpack/test')
-rw-r--r--actionpack/test/controller/http_token_authentication_test.rb35
-rw-r--r--actionpack/test/controller/log_subscriber_test.rb32
-rw-r--r--actionpack/test/dispatch/request/multipart_params_parsing_test.rb12
-rw-r--r--actionpack/test/dispatch/request_test.rb7
-rw-r--r--actionpack/test/dispatch/routing/route_set_test.rb86
-rw-r--r--actionpack/test/journey/gtg/builder_test.rb79
-rw-r--r--actionpack/test/journey/gtg/transition_table_test.rb115
-rw-r--r--actionpack/test/journey/nfa/simulator_test.rb98
-rw-r--r--actionpack/test/journey/nfa/transition_table_test.rb72
-rw-r--r--actionpack/test/journey/nodes/symbol_test.rb17
-rw-r--r--actionpack/test/journey/path/pattern_test.rb284
-rw-r--r--actionpack/test/journey/route/definition/parser_test.rb110
-rw-r--r--actionpack/test/journey/route/definition/scanner_test.rb56
-rw-r--r--actionpack/test/journey/route_test.rb103
-rw-r--r--actionpack/test/journey/router/strexp_test.rb32
-rw-r--r--actionpack/test/journey/router/utils_test.rb21
-rw-r--r--actionpack/test/journey/router_test.rb575
-rw-r--r--actionpack/test/journey/routes_test.rb53
-rw-r--r--actionpack/test/template/asset_tag_helper_test.rb24
-rw-r--r--actionpack/test/template/url_helper_test.rb56
20 files changed, 1788 insertions, 79 deletions
diff --git a/actionpack/test/controller/http_token_authentication_test.rb b/actionpack/test/controller/http_token_authentication_test.rb
index 8a409d6ed2..faf923e929 100644
--- a/actionpack/test/controller/http_token_authentication_test.rb
+++ b/actionpack/test/controller/http_token_authentication_test.rb
@@ -104,17 +104,40 @@ class HttpTokenAuthenticationTest < ActionController::TestCase
assert_equal 'Token realm="SuperSecret"', @response.headers['WWW-Authenticate']
end
- test "authentication request with valid credential" do
- @request.env['HTTP_AUTHORIZATION'] = encode_credentials('"quote" pretty', :algorithm => 'test')
- get :display
+ test "token_and_options returns correct token" do
+ token = "rcHu+HzSFw89Ypyhn/896A=="
+ actual = ActionController::HttpAuthentication::Token.token_and_options(sample_request(token)).first
+ expected = token
+ assert_equal(expected, actual)
+ end
+
+ test "token_and_options returns correct token" do
+ token = 'rcHu+=HzSFw89Ypyhn/896A==f34'
+ actual = ActionController::HttpAuthentication::Token.token_and_options(sample_request(token)).first
+ expected = token
+ assert_equal(expected, actual)
+ end
- assert_response :success
- assert assigns(:logged_in)
- assert_equal 'Definitely Maybe', @response.body
+ test "token_and_options returns correct token" do
+ token = 'rcHu+\\\\"/896A'
+ actual = ActionController::HttpAuthentication::Token.token_and_options(sample_request(token)).first
+ expected = token
+ assert_equal(expected, actual)
+ end
+
+ test "token_and_options returns correct token" do
+ token = '\"quote\" pretty'
+ actual = ActionController::HttpAuthentication::Token.token_and_options(sample_request(token)).first
+ expected = token
+ assert_equal(expected, actual)
end
private
+ def sample_request(token)
+ @sample_request ||= OpenStruct.new authorization: %{Token token="#{token}"}
+ end
+
def encode_credentials(token, options = {})
ActionController::HttpAuthentication::Token.encode_credentials(token, options)
end
diff --git a/actionpack/test/controller/log_subscriber_test.rb b/actionpack/test/controller/log_subscriber_test.rb
index 929545fc10..075347be52 100644
--- a/actionpack/test/controller/log_subscriber_test.rb
+++ b/actionpack/test/controller/log_subscriber_test.rb
@@ -46,20 +46,20 @@ module Another
render :inline => "<%= cache('foo%bar'){ 'Contains % sign in key' } %>"
end
- def with_fragment_cache_and_if_true_condition
- render :inline => "<%= cache('foo', :if => true) { 'bar' } %>"
+ def with_fragment_cache_if_with_true_condition
+ render :inline => "<%= cache_if(true, 'foo') { 'bar' } %>"
end
- def with_fragment_cache_and_if_false_condition
- render :inline => "<%= cache('foo', :if => false) { 'bar' } %>"
+ def with_fragment_cache_if_with_false_condition
+ render :inline => "<%= cache_if(false, 'foo') { 'bar' } %>"
end
- def with_fragment_cache_and_unless_false_condition
- render :inline => "<%= cache('foo', :unless => false) { 'bar' } %>"
+ def with_fragment_cache_unless_with_false_condition
+ render :inline => "<%= cache_unless(false, 'foo') { 'bar' } %>"
end
- def with_fragment_cache_and_unless_true_condition
- render :inline => "<%= cache('foo', :unless => true) { 'bar' } %>"
+ def with_fragment_cache_unless_with_true_condition
+ render :inline => "<%= cache_unless(true, 'foo') { 'bar' } %>"
end
def with_exception
@@ -219,9 +219,9 @@ class ACLogSubscriberTest < ActionController::TestCase
@controller.config.perform_caching = true
end
- def test_with_fragment_cache_and_if_true
+ def test_with_fragment_cache_if_with_true
@controller.config.perform_caching = true
- get :with_fragment_cache_and_if_true_condition
+ get :with_fragment_cache_if_with_true_condition
wait
assert_equal 4, logs.size
@@ -231,9 +231,9 @@ class ACLogSubscriberTest < ActionController::TestCase
@controller.config.perform_caching = true
end
- def test_with_fragment_cache_and_if_false
+ def test_with_fragment_cache_if_with_false
@controller.config.perform_caching = true
- get :with_fragment_cache_and_if_false_condition
+ get :with_fragment_cache_if_with_false_condition
wait
assert_equal 2, logs.size
@@ -243,9 +243,9 @@ class ACLogSubscriberTest < ActionController::TestCase
@controller.config.perform_caching = true
end
- def test_with_fragment_cache_and_unless_true
+ def test_with_fragment_cache_unless_with_true
@controller.config.perform_caching = true
- get :with_fragment_cache_and_unless_true_condition
+ get :with_fragment_cache_unless_with_true_condition
wait
assert_equal 2, logs.size
@@ -255,9 +255,9 @@ class ACLogSubscriberTest < ActionController::TestCase
@controller.config.perform_caching = true
end
- def test_with_fragment_cache_and_unless_false
+ def test_with_fragment_cache_unless_with_false
@controller.config.perform_caching = true
- get :with_fragment_cache_and_unless_false_condition
+ get :with_fragment_cache_unless_with_false_condition
wait
assert_equal 4, logs.size
diff --git a/actionpack/test/dispatch/request/multipart_params_parsing_test.rb b/actionpack/test/dispatch/request/multipart_params_parsing_test.rb
index 63c5ea26a6..399f15199c 100644
--- a/actionpack/test/dispatch/request/multipart_params_parsing_test.rb
+++ b/actionpack/test/dispatch/request/multipart_params_parsing_test.rb
@@ -123,6 +123,18 @@ class MultipartParamsParsingTest < ActionDispatch::IntegrationTest
end
end
+ # This can happen in Internet Explorer when redirecting after multipart form submit.
+ test "does not raise EOFError on GET request with multipart content-type" do
+ with_routing do |set|
+ set.draw do
+ get ':action', to: 'multipart_params_parsing_test/test'
+ end
+ headers = { "CONTENT_TYPE" => "multipart/form-data; boundary=AaB03x" }
+ get "/parse", {}, headers
+ assert_response :ok
+ end
+ end
+
private
def fixture(name)
File.open(File.join(FIXTURE_PATH, name), 'rb') do |file|
diff --git a/actionpack/test/dispatch/request_test.rb b/actionpack/test/dispatch/request_test.rb
index f2bacf3e20..263853fb6c 100644
--- a/actionpack/test/dispatch/request_test.rb
+++ b/actionpack/test/dispatch/request_test.rb
@@ -650,6 +650,13 @@ class RequestTest < ActiveSupport::TestCase
assert_equal Mime::XML, request.negotiate_mime([Mime::XML, Mime::CSV])
end
+ test "raw_post rewinds rack.input if RAW_POST_DATA is nil" do
+ request = stub_request('rack.input' => StringIO.new("foo"),
+ 'CONTENT_LENGTH' => 3)
+ assert_equal "foo", request.raw_post
+ assert_equal "foo", request.env['rack.input'].read
+ end
+
test "process parameter filter" do
test_hashes = [
[{'foo'=>'bar'},{'foo'=>'bar'},%w'food'],
diff --git a/actionpack/test/dispatch/routing/route_set_test.rb b/actionpack/test/dispatch/routing/route_set_test.rb
new file mode 100644
index 0000000000..d57b1a5637
--- /dev/null
+++ b/actionpack/test/dispatch/routing/route_set_test.rb
@@ -0,0 +1,86 @@
+require 'abstract_unit'
+
+module ActionDispatch
+ module Routing
+ class RouteSetTest < ActiveSupport::TestCase
+ class SimpleApp
+ def initialize(response)
+ @response = response
+ end
+
+ def call(env)
+ [ 200, { 'Content-Type' => 'text/plain' }, [response] ]
+ end
+ end
+
+ setup do
+ @set = RouteSet.new
+ end
+
+ test "url helpers are added when route is added" do
+ draw do
+ get 'foo', to: SimpleApp.new('foo#index')
+ end
+
+ assert_equal '/foo', url_helpers.foo_path
+ assert_raises NoMethodError do
+ assert_equal '/bar', url_helpers.bar_path
+ end
+
+ draw do
+ get 'foo', to: SimpleApp.new('foo#index')
+ get 'bar', to: SimpleApp.new('bar#index')
+ end
+
+ assert_equal '/foo', url_helpers.foo_path
+ assert_equal '/bar', url_helpers.bar_path
+ end
+
+ test "url helpers are updated when route is updated" do
+ draw do
+ get 'bar', to: SimpleApp.new('bar#index'), as: :bar
+ end
+
+ assert_equal '/bar', url_helpers.bar_path
+
+ draw do
+ get 'baz', to: SimpleApp.new('baz#index'), as: :bar
+ end
+
+ assert_equal '/baz', url_helpers.bar_path
+ end
+
+ test "url helpers are removed when route is removed" do
+ draw do
+ get 'foo', to: SimpleApp.new('foo#index')
+ get 'bar', to: SimpleApp.new('bar#index')
+ end
+
+ assert_equal '/foo', url_helpers.foo_path
+ assert_equal '/bar', url_helpers.bar_path
+
+ draw do
+ get 'foo', to: SimpleApp.new('foo#index')
+ end
+
+ assert_equal '/foo', url_helpers.foo_path
+ assert_raises NoMethodError do
+ assert_equal '/bar', url_helpers.bar_path
+ end
+ end
+
+ private
+ def clear!
+ @set.clear!
+ end
+
+ def draw(&block)
+ @set.draw(&block)
+ end
+
+ def url_helpers
+ @set.url_helpers
+ end
+ end
+ end
+end
diff --git a/actionpack/test/journey/gtg/builder_test.rb b/actionpack/test/journey/gtg/builder_test.rb
new file mode 100644
index 0000000000..a633c3eea6
--- /dev/null
+++ b/actionpack/test/journey/gtg/builder_test.rb
@@ -0,0 +1,79 @@
+require 'abstract_unit'
+
+module ActionDispatch
+ module Journey
+ module GTG
+ class TestBuilder < MiniTest::Unit::TestCase
+ def test_following_states_multi
+ table = tt ['a|a']
+ assert_equal 1, table.move([0], 'a').length
+ end
+
+ def test_following_states_multi_regexp
+ table = tt [':a|b']
+ assert_equal 1, table.move([0], 'fooo').length
+ assert_equal 2, table.move([0], 'b').length
+ end
+
+ def test_multi_path
+ table = tt ['/:a/d', '/b/c']
+
+ [
+ [1, '/'],
+ [2, 'b'],
+ [2, '/'],
+ [1, 'c'],
+ ].inject([0]) { |state, (exp, sym)|
+ new = table.move(state, sym)
+ assert_equal exp, new.length
+ new
+ }
+ end
+
+ def test_match_data_ambiguous
+ table = tt %w{
+ /articles(.:format)
+ /articles/new(.:format)
+ /articles/:id/edit(.:format)
+ /articles/:id(.:format)
+ }
+
+ sim = NFA::Simulator.new table
+
+ match = sim.match '/articles/new'
+ assert_equal 2, match.memos.length
+ end
+
+ ##
+ # Identical Routes may have different restrictions.
+ def test_match_same_paths
+ table = tt %w{
+ /articles/new(.:format)
+ /articles/new(.:format)
+ }
+
+ sim = NFA::Simulator.new table
+
+ match = sim.match '/articles/new'
+ assert_equal 2, match.memos.length
+ end
+
+ private
+ def ast strings
+ parser = Journey::Parser.new
+ asts = strings.map { |string|
+ memo = Object.new
+ ast = parser.parse string
+ ast.each { |n| n.memo = memo }
+ ast
+ }
+ Nodes::Or.new asts
+ end
+
+ def tt strings
+ Builder.new(ast(strings)).transition_table
+ end
+ end
+ end
+ end
+end
diff --git a/actionpack/test/journey/gtg/transition_table_test.rb b/actionpack/test/journey/gtg/transition_table_test.rb
new file mode 100644
index 0000000000..6d81b72c41
--- /dev/null
+++ b/actionpack/test/journey/gtg/transition_table_test.rb
@@ -0,0 +1,115 @@
+require 'abstract_unit'
+require 'json'
+
+module ActionDispatch
+ module Journey
+ module GTG
+ class TestGeneralizedTable < MiniTest::Unit::TestCase
+ def test_to_json
+ table = tt %w{
+ /articles(.:format)
+ /articles/new(.:format)
+ /articles/:id/edit(.:format)
+ /articles/:id(.:format)
+ }
+
+ json = JSON.load table.to_json
+ assert json['regexp_states']
+ assert json['string_states']
+ assert json['accepting']
+ end
+
+ if system("dot -V 2>/dev/null")
+ def test_to_svg
+ table = tt %w{
+ /articles(.:format)
+ /articles/new(.:format)
+ /articles/:id/edit(.:format)
+ /articles/:id(.:format)
+ }
+ svg = table.to_svg
+ assert svg
+ refute_match(/DOCTYPE/, svg)
+ end
+ end
+
+ def test_simulate_gt
+ sim = simulator_for ['/foo', '/bar']
+ assert_match sim, '/foo'
+ end
+
+ def test_simulate_gt_regexp
+ sim = simulator_for [':foo']
+ assert_match sim, 'foo'
+ end
+
+ def test_simulate_gt_regexp_mix
+ sim = simulator_for ['/get', '/:method/foo']
+ assert_match sim, '/get'
+ assert_match sim, '/get/foo'
+ end
+
+ def test_simulate_optional
+ sim = simulator_for ['/foo(/bar)']
+ assert_match sim, '/foo'
+ assert_match sim, '/foo/bar'
+ refute_match sim, '/foo/'
+ end
+
+ def test_match_data
+ path_asts = asts %w{ /get /:method/foo }
+ paths = path_asts.dup
+
+ builder = GTG::Builder.new Nodes::Or.new path_asts
+ tt = builder.transition_table
+
+ sim = GTG::Simulator.new tt
+
+ match = sim.match '/get'
+ assert_equal [paths.first], match.memos
+
+ match = sim.match '/get/foo'
+ assert_equal [paths.last], match.memos
+ end
+
+ def test_match_data_ambiguous
+ path_asts = asts %w{
+ /articles(.:format)
+ /articles/new(.:format)
+ /articles/:id/edit(.:format)
+ /articles/:id(.:format)
+ }
+
+ paths = path_asts.dup
+ ast = Nodes::Or.new path_asts
+
+ builder = GTG::Builder.new ast
+ sim = GTG::Simulator.new builder.transition_table
+
+ match = sim.match '/articles/new'
+ assert_equal [paths[1], paths[3]], match.memos
+ end
+
+ private
+ def asts paths
+ parser = Journey::Parser.new
+ paths.map { |x|
+ ast = parser.parse x
+ ast.each { |n| n.memo = ast}
+ ast
+ }
+ end
+
+ def tt paths
+ x = asts paths
+ builder = GTG::Builder.new Nodes::Or.new x
+ builder.transition_table
+ end
+
+ def simulator_for paths
+ GTG::Simulator.new tt(paths)
+ end
+ end
+ end
+ end
+end
diff --git a/actionpack/test/journey/nfa/simulator_test.rb b/actionpack/test/journey/nfa/simulator_test.rb
new file mode 100644
index 0000000000..9f89329b57
--- /dev/null
+++ b/actionpack/test/journey/nfa/simulator_test.rb
@@ -0,0 +1,98 @@
+require 'abstract_unit'
+
+module ActionDispatch
+ module Journey
+ module NFA
+ class TestSimulator < MiniTest::Unit::TestCase
+ def test_simulate_simple
+ sim = simulator_for ['/foo']
+ assert_match sim, '/foo'
+ end
+
+ def test_simulate_simple_no_match
+ sim = simulator_for ['/foo']
+ refute_match sim, 'foo'
+ end
+
+ def test_simulate_simple_no_match_too_long
+ sim = simulator_for ['/foo']
+ refute_match sim, '/foo/bar'
+ end
+
+ def test_simulate_simple_no_match_wrong_string
+ sim = simulator_for ['/foo']
+ refute_match sim, '/bar'
+ end
+
+ def test_simulate_regex
+ sim = simulator_for ['/:foo/bar']
+ assert_match sim, '/bar/bar'
+ assert_match sim, '/foo/bar'
+ end
+
+ def test_simulate_or
+ sim = simulator_for ['/foo', '/bar']
+ assert_match sim, '/bar'
+ assert_match sim, '/foo'
+ refute_match sim, '/baz'
+ end
+
+ def test_simulate_optional
+ sim = simulator_for ['/foo(/bar)']
+ assert_match sim, '/foo'
+ assert_match sim, '/foo/bar'
+ refute_match sim, '/foo/'
+ end
+
+ def test_matchdata_has_memos
+ paths = %w{ /foo /bar }
+ parser = Journey::Parser.new
+ asts = paths.map { |x|
+ ast = parser.parse x
+ ast.each { |n| n.memo = ast}
+ ast
+ }
+
+ expected = asts.first
+
+ builder = Builder.new Nodes::Or.new asts
+
+ sim = Simulator.new builder.transition_table
+
+ md = sim.match '/foo'
+ assert_equal [expected], md.memos
+ end
+
+ def test_matchdata_memos_on_merge
+ parser = Journey::Parser.new
+ routes = [
+ '/articles(.:format)',
+ '/articles/new(.:format)',
+ '/articles/:id/edit(.:format)',
+ '/articles/:id(.:format)',
+ ].map { |path|
+ ast = parser.parse path
+ ast.each { |n| n.memo = ast }
+ ast
+ }
+
+ asts = routes.dup
+
+ ast = Nodes::Or.new routes
+
+ nfa = Journey::NFA::Builder.new ast
+ sim = Simulator.new nfa.transition_table
+ md = sim.match '/articles'
+ assert_equal [asts.first], md.memos
+ end
+
+ def simulator_for paths
+ parser = Journey::Parser.new
+ asts = paths.map { |x| parser.parse x }
+ builder = Builder.new Nodes::Or.new asts
+ Simulator.new builder.transition_table
+ end
+ end
+ end
+ end
+end
diff --git a/actionpack/test/journey/nfa/transition_table_test.rb b/actionpack/test/journey/nfa/transition_table_test.rb
new file mode 100644
index 0000000000..72cefe42bf
--- /dev/null
+++ b/actionpack/test/journey/nfa/transition_table_test.rb
@@ -0,0 +1,72 @@
+require 'abstract_unit'
+
+module ActionDispatch
+ module Journey
+ module NFA
+ class TestTransitionTable < MiniTest::Unit::TestCase
+ def setup
+ @parser = Journey::Parser.new
+ end
+
+ def test_eclosure
+ table = tt '/'
+ assert_equal [0], table.eclosure(0)
+
+ table = tt ':a|:b'
+ assert_equal 3, table.eclosure(0).length
+
+ table = tt '(:a|:b)'
+ assert_equal 5, table.eclosure(0).length
+ assert_equal 5, table.eclosure([0]).length
+ end
+
+ def test_following_states_one
+ table = tt '/'
+
+ assert_equal [1], table.following_states(0, '/')
+ assert_equal [1], table.following_states([0], '/')
+ end
+
+ def test_following_states_group
+ table = tt 'a|b'
+ states = table.eclosure 0
+
+ assert_equal 1, table.following_states(states, 'a').length
+ assert_equal 1, table.following_states(states, 'b').length
+ end
+
+ def test_following_states_multi
+ table = tt 'a|a'
+ states = table.eclosure 0
+
+ assert_equal 2, table.following_states(states, 'a').length
+ assert_equal 0, table.following_states(states, 'b').length
+ end
+
+ def test_following_states_regexp
+ table = tt 'a|:a'
+ states = table.eclosure 0
+
+ assert_equal 1, table.following_states(states, 'a').length
+ assert_equal 1, table.following_states(states, /[^\.\/\?]+/).length
+ assert_equal 0, table.following_states(states, 'b').length
+ end
+
+ def test_alphabet
+ table = tt 'a|:a'
+ assert_equal [/[^\.\/\?]+/, 'a'], table.alphabet
+
+ table = tt 'a|a'
+ assert_equal ['a'], table.alphabet
+ end
+
+ private
+ def tt string
+ ast = @parser.parse string
+ builder = Builder.new ast
+ builder.transition_table
+ end
+ end
+ end
+ end
+end
diff --git a/actionpack/test/journey/nodes/symbol_test.rb b/actionpack/test/journey/nodes/symbol_test.rb
new file mode 100644
index 0000000000..f53840274a
--- /dev/null
+++ b/actionpack/test/journey/nodes/symbol_test.rb
@@ -0,0 +1,17 @@
+require 'abstract_unit'
+
+module ActionDispatch
+ module Journey
+ module Nodes
+ class TestSymbol < MiniTest::Unit::TestCase
+ def test_default_regexp?
+ sym = Symbol.new nil
+ assert sym.default_regexp?
+
+ sym.regexp = nil
+ refute sym.default_regexp?
+ end
+ end
+ end
+ end
+end
diff --git a/actionpack/test/journey/path/pattern_test.rb b/actionpack/test/journey/path/pattern_test.rb
new file mode 100644
index 0000000000..0f2d0d44c0
--- /dev/null
+++ b/actionpack/test/journey/path/pattern_test.rb
@@ -0,0 +1,284 @@
+require 'abstract_unit'
+
+module ActionDispatch
+ module Journey
+ module Path
+ class TestPattern < MiniTest::Unit::TestCase
+ x = /.+/
+ {
+ '/:controller(/:action)' => %r{\A/(#{x})(?:/([^/.?]+))?\Z},
+ '/:controller/foo' => %r{\A/(#{x})/foo\Z},
+ '/:controller/:action' => %r{\A/(#{x})/([^/.?]+)\Z},
+ '/:controller' => %r{\A/(#{x})\Z},
+ '/:controller(/:action(/:id))' => %r{\A/(#{x})(?:/([^/.?]+)(?:/([^/.?]+))?)?\Z},
+ '/:controller/:action.xml' => %r{\A/(#{x})/([^/.?]+)\.xml\Z},
+ '/:controller.:format' => %r{\A/(#{x})\.([^/.?]+)\Z},
+ '/:controller(.:format)' => %r{\A/(#{x})(?:\.([^/.?]+))?\Z},
+ '/:controller/*foo' => %r{\A/(#{x})/(.+)\Z},
+ '/:controller/*foo/bar' => %r{\A/(#{x})/(.+)/bar\Z},
+ }.each do |path, expected|
+ define_method(:"test_to_regexp_#{path}") do
+ strexp = Router::Strexp.new(
+ path,
+ { :controller => /.+/ },
+ ["/", ".", "?"]
+ )
+ path = Pattern.new strexp
+ assert_equal(expected, path.to_regexp)
+ end
+ end
+
+ {
+ '/:controller(/:action)' => %r{\A/(#{x})(?:/([^/.?]+))?},
+ '/:controller/foo' => %r{\A/(#{x})/foo},
+ '/:controller/:action' => %r{\A/(#{x})/([^/.?]+)},
+ '/:controller' => %r{\A/(#{x})},
+ '/:controller(/:action(/:id))' => %r{\A/(#{x})(?:/([^/.?]+)(?:/([^/.?]+))?)?},
+ '/:controller/:action.xml' => %r{\A/(#{x})/([^/.?]+)\.xml},
+ '/:controller.:format' => %r{\A/(#{x})\.([^/.?]+)},
+ '/:controller(.:format)' => %r{\A/(#{x})(?:\.([^/.?]+))?},
+ '/:controller/*foo' => %r{\A/(#{x})/(.+)},
+ '/:controller/*foo/bar' => %r{\A/(#{x})/(.+)/bar},
+ }.each do |path, expected|
+ define_method(:"test_to_non_anchored_regexp_#{path}") do
+ strexp = Router::Strexp.new(
+ path,
+ { :controller => /.+/ },
+ ["/", ".", "?"],
+ false
+ )
+ path = Pattern.new strexp
+ assert_equal(expected, path.to_regexp)
+ end
+ end
+
+ {
+ '/:controller(/:action)' => %w{ controller action },
+ '/:controller/foo' => %w{ controller },
+ '/:controller/:action' => %w{ controller action },
+ '/:controller' => %w{ controller },
+ '/:controller(/:action(/:id))' => %w{ controller action id },
+ '/:controller/:action.xml' => %w{ controller action },
+ '/:controller.:format' => %w{ controller format },
+ '/:controller(.:format)' => %w{ controller format },
+ '/:controller/*foo' => %w{ controller foo },
+ '/:controller/*foo/bar' => %w{ controller foo },
+ }.each do |path, expected|
+ define_method(:"test_names_#{path}") do
+ strexp = Router::Strexp.new(
+ path,
+ { :controller => /.+/ },
+ ["/", ".", "?"]
+ )
+ path = Pattern.new strexp
+ assert_equal(expected, path.names)
+ end
+ end
+
+ def test_to_regexp_with_extended_group
+ strexp = Router::Strexp.new(
+ '/page/:name',
+ { :name => /
+ #ROFL
+ (tender|love
+ #MAO
+ )/x },
+ ["/", ".", "?"]
+ )
+ path = Pattern.new strexp
+ assert_match(path, '/page/tender')
+ assert_match(path, '/page/love')
+ refute_match(path, '/page/loving')
+ end
+
+ def test_optional_names
+ [
+ ['/:foo(/:bar(/:baz))', %w{ bar baz }],
+ ['/:foo(/:bar)', %w{ bar }],
+ ['/:foo(/:bar)/:lol(/:baz)', %w{ bar baz }],
+ ].each do |pattern, list|
+ path = Pattern.new pattern
+ assert_equal list.sort, path.optional_names.sort
+ end
+ end
+
+ def test_to_regexp_match_non_optional
+ strexp = Router::Strexp.new(
+ '/:name',
+ { :name => /\d+/ },
+ ["/", ".", "?"]
+ )
+ path = Pattern.new strexp
+ assert_match(path, '/123')
+ refute_match(path, '/')
+ end
+
+ def test_to_regexp_with_group
+ strexp = Router::Strexp.new(
+ '/page/:name',
+ { :name => /(tender|love)/ },
+ ["/", ".", "?"]
+ )
+ path = Pattern.new strexp
+ assert_match(path, '/page/tender')
+ assert_match(path, '/page/love')
+ refute_match(path, '/page/loving')
+ end
+
+ def test_ast_sets_regular_expressions
+ requirements = { :name => /(tender|love)/, :value => /./ }
+ strexp = Router::Strexp.new(
+ '/page/:name/:value',
+ requirements,
+ ["/", ".", "?"]
+ )
+
+ assert_equal requirements, strexp.requirements
+
+ path = Pattern.new strexp
+ nodes = path.ast.grep(Nodes::Symbol)
+ assert_equal 2, nodes.length
+ nodes.each do |node|
+ assert_equal requirements[node.to_sym], node.regexp
+ end
+ end
+
+ def test_match_data_with_group
+ strexp = Router::Strexp.new(
+ '/page/:name',
+ { :name => /(tender|love)/ },
+ ["/", ".", "?"]
+ )
+ path = Pattern.new strexp
+ match = path.match '/page/tender'
+ assert_equal 'tender', match[1]
+ assert_equal 2, match.length
+ end
+
+ def test_match_data_with_multi_group
+ strexp = Router::Strexp.new(
+ '/page/:name/:id',
+ { :name => /t(((ender|love)))()/ },
+ ["/", ".", "?"]
+ )
+ path = Pattern.new strexp
+ match = path.match '/page/tender/10'
+ assert_equal 'tender', match[1]
+ assert_equal '10', match[2]
+ assert_equal 3, match.length
+ assert_equal %w{ tender 10 }, match.captures
+ end
+
+ def test_star_with_custom_re
+ z = /\d+/
+ strexp = Router::Strexp.new(
+ '/page/*foo',
+ { :foo => z },
+ ["/", ".", "?"]
+ )
+ path = Pattern.new strexp
+ assert_equal(%r{\A/page/(#{z})\Z}, path.to_regexp)
+ end
+
+ def test_insensitive_regexp_with_group
+ strexp = Router::Strexp.new(
+ '/page/:name/aaron',
+ { :name => /(tender|love)/i },
+ ["/", ".", "?"]
+ )
+ path = Pattern.new strexp
+ assert_match(path, '/page/TENDER/aaron')
+ assert_match(path, '/page/loVE/aaron')
+ refute_match(path, '/page/loVE/AAron')
+ end
+
+ def test_to_regexp_with_strexp
+ strexp = Router::Strexp.new('/:controller', { }, ["/", ".", "?"])
+ path = Pattern.new strexp
+ x = %r{\A/([^/.?]+)\Z}
+
+ assert_equal(x.source, path.source)
+ end
+
+ def test_to_regexp_defaults
+ path = Pattern.new '/:controller(/:action(/:id))'
+ expected = %r{\A/([^/.?]+)(?:/([^/.?]+)(?:/([^/.?]+))?)?\Z}
+ assert_equal expected, path.to_regexp
+ end
+
+ def test_failed_match
+ path = Pattern.new '/:controller(/:action(/:id(.:format)))'
+ uri = 'content'
+
+ refute path =~ uri
+ end
+
+ def test_match_controller
+ path = Pattern.new '/:controller(/:action(/:id(.:format)))'
+ uri = '/content'
+
+ match = path =~ uri
+ assert_equal %w{ controller action id format }, match.names
+ assert_equal 'content', match[1]
+ assert_nil match[2]
+ assert_nil match[3]
+ assert_nil match[4]
+ end
+
+ def test_match_controller_action
+ path = Pattern.new '/:controller(/:action(/:id(.:format)))'
+ uri = '/content/list'
+
+ match = path =~ uri
+ assert_equal %w{ controller action id format }, match.names
+ assert_equal 'content', match[1]
+ assert_equal 'list', match[2]
+ assert_nil match[3]
+ assert_nil match[4]
+ end
+
+ def test_match_controller_action_id
+ path = Pattern.new '/:controller(/:action(/:id(.:format)))'
+ uri = '/content/list/10'
+
+ match = path =~ uri
+ assert_equal %w{ controller action id format }, match.names
+ assert_equal 'content', match[1]
+ assert_equal 'list', match[2]
+ assert_equal '10', match[3]
+ assert_nil match[4]
+ end
+
+ def test_match_literal
+ path = Path::Pattern.new "/books(/:action(.:format))"
+
+ uri = '/books'
+ match = path =~ uri
+ assert_equal %w{ action format }, match.names
+ assert_nil match[1]
+ assert_nil match[2]
+ end
+
+ def test_match_literal_with_action
+ path = Path::Pattern.new "/books(/:action(.:format))"
+
+ uri = '/books/list'
+ match = path =~ uri
+ assert_equal %w{ action format }, match.names
+ assert_equal 'list', match[1]
+ assert_nil match[2]
+ end
+
+ def test_match_literal_with_action_and_format
+ path = Path::Pattern.new "/books(/:action(.:format))"
+
+ uri = '/books/list.rss'
+ match = path =~ uri
+ assert_equal %w{ action format }, match.names
+ assert_equal 'list', match[1]
+ assert_equal 'rss', match[2]
+ end
+ end
+ end
+ end
+end
diff --git a/actionpack/test/journey/route/definition/parser_test.rb b/actionpack/test/journey/route/definition/parser_test.rb
new file mode 100644
index 0000000000..580235c6a1
--- /dev/null
+++ b/actionpack/test/journey/route/definition/parser_test.rb
@@ -0,0 +1,110 @@
+require 'abstract_unit'
+
+module ActionDispatch
+ module Journey
+ module Definition
+ class TestParser < MiniTest::Unit::TestCase
+ def setup
+ @parser = Parser.new
+ end
+
+ def test_slash
+ assert_equal :SLASH, @parser.parse('/').type
+ assert_round_trip '/'
+ end
+
+ def test_segment
+ assert_round_trip '/foo'
+ end
+
+ def test_segments
+ assert_round_trip '/foo/bar'
+ end
+
+ def test_segment_symbol
+ assert_round_trip '/foo/:id'
+ end
+
+ def test_symbol
+ assert_round_trip '/:foo'
+ end
+
+ def test_group
+ assert_round_trip '(/:foo)'
+ end
+
+ def test_groups
+ assert_round_trip '(/:foo)(/:bar)'
+ end
+
+ def test_nested_groups
+ assert_round_trip '(/:foo(/:bar))'
+ end
+
+ def test_dot_symbol
+ assert_round_trip('.:format')
+ end
+
+ def test_dot_literal
+ assert_round_trip('.xml')
+ end
+
+ def test_segment_dot
+ assert_round_trip('/foo.:bar')
+ end
+
+ def test_segment_group_dot
+ assert_round_trip('/foo(.:bar)')
+ end
+
+ def test_segment_group
+ assert_round_trip('/foo(/:action)')
+ end
+
+ def test_segment_groups
+ assert_round_trip('/foo(/:action)(/:bar)')
+ end
+
+ def test_segment_nested_groups
+ assert_round_trip('/foo(/:action(/:bar))')
+ end
+
+ def test_group_followed_by_path
+ assert_round_trip('/foo(/:action)/:bar')
+ end
+
+ def test_star
+ assert_round_trip('*foo')
+ assert_round_trip('/*foo')
+ assert_round_trip('/bar/*foo')
+ assert_round_trip('/bar/(*foo)')
+ end
+
+ def test_or
+ assert_round_trip('a|b')
+ assert_round_trip('a|b|c')
+ assert_round_trip('(a|b)|c')
+ assert_round_trip('a|(b|c)')
+ assert_round_trip('*a|(b|c)')
+ assert_round_trip('*a|:b|c')
+ end
+
+ def test_arbitrary
+ assert_round_trip('/bar/*foo#')
+ end
+
+ def test_literal_dot_paren
+ assert_round_trip "/sprockets.js(.:format)"
+ end
+
+ def test_groups_with_dot
+ assert_round_trip "/(:locale)(.:format)"
+ end
+
+ def assert_round_trip str
+ assert_equal str, @parser.parse(str).to_s
+ end
+ end
+ end
+ end
+end
diff --git a/actionpack/test/journey/route/definition/scanner_test.rb b/actionpack/test/journey/route/definition/scanner_test.rb
new file mode 100644
index 0000000000..110baf9977
--- /dev/null
+++ b/actionpack/test/journey/route/definition/scanner_test.rb
@@ -0,0 +1,56 @@
+require 'abstract_unit'
+
+module ActionDispatch
+ module Journey
+ module Definition
+ class TestScanner < MiniTest::Unit::TestCase
+ def setup
+ @scanner = Scanner.new
+ end
+
+ # /page/:id(/:action)(.:format)
+ def test_tokens
+ [
+ ['/', [[:SLASH, '/']]],
+ ['*omg', [[:STAR, '*omg']]],
+ ['/page', [[:SLASH, '/'], [:LITERAL, 'page']]],
+ ['/~page', [[:SLASH, '/'], [:LITERAL, '~page']]],
+ ['/pa-ge', [[:SLASH, '/'], [:LITERAL, 'pa-ge']]],
+ ['/:page', [[:SLASH, '/'], [:SYMBOL, ':page']]],
+ ['/(:page)', [
+ [:SLASH, '/'],
+ [:LPAREN, '('],
+ [:SYMBOL, ':page'],
+ [:RPAREN, ')'],
+ ]],
+ ['(/:action)', [
+ [:LPAREN, '('],
+ [:SLASH, '/'],
+ [:SYMBOL, ':action'],
+ [:RPAREN, ')'],
+ ]],
+ ['(())', [[:LPAREN, '('],
+ [:LPAREN, '('], [:RPAREN, ')'], [:RPAREN, ')']]],
+ ['(.:format)', [
+ [:LPAREN, '('],
+ [:DOT, '.'],
+ [:SYMBOL, ':format'],
+ [:RPAREN, ')'],
+ ]],
+ ].each do |str, expected|
+ @scanner.scan_setup str
+ assert_tokens expected, @scanner
+ end
+ end
+
+ def assert_tokens tokens, scanner
+ toks = []
+ while tok = scanner.next_token
+ toks << tok
+ end
+ assert_equal tokens, toks
+ end
+ end
+ end
+ end
+end
diff --git a/actionpack/test/journey/route_test.rb b/actionpack/test/journey/route_test.rb
new file mode 100644
index 0000000000..b205db5fbc
--- /dev/null
+++ b/actionpack/test/journey/route_test.rb
@@ -0,0 +1,103 @@
+require 'abstract_unit'
+
+module ActionDispatch
+ module Journey
+ class TestRoute < MiniTest::Unit::TestCase
+ def test_initialize
+ app = Object.new
+ path = Path::Pattern.new '/:controller(/:action(/:id(.:format)))'
+ defaults = Object.new
+ route = Route.new("name", app, path, {}, defaults)
+
+ assert_equal app, route.app
+ assert_equal path, route.path
+ assert_equal defaults, route.defaults
+ end
+
+ def test_route_adds_itself_as_memo
+ app = Object.new
+ path = Path::Pattern.new '/:controller(/:action(/:id(.:format)))'
+ defaults = Object.new
+ route = Route.new("name", app, path, {}, defaults)
+
+ route.ast.grep(Nodes::Terminal).each do |node|
+ assert_equal route, node.memo
+ end
+ end
+
+ def test_ip_address
+ path = Path::Pattern.new '/messages/:id(.:format)'
+ route = Route.new("name", nil, path, {:ip => '192.168.1.1'},
+ { :controller => 'foo', :action => 'bar' })
+ assert_equal '192.168.1.1', route.ip
+ end
+
+ def test_default_ip
+ path = Path::Pattern.new '/messages/:id(.:format)'
+ route = Route.new("name", nil, path, {},
+ { :controller => 'foo', :action => 'bar' })
+ assert_equal(//, route.ip)
+ end
+
+ def test_format_with_star
+ path = Path::Pattern.new '/:controller/*extra'
+ route = Route.new("name", nil, path, {},
+ { :controller => 'foo', :action => 'bar' })
+ assert_equal '/foo/himom', route.format({
+ :controller => 'foo',
+ :extra => 'himom',
+ })
+ end
+
+ def test_connects_all_match
+ path = Path::Pattern.new '/:controller(/:action(/:id(.:format)))'
+ route = Route.new("name", nil, path, {:action => 'bar'}, { :controller => 'foo' })
+
+ assert_equal '/foo/bar/10', route.format({
+ :controller => 'foo',
+ :action => 'bar',
+ :id => 10
+ })
+ end
+
+ def test_extras_are_not_included_if_optional
+ path = Path::Pattern.new '/page/:id(/:action)'
+ route = Route.new("name", nil, path, { }, { :action => 'show' })
+
+ assert_equal '/page/10', route.format({ :id => 10 })
+ end
+
+ def test_extras_are_not_included_if_optional_with_parameter
+ path = Path::Pattern.new '(/sections/:section)/pages/:id'
+ route = Route.new("name", nil, path, { }, { :action => 'show' })
+
+ assert_equal '/pages/10', route.format({:id => 10})
+ end
+
+ def test_extras_are_not_included_if_optional_parameter_is_nil
+ path = Path::Pattern.new '(/sections/:section)/pages/:id'
+ route = Route.new("name", nil, path, { }, { :action => 'show' })
+
+ assert_equal '/pages/10', route.format({:id => 10, :section => nil})
+ end
+
+ def test_score
+ path = Path::Pattern.new "/page/:id(/:action)(.:format)"
+ specific = Route.new "name", nil, path, {}, {:controller=>"pages", :action=>"show"}
+
+ path = Path::Pattern.new "/:controller(/:action(/:id))(.:format)"
+ generic = Route.new "name", nil, path, {}
+
+ knowledge = {:id=>20, :controller=>"pages", :action=>"show"}
+
+ routes = [specific, generic]
+
+ refute_equal specific.score(knowledge), generic.score(knowledge)
+
+ found = routes.sort_by { |r| r.score(knowledge) }.last
+
+ assert_equal specific, found
+ end
+ end
+ end
+end
diff --git a/actionpack/test/journey/router/strexp_test.rb b/actionpack/test/journey/router/strexp_test.rb
new file mode 100644
index 0000000000..9e0337f144
--- /dev/null
+++ b/actionpack/test/journey/router/strexp_test.rb
@@ -0,0 +1,32 @@
+require 'abstract_unit'
+
+module ActionDispatch
+ module Journey
+ class Router
+ class TestStrexp < MiniTest::Unit::TestCase
+ def test_many_names
+ exp = Strexp.new(
+ "/:controller(/:action(/:id(.:format)))",
+ {:controller=>/.+?/},
+ ["/", ".", "?"],
+ true)
+
+ assert_equal ["controller", "action", "id", "format"], exp.names
+ end
+
+ def test_names
+ {
+ "/bar(.:format)" => %w{ format },
+ ":format" => %w{ format },
+ ":format-" => %w{ format },
+ ":format0" => %w{ format0 },
+ ":format1,:format2" => %w{ format1 format2 },
+ }.each do |string, expected|
+ exp = Strexp.new(string, {}, ["/", ".", "?"])
+ assert_equal expected, exp.names
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/actionpack/test/journey/router/utils_test.rb b/actionpack/test/journey/router/utils_test.rb
new file mode 100644
index 0000000000..97a6449c99
--- /dev/null
+++ b/actionpack/test/journey/router/utils_test.rb
@@ -0,0 +1,21 @@
+require 'abstract_unit'
+
+module ActionDispatch
+ module Journey
+ class Router
+ class TestUtils < MiniTest::Unit::TestCase
+ def test_path_escape
+ assert_equal "a/b%20c+d", Utils.escape_path("a/b c+d")
+ end
+
+ def test_fragment_escape
+ assert_equal "a/b%20c+d?e", Utils.escape_fragment("a/b c+d?e")
+ end
+
+ def test_uri_unescape
+ assert_equal "a/b c+d", Utils.unescape_uri("a%2Fb%20c+d")
+ end
+ end
+ end
+ end
+end
diff --git a/actionpack/test/journey/router_test.rb b/actionpack/test/journey/router_test.rb
new file mode 100644
index 0000000000..1b64600ba8
--- /dev/null
+++ b/actionpack/test/journey/router_test.rb
@@ -0,0 +1,575 @@
+# encoding: UTF-8
+require 'abstract_unit'
+
+module ActionDispatch
+ module Journey
+ class TestRouter < MiniTest::Unit::TestCase
+ attr_reader :routes
+
+ def setup
+ @routes = Routes.new
+ @router = Router.new(@routes, {})
+ @formatter = Formatter.new(@routes)
+ end
+
+ def test_request_class_reader
+ klass = Object.new
+ router = Router.new(routes, :request_class => klass)
+ assert_equal klass, router.request_class
+ end
+
+ class FakeRequestFeeler < Struct.new(:env, :called)
+ def new env
+ self.env = env
+ self
+ end
+
+ def hello
+ self.called = true
+ 'world'
+ end
+
+ def path_info; env['PATH_INFO']; end
+ def request_method; env['REQUEST_METHOD']; end
+ def ip; env['REMOTE_ADDR']; end
+ end
+
+ def test_dashes
+ router = Router.new(routes, {})
+
+ exp = Router::Strexp.new '/foo-bar-baz', {}, ['/.?']
+ path = Path::Pattern.new exp
+
+ routes.add_route nil, path, {}, {:id => nil}, {}
+
+ env = rails_env 'PATH_INFO' => '/foo-bar-baz'
+ called = false
+ router.recognize(env) do |r, _, params|
+ called = true
+ end
+ assert called
+ end
+
+ def test_unicode
+ router = Router.new(routes, {})
+
+ #match the escaped version of /ほげ
+ exp = Router::Strexp.new '/%E3%81%BB%E3%81%92', {}, ['/.?']
+ path = Path::Pattern.new exp
+
+ routes.add_route nil, path, {}, {:id => nil}, {}
+
+ env = rails_env 'PATH_INFO' => '/%E3%81%BB%E3%81%92'
+ called = false
+ router.recognize(env) do |r, _, params|
+ called = true
+ end
+ assert called
+ end
+
+ def test_request_class_and_requirements_success
+ klass = FakeRequestFeeler.new nil
+ router = Router.new(routes, {:request_class => klass })
+
+ requirements = { :hello => /world/ }
+
+ exp = Router::Strexp.new '/foo(/:id)', {}, ['/.?']
+ path = Path::Pattern.new exp
+
+ routes.add_route nil, path, requirements, {:id => nil}, {}
+
+ env = rails_env 'PATH_INFO' => '/foo/10'
+ router.recognize(env) do |r, _, params|
+ assert_equal({:id => '10'}, params)
+ end
+
+ assert klass.called, 'hello should have been called'
+ assert_equal env.env, klass.env
+ end
+
+ def test_request_class_and_requirements_fail
+ klass = FakeRequestFeeler.new nil
+ router = Router.new(routes, {:request_class => klass })
+
+ requirements = { :hello => /mom/ }
+
+ exp = Router::Strexp.new '/foo(/:id)', {}, ['/.?']
+ path = Path::Pattern.new exp
+
+ router.routes.add_route nil, path, requirements, {:id => nil}, {}
+
+ env = rails_env 'PATH_INFO' => '/foo/10'
+ router.recognize(env) do |r, _, params|
+ flunk 'route should not be found'
+ end
+
+ assert klass.called, 'hello should have been called'
+ assert_equal env.env, klass.env
+ end
+
+ class CustomPathRequest < Router::NullReq
+ def path_info
+ env['custom.path_info']
+ end
+ end
+
+ def test_request_class_overrides_path_info
+ router = Router.new(routes, {:request_class => CustomPathRequest })
+
+ exp = Router::Strexp.new '/bar', {}, ['/.?']
+ path = Path::Pattern.new exp
+
+ routes.add_route nil, path, {}, {}, {}
+
+ env = rails_env 'PATH_INFO' => '/foo', 'custom.path_info' => '/bar'
+
+ recognized = false
+ router.recognize(env) do |r, _, params|
+ recognized = true
+ end
+
+ assert recognized, "route should have been recognized"
+ end
+
+ def test_regexp_first_precedence
+ add_routes @router, [
+ Router::Strexp.new("/whois/:domain", {:domain => /\w+\.[\w\.]+/}, ['/', '.', '?']),
+ Router::Strexp.new("/whois/:id(.:format)", {}, ['/', '.', '?'])
+ ]
+
+ env = rails_env 'PATH_INFO' => '/whois/example.com'
+
+ list = []
+ @router.recognize(env) do |r, _, params|
+ list << r
+ end
+ assert_equal 2, list.length
+
+ r = list.first
+
+ assert_equal '/whois/:domain', r.path.spec.to_s
+ end
+
+ def test_required_parts_verified_are_anchored
+ add_routes @router, [
+ Router::Strexp.new("/foo/:id", { :id => /\d/ }, ['/', '.', '?'], false)
+ ]
+
+ assert_raises(Router::RoutingError) do
+ @formatter.generate(:path_info, nil, { :id => '10' }, { })
+ end
+ end
+
+ def test_required_parts_are_verified_when_building
+ add_routes @router, [
+ Router::Strexp.new("/foo/:id", { :id => /\d+/ }, ['/', '.', '?'], false)
+ ]
+
+ path, _ = @formatter.generate(:path_info, nil, { :id => '10' }, { })
+ assert_equal '/foo/10', path
+
+ assert_raises(Router::RoutingError) do
+ @formatter.generate(:path_info, nil, { :id => 'aa' }, { })
+ end
+ end
+
+ def test_only_required_parts_are_verified
+ add_routes @router, [
+ Router::Strexp.new("/foo(/:id)", {:id => /\d/}, ['/', '.', '?'], false)
+ ]
+
+ path, _ = @formatter.generate(:path_info, nil, { :id => '10' }, { })
+ assert_equal '/foo/10', path
+
+ path, _ = @formatter.generate(:path_info, nil, { }, { })
+ assert_equal '/foo', path
+
+ path, _ = @formatter.generate(:path_info, nil, { :id => 'aa' }, { })
+ assert_equal '/foo/aa', path
+ end
+
+ def test_knows_what_parts_are_missing_from_named_route
+ route_name = "gorby_thunderhorse"
+ pattern = Router::Strexp.new("/foo/:id", { :id => /\d+/ }, ['/', '.', '?'], false)
+ path = Path::Pattern.new pattern
+ @router.routes.add_route nil, path, {}, {}, route_name
+
+ error = assert_raises(Router::RoutingError) do
+ @formatter.generate(:path_info, route_name, { }, { })
+ end
+
+ assert_match(/required keys: \[:id\]/, error.message)
+ end
+
+ def test_X_Cascade
+ add_routes @router, [ "/messages(.:format)" ]
+ resp = @router.call({ 'REQUEST_METHOD' => 'GET', 'PATH_INFO' => '/lol' })
+ assert_equal ['Not Found'], resp.last
+ assert_equal 'pass', resp[1]['X-Cascade']
+ assert_equal 404, resp.first
+ end
+
+ def test_clear_trailing_slash_from_script_name_on_root_unanchored_routes
+ strexp = Router::Strexp.new("/", {}, ['/', '.', '?'], false)
+ path = Path::Pattern.new strexp
+ app = lambda { |env| [200, {}, ['success!']] }
+ @router.routes.add_route(app, path, {}, {}, {})
+
+ env = rack_env('SCRIPT_NAME' => '', 'PATH_INFO' => '/weblog')
+ resp = @router.call(env)
+ assert_equal ['success!'], resp.last
+ assert_equal '', env['SCRIPT_NAME']
+ end
+
+ def test_defaults_merge_correctly
+ path = Path::Pattern.new '/foo(/:id)'
+ @router.routes.add_route nil, path, {}, {:id => nil}, {}
+
+ env = rails_env 'PATH_INFO' => '/foo/10'
+ @router.recognize(env) do |r, _, params|
+ assert_equal({:id => '10'}, params)
+ end
+
+ env = rails_env 'PATH_INFO' => '/foo'
+ @router.recognize(env) do |r, _, params|
+ assert_equal({:id => nil}, params)
+ end
+ end
+
+ def test_recognize_with_unbound_regexp
+ add_routes @router, [
+ Router::Strexp.new("/foo", { }, ['/', '.', '?'], false)
+ ]
+
+ env = rails_env 'PATH_INFO' => '/foo/bar'
+
+ @router.recognize(env) { |*_| }
+
+ assert_equal '/foo', env.env['SCRIPT_NAME']
+ assert_equal '/bar', env.env['PATH_INFO']
+ end
+
+ def test_bound_regexp_keeps_path_info
+ add_routes @router, [
+ Router::Strexp.new("/foo", { }, ['/', '.', '?'], true)
+ ]
+
+ env = rails_env 'PATH_INFO' => '/foo'
+
+ before = env.env['SCRIPT_NAME']
+
+ @router.recognize(env) { |*_| }
+
+ assert_equal before, env.env['SCRIPT_NAME']
+ assert_equal '/foo', env.env['PATH_INFO']
+ end
+
+ def test_path_not_found
+ add_routes @router, [
+ "/messages(.:format)",
+ "/messages/new(.:format)",
+ "/messages/:id/edit(.:format)",
+ "/messages/:id(.:format)"
+ ]
+ env = rails_env 'PATH_INFO' => '/messages/unknown/path'
+ yielded = false
+
+ @router.recognize(env) do |*whatever|
+ yielded = true
+ end
+ refute yielded
+ end
+
+ def test_required_part_in_recall
+ add_routes @router, [ "/messages/:a/:b" ]
+
+ path, _ = @formatter.generate(:path_info, nil, { :a => 'a' }, { :b => 'b' })
+ assert_equal "/messages/a/b", path
+ end
+
+ def test_splat_in_recall
+ add_routes @router, [ "/*path" ]
+
+ path, _ = @formatter.generate(:path_info, nil, { }, { :path => 'b' })
+ assert_equal "/b", path
+ end
+
+ def test_recall_should_be_used_when_scoring
+ add_routes @router, [
+ "/messages/:action(/:id(.:format))",
+ "/messages/:id(.:format)"
+ ]
+
+ path, _ = @formatter.generate(:path_info, nil, { :id => 10 }, { :action => 'index' })
+ assert_equal "/messages/index/10", path
+ end
+
+ def test_nil_path_parts_are_ignored
+ path = Path::Pattern.new "/:controller(/:action(.:format))"
+ @router.routes.add_route nil, path, {}, {}, {}
+
+ params = { :controller => "tasks", :format => nil }
+ extras = { :action => 'lol' }
+
+ path, _ = @formatter.generate(:path_info, nil, params, extras)
+ assert_equal '/tasks', path
+ end
+
+ def test_generate_slash
+ params = [ [:controller, "tasks"],
+ [:action, "show"] ]
+ str = Router::Strexp.new("/", Hash[params], ['/', '.', '?'], true)
+ path = Path::Pattern.new str
+
+ @router.routes.add_route nil, path, {}, {}, {}
+
+ path, _ = @formatter.generate(:path_info, nil, Hash[params], {})
+ assert_equal '/', path
+ end
+
+ def test_generate_calls_param_proc
+ path = Path::Pattern.new '/:controller(/:action)'
+ @router.routes.add_route nil, path, {}, {}, {}
+
+ parameterized = []
+ params = [ [:controller, "tasks"],
+ [:action, "show"] ]
+
+ @formatter.generate(
+ :path_info,
+ nil,
+ Hash[params],
+ {},
+ lambda { |k,v| parameterized << [k,v]; v })
+
+ assert_equal params.map(&:to_s).sort, parameterized.map(&:to_s).sort
+ end
+
+ def test_generate_id
+ path = Path::Pattern.new '/:controller(/:action)'
+ @router.routes.add_route nil, path, {}, {}, {}
+
+ path, params = @formatter.generate(
+ :path_info, nil, {:id=>1, :controller=>"tasks", :action=>"show"}, {})
+ assert_equal '/tasks/show', path
+ assert_equal({:id => 1}, params)
+ end
+
+ def test_generate_escapes
+ path = Path::Pattern.new '/:controller(/:action)'
+ @router.routes.add_route nil, path, {}, {}, {}
+
+ path, _ = @formatter.generate(:path_info,
+ nil, { :controller => "tasks",
+ :action => "a/b c+d",
+ }, {})
+ assert_equal '/tasks/a/b%20c+d', path
+ end
+
+ def test_generate_extra_params
+ path = Path::Pattern.new '/:controller(/:action)'
+ @router.routes.add_route nil, path, {}, {}, {}
+
+ path, params = @formatter.generate(:path_info,
+ nil, { :id => 1,
+ :controller => "tasks",
+ :action => "show",
+ :relative_url_root => nil
+ }, {})
+ assert_equal '/tasks/show', path
+ assert_equal({:id => 1, :relative_url_root => nil}, params)
+ end
+
+ def test_generate_uses_recall_if_needed
+ path = Path::Pattern.new '/:controller(/:action(/:id))'
+ @router.routes.add_route nil, path, {}, {}, {}
+
+ path, params = @formatter.generate(:path_info,
+ nil,
+ {:controller =>"tasks", :id => 10},
+ {:action =>"index"})
+ assert_equal '/tasks/index/10', path
+ assert_equal({}, params)
+ end
+
+ def test_generate_with_name
+ path = Path::Pattern.new '/:controller(/:action)'
+ @router.routes.add_route nil, path, {}, {}, {}
+
+ path, params = @formatter.generate(:path_info,
+ "tasks",
+ {:controller=>"tasks"},
+ {:controller=>"tasks", :action=>"index"})
+ assert_equal '/tasks', path
+ assert_equal({}, params)
+ end
+
+ {
+ '/content' => { :controller => 'content' },
+ '/content/list' => { :controller => 'content', :action => 'list' },
+ '/content/show/10' => { :controller => 'content', :action => 'show', :id => "10" },
+ }.each do |request_path, expected|
+ define_method("test_recognize_#{expected.keys.map(&:to_s).join('_')}") do
+ path = Path::Pattern.new "/:controller(/:action(/:id))"
+ app = Object.new
+ route = @router.routes.add_route(app, path, {}, {}, {})
+
+ env = rails_env 'PATH_INFO' => request_path
+ called = false
+
+ @router.recognize(env) do |r, _, params|
+ assert_equal route, r
+ assert_equal(expected, params)
+ called = true
+ end
+
+ assert called
+ end
+ end
+
+ {
+ :segment => ['/a%2Fb%20c+d/splat', { :segment => 'a/b c+d', :splat => 'splat' }],
+ :splat => ['/segment/a/b%20c+d', { :segment => 'segment', :splat => 'a/b c+d' }]
+ }.each do |name, (request_path, expected)|
+ define_method("test_recognize_#{name}") do
+ path = Path::Pattern.new '/:segment/*splat'
+ app = Object.new
+ route = @router.routes.add_route(app, path, {}, {}, {})
+
+ env = rails_env 'PATH_INFO' => request_path
+ called = false
+
+ @router.recognize(env) do |r, _, params|
+ assert_equal route, r
+ assert_equal(expected, params)
+ called = true
+ end
+
+ assert called
+ end
+ end
+
+ def test_namespaced_controller
+ strexp = Router::Strexp.new(
+ "/:controller(/:action(/:id))",
+ { :controller => /.+?/ },
+ ["/", ".", "?"]
+ )
+ path = Path::Pattern.new strexp
+ app = Object.new
+ route = @router.routes.add_route(app, path, {}, {}, {})
+
+ env = rails_env 'PATH_INFO' => '/admin/users/show/10'
+ called = false
+ expected = {
+ :controller => 'admin/users',
+ :action => 'show',
+ :id => '10'
+ }
+
+ @router.recognize(env) do |r, _, params|
+ assert_equal route, r
+ assert_equal(expected, params)
+ called = true
+ end
+ assert called
+ end
+
+ def test_recognize_literal
+ path = Path::Pattern.new "/books(/:action(.:format))"
+ app = Object.new
+ route = @router.routes.add_route(app, path, {}, {:controller => 'books'})
+
+ env = rails_env 'PATH_INFO' => '/books/list.rss'
+ expected = { :controller => 'books', :action => 'list', :format => 'rss' }
+ called = false
+ @router.recognize(env) do |r, _, params|
+ assert_equal route, r
+ assert_equal(expected, params)
+ called = true
+ end
+
+ assert called
+ end
+
+ def test_recognize_head_request_as_get_route
+ path = Path::Pattern.new "/books(/:action(.:format))"
+ app = Object.new
+ conditions = {
+ :request_method => 'GET'
+ }
+ @router.routes.add_route(app, path, conditions, {})
+
+ env = rails_env 'PATH_INFO' => '/books/list.rss',
+ "REQUEST_METHOD" => "HEAD"
+
+ called = false
+ @router.recognize(env) do |r, _, params|
+ called = true
+ end
+
+ assert called
+ end
+
+ def test_recognize_cares_about_verbs
+ path = Path::Pattern.new "/books(/:action(.:format))"
+ app = Object.new
+ conditions = {
+ :request_method => 'GET'
+ }
+ @router.routes.add_route(app, path, conditions, {})
+
+ conditions = conditions.dup
+ conditions[:request_method] = 'POST'
+
+ post = @router.routes.add_route(app, path, conditions, {})
+
+ env = rails_env 'PATH_INFO' => '/books/list.rss',
+ "REQUEST_METHOD" => "POST"
+
+ called = false
+ @router.recognize(env) do |r, _, params|
+ assert_equal post, r
+ called = true
+ end
+
+ assert called
+ end
+
+ private
+
+ def add_routes router, paths
+ paths.each do |path|
+ path = Path::Pattern.new path
+ router.routes.add_route nil, path, {}, {}, {}
+ end
+ end
+
+ RailsEnv = Struct.new(:env)
+
+ def rails_env env
+ RailsEnv.new rack_env env
+ end
+
+ def rack_env env
+ {
+ "rack.version" => [1, 1],
+ "rack.input" => StringIO.new,
+ "rack.errors" => StringIO.new,
+ "rack.multithread" => true,
+ "rack.multiprocess" => true,
+ "rack.run_once" => false,
+ "REQUEST_METHOD" => "GET",
+ "SERVER_NAME" => "example.org",
+ "SERVER_PORT" => "80",
+ "QUERY_STRING" => "",
+ "PATH_INFO" => "/content",
+ "rack.url_scheme" => "http",
+ "HTTPS" => "off",
+ "SCRIPT_NAME" => "",
+ "CONTENT_LENGTH" => "0"
+ }.merge env
+ end
+ end
+ end
+end
diff --git a/actionpack/test/journey/routes_test.rb b/actionpack/test/journey/routes_test.rb
new file mode 100644
index 0000000000..3b17bd53b7
--- /dev/null
+++ b/actionpack/test/journey/routes_test.rb
@@ -0,0 +1,53 @@
+require 'abstract_unit'
+
+module ActionDispatch
+ module Journey
+ class TestRoutes < MiniTest::Unit::TestCase
+ def test_clear
+ routes = Routes.new
+ exp = Router::Strexp.new '/foo(/:id)', {}, ['/.?']
+ path = Path::Pattern.new exp
+ requirements = { :hello => /world/ }
+
+ routes.add_route nil, path, requirements, {:id => nil}, {}
+ assert_equal 1, routes.length
+
+ routes.clear
+ assert_equal 0, routes.length
+ end
+
+ def test_ast
+ routes = Routes.new
+ path = Path::Pattern.new '/hello'
+
+ routes.add_route nil, path, {}, {}, {}
+ ast = routes.ast
+ routes.add_route nil, path, {}, {}, {}
+ refute_equal ast, routes.ast
+ end
+
+ def test_simulator_changes
+ routes = Routes.new
+ path = Path::Pattern.new '/hello'
+
+ routes.add_route nil, path, {}, {}, {}
+ sim = routes.simulator
+ routes.add_route nil, path, {}, {}, {}
+ refute_equal sim, routes.simulator
+ end
+
+ def test_first_name_wins
+ #def add_route app, path, conditions, defaults, name = nil
+ routes = Routes.new
+
+ one = Path::Pattern.new '/hello'
+ two = Path::Pattern.new '/aaron'
+
+ routes.add_route nil, one, {}, {}, 'aaron'
+ routes.add_route nil, two, {}, {}, 'aaron'
+
+ assert_equal '/hello', routes.named_routes['aaron'].path.spec.to_s
+ end
+ end
+ end
+end
diff --git a/actionpack/test/template/asset_tag_helper_test.rb b/actionpack/test/template/asset_tag_helper_test.rb
index eb1a54a81f..82c9d383ac 100644
--- a/actionpack/test/template/asset_tag_helper_test.rb
+++ b/actionpack/test/template/asset_tag_helper_test.rb
@@ -358,6 +358,17 @@ class AssetTagHelperTest < ActionView::TestCase
assert javascript_include_tag("prototype").html_safe?
end
+ def test_javascript_include_tag_relative_protocol
+ @controller.config.asset_host = "assets.example.com"
+ assert_dom_equal %(<script src="//assets.example.com/javascripts/prototype.js"></script>), javascript_include_tag('prototype', protocol: :relative)
+ end
+
+ def test_javascript_include_tag_default_protocol
+ @controller.config.asset_host = "assets.example.com"
+ @controller.config.default_asset_host_protocol = :relative
+ assert_dom_equal %(<script src="//assets.example.com/javascripts/prototype.js"></script>), javascript_include_tag('prototype')
+ end
+
def test_stylesheet_path
StylePathToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
end
@@ -398,7 +409,18 @@ class AssetTagHelperTest < ActionView::TestCase
end
def test_stylesheet_link_tag_should_not_output_the_same_asset_twice
- assert_dom_equal %(<link href="/stylesheets/wellington.css" media="screen" rel="stylesheet" />\n<link href="/stylesheets/amsterdam.css" media="screen" rel="stylesheet" />), stylesheet_link_tag('wellington', 'wellington', 'amsterdam')
+ assert_dom_equal %(<link href="/stylesheets/wellington.css" media="screen" rel="stylesheet" />\n<link href="/stylesheets/amsterdam.css" media="screen" rel="stylesheet" />), stylesheet_link_tag('wellington', 'wellington', 'amsterdam')
+ end
+
+ def test_stylesheet_link_tag_with_relative_protocol
+ @controller.config.asset_host = "assets.example.com"
+ assert_dom_equal %(<link href="//assets.example.com/stylesheets/wellington.css" media="screen" rel="stylesheet" />), stylesheet_link_tag('wellington', protocol: :relative)
+ end
+
+ def test_stylesheet_link_tag_with_default_protocol
+ @controller.config.asset_host = "assets.example.com"
+ @controller.config.default_asset_host_protocol = :relative
+ assert_dom_equal %(<link href="//assets.example.com/stylesheets/wellington.css" media="screen" rel="stylesheet" />), stylesheet_link_tag('wellington')
end
def test_image_path
diff --git a/actionpack/test/template/url_helper_test.rb b/actionpack/test/template/url_helper_test.rb
index 1bb625213d..ba65349b6a 100644
--- a/actionpack/test/template/url_helper_test.rb
+++ b/actionpack/test/template/url_helper_test.rb
@@ -517,16 +517,6 @@ class UrlHelperTest < ActiveSupport::TestCase
mail_to("david@loudthinking.com", "David Heinemeier Hansson", class: "admin")
end
- def test_mail_to_with_javascript
- snippet = mail_to("me@domain.com", "My email", encode: "javascript")
- assert_dom_equal "<script>eval(decodeURIComponent('%64%6f%63%75%6d%65%6e%74%2e%77%72%69%74%65%28%27%3c%61%20%68%72%65%66%3d%5c%22%6d%61%69%6c%74%6f%3a%6d%65%40%64%6f%6d%61%69%6e%2e%63%6f%6d%5c%22%3e%4d%79%20%65%6d%61%69%6c%3c%5c%2f%61%3e%27%29%3b'))</script>", snippet
- end
-
- def test_mail_to_with_javascript_unicode
- snippet = mail_to("unicode@example.com", "únicode", encode: "javascript")
- assert_dom_equal "<script>eval(decodeURIComponent('%64%6f%63%75%6d%65%6e%74%2e%77%72%69%74%65%28%27%3c%61%20%68%72%65%66%3d%5c%22%6d%61%69%6c%74%6f%3a%75%6e%69%63%6f%64%65%40%65%78%61%6d%70%6c%65%2e%63%6f%6d%5c%22%3e%c3%ba%6e%69%63%6f%64%65%3c%5c%2f%61%3e%27%29%3b'))</script>", snippet
- end
-
def test_mail_with_options
assert_dom_equal(
%{<a href="mailto:me@example.com?cc=ccaddress%40example.com&amp;bcc=bccaddress%40example.com&amp;body=This%20is%20the%20body%20of%20the%20message.&amp;subject=This%20is%20an%20example%20email">My email</a>},
@@ -539,54 +529,8 @@ class UrlHelperTest < ActiveSupport::TestCase
mail_to('feedback@example.com', '<img src="/feedback.png" />'.html_safe)
end
- def test_mail_to_with_hex
- assert_dom_equal(
- %{<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;%6d%65@%64%6f%6d%61%69%6e.%63%6f%6d">My email</a>},
- mail_to("me@domain.com", "My email", encode: "hex")
- )
-
- assert_dom_equal(
- %{<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;%6d%65@%64%6f%6d%61%69%6e.%63%6f%6d">&#109;&#101;&#64;&#100;&#111;&#109;&#97;&#105;&#110;&#46;&#99;&#111;&#109;</a>},
- mail_to("me@domain.com", nil, encode: "hex")
- )
- end
-
- def test_mail_to_with_replace_options
- assert_dom_equal(
- %{<a href="mailto:wolfgang@stufenlos.net">wolfgang(at)stufenlos(dot)net</a>},
- mail_to("wolfgang@stufenlos.net", nil, replace_at: "(at)", replace_dot: "(dot)")
- )
-
- assert_dom_equal(
- %{<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;%6d%65@%64%6f%6d%61%69%6e.%63%6f%6d">&#109;&#101;&#40;&#97;&#116;&#41;&#100;&#111;&#109;&#97;&#105;&#110;&#46;&#99;&#111;&#109;</a>},
- mail_to("me@domain.com", nil, encode: "hex", replace_at: "(at)")
- )
-
- assert_dom_equal(
- %{<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;%6d%65@%64%6f%6d%61%69%6e.%63%6f%6d">My email</a>},
- mail_to("me@domain.com", "My email", encode: "hex", replace_at: "(at)")
- )
-
- assert_dom_equal(
- %{<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;%6d%65@%64%6f%6d%61%69%6e.%63%6f%6d">&#109;&#101;&#40;&#97;&#116;&#41;&#100;&#111;&#109;&#97;&#105;&#110;&#40;&#100;&#111;&#116;&#41;&#99;&#111;&#109;</a>},
- mail_to("me@domain.com", nil, encode: "hex", replace_at: "(at)", replace_dot: "(dot)")
- )
-
- assert_dom_equal(
- %{<script>eval(decodeURIComponent('%64%6f%63%75%6d%65%6e%74%2e%77%72%69%74%65%28%27%3c%61%20%68%72%65%66%3d%5c%22%6d%61%69%6c%74%6f%3a%6d%65%40%64%6f%6d%61%69%6e%2e%63%6f%6d%5c%22%3e%4d%79%20%65%6d%61%69%6c%3c%5c%2f%61%3e%27%29%3b'))</script>},
- mail_to("me@domain.com", "My email", encode: "javascript", replace_at: "(at)", replace_dot: "(dot)")
- )
-
- assert_dom_equal(
- %{<script>eval(decodeURIComponent('%64%6f%63%75%6d%65%6e%74%2e%77%72%69%74%65%28%27%3c%61%20%68%72%65%66%3d%5c%22%6d%61%69%6c%74%6f%3a%6d%65%40%64%6f%6d%61%69%6e%2e%63%6f%6d%5c%22%3e%6d%65%28%61%74%29%64%6f%6d%61%69%6e%28%64%6f%74%29%63%6f%6d%3c%5c%2f%61%3e%27%29%3b'))</script>},
- mail_to("me@domain.com", nil, encode: "javascript", replace_at: "(at)", replace_dot: "(dot)")
- )
- end
-
def test_mail_to_returns_html_safe_string
assert mail_to("david@loudthinking.com").html_safe?
- assert mail_to("me@domain.com", "My email", encode: "javascript").html_safe?
- assert mail_to("me@domain.com", "My email", encode: "hex").html_safe?
end
def protect_against_forgery?