1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00
rails--rails/actionpack/test/dispatch/routing_test.rb
Andrew White 6520ea5f7e Deprecate :controller and :action path parameters
Allowing :controller and :action values to be specified via the path
in config/routes.rb has been an underlying cause of a number of issues
in Rails that have resulted in security releases. In light of this it's
better that controllers and actions are explicitly whitelisted rather
than trying to blacklist or sanitize 'bad' values.
2016-03-01 08:48:53 +00:00

4757 lines
134 KiB
Ruby

require 'erb'
require 'abstract_unit'
require 'controller/fake_controllers'
class TestRoutingMapper < ActionDispatch::IntegrationTest
SprocketsApp = lambda { |env|
[200, {"Content-Type" => "text/html"}, ["javascripts"]]
}
class IpRestrictor
def self.matches?(request)
request.ip =~ /192\.168\.1\.1\d\d/
end
end
class GrumpyRestrictor
def self.matches?(request)
false
end
end
class YoutubeFavoritesRedirector
def self.call(params, request)
"http://www.youtube.com/watch?v=#{params[:youtube_id]}"
end
end
def test_logout
draw do
controller :sessions do
delete 'logout' => :destroy
end
end
delete '/logout'
assert_equal 'sessions#destroy', @response.body
assert_equal '/logout', logout_path
assert_equal '/logout', url_for(:controller => 'sessions', :action => 'destroy', :only_path => true)
end
def test_login
draw do
default_url_options :host => "rubyonrails.org"
controller :sessions do
get 'login' => :new
post 'login' => :create
end
end
get '/login'
assert_equal 'sessions#new', @response.body
assert_equal '/login', login_path
post '/login'
assert_equal 'sessions#create', @response.body
assert_equal '/login', url_for(:controller => 'sessions', :action => 'create', :only_path => true)
assert_equal '/login', url_for(:controller => 'sessions', :action => 'new', :only_path => true)
assert_equal 'http://rubyonrails.org/login', url_for(:controller => 'sessions', :action => 'create')
assert_equal 'http://rubyonrails.org/login', login_url
end
def test_login_redirect
draw do
get 'account/login', :to => redirect("/login")
end
get '/account/login'
verify_redirect 'http://www.example.com/login'
end
def test_logout_redirect_without_to
draw do
get 'account/logout' => redirect("/logout"), :as => :logout_redirect
end
assert_equal '/account/logout', logout_redirect_path
get '/account/logout'
verify_redirect 'http://www.example.com/logout'
end
def test_namespace_redirect
draw do
namespace :private do
root :to => redirect('/private/index')
get "index", :to => 'private#index'
end
end
get '/private'
verify_redirect 'http://www.example.com/private/index'
end
def test_redirect_with_failing_constraint
draw do
get 'hi', to: redirect("/foo"), constraints: ::TestRoutingMapper::GrumpyRestrictor
end
get '/hi'
assert_equal 404, status
end
def test_redirect_with_passing_constraint
draw do
get 'hi', to: redirect("/foo"), constraints: ->(req) { true }
end
get '/hi'
assert_equal 301, status
end
def test_namespace_with_controller_segment
assert_raise(ArgumentError) do
draw do
namespace :admin do
ActiveSupport::Deprecation.silence do
get '/:controller(/:action(/:id(.:format)))'
end
end
end
end
end
def test_namespace_without_controller_segment
draw do
namespace :admin do
ActiveSupport::Deprecation.silence do
get 'hello/:controllers/:action'
end
end
end
get '/admin/hello/foo/new'
assert_equal 'foo', @request.params["controllers"]
end
def test_session_singleton_resource
draw do
resource :session do
get :create
post :reset
end
end
get '/session'
assert_equal 'sessions#create', @response.body
assert_equal '/session', session_path
post '/session'
assert_equal 'sessions#create', @response.body
put '/session'
assert_equal 'sessions#update', @response.body
delete '/session'
assert_equal 'sessions#destroy', @response.body
get '/session/new'
assert_equal 'sessions#new', @response.body
assert_equal '/session/new', new_session_path
get '/session/edit'
assert_equal 'sessions#edit', @response.body
assert_equal '/session/edit', edit_session_path
post '/session/reset'
assert_equal 'sessions#reset', @response.body
assert_equal '/session/reset', reset_session_path
end
def test_session_singleton_resource_for_api_app
config = ActionDispatch::Routing::RouteSet::Config.new
config.api_only = true
self.class.stub_controllers(config) do |routes|
routes.draw do
resource :session do
get :create
post :reset
end
end
@app = RoutedRackApp.new routes
end
get '/session'
assert_equal 'sessions#create', @response.body
assert_equal '/session', session_path
post '/session'
assert_equal 'sessions#create', @response.body
put '/session'
assert_equal 'sessions#update', @response.body
delete '/session'
assert_equal 'sessions#destroy', @response.body
post '/session/reset'
assert_equal 'sessions#reset', @response.body
assert_equal '/session/reset', reset_session_path
get '/session/new'
assert_equal 'Not Found', @response.body
get '/session/edit'
assert_equal 'Not Found', @response.body
end
def test_session_info_nested_singleton_resource
draw do
resource :session do
resource :info
end
end
get '/session/info'
assert_equal 'infos#show', @response.body
assert_equal '/session/info', session_info_path
end
def test_member_on_resource
draw do
resource :session do
member do
get :crush
end
end
end
get '/session/crush'
assert_equal 'sessions#crush', @response.body
assert_equal '/session/crush', crush_session_path
end
def test_redirect_modulo
draw do
get 'account/modulo/:name', :to => redirect("/%{name}s")
end
get '/account/modulo/name'
verify_redirect 'http://www.example.com/names'
end
def test_redirect_proc
draw do
get 'account/proc/:name', :to => redirect {|params, req| "/#{params[:name].pluralize}" }
end
get '/account/proc/person'
verify_redirect 'http://www.example.com/people'
end
def test_redirect_proc_with_request
draw do
get 'account/proc_req' => redirect {|params, req| "/#{req.method}" }
end
get '/account/proc_req'
verify_redirect 'http://www.example.com/GET'
end
def test_redirect_hash_with_subdomain
draw do
get 'mobile', :to => redirect(:subdomain => 'mobile')
end
get '/mobile'
verify_redirect 'http://mobile.example.com/mobile'
end
def test_redirect_hash_with_domain_and_path
draw do
get 'documentation', :to => redirect(:domain => 'example-documentation.com', :path => '')
end
get '/documentation'
verify_redirect 'http://www.example-documentation.com'
end
def test_redirect_hash_with_path
draw do
get 'new_documentation', :to => redirect(:path => '/documentation/new')
end
get '/new_documentation'
verify_redirect 'http://www.example.com/documentation/new'
end
def test_redirect_hash_with_host
draw do
get 'super_new_documentation', :to => redirect(:host => 'super-docs.com')
end
get '/super_new_documentation?section=top'
verify_redirect 'http://super-docs.com/super_new_documentation?section=top'
end
def test_redirect_hash_path_substitution
draw do
get 'stores/:name', :to => redirect(:subdomain => 'stores', :path => '/%{name}')
end
get '/stores/iernest'
verify_redirect 'http://stores.example.com/iernest'
end
def test_redirect_hash_path_substitution_with_catch_all
draw do
get 'stores/:name(*rest)', :to => redirect(:subdomain => 'stores', :path => '/%{name}%{rest}')
end
get '/stores/iernest/products'
verify_redirect 'http://stores.example.com/iernest/products'
end
def test_redirect_class
draw do
get 'youtube_favorites/:youtube_id/:name', :to => redirect(YoutubeFavoritesRedirector)
end
get '/youtube_favorites/oHg5SJYRHA0/rick-rolld'
verify_redirect 'http://www.youtube.com/watch?v=oHg5SJYRHA0'
end
def test_openid
draw do
match 'openid/login', :via => [:get, :post], :to => "openid#login"
end
get '/openid/login'
assert_equal 'openid#login', @response.body
post '/openid/login'
assert_equal 'openid#login', @response.body
end
def test_bookmarks
draw do
scope "bookmark", :controller => "bookmarks", :as => :bookmark do
get :new, :path => "build"
post :create, :path => "create", :as => ""
put :update
get :remove, :action => :destroy, :as => :remove
end
end
get '/bookmark/build'
assert_equal 'bookmarks#new', @response.body
assert_equal '/bookmark/build', bookmark_new_path
post '/bookmark/create'
assert_equal 'bookmarks#create', @response.body
assert_equal '/bookmark/create', bookmark_path
put '/bookmark/update'
assert_equal 'bookmarks#update', @response.body
assert_equal '/bookmark/update', bookmark_update_path
get '/bookmark/remove'
assert_equal 'bookmarks#destroy', @response.body
assert_equal '/bookmark/remove', bookmark_remove_path
end
def test_pagemarks
tc = self
draw do
scope "pagemark", :controller => "pagemarks", :as => :pagemark do
tc.assert_deprecated do
get "new", :path => "build"
end
post "create", :as => ""
put "update"
get "remove", :action => :destroy, :as => :remove
end
end
get '/pagemark/build'
assert_equal 'pagemarks#new', @response.body
assert_equal '/pagemark/build', pagemark_new_path
post '/pagemark/create'
assert_equal 'pagemarks#create', @response.body
assert_equal '/pagemark/create', pagemark_path
put '/pagemark/update'
assert_equal 'pagemarks#update', @response.body
assert_equal '/pagemark/update', pagemark_update_path
get '/pagemark/remove'
assert_equal 'pagemarks#destroy', @response.body
assert_equal '/pagemark/remove', pagemark_remove_path
end
def test_admin
draw do
constraints(:ip => /192\.168\.1\.\d\d\d/) do
get 'admin' => "queenbee#index"
end
constraints ::TestRoutingMapper::IpRestrictor do
get 'admin/accounts' => "queenbee#accounts"
end
get 'admin/passwords' => "queenbee#passwords", :constraints => ::TestRoutingMapper::IpRestrictor
end
get '/admin', headers: { 'REMOTE_ADDR' => '192.168.1.100' }
assert_equal 'queenbee#index', @response.body
get '/admin', headers: { 'REMOTE_ADDR' => '10.0.0.100' }
assert_equal 'pass', @response.headers['X-Cascade']
get '/admin/accounts', headers: { 'REMOTE_ADDR' => '192.168.1.100' }
assert_equal 'queenbee#accounts', @response.body
get '/admin/accounts', headers: { 'REMOTE_ADDR' => '10.0.0.100' }
assert_equal 'pass', @response.headers['X-Cascade']
get '/admin/passwords', headers: { 'REMOTE_ADDR' => '192.168.1.100' }
assert_equal 'queenbee#passwords', @response.body
get '/admin/passwords', headers: { 'REMOTE_ADDR' => '10.0.0.100' }
assert_equal 'pass', @response.headers['X-Cascade']
end
def test_global
draw do
controller(:global) do
get 'global/hide_notice'
get 'global/export', :action => :export, :as => :export_request
get '/export/:id/:file', :action => :export, :as => :export_download, :constraints => { :file => /.*/ }
ActiveSupport::Deprecation.silence do
get 'global/:action'
end
end
end
get '/global/dashboard'
assert_equal 'global#dashboard', @response.body
get '/global/export'
assert_equal 'global#export', @response.body
get '/global/hide_notice'
assert_equal 'global#hide_notice', @response.body
get '/export/123/foo.txt'
assert_equal 'global#export', @response.body
assert_equal '/global/export', export_request_path
assert_equal '/global/hide_notice', global_hide_notice_path
assert_equal '/export/123/foo.txt', export_download_path(:id => 123, :file => 'foo.txt')
end
def test_local
draw do
ActiveSupport::Deprecation.silence do
get "/local/:action", :controller => "local"
end
end
get '/local/dashboard'
assert_equal 'local#dashboard', @response.body
end
# tests the use of dup in url_for
def test_url_for_with_no_side_effects
draw do
get "/projects/status(.:format)"
end
# without dup, additional (and possibly unwanted) values will be present in the options (eg. :host)
original_options = {:controller => 'projects', :action => 'status'}
options = original_options.dup
url_for options
# verify that the options passed in have not changed from the original ones
assert_equal original_options, options
end
def test_url_for_does_not_modify_controller
draw do
get "/projects/status(.:format)"
end
controller = '/projects'
options = {:controller => controller, :action => 'status', :only_path => true}
url = url_for(options)
assert_equal '/projects/status', url
assert_equal '/projects', controller
end
# tests the arguments modification free version of define_hash_access
def test_named_route_with_no_side_effects
draw do
resources :customers do
get "profile", :on => :member
end
end
original_options = { :host => 'test.host' }
options = original_options.dup
profile_customer_url("customer_model", options)
# verify that the options passed in have not changed from the original ones
assert_equal original_options, options
end
def test_projects_status
draw do
get "/projects/status(.:format)"
end
assert_equal '/projects/status', url_for(:controller => 'projects', :action => 'status', :only_path => true)
assert_equal '/projects/status.json', url_for(:controller => 'projects', :action => 'status', :format => 'json', :only_path => true)
end
def test_projects
draw do
resources :projects, :controller => :project
end
get '/projects'
assert_equal 'project#index', @response.body
assert_equal '/projects', projects_path
post '/projects'
assert_equal 'project#create', @response.body
get '/projects.xml'
assert_equal 'project#index', @response.body
assert_equal '/projects.xml', projects_path(:format => 'xml')
get '/projects/new'
assert_equal 'project#new', @response.body
assert_equal '/projects/new', new_project_path
get '/projects/new.xml'
assert_equal 'project#new', @response.body
assert_equal '/projects/new.xml', new_project_path(:format => 'xml')
get '/projects/1'
assert_equal 'project#show', @response.body
assert_equal '/projects/1', project_path(:id => '1')
get '/projects/1.xml'
assert_equal 'project#show', @response.body
assert_equal '/projects/1.xml', project_path(:id => '1', :format => 'xml')
get '/projects/1/edit'
assert_equal 'project#edit', @response.body
assert_equal '/projects/1/edit', edit_project_path(:id => '1')
end
def test_projects_for_api_app
config = ActionDispatch::Routing::RouteSet::Config.new
config.api_only = true
self.class.stub_controllers(config) do |routes|
routes.draw do
resources :projects, controller: :project
end
@app = RoutedRackApp.new routes
end
get '/projects'
assert_equal 'project#index', @response.body
assert_equal '/projects', projects_path
post '/projects'
assert_equal 'project#create', @response.body
get '/projects.xml'
assert_equal 'project#index', @response.body
assert_equal '/projects.xml', projects_path(format: 'xml')
get '/projects/1'
assert_equal 'project#show', @response.body
assert_equal '/projects/1', project_path(id: '1')
get '/projects/1.xml'
assert_equal 'project#show', @response.body
assert_equal '/projects/1.xml', project_path(id: '1', format: 'xml')
get '/projects/1/edit'
assert_equal 'Not Found', @response.body
end
def test_projects_with_post_action_and_new_path_on_collection
draw do
resources :projects, :controller => :project do
post 'new', :action => 'new', :on => :collection, :as => :new
end
end
post '/projects/new'
assert_equal "project#new", @response.body
assert_equal "/projects/new", new_projects_path
end
def test_projects_involvements
draw do
resources :projects, :controller => :project do
resources :involvements, :attachments
end
end
get '/projects/1/involvements'
assert_equal 'involvements#index', @response.body
assert_equal '/projects/1/involvements', project_involvements_path(:project_id => '1')
get '/projects/1/involvements/new'
assert_equal 'involvements#new', @response.body
assert_equal '/projects/1/involvements/new', new_project_involvement_path(:project_id => '1')
get '/projects/1/involvements/1'
assert_equal 'involvements#show', @response.body
assert_equal '/projects/1/involvements/1', project_involvement_path(:project_id => '1', :id => '1')
put '/projects/1/involvements/1'
assert_equal 'involvements#update', @response.body
delete '/projects/1/involvements/1'
assert_equal 'involvements#destroy', @response.body
get '/projects/1/involvements/1/edit'
assert_equal 'involvements#edit', @response.body
assert_equal '/projects/1/involvements/1/edit', edit_project_involvement_path(:project_id => '1', :id => '1')
end
def test_projects_attachments
draw do
resources :projects, :controller => :project do
resources :involvements, :attachments
end
end
get '/projects/1/attachments'
assert_equal 'attachments#index', @response.body
assert_equal '/projects/1/attachments', project_attachments_path(:project_id => '1')
end
def test_projects_participants
draw do
resources :projects, :controller => :project do
resources :participants do
put :update_all, :on => :collection
end
end
end
get '/projects/1/participants'
assert_equal 'participants#index', @response.body
assert_equal '/projects/1/participants', project_participants_path(:project_id => '1')
put '/projects/1/participants/update_all'
assert_equal 'participants#update_all', @response.body
assert_equal '/projects/1/participants/update_all', update_all_project_participants_path(:project_id => '1')
end
def test_projects_companies
draw do
resources :projects, :controller => :project do
resources :companies do
resources :people
resource :avatar, :controller => :avatar
end
end
end
get '/projects/1/companies'
assert_equal 'companies#index', @response.body
assert_equal '/projects/1/companies', project_companies_path(:project_id => '1')
get '/projects/1/companies/1/people'
assert_equal 'people#index', @response.body
assert_equal '/projects/1/companies/1/people', project_company_people_path(:project_id => '1', :company_id => '1')
get '/projects/1/companies/1/avatar'
assert_equal 'avatar#show', @response.body
assert_equal '/projects/1/companies/1/avatar', project_company_avatar_path(:project_id => '1', :company_id => '1')
end
def test_project_manager
draw do
resources :projects do
resource :manager, :as => :super_manager do
post :fire
end
end
end
get '/projects/1/manager'
assert_equal 'managers#show', @response.body
assert_equal '/projects/1/manager', project_super_manager_path(:project_id => '1')
get '/projects/1/manager/new'
assert_equal 'managers#new', @response.body
assert_equal '/projects/1/manager/new', new_project_super_manager_path(:project_id => '1')
post '/projects/1/manager/fire'
assert_equal 'managers#fire', @response.body
assert_equal '/projects/1/manager/fire', fire_project_super_manager_path(:project_id => '1')
end
def test_project_images
draw do
resources :projects do
resources :images, :as => :funny_images do
post :revise, :on => :member
end
end
end
get '/projects/1/images'
assert_equal 'images#index', @response.body
assert_equal '/projects/1/images', project_funny_images_path(:project_id => '1')
get '/projects/1/images/new'
assert_equal 'images#new', @response.body
assert_equal '/projects/1/images/new', new_project_funny_image_path(:project_id => '1')
post '/projects/1/images/1/revise'
assert_equal 'images#revise', @response.body
assert_equal '/projects/1/images/1/revise', revise_project_funny_image_path(:project_id => '1', :id => '1')
end
def test_projects_people
draw do
resources :projects do
resources :people do
nested do
scope "/:access_token" do
resource :avatar
end
end
member do
put :accessible_projects
post :resend, :generate_new_password
end
end
end
end
get '/projects/1/people'
assert_equal 'people#index', @response.body
assert_equal '/projects/1/people', project_people_path(:project_id => '1')
get '/projects/1/people/1'
assert_equal 'people#show', @response.body
assert_equal '/projects/1/people/1', project_person_path(:project_id => '1', :id => '1')
get '/projects/1/people/1/7a2dec8/avatar'
assert_equal 'avatars#show', @response.body
assert_equal '/projects/1/people/1/7a2dec8/avatar', project_person_avatar_path(:project_id => '1', :person_id => '1', :access_token => '7a2dec8')
put '/projects/1/people/1/accessible_projects'
assert_equal 'people#accessible_projects', @response.body
assert_equal '/projects/1/people/1/accessible_projects', accessible_projects_project_person_path(:project_id => '1', :id => '1')
post '/projects/1/people/1/resend'
assert_equal 'people#resend', @response.body
assert_equal '/projects/1/people/1/resend', resend_project_person_path(:project_id => '1', :id => '1')
post '/projects/1/people/1/generate_new_password'
assert_equal 'people#generate_new_password', @response.body
assert_equal '/projects/1/people/1/generate_new_password', generate_new_password_project_person_path(:project_id => '1', :id => '1')
end
def test_projects_with_resources_path_names
draw do
resources_path_names :correlation_indexes => "info_about_correlation_indexes"
resources :projects do
get :correlation_indexes, :on => :collection
end
end
get '/projects/info_about_correlation_indexes'
assert_equal 'projects#correlation_indexes', @response.body
assert_equal '/projects/info_about_correlation_indexes', correlation_indexes_projects_path
end
def test_projects_posts
draw do
resources :projects do
resources :posts do
get :archive, :toggle_view, :on => :collection
post :preview, :on => :member
resource :subscription
resources :comments do
post :preview, :on => :collection
end
end
end
end
get '/projects/1/posts'
assert_equal 'posts#index', @response.body
assert_equal '/projects/1/posts', project_posts_path(:project_id => '1')
get '/projects/1/posts/archive'
assert_equal 'posts#archive', @response.body
assert_equal '/projects/1/posts/archive', archive_project_posts_path(:project_id => '1')
get '/projects/1/posts/toggle_view'
assert_equal 'posts#toggle_view', @response.body
assert_equal '/projects/1/posts/toggle_view', toggle_view_project_posts_path(:project_id => '1')
post '/projects/1/posts/1/preview'
assert_equal 'posts#preview', @response.body
assert_equal '/projects/1/posts/1/preview', preview_project_post_path(:project_id => '1', :id => '1')
get '/projects/1/posts/1/subscription'
assert_equal 'subscriptions#show', @response.body
assert_equal '/projects/1/posts/1/subscription', project_post_subscription_path(:project_id => '1', :post_id => '1')
get '/projects/1/posts/1/comments'
assert_equal 'comments#index', @response.body
assert_equal '/projects/1/posts/1/comments', project_post_comments_path(:project_id => '1', :post_id => '1')
post '/projects/1/posts/1/comments/preview'
assert_equal 'comments#preview', @response.body
assert_equal '/projects/1/posts/1/comments/preview', preview_project_post_comments_path(:project_id => '1', :post_id => '1')
end
def test_replies
draw do
resources :replies do
member do
put :answer, :action => :mark_as_answer
delete :answer, :action => :unmark_as_answer
end
end
end
put '/replies/1/answer'
assert_equal 'replies#mark_as_answer', @response.body
delete '/replies/1/answer'
assert_equal 'replies#unmark_as_answer', @response.body
end
def test_resource_routes_with_only_and_except
draw do
resources :posts, :only => [:index, :show] do
resources :comments, :except => :destroy
end
end
get '/posts'
assert_equal 'posts#index', @response.body
assert_equal '/posts', posts_path
get '/posts/1'
assert_equal 'posts#show', @response.body
assert_equal '/posts/1', post_path(:id => 1)
get '/posts/1/comments'
assert_equal 'comments#index', @response.body
assert_equal '/posts/1/comments', post_comments_path(:post_id => 1)
post '/posts'
assert_equal 'pass', @response.headers['X-Cascade']
put '/posts/1'
assert_equal 'pass', @response.headers['X-Cascade']
delete '/posts/1'
assert_equal 'pass', @response.headers['X-Cascade']
delete '/posts/1/comments'
assert_equal 'pass', @response.headers['X-Cascade']
end
def test_resource_routes_only_create_update_destroy
draw do
resource :past, :only => :destroy
resource :present, :only => :update
resource :future, :only => :create
end
delete '/past'
assert_equal 'pasts#destroy', @response.body
assert_equal '/past', past_path
patch '/present'
assert_equal 'presents#update', @response.body
assert_equal '/present', present_path
put '/present'
assert_equal 'presents#update', @response.body
assert_equal '/present', present_path
post '/future'
assert_equal 'futures#create', @response.body
assert_equal '/future', future_path
end
def test_resources_routes_only_create_update_destroy
draw do
resources :relationships, :only => [:create, :destroy]
resources :friendships, :only => [:update]
end
post '/relationships'
assert_equal 'relationships#create', @response.body
assert_equal '/relationships', relationships_path
delete '/relationships/1'
assert_equal 'relationships#destroy', @response.body
assert_equal '/relationships/1', relationship_path(1)
patch '/friendships/1'
assert_equal 'friendships#update', @response.body
assert_equal '/friendships/1', friendship_path(1)
put '/friendships/1'
assert_equal 'friendships#update', @response.body
assert_equal '/friendships/1', friendship_path(1)
end
def test_resource_with_slugs_in_ids
draw do
resources :posts
end
get '/posts/rails-rocks'
assert_equal 'posts#show', @response.body
assert_equal '/posts/rails-rocks', post_path(:id => 'rails-rocks')
end
def test_resources_for_uncountable_names
draw do
resources :sheep do
get "_it", :on => :member
end
end
assert_equal '/sheep', sheep_index_path
assert_equal '/sheep/1', sheep_path(1)
assert_equal '/sheep/new', new_sheep_path
assert_equal '/sheep/1/edit', edit_sheep_path(1)
assert_equal '/sheep/1/_it', _it_sheep_path(1)
end
def test_resource_does_not_modify_passed_options
options = {:id => /.+?/, :format => /json|xml/}
draw { resource :user, options }
assert_equal({:id => /.+?/, :format => /json|xml/}, options)
end
def test_resources_does_not_modify_passed_options
options = {:id => /.+?/, :format => /json|xml/}
draw { resources :users, options }
assert_equal({:id => /.+?/, :format => /json|xml/}, options)
end
def test_path_names
draw do
scope 'pt', :as => 'pt' do
resources :projects, :path_names => { :edit => 'editar', :new => 'novo' }, :path => 'projetos'
resource :admin, :path_names => { :new => 'novo', :activate => 'ativar' }, :path => 'administrador' do
put :activate, :on => :member
end
end
end
get '/pt/projetos'
assert_equal 'projects#index', @response.body
assert_equal '/pt/projetos', pt_projects_path
get '/pt/projetos/1/editar'
assert_equal 'projects#edit', @response.body
assert_equal '/pt/projetos/1/editar', edit_pt_project_path(1)
get '/pt/administrador'
assert_equal 'admins#show', @response.body
assert_equal '/pt/administrador', pt_admin_path
get '/pt/administrador/novo'
assert_equal 'admins#new', @response.body
assert_equal '/pt/administrador/novo', new_pt_admin_path
put '/pt/administrador/ativar'
assert_equal 'admins#activate', @response.body
assert_equal '/pt/administrador/ativar', activate_pt_admin_path
end
def test_path_option_override
draw do
scope 'pt', :as => 'pt' do
resources :projects, :path_names => { :new => 'novo' }, :path => 'projetos' do
put :close, :on => :member, :path => 'fechar'
get :open, :on => :new, :path => 'abrir'
end
end
end
get '/pt/projetos/novo/abrir'
assert_equal 'projects#open', @response.body
assert_equal '/pt/projetos/novo/abrir', open_new_pt_project_path
put '/pt/projetos/1/fechar'
assert_equal 'projects#close', @response.body
assert_equal '/pt/projetos/1/fechar', close_pt_project_path(1)
end
def test_sprockets
draw do
get 'sprockets.js' => ::TestRoutingMapper::SprocketsApp
end
get '/sprockets.js'
assert_equal 'javascripts', @response.body
end
def test_update_person_route
draw do
get 'people/:id/update', :to => 'people#update', :as => :update_person
end
get '/people/1/update'
assert_equal 'people#update', @response.body
assert_equal '/people/1/update', update_person_path(:id => 1)
end
def test_update_project_person
draw do
get '/projects/:project_id/people/:id/update', :to => 'people#update', :as => :update_project_person
end
get '/projects/1/people/2/update'
assert_equal 'people#update', @response.body
assert_equal '/projects/1/people/2/update', update_project_person_path(:project_id => 1, :id => 2)
end
def test_forum_products
draw do
namespace :forum do
resources :products, :path => '' do
resources :questions
end
end
end
get '/forum'
assert_equal 'forum/products#index', @response.body
assert_equal '/forum', forum_products_path
get '/forum/basecamp'
assert_equal 'forum/products#show', @response.body
assert_equal '/forum/basecamp', forum_product_path(:id => 'basecamp')
get '/forum/basecamp/questions'
assert_equal 'forum/questions#index', @response.body
assert_equal '/forum/basecamp/questions', forum_product_questions_path(:product_id => 'basecamp')
get '/forum/basecamp/questions/1'
assert_equal 'forum/questions#show', @response.body
assert_equal '/forum/basecamp/questions/1', forum_product_question_path(:product_id => 'basecamp', :id => 1)
end
def test_articles_perma
draw do
get 'articles/:year/:month/:day/:title', :to => "articles#show", :as => :article
end
get '/articles/2009/08/18/rails-3'
assert_equal 'articles#show', @response.body
assert_equal '/articles/2009/8/18/rails-3', article_path(:year => 2009, :month => 8, :day => 18, :title => 'rails-3')
end
def test_account_namespace
draw do
namespace :account do
resource :subscription, :credit, :credit_card
end
end
get '/account/subscription'
assert_equal 'account/subscriptions#show', @response.body
assert_equal '/account/subscription', account_subscription_path
get '/account/credit'
assert_equal 'account/credits#show', @response.body
assert_equal '/account/credit', account_credit_path
get '/account/credit_card'
assert_equal 'account/credit_cards#show', @response.body
assert_equal '/account/credit_card', account_credit_card_path
end
def test_nested_namespace
draw do
namespace :account do
namespace :admin do
resource :subscription
end
end
end
get '/account/admin/subscription'
assert_equal 'account/admin/subscriptions#show', @response.body
assert_equal '/account/admin/subscription', account_admin_subscription_path
end
def test_namespace_nested_in_resources
draw do
resources :clients do
namespace :google do
resource :account do
namespace :secret do
resource :info
end
end
end
end
end
get '/clients/1/google/account'
assert_equal '/clients/1/google/account', client_google_account_path(1)
assert_equal 'google/accounts#show', @response.body
get '/clients/1/google/account/secret/info'
assert_equal '/clients/1/google/account/secret/info', client_google_account_secret_info_path(1)
assert_equal 'google/secret/infos#show', @response.body
end
def test_namespace_with_options
draw do
namespace :users, :path => 'usuarios' do
root :to => 'home#index'
end
end
get '/usuarios'
assert_equal '/usuarios', users_root_path
assert_equal 'users/home#index', @response.body
end
def test_namespaced_shallow_routes_with_module_option
draw do
namespace :foo, module: 'bar' do
resources :posts, only: [:index, :show] do
resources :comments, only: [:index, :show], shallow: true
end
end
end
get '/foo/posts'
assert_equal '/foo/posts', foo_posts_path
assert_equal 'bar/posts#index', @response.body
get '/foo/posts/1'
assert_equal '/foo/posts/1', foo_post_path('1')
assert_equal 'bar/posts#show', @response.body
get '/foo/posts/1/comments'
assert_equal '/foo/posts/1/comments', foo_post_comments_path('1')
assert_equal 'bar/comments#index', @response.body
get '/foo/comments/2'
assert_equal '/foo/comments/2', foo_comment_path('2')
assert_equal 'bar/comments#show', @response.body
end
def test_namespaced_shallow_routes_with_path_option
draw do
namespace :foo, path: 'bar' do
resources :posts, only: [:index, :show] do
resources :comments, only: [:index, :show], shallow: true
end
end
end
get '/bar/posts'
assert_equal '/bar/posts', foo_posts_path
assert_equal 'foo/posts#index', @response.body
get '/bar/posts/1'
assert_equal '/bar/posts/1', foo_post_path('1')
assert_equal 'foo/posts#show', @response.body
get '/bar/posts/1/comments'
assert_equal '/bar/posts/1/comments', foo_post_comments_path('1')
assert_equal 'foo/comments#index', @response.body
get '/bar/comments/2'
assert_equal '/bar/comments/2', foo_comment_path('2')
assert_equal 'foo/comments#show', @response.body
end
def test_namespaced_shallow_routes_with_as_option
draw do
namespace :foo, as: 'bar' do
resources :posts, only: [:index, :show] do
resources :comments, only: [:index, :show], shallow: true
end
end
end
get '/foo/posts'
assert_equal '/foo/posts', bar_posts_path
assert_equal 'foo/posts#index', @response.body
get '/foo/posts/1'
assert_equal '/foo/posts/1', bar_post_path('1')
assert_equal 'foo/posts#show', @response.body
get '/foo/posts/1/comments'
assert_equal '/foo/posts/1/comments', bar_post_comments_path('1')
assert_equal 'foo/comments#index', @response.body
get '/foo/comments/2'
assert_equal '/foo/comments/2', bar_comment_path('2')
assert_equal 'foo/comments#show', @response.body
end
def test_namespaced_shallow_routes_with_shallow_path_option
draw do
namespace :foo, shallow_path: 'bar' do
resources :posts, only: [:index, :show] do
resources :comments, only: [:index, :show], shallow: true
end
end
end
get '/foo/posts'
assert_equal '/foo/posts', foo_posts_path
assert_equal 'foo/posts#index', @response.body
get '/foo/posts/1'
assert_equal '/foo/posts/1', foo_post_path('1')
assert_equal 'foo/posts#show', @response.body
get '/foo/posts/1/comments'
assert_equal '/foo/posts/1/comments', foo_post_comments_path('1')
assert_equal 'foo/comments#index', @response.body
get '/bar/comments/2'
assert_equal '/bar/comments/2', foo_comment_path('2')
assert_equal 'foo/comments#show', @response.body
end
def test_namespaced_shallow_routes_with_shallow_prefix_option
draw do
namespace :foo, shallow_prefix: 'bar' do
resources :posts, only: [:index, :show] do
resources :comments, only: [:index, :show], shallow: true
end
end
end
get '/foo/posts'
assert_equal '/foo/posts', foo_posts_path
assert_equal 'foo/posts#index', @response.body
get '/foo/posts/1'
assert_equal '/foo/posts/1', foo_post_path('1')
assert_equal 'foo/posts#show', @response.body
get '/foo/posts/1/comments'
assert_equal '/foo/posts/1/comments', foo_post_comments_path('1')
assert_equal 'foo/comments#index', @response.body
get '/foo/comments/2'
assert_equal '/foo/comments/2', bar_comment_path('2')
assert_equal 'foo/comments#show', @response.body
end
def test_namespace_containing_numbers
draw do
namespace :v2 do
resources :subscriptions
end
end
get '/v2/subscriptions'
assert_equal 'v2/subscriptions#index', @response.body
assert_equal '/v2/subscriptions', v2_subscriptions_path
end
def test_articles_with_id
draw do
controller :articles do
scope '/articles', :as => 'article' do
scope :path => '/:title', :title => /[a-z]+/, :as => :with_title do
get '/:id', :action => :with_id, :as => ""
end
end
end
end
get '/articles/rails/1'
assert_equal 'articles#with_id', @response.body
get '/articles/123/1'
assert_equal 'pass', @response.headers['X-Cascade']
assert_equal '/articles/rails/1', article_with_title_path(:title => 'rails', :id => 1)
end
def test_access_token_rooms
draw do
scope ':access_token', :constraints => { :access_token => /\w{5,5}/ } do
resources :rooms
end
end
get '/12345/rooms'
assert_equal 'rooms#index', @response.body
get '/12345/rooms/1'
assert_equal 'rooms#show', @response.body
get '/12345/rooms/1/edit'
assert_equal 'rooms#edit', @response.body
end
def test_root
draw do
root :to => 'projects#index'
end
assert_equal '/', root_path
get '/'
assert_equal 'projects#index', @response.body
end
def test_scoped_root
draw do
scope '(:locale)', :locale => /en|pl/ do
root :to => 'projects#index'
end
end
assert_equal '/en', root_path(:locale => 'en')
get '/en'
assert_equal 'projects#index', @response.body
end
def test_scoped_root_as_name
draw do
scope '(:locale)', :locale => /en|pl/ do
root :to => 'projects#index', :as => 'projects'
end
end
assert_equal '/en', projects_path(:locale => 'en')
assert_equal '/', projects_path
get '/en'
assert_equal 'projects#index', @response.body
end
def test_scope_with_format_option
draw do
get "direct/index", as: :no_format_direct, format: false
scope format: false do
get "scoped/index", as: :no_format_scoped
end
end
assert_equal "/direct/index", no_format_direct_path
assert_equal "/direct/index?format=html", no_format_direct_path(format: "html")
assert_equal "/scoped/index", no_format_scoped_path
assert_equal "/scoped/index?format=html", no_format_scoped_path(format: "html")
get '/scoped/index'
assert_equal "scoped#index", @response.body
get '/scoped/index.html'
assert_equal "Not Found", @response.body
end
def test_resources_with_format_false_from_scope
draw do
scope format: false do
resources :posts
resource :user
end
end
get "/posts"
assert_response :success
assert_equal "posts#index", @response.body
assert_equal "/posts", posts_path
get "/posts.html"
assert_response :not_found
assert_equal "Not Found", @response.body
assert_equal "/posts?format=html", posts_path(format: "html")
get "/user"
assert_response :success
assert_equal "users#show", @response.body
assert_equal "/user", user_path
get "/user.html"
assert_response :not_found
assert_equal "Not Found", @response.body
assert_equal "/user?format=html", user_path(format: "html")
end
def test_index
draw do
get '/info' => 'projects#info', :as => 'info'
end
assert_equal '/info', info_path
get '/info'
assert_equal 'projects#info', @response.body
end
def test_match_with_many_paths_containing_a_slash
draw do
get 'get/first', 'get/second', 'get/third', :to => 'get#show'
end
get '/get/first'
assert_equal 'get#show', @response.body
get '/get/second'
assert_equal 'get#show', @response.body
get '/get/third'
assert_equal 'get#show', @response.body
end
def test_match_shorthand_with_no_scope
draw do
get 'account/overview'
end
assert_equal '/account/overview', account_overview_path
get '/account/overview'
assert_equal 'account#overview', @response.body
end
def test_match_shorthand_inside_namespace
draw do
namespace :account do
get 'shorthand'
end
end
assert_equal '/account/shorthand', account_shorthand_path
get '/account/shorthand'
assert_equal 'account#shorthand', @response.body
end
def test_match_shorthand_with_multiple_paths_inside_namespace
draw do
namespace :proposals do
put 'activate', 'inactivate'
end
end
put '/proposals/activate'
assert_equal 'proposals#activate', @response.body
put '/proposals/inactivate'
assert_equal 'proposals#inactivate', @response.body
end
def test_match_shorthand_inside_namespace_with_controller
draw do
namespace :api do
get "products/list"
end
end
assert_equal '/api/products/list', api_products_list_path
get '/api/products/list'
assert_equal 'api/products#list', @response.body
end
def test_match_shorthand_inside_scope_with_variables_with_controller
draw do
scope ':locale' do
match 'questions/new', via: [:get]
end
end
get '/de/questions/new'
assert_equal 'questions#new', @response.body
assert_equal 'de', @request.params[:locale]
end
def test_match_shorthand_inside_nested_namespaces_and_scopes_with_controller
draw do
namespace :api do
namespace :v3 do
scope ':locale' do
get "products/list"
end
end
end
end
get '/api/v3/en/products/list'
assert_equal 'api/v3/products#list', @response.body
end
def test_not_matching_shorthand_with_dynamic_parameters
draw do
ActiveSupport::Deprecation.silence do
get ':controller/:action/admin'
end
end
get '/finances/overview/admin'
assert_equal 'finances#overview', @response.body
end
def test_controller_option_with_nesting_and_leading_slash
draw do
scope '/job', controller: 'job' do
scope ':id', action: 'manage_applicant' do
get "/active"
end
end
end
get '/job/5/active'
assert_equal 'job#manage_applicant', @response.body
end
def test_dynamically_generated_helpers_on_collection_do_not_clobber_resources_url_helper
draw do
resources :replies do
collection do
get 'page/:page' => 'replies#index', :page => %r{\d+}
get ':page' => 'replies#index', :page => %r{\d+}
end
end
end
assert_equal '/replies', replies_path
end
def test_scoped_controller_with_namespace_and_action
draw do
namespace :account do
ActiveSupport::Deprecation.silence do
get ':action/callback', :action => /twitter|github/, :controller => "callbacks", :as => :callback
end
end
end
assert_equal '/account/twitter/callback', account_callback_path("twitter")
get '/account/twitter/callback'
assert_equal 'account/callbacks#twitter', @response.body
get '/account/whatever/callback'
assert_equal 'Not Found', @response.body
end
def test_convention_match_nested_and_with_leading_slash
draw do
get '/account/nested/overview'
end
assert_equal '/account/nested/overview', account_nested_overview_path
get '/account/nested/overview'
assert_equal 'account/nested#overview', @response.body
end
def test_convention_with_explicit_end
draw do
get 'sign_in' => "sessions#new"
end
get '/sign_in'
assert_equal 'sessions#new', @response.body
assert_equal '/sign_in', sign_in_path
end
def test_redirect_with_complete_url_and_status
draw do
get 'account/google' => redirect('http://www.google.com/', :status => 302)
end
get '/account/google'
verify_redirect 'http://www.google.com/', 302
end
def test_redirect_with_port
draw do
get 'account/login', :to => redirect("/login")
end
previous_host, self.host = self.host, 'www.example.com:3000'
get '/account/login'
verify_redirect 'http://www.example.com:3000/login'
ensure
self.host = previous_host
end
def test_normalize_namespaced_matches
draw do
namespace :account do
get 'description', :action => :description, :as => "description"
end
end
assert_equal '/account/description', account_description_path
get '/account/description'
assert_equal 'account#description', @response.body
end
def test_namespaced_roots
draw do
namespace :account do
root :to => "account#index"
end
end
assert_equal '/account', account_root_path
get '/account'
assert_equal 'account/account#index', @response.body
end
def test_optional_scoped_root
draw do
scope '(:locale)', :locale => /en|pl/ do
root :to => 'projects#index'
end
end
assert_equal '/en', root_path("en")
get '/en'
assert_equal 'projects#index', @response.body
end
def test_optional_scoped_path
draw do
scope '(:locale)', :locale => /en|pl/ do
resources :descriptions
end
end
assert_equal '/en/descriptions', descriptions_path("en")
assert_equal '/descriptions', descriptions_path(nil)
assert_equal '/en/descriptions/1', description_path("en", 1)
assert_equal '/descriptions/1', description_path(nil, 1)
get '/en/descriptions'
assert_equal 'descriptions#index', @response.body
get '/descriptions'
assert_equal 'descriptions#index', @response.body
get '/en/descriptions/1'
assert_equal 'descriptions#show', @response.body
get '/descriptions/1'
assert_equal 'descriptions#show', @response.body
end
def test_nested_optional_scoped_path
draw do
namespace :admin do
scope '(:locale)', :locale => /en|pl/ do
resources :descriptions
end
end
end
assert_equal '/admin/en/descriptions', admin_descriptions_path("en")
assert_equal '/admin/descriptions', admin_descriptions_path(nil)
assert_equal '/admin/en/descriptions/1', admin_description_path("en", 1)
assert_equal '/admin/descriptions/1', admin_description_path(nil, 1)
get '/admin/en/descriptions'
assert_equal 'admin/descriptions#index', @response.body
get '/admin/descriptions'
assert_equal 'admin/descriptions#index', @response.body
get '/admin/en/descriptions/1'
assert_equal 'admin/descriptions#show', @response.body
get '/admin/descriptions/1'
assert_equal 'admin/descriptions#show', @response.body
end
def test_nested_optional_path_shorthand
draw do
scope '(:locale)', :locale => /en|pl/ do
get "registrations/new"
end
end
get '/registrations/new'
assert_nil @request.params[:locale]
get '/en/registrations/new'
assert_equal 'en', @request.params[:locale]
end
def test_default_string_params
draw do
get 'inline_pages/(:id)', :to => 'pages#show', :id => 'home'
get 'default_pages/(:id)', :to => 'pages#show', :defaults => { :id => 'home' }
defaults :id => 'home' do
get 'scoped_pages/(:id)', :to => 'pages#show'
end
end
get '/inline_pages'
assert_equal 'home', @request.params[:id]
get '/default_pages'
assert_equal 'home', @request.params[:id]
get '/scoped_pages'
assert_equal 'home', @request.params[:id]
end
def test_default_integer_params
draw do
get 'inline_pages/(:page)', to: 'pages#show', page: 1
get 'default_pages/(:page)', to: 'pages#show', defaults: { page: 1 }
defaults page: 1 do
get 'scoped_pages/(:page)', to: 'pages#show'
end
end
get '/inline_pages'
assert_equal 1, @request.params[:page]
get '/default_pages'
assert_equal 1, @request.params[:page]
get '/scoped_pages'
assert_equal 1, @request.params[:page]
end
def test_resource_constraints
draw do
resources :products, :constraints => { :id => /\d{4}/ } do
root :to => "products#root"
get :favorite, :on => :collection
resources :images
end
resource :dashboard, :constraints => { :ip => /192\.168\.1\.\d{1,3}/ }
end
get '/products/1'
assert_equal 'pass', @response.headers['X-Cascade']
get '/products'
assert_equal 'products#root', @response.body
get '/products/favorite'
assert_equal 'products#favorite', @response.body
get '/products/0001'
assert_equal 'products#show', @response.body
get '/products/1/images'
assert_equal 'pass', @response.headers['X-Cascade']
get '/products/0001/images'
assert_equal 'images#index', @response.body
get '/products/0001/images/0001'
assert_equal 'images#show', @response.body
get '/dashboard', headers: { 'REMOTE_ADDR' => '10.0.0.100' }
assert_equal 'pass', @response.headers['X-Cascade']
get '/dashboard', headers: { 'REMOTE_ADDR' => '192.168.1.100' }
assert_equal 'dashboards#show', @response.body
end
def test_root_works_in_the_resources_scope
draw do
resources :products do
root :to => "products#root"
end
end
get '/products'
assert_equal 'products#root', @response.body
assert_equal '/products', products_root_path
end
def test_module_scope
draw do
resource :token, :module => :api
end
get '/token'
assert_equal 'api/tokens#show', @response.body
assert_equal '/token', token_path
end
def test_path_scope
draw do
scope :path => 'api' do
resource :me
get '/' => 'mes#index'
end
end
get '/api/me'
assert_equal 'mes#show', @response.body
assert_equal '/api/me', me_path
get '/api'
assert_equal 'mes#index', @response.body
end
def test_symbol_scope
draw do
scope :path => 'api' do
scope :v2 do
resource :me, as: 'v2_me'
get '/' => 'mes#index'
end
scope :v3, :admin do
resource :me, as: 'v3_me'
end
end
end
get '/api/v2/me'
assert_equal 'mes#show', @response.body
assert_equal '/api/v2/me', v2_me_path
get '/api/v2'
assert_equal 'mes#index', @response.body
get '/api/v3/admin/me'
assert_equal 'mes#show', @response.body
end
def test_url_generator_for_generic_route
draw do
ActiveSupport::Deprecation.silence do
get "whatever/:controller(/:action(/:id))"
end
end
get '/whatever/foo/bar'
assert_equal 'foo#bar', @response.body
assert_equal 'http://www.example.com/whatever/foo/bar/1',
url_for(:controller => "foo", :action => "bar", :id => 1)
end
def test_url_generator_for_namespaced_generic_route
draw do
ActiveSupport::Deprecation.silence do
get "whatever/:controller(/:action(/:id))", :id => /\d+/
end
end
get '/whatever/foo/bar/show'
assert_equal 'foo/bar#show', @response.body
get '/whatever/foo/bar/show/1'
assert_equal 'foo/bar#show', @response.body
assert_equal 'http://www.example.com/whatever/foo/bar/show',
url_for(:controller => "foo/bar", :action => "show")
assert_equal 'http://www.example.com/whatever/foo/bar/show/1',
url_for(:controller => "foo/bar", :action => "show", :id => '1')
end
def test_resource_new_actions
draw do
resources :replies do
new do
post :preview
end
end
scope 'pt', :as => 'pt' do
resources :projects, :path_names => { :new => 'novo' }, :path => 'projetos' do
post :preview, :on => :new
end
resource :admin, :path_names => { :new => 'novo' }, :path => 'administrador' do
post :preview, :on => :new
end
resources :products, :path_names => { :new => 'novo' } do
new do
post :preview
end
end
end
resource :profile do
new do
post :preview
end
end
end
assert_equal '/replies/new/preview', preview_new_reply_path
assert_equal '/pt/projetos/novo/preview', preview_new_pt_project_path
assert_equal '/pt/administrador/novo/preview', preview_new_pt_admin_path
assert_equal '/pt/products/novo/preview', preview_new_pt_product_path
assert_equal '/profile/new/preview', preview_new_profile_path
post '/replies/new/preview'
assert_equal 'replies#preview', @response.body
post '/pt/projetos/novo/preview'
assert_equal 'projects#preview', @response.body
post '/pt/administrador/novo/preview'
assert_equal 'admins#preview', @response.body
post '/pt/products/novo/preview'
assert_equal 'products#preview', @response.body
post '/profile/new/preview'
assert_equal 'profiles#preview', @response.body
end
def test_resource_merges_options_from_scope
draw do
scope :only => :show do
resource :account
end
end
assert_raise(NoMethodError) { new_account_path }
get '/account/new'
assert_equal 404, status
end
def test_resources_merges_options_from_scope
draw do
scope :only => [:index, :show] do
resources :products do
resources :images
end
end
end
assert_raise(NoMethodError) { edit_product_path('1') }
get '/products/1/edit'
assert_equal 404, status
assert_raise(NoMethodError) { edit_product_image_path('1', '2') }
post '/products/1/images/2/edit'
assert_equal 404, status
end
def test_shallow_nested_resources
draw do
shallow do
namespace :api do
resources :teams do
resources :players
resource :captain
end
end
end
resources :threads, :shallow => true do
resource :owner
resources :messages do
resources :comments do
member do
post :preview
end
end
end
end
end
get '/api/teams'
assert_equal 'api/teams#index', @response.body
assert_equal '/api/teams', api_teams_path
get '/api/teams/new'
assert_equal 'api/teams#new', @response.body
assert_equal '/api/teams/new', new_api_team_path
get '/api/teams/1'
assert_equal 'api/teams#show', @response.body
assert_equal '/api/teams/1', api_team_path(:id => '1')
get '/api/teams/1/edit'
assert_equal 'api/teams#edit', @response.body
assert_equal '/api/teams/1/edit', edit_api_team_path(:id => '1')
get '/api/teams/1/players'
assert_equal 'api/players#index', @response.body
assert_equal '/api/teams/1/players', api_team_players_path(:team_id => '1')
get '/api/teams/1/players/new'
assert_equal 'api/players#new', @response.body
assert_equal '/api/teams/1/players/new', new_api_team_player_path(:team_id => '1')
get '/api/players/2'
assert_equal 'api/players#show', @response.body
assert_equal '/api/players/2', api_player_path(:id => '2')
get '/api/players/2/edit'
assert_equal 'api/players#edit', @response.body
assert_equal '/api/players/2/edit', edit_api_player_path(:id => '2')
get '/api/teams/1/captain'
assert_equal 'api/captains#show', @response.body
assert_equal '/api/teams/1/captain', api_team_captain_path(:team_id => '1')
get '/api/teams/1/captain/new'
assert_equal 'api/captains#new', @response.body
assert_equal '/api/teams/1/captain/new', new_api_team_captain_path(:team_id => '1')
get '/api/teams/1/captain/edit'
assert_equal 'api/captains#edit', @response.body
assert_equal '/api/teams/1/captain/edit', edit_api_team_captain_path(:team_id => '1')
get '/threads'
assert_equal 'threads#index', @response.body
assert_equal '/threads', threads_path
get '/threads/new'
assert_equal 'threads#new', @response.body
assert_equal '/threads/new', new_thread_path
get '/threads/1'
assert_equal 'threads#show', @response.body
assert_equal '/threads/1', thread_path(:id => '1')
get '/threads/1/edit'
assert_equal 'threads#edit', @response.body
assert_equal '/threads/1/edit', edit_thread_path(:id => '1')
get '/threads/1/owner'
assert_equal 'owners#show', @response.body
assert_equal '/threads/1/owner', thread_owner_path(:thread_id => '1')
get '/threads/1/messages'
assert_equal 'messages#index', @response.body
assert_equal '/threads/1/messages', thread_messages_path(:thread_id => '1')
get '/threads/1/messages/new'
assert_equal 'messages#new', @response.body
assert_equal '/threads/1/messages/new', new_thread_message_path(:thread_id => '1')
get '/messages/2'
assert_equal 'messages#show', @response.body
assert_equal '/messages/2', message_path(:id => '2')
get '/messages/2/edit'
assert_equal 'messages#edit', @response.body
assert_equal '/messages/2/edit', edit_message_path(:id => '2')
get '/messages/2/comments'
assert_equal 'comments#index', @response.body
assert_equal '/messages/2/comments', message_comments_path(:message_id => '2')
get '/messages/2/comments/new'
assert_equal 'comments#new', @response.body
assert_equal '/messages/2/comments/new', new_message_comment_path(:message_id => '2')
get '/comments/3'
assert_equal 'comments#show', @response.body
assert_equal '/comments/3', comment_path(:id => '3')
get '/comments/3/edit'
assert_equal 'comments#edit', @response.body
assert_equal '/comments/3/edit', edit_comment_path(:id => '3')
post '/comments/3/preview'
assert_equal 'comments#preview', @response.body
assert_equal '/comments/3/preview', preview_comment_path(:id => '3')
end
def test_shallow_nested_resources_inside_resource
draw do
resource :membership, shallow: true do
resources :cards
end
end
get '/membership/cards'
assert_equal 'cards#index', @response.body
assert_equal '/membership/cards', membership_cards_path
get '/membership/cards/new'
assert_equal 'cards#new', @response.body
assert_equal '/membership/cards/new', new_membership_card_path
post '/membership/cards'
assert_equal 'cards#create', @response.body
get '/cards/1'
assert_equal 'cards#show', @response.body
assert_equal '/cards/1', card_path('1')
get '/cards/1/edit'
assert_equal 'cards#edit', @response.body
assert_equal '/cards/1/edit', edit_card_path('1')
put '/cards/1'
assert_equal 'cards#update', @response.body
patch '/cards/1'
assert_equal 'cards#update', @response.body
delete '/cards/1'
assert_equal 'cards#destroy', @response.body
end
def test_shallow_deeply_nested_resources
draw do
resources :blogs do
resources :posts do
resources :comments, shallow: true
end
end
end
get '/comments/1'
assert_equal 'comments#show', @response.body
assert_equal '/comments/1', comment_path('1')
assert_equal '/blogs/new', new_blog_path
assert_equal '/blogs/1/posts/new', new_blog_post_path(:blog_id => 1)
assert_equal '/blogs/1/posts/2/comments/new', new_blog_post_comment_path(:blog_id => 1, :post_id => 2)
end
def test_direct_children_of_shallow_resources
draw do
resources :blogs do
resources :posts, shallow: true do
resources :comments
end
end
end
post '/posts/1/comments'
assert_equal 'comments#create', @response.body
assert_equal '/posts/1/comments', post_comments_path('1')
get '/posts/2/comments/new'
assert_equal 'comments#new', @response.body
assert_equal '/posts/2/comments/new', new_post_comment_path('2')
get '/posts/1/comments'
assert_equal 'comments#index', @response.body
assert_equal '/posts/1/comments', post_comments_path('1')
end
def test_shallow_nested_resources_within_scope
draw do
scope '/hello' do
shallow do
resources :notes do
resources :trackbacks
end
end
end
end
get '/hello/notes/1/trackbacks'
assert_equal 'trackbacks#index', @response.body
assert_equal '/hello/notes/1/trackbacks', note_trackbacks_path(:note_id => 1)
get '/hello/notes/1/edit'
assert_equal 'notes#edit', @response.body
assert_equal '/hello/notes/1/edit', edit_note_path(:id => '1')
get '/hello/notes/1/trackbacks/new'
assert_equal 'trackbacks#new', @response.body
assert_equal '/hello/notes/1/trackbacks/new', new_note_trackback_path(:note_id => 1)
get '/hello/trackbacks/1'
assert_equal 'trackbacks#show', @response.body
assert_equal '/hello/trackbacks/1', trackback_path(:id => '1')
get '/hello/trackbacks/1/edit'
assert_equal 'trackbacks#edit', @response.body
assert_equal '/hello/trackbacks/1/edit', edit_trackback_path(:id => '1')
put '/hello/trackbacks/1'
assert_equal 'trackbacks#update', @response.body
post '/hello/notes/1/trackbacks'
assert_equal 'trackbacks#create', @response.body
delete '/hello/trackbacks/1'
assert_equal 'trackbacks#destroy', @response.body
get '/hello/notes'
assert_equal 'notes#index', @response.body
post '/hello/notes'
assert_equal 'notes#create', @response.body
get '/hello/notes/new'
assert_equal 'notes#new', @response.body
assert_equal '/hello/notes/new', new_note_path
get '/hello/notes/1'
assert_equal 'notes#show', @response.body
assert_equal '/hello/notes/1', note_path(:id => 1)
put '/hello/notes/1'
assert_equal 'notes#update', @response.body
delete '/hello/notes/1'
assert_equal 'notes#destroy', @response.body
end
def test_shallow_option_nested_resources_within_scope
draw do
scope '/hello' do
resources :notes, :shallow => true do
resources :trackbacks
end
end
end
get '/hello/notes/1/trackbacks'
assert_equal 'trackbacks#index', @response.body
assert_equal '/hello/notes/1/trackbacks', note_trackbacks_path(:note_id => 1)
get '/hello/notes/1/edit'
assert_equal 'notes#edit', @response.body
assert_equal '/hello/notes/1/edit', edit_note_path(:id => '1')
get '/hello/notes/1/trackbacks/new'
assert_equal 'trackbacks#new', @response.body
assert_equal '/hello/notes/1/trackbacks/new', new_note_trackback_path(:note_id => 1)
get '/hello/trackbacks/1'
assert_equal 'trackbacks#show', @response.body
assert_equal '/hello/trackbacks/1', trackback_path(:id => '1')
get '/hello/trackbacks/1/edit'
assert_equal 'trackbacks#edit', @response.body
assert_equal '/hello/trackbacks/1/edit', edit_trackback_path(:id => '1')
put '/hello/trackbacks/1'
assert_equal 'trackbacks#update', @response.body
post '/hello/notes/1/trackbacks'
assert_equal 'trackbacks#create', @response.body
delete '/hello/trackbacks/1'
assert_equal 'trackbacks#destroy', @response.body
get '/hello/notes'
assert_equal 'notes#index', @response.body
post '/hello/notes'
assert_equal 'notes#create', @response.body
get '/hello/notes/new'
assert_equal 'notes#new', @response.body
assert_equal '/hello/notes/new', new_note_path
get '/hello/notes/1'
assert_equal 'notes#show', @response.body
assert_equal '/hello/notes/1', note_path(:id => 1)
put '/hello/notes/1'
assert_equal 'notes#update', @response.body
delete '/hello/notes/1'
assert_equal 'notes#destroy', @response.body
end
def test_custom_resource_routes_are_scoped
draw do
resources :customers do
get :recent, :on => :collection
get "profile", :on => :member
get "secret/profile" => "customers#secret", :on => :member
post "preview" => "customers#preview", :as => :another_preview, :on => :new
resource :avatar do
get "thumbnail" => "avatars#thumbnail", :as => :thumbnail, :on => :member
end
resources :invoices do
get "outstanding" => "invoices#outstanding", :on => :collection
get "overdue", :action => :overdue, :on => :collection
get "print" => "invoices#print", :as => :print, :on => :member
post "preview" => "invoices#preview", :as => :preview, :on => :new
end
resources :notes, :shallow => true do
get "preview" => "notes#preview", :as => :preview, :on => :new
get "print" => "notes#print", :as => :print, :on => :member
end
end
namespace :api do
resources :customers do
get "recent" => "customers#recent", :as => :recent, :on => :collection
get "profile" => "customers#profile", :as => :profile, :on => :member
post "preview" => "customers#preview", :as => :preview, :on => :new
end
end
end
assert_equal '/customers/recent', recent_customers_path
assert_equal '/customers/1/profile', profile_customer_path(:id => '1')
assert_equal '/customers/1/secret/profile', secret_profile_customer_path(:id => '1')
assert_equal '/customers/new/preview', another_preview_new_customer_path
assert_equal '/customers/1/avatar/thumbnail.jpg', thumbnail_customer_avatar_path(:customer_id => '1', :format => :jpg)
assert_equal '/customers/1/invoices/outstanding', outstanding_customer_invoices_path(:customer_id => '1')
assert_equal '/customers/1/invoices/2/print', print_customer_invoice_path(:customer_id => '1', :id => '2')
assert_equal '/customers/1/invoices/new/preview', preview_new_customer_invoice_path(:customer_id => '1')
assert_equal '/customers/1/notes/new/preview', preview_new_customer_note_path(:customer_id => '1')
assert_equal '/notes/1/print', print_note_path(:id => '1')
assert_equal '/api/customers/recent', recent_api_customers_path
assert_equal '/api/customers/1/profile', profile_api_customer_path(:id => '1')
assert_equal '/api/customers/new/preview', preview_new_api_customer_path
get '/customers/1/invoices/overdue'
assert_equal 'invoices#overdue', @response.body
get '/customers/1/secret/profile'
assert_equal 'customers#secret', @response.body
end
def test_shallow_nested_routes_ignore_module
draw do
scope :module => :api do
resources :errors, :shallow => true do
resources :notices
end
end
end
get '/errors/1/notices'
assert_equal 'api/notices#index', @response.body
assert_equal '/errors/1/notices', error_notices_path(:error_id => '1')
get '/notices/1'
assert_equal 'api/notices#show', @response.body
assert_equal '/notices/1', notice_path(:id => '1')
end
def test_non_greedy_regexp
draw do
namespace :api do
scope(':version', :version => /.+/) do
resources :users, :id => /.+?/, :format => /json|xml/
end
end
end
get '/api/1.0/users'
assert_equal 'api/users#index', @response.body
assert_equal '/api/1.0/users', api_users_path(:version => '1.0')
get '/api/1.0/users.json'
assert_equal 'api/users#index', @response.body
assert_equal true, @request.format.json?
assert_equal '/api/1.0/users.json', api_users_path(:version => '1.0', :format => :json)
get '/api/1.0/users/first.last'
assert_equal 'api/users#show', @response.body
assert_equal 'first.last', @request.params[:id]
assert_equal '/api/1.0/users/first.last', api_user_path(:version => '1.0', :id => 'first.last')
get '/api/1.0/users/first.last.xml'
assert_equal 'api/users#show', @response.body
assert_equal 'first.last', @request.params[:id]
assert_equal true, @request.format.xml?
assert_equal '/api/1.0/users/first.last.xml', api_user_path(:version => '1.0', :id => 'first.last', :format => :xml)
end
def test_match_without_via
assert_raises(ArgumentError) do
draw do
match '/foo/bar', :to => 'files#show'
end
end
end
def test_match_with_empty_via
assert_raises(ArgumentError) do
draw do
match '/foo/bar', :to => 'files#show', :via => []
end
end
end
def test_glob_parameter_accepts_regexp
draw do
get '/:locale/*file.:format', :to => 'files#show', :file => /path\/to\/existing\/file/
end
get '/en/path/to/existing/file.html'
assert_equal 200, @response.status
end
def test_resources_controller_name_is_not_pluralized
draw do
resources :content
end
get '/content'
assert_equal 'content#index', @response.body
end
def test_url_generator_for_optional_prefix_dynamic_segment
draw do
get "(/:username)/followers" => "followers#index"
end
get '/bob/followers'
assert_equal 'followers#index', @response.body
assert_equal 'http://www.example.com/bob/followers',
url_for(:controller => "followers", :action => "index", :username => "bob")
get '/followers'
assert_equal 'followers#index', @response.body
assert_equal 'http://www.example.com/followers',
url_for(:controller => "followers", :action => "index", :username => nil)
end
def test_url_generator_for_optional_suffix_static_and_dynamic_segment
draw do
get "/groups(/user/:username)" => "groups#index"
end
get '/groups/user/bob'
assert_equal 'groups#index', @response.body
assert_equal 'http://www.example.com/groups/user/bob',
url_for(:controller => "groups", :action => "index", :username => "bob")
get '/groups'
assert_equal 'groups#index', @response.body
assert_equal 'http://www.example.com/groups',
url_for(:controller => "groups", :action => "index", :username => nil)
end
def test_url_generator_for_optional_prefix_static_and_dynamic_segment
draw do
get "(/user/:username)/photos" => "photos#index"
end
get '/user/bob/photos'
assert_equal 'photos#index', @response.body
assert_equal 'http://www.example.com/user/bob/photos',
url_for(:controller => "photos", :action => "index", :username => "bob")
get '/photos'
assert_equal 'photos#index', @response.body
assert_equal 'http://www.example.com/photos',
url_for(:controller => "photos", :action => "index", :username => nil)
end
def test_url_recognition_for_optional_static_segments
draw do
scope '(groups)' do
scope '(discussions)' do
resources :messages
end
end
end
get '/groups/discussions/messages'
assert_equal 'messages#index', @response.body
get '/groups/discussions/messages/1'
assert_equal 'messages#show', @response.body
get '/groups/messages'
assert_equal 'messages#index', @response.body
get '/groups/messages/1'
assert_equal 'messages#show', @response.body
get '/discussions/messages'
assert_equal 'messages#index', @response.body
get '/discussions/messages/1'
assert_equal 'messages#show', @response.body
get '/messages'
assert_equal 'messages#index', @response.body
get '/messages/1'
assert_equal 'messages#show', @response.body
end
def test_router_removes_invalid_conditions
draw do
scope :constraints => { :id => /\d+/ } do
get '/tickets', :to => 'tickets#index', :as => :tickets
end
end
get '/tickets'
assert_equal 'tickets#index', @response.body
assert_equal '/tickets', tickets_path
end
def test_constraints_are_merged_from_scope
draw do
scope :constraints => { :id => /\d{4}/ } do
resources :movies do
resources :reviews
resource :trailer
end
end
end
get '/movies/0001'
assert_equal 'movies#show', @response.body
assert_equal '/movies/0001', movie_path(:id => '0001')
get '/movies/00001'
assert_equal 'Not Found', @response.body
assert_raises(ActionController::UrlGenerationError){ movie_path(:id => '00001') }
get '/movies/0001/reviews'
assert_equal 'reviews#index', @response.body
assert_equal '/movies/0001/reviews', movie_reviews_path(:movie_id => '0001')
get '/movies/00001/reviews'
assert_equal 'Not Found', @response.body
assert_raises(ActionController::UrlGenerationError){ movie_reviews_path(:movie_id => '00001') }
get '/movies/0001/reviews/0001'
assert_equal 'reviews#show', @response.body
assert_equal '/movies/0001/reviews/0001', movie_review_path(:movie_id => '0001', :id => '0001')
get '/movies/00001/reviews/0001'
assert_equal 'Not Found', @response.body
assert_raises(ActionController::UrlGenerationError){ movie_path(:movie_id => '00001', :id => '00001') }
get '/movies/0001/trailer'
assert_equal 'trailers#show', @response.body
assert_equal '/movies/0001/trailer', movie_trailer_path(:movie_id => '0001')
get '/movies/00001/trailer'
assert_equal 'Not Found', @response.body
assert_raises(ActionController::UrlGenerationError){ movie_trailer_path(:movie_id => '00001') }
end
def test_only_should_be_read_from_scope
draw do
scope :only => [:index, :show] do
namespace :only do
resources :clubs do
resources :players
resource :chairman
end
end
end
end
get '/only/clubs'
assert_equal 'only/clubs#index', @response.body
assert_equal '/only/clubs', only_clubs_path
get '/only/clubs/1/edit'
assert_equal 'Not Found', @response.body
assert_raise(NoMethodError) { edit_only_club_path(:id => '1') }
get '/only/clubs/1/players'
assert_equal 'only/players#index', @response.body
assert_equal '/only/clubs/1/players', only_club_players_path(:club_id => '1')
get '/only/clubs/1/players/2/edit'
assert_equal 'Not Found', @response.body
assert_raise(NoMethodError) { edit_only_club_player_path(:club_id => '1', :id => '2') }
get '/only/clubs/1/chairman'
assert_equal 'only/chairmen#show', @response.body
assert_equal '/only/clubs/1/chairman', only_club_chairman_path(:club_id => '1')
get '/only/clubs/1/chairman/edit'
assert_equal 'Not Found', @response.body
assert_raise(NoMethodError) { edit_only_club_chairman_path(:club_id => '1') }
end
def test_except_should_be_read_from_scope
draw do
scope :except => [:new, :create, :edit, :update, :destroy] do
namespace :except do
resources :clubs do
resources :players
resource :chairman
end
end
end
end
get '/except/clubs'
assert_equal 'except/clubs#index', @response.body
assert_equal '/except/clubs', except_clubs_path
get '/except/clubs/1/edit'
assert_equal 'Not Found', @response.body
assert_raise(NoMethodError) { edit_except_club_path(:id => '1') }
get '/except/clubs/1/players'
assert_equal 'except/players#index', @response.body
assert_equal '/except/clubs/1/players', except_club_players_path(:club_id => '1')
get '/except/clubs/1/players/2/edit'
assert_equal 'Not Found', @response.body
assert_raise(NoMethodError) { edit_except_club_player_path(:club_id => '1', :id => '2') }
get '/except/clubs/1/chairman'
assert_equal 'except/chairmen#show', @response.body
assert_equal '/except/clubs/1/chairman', except_club_chairman_path(:club_id => '1')
get '/except/clubs/1/chairman/edit'
assert_equal 'Not Found', @response.body
assert_raise(NoMethodError) { edit_except_club_chairman_path(:club_id => '1') }
end
def test_only_option_should_override_scope
draw do
scope :only => :show do
namespace :only do
resources :sectors, :only => :index
end
end
end
get '/only/sectors'
assert_equal 'only/sectors#index', @response.body
assert_equal '/only/sectors', only_sectors_path
get '/only/sectors/1'
assert_equal 'Not Found', @response.body
assert_raise(NoMethodError) { only_sector_path(:id => '1') }
end
def test_only_option_should_not_inherit
draw do
scope :only => :show do
namespace :only do
resources :sectors, :only => :index do
resources :companies
resource :leader
end
end
end
end
get '/only/sectors/1/companies/2'
assert_equal 'only/companies#show', @response.body
assert_equal '/only/sectors/1/companies/2', only_sector_company_path(:sector_id => '1', :id => '2')
get '/only/sectors/1/leader'
assert_equal 'only/leaders#show', @response.body
assert_equal '/only/sectors/1/leader', only_sector_leader_path(:sector_id => '1')
end
def test_except_option_should_override_scope
draw do
scope :except => :index do
namespace :except do
resources :sectors, :except => [:show, :update, :destroy]
end
end
end
get '/except/sectors'
assert_equal 'except/sectors#index', @response.body
assert_equal '/except/sectors', except_sectors_path
get '/except/sectors/1'
assert_equal 'Not Found', @response.body
assert_raise(NoMethodError) { except_sector_path(:id => '1') }
end
def test_except_option_should_not_inherit
draw do
scope :except => :index do
namespace :except do
resources :sectors, :except => [:show, :update, :destroy] do
resources :companies
resource :leader
end
end
end
end
get '/except/sectors/1/companies/2'
assert_equal 'except/companies#show', @response.body
assert_equal '/except/sectors/1/companies/2', except_sector_company_path(:sector_id => '1', :id => '2')
get '/except/sectors/1/leader'
assert_equal 'except/leaders#show', @response.body
assert_equal '/except/sectors/1/leader', except_sector_leader_path(:sector_id => '1')
end
def test_except_option_should_override_scoped_only
draw do
scope :only => :show do
namespace :only do
resources :sectors, :only => :index do
resources :managers, :except => [:show, :update, :destroy]
end
end
end
end
get '/only/sectors/1/managers'
assert_equal 'only/managers#index', @response.body
assert_equal '/only/sectors/1/managers', only_sector_managers_path(:sector_id => '1')
get '/only/sectors/1/managers/2'
assert_equal 'Not Found', @response.body
assert_raise(NoMethodError) { only_sector_manager_path(:sector_id => '1', :id => '2') }
end
def test_only_option_should_override_scoped_except
draw do
scope :except => :index do
namespace :except do
resources :sectors, :except => [:show, :update, :destroy] do
resources :managers, :only => :index
end
end
end
end
get '/except/sectors/1/managers'
assert_equal 'except/managers#index', @response.body
assert_equal '/except/sectors/1/managers', except_sector_managers_path(:sector_id => '1')
get '/except/sectors/1/managers/2'
assert_equal 'Not Found', @response.body
assert_raise(NoMethodError) { except_sector_manager_path(:sector_id => '1', :id => '2') }
end
def test_only_scope_should_override_parent_scope
draw do
scope :only => :show do
namespace :only do
resources :sectors, :only => :index do
resources :companies do
scope :only => :index do
resources :divisions
end
end
end
end
end
end
get '/only/sectors/1/companies/2/divisions'
assert_equal 'only/divisions#index', @response.body
assert_equal '/only/sectors/1/companies/2/divisions', only_sector_company_divisions_path(:sector_id => '1', :company_id => '2')
get '/only/sectors/1/companies/2/divisions/3'
assert_equal 'Not Found', @response.body
assert_raise(NoMethodError) { only_sector_company_division_path(:sector_id => '1', :company_id => '2', :id => '3') }
end
def test_except_scope_should_override_parent_scope
draw do
scope :except => :index do
namespace :except do
resources :sectors, :except => [:show, :update, :destroy] do
resources :companies do
scope :except => [:show, :update, :destroy] do
resources :divisions
end
end
end
end
end
end
get '/except/sectors/1/companies/2/divisions'
assert_equal 'except/divisions#index', @response.body
assert_equal '/except/sectors/1/companies/2/divisions', except_sector_company_divisions_path(:sector_id => '1', :company_id => '2')
get '/except/sectors/1/companies/2/divisions/3'
assert_equal 'Not Found', @response.body
assert_raise(NoMethodError) { except_sector_company_division_path(:sector_id => '1', :company_id => '2', :id => '3') }
end
def test_except_scope_should_override_parent_only_scope
draw do
scope :only => :show do
namespace :only do
resources :sectors, :only => :index do
resources :companies do
scope :except => [:show, :update, :destroy] do
resources :departments
end
end
end
end
end
end
get '/only/sectors/1/companies/2/departments'
assert_equal 'only/departments#index', @response.body
assert_equal '/only/sectors/1/companies/2/departments', only_sector_company_departments_path(:sector_id => '1', :company_id => '2')
get '/only/sectors/1/companies/2/departments/3'
assert_equal 'Not Found', @response.body
assert_raise(NoMethodError) { only_sector_company_department_path(:sector_id => '1', :company_id => '2', :id => '3') }
end
def test_only_scope_should_override_parent_except_scope
draw do
scope :except => :index do
namespace :except do
resources :sectors, :except => [:show, :update, :destroy] do
resources :companies do
scope :only => :index do
resources :departments
end
end
end
end
end
end
get '/except/sectors/1/companies/2/departments'
assert_equal 'except/departments#index', @response.body
assert_equal '/except/sectors/1/companies/2/departments', except_sector_company_departments_path(:sector_id => '1', :company_id => '2')
get '/except/sectors/1/companies/2/departments/3'
assert_equal 'Not Found', @response.body
assert_raise(NoMethodError) { except_sector_company_department_path(:sector_id => '1', :company_id => '2', :id => '3') }
end
def test_resources_are_not_pluralized
draw do
namespace :transport do
resources :taxis
end
end
get '/transport/taxis'
assert_equal 'transport/taxis#index', @response.body
assert_equal '/transport/taxis', transport_taxis_path
get '/transport/taxis/new'
assert_equal 'transport/taxis#new', @response.body
assert_equal '/transport/taxis/new', new_transport_taxi_path
post '/transport/taxis'
assert_equal 'transport/taxis#create', @response.body
get '/transport/taxis/1'
assert_equal 'transport/taxis#show', @response.body
assert_equal '/transport/taxis/1', transport_taxi_path(:id => '1')
get '/transport/taxis/1/edit'
assert_equal 'transport/taxis#edit', @response.body
assert_equal '/transport/taxis/1/edit', edit_transport_taxi_path(:id => '1')
put '/transport/taxis/1'
assert_equal 'transport/taxis#update', @response.body
delete '/transport/taxis/1'
assert_equal 'transport/taxis#destroy', @response.body
end
def test_singleton_resources_are_not_singularized
draw do
namespace :medical do
resource :taxis
end
end
get '/medical/taxis/new'
assert_equal 'medical/taxis#new', @response.body
assert_equal '/medical/taxis/new', new_medical_taxis_path
post '/medical/taxis'
assert_equal 'medical/taxis#create', @response.body
get '/medical/taxis'
assert_equal 'medical/taxis#show', @response.body
assert_equal '/medical/taxis', medical_taxis_path
get '/medical/taxis/edit'
assert_equal 'medical/taxis#edit', @response.body
assert_equal '/medical/taxis/edit', edit_medical_taxis_path
put '/medical/taxis'
assert_equal 'medical/taxis#update', @response.body
delete '/medical/taxis'
assert_equal 'medical/taxis#destroy', @response.body
end
def test_greedy_resource_id_regexp_doesnt_match_edit_and_custom_action
draw do
resources :sections, :id => /.+/ do
get :preview, :on => :member
end
end
get '/sections/1/edit'
assert_equal 'sections#edit', @response.body
assert_equal '/sections/1/edit', edit_section_path(:id => '1')
get '/sections/1/preview'
assert_equal 'sections#preview', @response.body
assert_equal '/sections/1/preview', preview_section_path(:id => '1')
end
def test_resource_constraints_are_pushed_to_scope
draw do
namespace :wiki do
resources :articles, :id => /[^\/]+/ do
resources :comments, :only => [:create, :new]
end
end
end
get '/wiki/articles/Ruby_on_Rails_3.0'
assert_equal 'wiki/articles#show', @response.body
assert_equal '/wiki/articles/Ruby_on_Rails_3.0', wiki_article_path(:id => 'Ruby_on_Rails_3.0')
get '/wiki/articles/Ruby_on_Rails_3.0/comments/new'
assert_equal 'wiki/comments#new', @response.body
assert_equal '/wiki/articles/Ruby_on_Rails_3.0/comments/new', new_wiki_article_comment_path(:article_id => 'Ruby_on_Rails_3.0')
post '/wiki/articles/Ruby_on_Rails_3.0/comments'
assert_equal 'wiki/comments#create', @response.body
assert_equal '/wiki/articles/Ruby_on_Rails_3.0/comments', wiki_article_comments_path(:article_id => 'Ruby_on_Rails_3.0')
end
def test_resources_path_can_be_a_symbol
draw do
resources :wiki_pages, :path => :pages
resource :wiki_account, :path => :my_account
end
get '/pages'
assert_equal 'wiki_pages#index', @response.body
assert_equal '/pages', wiki_pages_path
get '/pages/Ruby_on_Rails'
assert_equal 'wiki_pages#show', @response.body
assert_equal '/pages/Ruby_on_Rails', wiki_page_path(:id => 'Ruby_on_Rails')
get '/my_account'
assert_equal 'wiki_accounts#show', @response.body
assert_equal '/my_account', wiki_account_path
end
def test_redirect_https
draw do
get 'secure', :to => redirect("/secure/login")
end
with_https do
get '/secure'
verify_redirect 'https://www.example.com/secure/login'
end
end
def test_path_parameters_is_not_stale
draw do
scope '/countries/:country', :constraints => lambda { |params, req| %w(all France).include?(params[:country]) } do
get '/', :to => 'countries#index'
get '/cities', :to => 'countries#cities'
end
get '/countries/:country/(*other)', :to => redirect{ |params, req| params[:other] ? "/countries/all/#{params[:other]}" : '/countries/all' }
end
get '/countries/France'
assert_equal 'countries#index', @response.body
get '/countries/France/cities'
assert_equal 'countries#cities', @response.body
get '/countries/UK'
verify_redirect 'http://www.example.com/countries/all'
get '/countries/UK/cities'
verify_redirect 'http://www.example.com/countries/all/cities'
end
def test_constraints_block_not_carried_to_following_routes
draw do
scope '/italians' do
get '/writers', :to => 'italians#writers', :constraints => ::TestRoutingMapper::IpRestrictor
get '/sculptors', :to => 'italians#sculptors'
get '/painters/:painter', :to => 'italians#painters', :constraints => {:painter => /michelangelo/}
end
end
get '/italians/writers'
assert_equal 'Not Found', @response.body
get '/italians/sculptors'
assert_equal 'italians#sculptors', @response.body
get '/italians/painters/botticelli'
assert_equal 'Not Found', @response.body
get '/italians/painters/michelangelo'
assert_equal 'italians#painters', @response.body
end
def test_custom_resource_actions_defined_using_string
draw do
resources :customers do
resources :invoices do
get "aged/:months", :on => :collection, :action => :aged, :as => :aged
end
get "inactive", :on => :collection
post "deactivate", :on => :member
get "old", :on => :collection, :as => :stale
end
end
get '/customers/inactive'
assert_equal 'customers#inactive', @response.body
assert_equal '/customers/inactive', inactive_customers_path
post '/customers/1/deactivate'
assert_equal 'customers#deactivate', @response.body
assert_equal '/customers/1/deactivate', deactivate_customer_path(:id => '1')
get '/customers/old'
assert_equal 'customers#old', @response.body
assert_equal '/customers/old', stale_customers_path
get '/customers/1/invoices/aged/3'
assert_equal 'invoices#aged', @response.body
assert_equal '/customers/1/invoices/aged/3', aged_customer_invoices_path(:customer_id => '1', :months => '3')
end
def test_route_defined_in_resources_scope_level
draw do
resources :customers do
get "export"
end
end
get '/customers/1/export'
assert_equal 'customers#export', @response.body
assert_equal '/customers/1/export', customer_export_path(:customer_id => '1')
end
def test_named_character_classes_in_regexp_constraints
draw do
get '/purchases/:token/:filename',
:to => 'purchases#fetch',
:token => /[[:alnum:]]{10}/,
:filename => /(.+)/,
:as => :purchase
end
get '/purchases/315004be7e/Ruby_on_Rails_3.pdf'
assert_equal 'purchases#fetch', @response.body
assert_equal '/purchases/315004be7e/Ruby_on_Rails_3.pdf', purchase_path(:token => '315004be7e', :filename => 'Ruby_on_Rails_3.pdf')
end
def test_nested_resource_constraints
draw do
resources :lists, :id => /([A-Za-z0-9]{25})|default/ do
resources :todos, :id => /\d+/
end
end
get '/lists/01234012340123401234fffff'
assert_equal 'lists#show', @response.body
assert_equal '/lists/01234012340123401234fffff', list_path(:id => '01234012340123401234fffff')
get '/lists/01234012340123401234fffff/todos/1'
assert_equal 'todos#show', @response.body
assert_equal '/lists/01234012340123401234fffff/todos/1', list_todo_path(:list_id => '01234012340123401234fffff', :id => '1')
get '/lists/2/todos/1'
assert_equal 'Not Found', @response.body
assert_raises(ActionController::UrlGenerationError){ list_todo_path(:list_id => '2', :id => '1') }
end
def test_redirect_argument_error
routes = Class.new { include ActionDispatch::Routing::Redirection }.new
assert_raises(ArgumentError) { routes.redirect Object.new }
end
def test_named_route_check
before, after = nil
draw do
before = has_named_route?(:hello)
get "/hello", as: :hello, to: "hello#world"
after = has_named_route?(:hello)
end
assert !before, "expected to not have named route :hello before route definition"
assert after, "expected to have named route :hello after route definition"
end
def test_explicitly_avoiding_the_named_route
draw do
scope :as => "routes" do
get "/c/:id", :as => :collision, :to => "collision#show"
get "/collision", :to => "collision#show"
get "/no_collision", :to => "collision#show", :as => nil
end
end
assert !respond_to?(:routes_no_collision_path)
end
def test_controller_name_with_leading_slash_raise_error
assert_raise(ArgumentError) do
draw { get '/feeds/:service', :to => '/feeds#show' }
end
assert_raise(ArgumentError) do
draw { get '/feeds/:service', :controller => '/feeds', :action => 'show' }
end
assert_raise(ArgumentError) do
draw { get '/api/feeds/:service', :to => '/api/feeds#show' }
end
assert_raise(ArgumentError) do
draw { resources :feeds, :controller => '/feeds' }
end
end
def test_invalid_route_name_raises_error
assert_raise(ArgumentError) do
draw { get '/products', :to => 'products#index', :as => 'products ' }
end
assert_raise(ArgumentError) do
draw { get '/products', :to => 'products#index', :as => ' products' }
end
assert_raise(ArgumentError) do
draw { get '/products', :to => 'products#index', :as => 'products!' }
end
assert_raise(ArgumentError) do
draw { get '/products', :to => 'products#index', :as => 'products index' }
end
assert_raise(ArgumentError) do
draw { get '/products', :to => 'products#index', :as => '1products' }
end
end
def test_duplicate_route_name_raises_error
assert_raise(ArgumentError) do
draw do
get '/collision', :to => 'collision#show', :as => 'collision'
get '/duplicate', :to => 'duplicate#show', :as => 'collision'
end
end
end
def test_duplicate_route_name_via_resources_raises_error
assert_raise(ArgumentError) do
draw do
resources :collisions
get '/collision', :to => 'collision#show', :as => 'collision'
end
end
end
def test_nested_route_in_nested_resource
draw do
resources :posts, :only => [:index, :show] do
resources :comments, :except => :destroy do
get "views" => "comments#views", :as => :views
end
end
end
get "/posts/1/comments/2/views"
assert_equal "comments#views", @response.body
assert_equal "/posts/1/comments/2/views", post_comment_views_path(:post_id => '1', :comment_id => '2')
end
def test_root_in_deeply_nested_scope
draw do
resources :posts, :only => [:index, :show] do
namespace :admin do
root :to => "index#index"
end
end
end
get "/posts/1/admin"
assert_equal "admin/index#index", @response.body
assert_equal "/posts/1/admin", post_admin_root_path(:post_id => '1')
end
def test_custom_param
draw do
resources :profiles, :param => :username do
get :details, :on => :member
resources :messages
end
end
get '/profiles/bob'
assert_equal 'profiles#show', @response.body
assert_equal 'bob', @request.params[:username]
get '/profiles/bob/details'
assert_equal 'bob', @request.params[:username]
get '/profiles/bob/messages/34'
assert_equal 'bob', @request.params[:profile_username]
assert_equal '34', @request.params[:id]
end
def test_custom_param_constraint
draw do
resources :profiles, :param => :username, :username => /[a-z]+/ do
get :details, :on => :member
resources :messages
end
end
get '/profiles/bob1'
assert_equal 404, @response.status
get '/profiles/bob1/details'
assert_equal 404, @response.status
get '/profiles/bob1/messages/34'
assert_equal 404, @response.status
end
def test_shallow_custom_param
draw do
resources :orders do
constraints :download => /[0-9a-f]{8}-(?:[0-9a-f]{4}-){3}[0-9a-f]{12}/ do
resources :downloads, :param => :download, :shallow => true
end
end
end
get '/downloads/0c0c0b68-d24b-11e1-a861-001ff3fffe6f.zip'
assert_equal 'downloads#show', @response.body
assert_equal '0c0c0b68-d24b-11e1-a861-001ff3fffe6f', @request.params[:download]
end
def test_action_from_path_is_not_frozen
draw do
get 'search' => 'search'
end
get '/search'
assert !@request.params[:action].frozen?
end
def test_multiple_positional_args_with_the_same_name
draw do
get '/downloads/:id/:id.tar' => 'downloads#show', as: :download, format: false
end
expected_params = {
controller: 'downloads',
action: 'show',
id: '1'
}
get '/downloads/1/1.tar'
assert_equal 'downloads#show', @response.body
assert_equal expected_params, @request.path_parameters
assert_equal '/downloads/1/1.tar', download_path('1')
assert_equal '/downloads/1/1.tar', download_path('1', '1')
end
def test_absolute_controller_namespace
draw do
namespace :foo do
get '/', to: '/bar#index', as: 'root'
end
end
get '/foo'
assert_equal 'bar#index', @response.body
assert_equal '/foo', foo_root_path
end
def test_namespace_as_controller
draw do
namespace :foo do
get '/', to: '/bar#index', as: 'root'
end
end
get '/foo'
assert_equal 'bar#index', @response.body
assert_equal '/foo', foo_root_path
end
def test_trailing_slash
draw do
resources :streams
end
get '/streams'
assert @response.ok?, 'route without trailing slash should work'
get '/streams/'
assert @response.ok?, 'route with trailing slash should work'
get '/streams?foobar'
assert @response.ok?, 'route without trailing slash and with QUERY_STRING should work'
get '/streams/?foobar'
assert @response.ok?, 'route with trailing slash and with QUERY_STRING should work'
end
def test_route_with_dashes_in_path
draw do
get '/contact-us', to: 'pages#contact_us'
end
get '/contact-us'
assert_equal 'pages#contact_us', @response.body
assert_equal '/contact-us', contact_us_path
end
def test_shorthand_route_with_dashes_in_path
draw do
get '/about-us/index'
end
get '/about-us/index'
assert_equal 'about_us#index', @response.body
assert_equal '/about-us/index', about_us_index_path
end
def test_resource_routes_with_dashes_in_path
draw do
resources :photos, only: [:show] do
get 'user-favorites', on: :collection
get 'preview-photo', on: :member
get 'summary-text'
end
end
get '/photos/user-favorites'
assert_equal 'photos#user_favorites', @response.body
assert_equal '/photos/user-favorites', user_favorites_photos_path
get '/photos/1/preview-photo'
assert_equal 'photos#preview_photo', @response.body
assert_equal '/photos/1/preview-photo', preview_photo_photo_path('1')
get '/photos/1/summary-text'
assert_equal 'photos#summary_text', @response.body
assert_equal '/photos/1/summary-text', photo_summary_text_path('1')
get '/photos/1'
assert_equal 'photos#show', @response.body
assert_equal '/photos/1', photo_path('1')
end
def test_shallow_path_inside_namespace_is_not_added_twice
draw do
namespace :admin do
shallow do
resources :posts do
resources :comments
end
end
end
end
get '/admin/posts/1/comments'
assert_equal 'admin/comments#index', @response.body
assert_equal '/admin/posts/1/comments', admin_post_comments_path('1')
end
def test_mix_string_to_controller_action
draw do
get '/projects', controller: 'project_files',
action: 'index',
to: 'comments#index'
end
get '/projects'
assert_equal 'comments#index', @response.body
end
def test_mix_string_to_controller
draw do
get '/projects', controller: 'project_files',
to: 'comments#index'
end
get '/projects'
assert_equal 'comments#index', @response.body
end
def test_mix_string_to_action
draw do
get '/projects', action: 'index',
to: 'comments#index'
end
get '/projects'
assert_equal 'comments#index', @response.body
end
def test_shallow_path_and_prefix_are_not_added_to_non_shallow_routes
draw do
scope shallow_path: 'projects', shallow_prefix: 'project' do
resources :projects do
resources :files, controller: 'project_files', shallow: true
end
end
end
get '/projects'
assert_equal 'projects#index', @response.body
assert_equal '/projects', projects_path
get '/projects/new'
assert_equal 'projects#new', @response.body
assert_equal '/projects/new', new_project_path
post '/projects'
assert_equal 'projects#create', @response.body
get '/projects/1'
assert_equal 'projects#show', @response.body
assert_equal '/projects/1', project_path('1')
get '/projects/1/edit'
assert_equal 'projects#edit', @response.body
assert_equal '/projects/1/edit', edit_project_path('1')
patch '/projects/1'
assert_equal 'projects#update', @response.body
delete '/projects/1'
assert_equal 'projects#destroy', @response.body
get '/projects/1/files'
assert_equal 'project_files#index', @response.body
assert_equal '/projects/1/files', project_files_path('1')
get '/projects/1/files/new'
assert_equal 'project_files#new', @response.body
assert_equal '/projects/1/files/new', new_project_file_path('1')
post '/projects/1/files'
assert_equal 'project_files#create', @response.body
get '/projects/files/2'
assert_equal 'project_files#show', @response.body
assert_equal '/projects/files/2', project_file_path('2')
get '/projects/files/2/edit'
assert_equal 'project_files#edit', @response.body
assert_equal '/projects/files/2/edit', edit_project_file_path('2')
patch '/projects/files/2'
assert_equal 'project_files#update', @response.body
delete '/projects/files/2'
assert_equal 'project_files#destroy', @response.body
end
def test_scope_path_is_copied_to_shallow_path
draw do
scope path: 'foo' do
resources :posts do
resources :comments, shallow: true
end
end
end
assert_equal '/foo/comments/1', comment_path('1')
end
def test_scope_as_is_copied_to_shallow_prefix
draw do
scope as: 'foo' do
resources :posts do
resources :comments, shallow: true
end
end
end
assert_equal '/comments/1', foo_comment_path('1')
end
def test_scope_shallow_prefix_is_not_overwritten_by_as
draw do
scope as: 'foo', shallow_prefix: 'bar' do
resources :posts do
resources :comments, shallow: true
end
end
end
assert_equal '/comments/1', bar_comment_path('1')
end
def test_scope_shallow_path_is_not_overwritten_by_path
draw do
scope path: 'foo', shallow_path: 'bar' do
resources :posts do
resources :comments, shallow: true
end
end
end
assert_equal '/bar/comments/1', comment_path('1')
end
def test_resource_where_as_is_empty
draw do
resource :post, as: ''
scope 'post', as: 'post' do
resource :comment, as: ''
end
end
assert_equal '/post/new', new_path
assert_equal '/post/comment/new', new_post_path
end
def test_resources_where_as_is_empty
draw do
resources :posts, as: ''
scope 'posts', as: 'posts' do
resources :comments, as: ''
end
end
assert_equal '/posts/new', new_path
assert_equal '/posts/comments/new', new_posts_path
end
def test_scope_where_as_is_empty
draw do
scope 'post', as: '' do
resource :user
resources :comments
end
end
assert_equal '/post/user/new', new_user_path
assert_equal '/post/comments/new', new_comment_path
end
def test_head_fetch_with_mount_on_root
draw do
get '/home' => 'test#index'
mount lambda { |env| [200, {}, [env['REQUEST_METHOD']]] }, at: '/'
end
# HEAD request should match `get /home` rather than the
# lower-precedence Rack app mounted at `/`.
head '/home'
assert_response :ok
assert_equal 'test#index', @response.body
# But the Rack app can still respond to its own HEAD requests.
head '/foobar'
assert_response :ok
assert_equal 'HEAD', @response.body
end
def test_passing_action_parameters_to_url_helpers_raises_error_if_parameters_are_not_permitted
draw do
root :to => 'projects#index'
end
params = ActionController::Parameters.new(id: '1')
assert_raises ArgumentError do
root_path(params)
end
end
def test_passing_action_parameters_to_url_helpers_is_allowed_if_parameters_are_permitted
draw do
root :to => 'projects#index'
end
params = ActionController::Parameters.new(id: '1')
params.permit!
assert_equal '/?id=1', root_path(params)
end
def test_dynamic_controller_segments_are_deprecated
assert_deprecated do
draw do
get '/:controller', action: 'index'
end
end
end
def test_dynamic_action_segments_are_deprecated
assert_deprecated do
draw do
get '/pages/:action', controller: 'pages'
end
end
end
private
def draw(&block)
self.class.stub_controllers do |routes|
routes.default_url_options = { host: 'www.example.com' }
routes.draw(&block)
@app = RoutedRackApp.new routes
end
end
def url_for(options = {})
@app.routes.url_helpers.url_for(options)
end
def method_missing(method, *args, &block)
if method.to_s =~ /_(path|url)$/
@app.routes.url_helpers.send(method, *args, &block)
else
super
end
end
def with_https
old_https = https?
https!
yield
ensure
https!(old_https)
end
def verify_redirect(url, status=301)
assert_equal status, @response.status
assert_equal url, @response.headers['Location']
assert_equal expected_redirect_body(url), @response.body
end
def expected_redirect_body(url)
%(<html><body>You are being <a href="#{ERB::Util.h(url)}">redirected</a>.</body></html>)
end
end
class TestAltApp < ActionDispatch::IntegrationTest
class AltRequest < ActionDispatch::Request
attr_accessor :path_parameters, :path_info, :script_name
attr_reader :env
def initialize(env)
@path_parameters = {}
@env = env
@path_info = "/"
@script_name = ""
super
end
def request_method
"GET"
end
def ip
"127.0.0.1"
end
def x_header
@env["HTTP_X_HEADER"] || ""
end
end
class XHeader
def call(env)
[200, {"Content-Type" => "text/html"}, ["XHeader"]]
end
end
class AltApp
def call(env)
[200, {"Content-Type" => "text/html"}, ["Alternative App"]]
end
end
AltRoutes = Class.new(ActionDispatch::Routing::RouteSet) {
def request_class
AltRequest
end
}.new
AltRoutes.draw do
get "/" => TestAltApp::XHeader.new, :constraints => {:x_header => /HEADER/}
get "/" => TestAltApp::AltApp.new
end
APP = build_app AltRoutes
def app
APP
end
def test_alt_request_without_header
get "/"
assert_equal "Alternative App", @response.body
end
def test_alt_request_with_matched_header
get "/", headers: { "HTTP_X_HEADER" => "HEADER" }
assert_equal "XHeader", @response.body
end
def test_alt_request_with_unmatched_header
get "/", headers: { "HTTP_X_HEADER" => "NON_MATCH" }
assert_equal "Alternative App", @response.body
end
end
class TestAppendingRoutes < ActionDispatch::IntegrationTest
def simple_app(resp)
lambda { |e| [ 200, { 'Content-Type' => 'text/plain' }, [resp] ] }
end
def setup
super
s = self
routes = ActionDispatch::Routing::RouteSet.new
routes.append do
get '/hello' => s.simple_app('fail')
get '/goodbye' => s.simple_app('goodbye')
end
routes.draw do
get '/hello' => s.simple_app('hello')
end
@app = self.class.build_app routes
end
def test_goodbye_should_be_available
get '/goodbye'
assert_equal 'goodbye', @response.body
end
def test_hello_should_not_be_overwritten
get '/hello'
assert_equal 'hello', @response.body
end
def test_missing_routes_are_still_missing
get '/random'
assert_equal 404, @response.status
end
end
class TestNamespaceWithControllerOption < ActionDispatch::IntegrationTest
module ::Admin
class StorageFilesController < ActionController::Base
def index
render plain: "admin/storage_files#index"
end
end
end
def draw(&block)
routes = ActionDispatch::Routing::RouteSet.new
routes.draw(&block)
@app = self.class.build_app routes
end
def test_missing_controller
ex = assert_raises(ArgumentError) {
draw do
get '/foo/bar', :action => :index
end
}
assert_match(/Missing :controller/, ex.message)
end
def test_missing_controller_with_to
ex = assert_raises(ArgumentError) {
draw do
get '/foo/bar', :to => 'foo'
end
}
assert_match(/Missing :controller/, ex.message)
end
def test_missing_action_on_hash
ex = assert_raises(ArgumentError) {
draw do
get '/foo/bar', :to => 'foo#'
end
}
assert_match(/Missing :action/, ex.message)
end
def test_valid_controller_options_inside_namespace
draw do
namespace :admin do
resources :storage_files, :controller => "storage_files"
end
end
get '/admin/storage_files'
assert_equal "admin/storage_files#index", @response.body
end
def test_resources_with_valid_namespaced_controller_option
draw do
resources :storage_files, :controller => 'admin/storage_files'
end
get '/storage_files'
assert_equal "admin/storage_files#index", @response.body
end
def test_warn_with_ruby_constant_syntax_controller_option
e = assert_raise(ArgumentError) do
draw do
namespace :admin do
resources :storage_files, :controller => "StorageFiles"
end
end
end
assert_match "'admin/StorageFiles' is not a supported controller name", e.message
end
def test_warn_with_ruby_constant_syntax_namespaced_controller_option
e = assert_raise(ArgumentError) do
draw do
resources :storage_files, :controller => 'Admin::StorageFiles'
end
end
assert_match "'Admin::StorageFiles' is not a supported controller name", e.message
end
def test_warn_with_ruby_constant_syntax_no_colons
e = assert_raise(ArgumentError) do
draw do
resources :storage_files, :controller => 'Admin'
end
end
assert_match "'Admin' is not a supported controller name", e.message
end
end
class TestDefaultScope < ActionDispatch::IntegrationTest
module ::Blog
class PostsController < ActionController::Base
def index
render plain: "blog/posts#index"
end
end
end
DefaultScopeRoutes = ActionDispatch::Routing::RouteSet.new
DefaultScopeRoutes.default_scope = {:module => :blog}
DefaultScopeRoutes.draw do
resources :posts
end
APP = build_app DefaultScopeRoutes
def app
APP
end
include DefaultScopeRoutes.url_helpers
def test_default_scope
get '/posts'
assert_equal "blog/posts#index", @response.body
end
end
class TestHttpMethods < ActionDispatch::IntegrationTest
RFC2616 = %w(OPTIONS GET HEAD POST PUT DELETE TRACE CONNECT)
RFC2518 = %w(PROPFIND PROPPATCH MKCOL COPY MOVE LOCK UNLOCK)
RFC3253 = %w(VERSION-CONTROL REPORT CHECKOUT CHECKIN UNCHECKOUT MKWORKSPACE UPDATE LABEL MERGE BASELINE-CONTROL MKACTIVITY)
RFC3648 = %w(ORDERPATCH)
RFC3744 = %w(ACL)
RFC5323 = %w(SEARCH)
RFC4791 = %w(MKCALENDAR)
RFC5789 = %w(PATCH)
def simple_app(response)
lambda { |env| [ 200, { 'Content-Type' => 'text/plain' }, [response] ] }
end
attr_reader :app
def setup
s = self
routes = ActionDispatch::Routing::RouteSet.new
@app = RoutedRackApp.new routes
routes.draw do
(RFC2616 + RFC2518 + RFC3253 + RFC3648 + RFC3744 + RFC5323 + RFC4791 + RFC5789).each do |method|
match '/' => s.simple_app(method), :via => method.underscore.to_sym
end
end
end
(RFC2616 + RFC2518 + RFC3253 + RFC3648 + RFC3744 + RFC5323 + RFC4791 + RFC5789).each do |method|
test "request method #{method.underscore} can be matched" do
get '/', headers: { 'REQUEST_METHOD' => method }
assert_equal method, @response.body
end
end
end
class TestUriPathEscaping < ActionDispatch::IntegrationTest
Routes = ActionDispatch::Routing::RouteSet.new.tap do |app|
app.draw do
get '/:segment' => lambda { |env|
path_params = env['action_dispatch.request.path_parameters']
[200, { 'Content-Type' => 'text/plain' }, [path_params[:segment]]]
}, :as => :segment
get '/*splat' => lambda { |env|
path_params = env['action_dispatch.request.path_parameters']
[200, { 'Content-Type' => 'text/plain' }, [path_params[:splat]]]
}, :as => :splat
end
end
include Routes.url_helpers
APP = build_app Routes
def app; APP end
test 'escapes slash in generated path segment' do
assert_equal '/a%20b%2Fc+d', segment_path(:segment => 'a b/c+d')
end
test 'unescapes recognized path segment' do
get '/a%20b%2Fc+d'
assert_equal 'a b/c+d', @response.body
end
test 'does not escape slash in generated path splat' do
assert_equal '/a%20b/c+d', splat_path(:splat => 'a b/c+d')
end
test 'unescapes recognized path splat' do
get '/a%20b/c+d'
assert_equal 'a b/c+d', @response.body
end
end
class TestUnicodePaths < ActionDispatch::IntegrationTest
Routes = ActionDispatch::Routing::RouteSet.new.tap do |app|
app.draw do
get "/ほげ" => lambda { |env|
[200, { 'Content-Type' => 'text/plain' }, []]
}, :as => :unicode_path
end
end
include Routes.url_helpers
APP = build_app Routes
def app; APP end
test 'recognizes unicode path' do
get "/#{Rack::Utils.escape("ほげ")}"
assert_equal "200", @response.code
end
end
class TestMultipleNestedController < ActionDispatch::IntegrationTest
module ::Foo
module Bar
class BazController < ActionController::Base
def index
render :inline => "<%= url_for :controller => '/pooh', :action => 'index' %>"
end
end
end
end
Routes = ActionDispatch::Routing::RouteSet.new.tap do |app|
app.draw do
namespace :foo do
namespace :bar do
get "baz" => "baz#index"
end
end
get "pooh" => "pooh#index"
end
end
include Routes.url_helpers
APP = build_app Routes
def app; APP end
test "controller option which starts with '/' from multiple nested controller" do
get "/foo/bar/baz"
assert_equal "/pooh", @response.body
end
end
class TestTildeAndMinusPaths < ActionDispatch::IntegrationTest
Routes = ActionDispatch::Routing::RouteSet.new.tap do |app|
app.draw do
ok = lambda { |env| [200, { 'Content-Type' => 'text/plain' }, []] }
get "/~user" => ok
get "/young-and-fine" => ok
end
end
include Routes.url_helpers
APP = build_app Routes
def app; APP end
test 'recognizes tilde path' do
get "/~user"
assert_equal "200", @response.code
end
test 'recognizes minus path' do
get "/young-and-fine"
assert_equal "200", @response.code
end
end
class TestRedirectInterpolation < ActionDispatch::IntegrationTest
Routes = ActionDispatch::Routing::RouteSet.new.tap do |app|
app.draw do
ok = lambda { |env| [200, { 'Content-Type' => 'text/plain' }, []] }
get "/foo/:id" => redirect("/foo/bar/%{id}")
get "/bar/:id" => redirect(:path => "/foo/bar/%{id}")
get "/baz/:id" => redirect("/baz?id=%{id}&foo=?&bar=1#id-%{id}")
get "/foo/bar/:id" => ok
get "/baz" => ok
end
end
APP = build_app Routes
def app; APP end
test "redirect escapes interpolated parameters with redirect proc" do
get "/foo/1%3E"
verify_redirect "http://www.example.com/foo/bar/1%3E"
end
test "redirect escapes interpolated parameters with option proc" do
get "/bar/1%3E"
verify_redirect "http://www.example.com/foo/bar/1%3E"
end
test "path redirect escapes interpolated parameters correctly" do
get "/foo/1%201"
verify_redirect "http://www.example.com/foo/bar/1%201"
get "/baz/1%201"
verify_redirect "http://www.example.com/baz?id=1+1&foo=?&bar=1#id-1%201"
end
private
def verify_redirect(url, status=301)
assert_equal status, @response.status
assert_equal url, @response.headers['Location']
assert_equal expected_redirect_body(url), @response.body
end
def expected_redirect_body(url)
%(<html><body>You are being <a href="#{ERB::Util.h(url)}">redirected</a>.</body></html>)
end
end
class TestConstraintsAccessingParameters < ActionDispatch::IntegrationTest
Routes = ActionDispatch::Routing::RouteSet.new.tap do |app|
app.draw do
ok = lambda { |env| [200, { 'Content-Type' => 'text/plain' }, []] }
get "/:foo" => ok, :constraints => lambda { |r| r.params[:foo] == 'foo' }
get "/:bar" => ok
end
end
APP = build_app Routes
def app; APP end
test "parameters are reset between constraint checks" do
get "/bar"
assert_equal nil, @request.params[:foo]
assert_equal "bar", @request.params[:bar]
end
end
class TestGlobRoutingMapper < ActionDispatch::IntegrationTest
Routes = ActionDispatch::Routing::RouteSet.new.tap do |app|
app.draw do
ok = lambda { |env| [200, { 'Content-Type' => 'text/plain' }, []] }
get "/*id" => redirect("/not_cars"), :constraints => {id: /dummy/}
get "/cars" => ok
end
end
#include Routes.url_helpers
APP = build_app Routes
def app; APP end
def test_glob_constraint
get "/dummy"
assert_equal "301", @response.code
assert_equal "/not_cars", @response.header['Location'].match('/[^/]+$')[0]
end
def test_glob_constraint_skip_route
get "/cars"
assert_equal "200", @response.code
end
def test_glob_constraint_skip_all
get "/missing"
assert_equal "404", @response.code
end
end
class TestOptimizedNamedRoutes < ActionDispatch::IntegrationTest
Routes = ActionDispatch::Routing::RouteSet.new.tap do |app|
app.draw do
ok = lambda { |env| [200, { 'Content-Type' => 'text/plain' }, []] }
get '/foo' => ok, as: :foo
ActiveSupport::Deprecation.silence do
get '/post(/:action(/:id))' => ok, as: :posts
end
get '/:foo/:foo_type/bars/:id' => ok, as: :bar
get '/projects/:id.:format' => ok, as: :project
get '/pages/:id' => ok, as: :page
get '/wiki/*page' => ok, as: :wiki
end
end
include Routes.url_helpers
APP = build_app Routes
def app; APP end
test 'enabled when not mounted and default_url_options is empty' do
assert Routes.url_helpers.optimize_routes_generation?
end
test 'named route called as singleton method' do
assert_equal '/foo', Routes.url_helpers.foo_path
end
test 'named route called on included module' do
assert_equal '/foo', foo_path
end
test 'nested optional segments are removed' do
assert_equal '/post', Routes.url_helpers.posts_path
assert_equal '/post', posts_path
end
test 'segments with same prefix are replaced correctly' do
assert_equal '/foo/baz/bars/1', Routes.url_helpers.bar_path('foo', 'baz', '1')
assert_equal '/foo/baz/bars/1', bar_path('foo', 'baz', '1')
end
test 'segments separated with a period are replaced correctly' do
assert_equal '/projects/1.json', Routes.url_helpers.project_path(1, :json)
assert_equal '/projects/1.json', project_path(1, :json)
end
test 'segments with question marks are escaped' do
assert_equal '/pages/foo%3Fbar', Routes.url_helpers.page_path('foo?bar')
assert_equal '/pages/foo%3Fbar', page_path('foo?bar')
end
test 'segments with slashes are escaped' do
assert_equal '/pages/foo%2Fbar', Routes.url_helpers.page_path('foo/bar')
assert_equal '/pages/foo%2Fbar', page_path('foo/bar')
end
test 'glob segments with question marks are escaped' do
assert_equal '/wiki/foo%3Fbar', Routes.url_helpers.wiki_path('foo?bar')
assert_equal '/wiki/foo%3Fbar', wiki_path('foo?bar')
end
test 'glob segments with slashes are not escaped' do
assert_equal '/wiki/foo/bar', Routes.url_helpers.wiki_path('foo/bar')
assert_equal '/wiki/foo/bar', wiki_path('foo/bar')
end
end
class TestNamedRouteUrlHelpers < ActionDispatch::IntegrationTest
class CategoriesController < ActionController::Base
def show
render plain: "categories#show"
end
end
class ProductsController < ActionController::Base
def show
render plain: "products#show"
end
end
Routes = ActionDispatch::Routing::RouteSet.new.tap do |app|
app.draw do
scope :module => "test_named_route_url_helpers" do
get "/categories/:id" => 'categories#show', :as => :category
get "/products/:id" => 'products#show', :as => :product
end
end
end
APP = build_app Routes
def app; APP end
include Routes.url_helpers
test "url helpers do not ignore nil parameters when using non-optimized routes" do
Routes.stub :optimize_routes_generation?, false do
get "/categories/1"
assert_response :success
assert_raises(ActionController::UrlGenerationError) { product_path(nil) }
end
end
end
class TestUrlConstraints < ActionDispatch::IntegrationTest
Routes = ActionDispatch::Routing::RouteSet.new.tap do |app|
app.draw do
ok = lambda { |env| [200, { 'Content-Type' => 'text/plain' }, []] }
constraints :subdomain => 'admin' do
get '/' => ok, :as => :admin_root
end
scope :constraints => { :protocol => 'https://' } do
get '/' => ok, :as => :secure_root
end
get '/' => ok, :as => :alternate_root, :constraints => { :port => 8080 }
get '/search' => ok, :constraints => { :subdomain => false }
get '/logs' => ok, :constraints => { :subdomain => true }
end
end
include Routes.url_helpers
APP = build_app Routes
def app; APP end
test "constraints are copied to defaults when using constraints method" do
assert_equal 'http://admin.example.com/', admin_root_url
get 'http://admin.example.com/'
assert_response :success
end
test "constraints are copied to defaults when using scope constraints hash" do
assert_equal 'https://www.example.com/', secure_root_url
get 'https://www.example.com/'
assert_response :success
end
test "constraints are copied to defaults when using route constraints hash" do
assert_equal 'http://www.example.com:8080/', alternate_root_url
get 'http://www.example.com:8080/'
assert_response :success
end
test "false constraint expressions check for absence of values" do
get 'http://example.com/search'
assert_response :success
assert_equal 'http://example.com/search', search_url
get 'http://api.example.com/search'
assert_response :not_found
end
test "true constraint expressions check for presence of values" do
get 'http://api.example.com/logs'
assert_response :success
assert_equal 'http://api.example.com/logs', logs_url
get 'http://example.com/logs'
assert_response :not_found
end
end
class TestInvalidUrls < ActionDispatch::IntegrationTest
class FooController < ActionController::Base
def show
render plain: "foo#show"
end
end
test "invalid UTF-8 encoding returns a 400 Bad Request" do
with_routing do |set|
ActiveSupport::Deprecation.silence do
set.draw do
get "/bar/:id", :to => redirect("/foo/show/%{id}")
get "/foo/show(/:id)", :to => "test_invalid_urls/foo#show"
ActiveSupport::Deprecation.silence do
get "/foo(/:action(/:id))", :controller => "test_invalid_urls/foo"
get "/:controller(/:action(/:id))"
end
end
end
get "/%E2%EF%BF%BD%A6"
assert_response :bad_request
get "/foo/%E2%EF%BF%BD%A6"
assert_response :bad_request
get "/foo/show/%E2%EF%BF%BD%A6"
assert_response :bad_request
get "/bar/%E2%EF%BF%BD%A6"
assert_response :bad_request
end
end
end
class TestOptionalRootSegments < ActionDispatch::IntegrationTest
stub_controllers do |routes|
Routes = routes
Routes.draw do
get '/(page/:page)', :to => 'pages#index', :as => :root
end
end
APP = build_app Routes
def app
APP
end
include Routes.url_helpers
def test_optional_root_segments
get '/'
assert_equal 'pages#index', @response.body
assert_equal '/', root_path
get '/page/1'
assert_equal 'pages#index', @response.body
assert_equal '1', @request.params[:page]
assert_equal '/page/1', root_path('1')
assert_equal '/page/1', root_path(:page => '1')
end
end
class TestPortConstraints < ActionDispatch::IntegrationTest
Routes = ActionDispatch::Routing::RouteSet.new.tap do |app|
app.draw do
ok = lambda { |env| [200, { 'Content-Type' => 'text/plain' }, []] }
get '/integer', to: ok, constraints: { :port => 8080 }
get '/string', to: ok, constraints: { :port => '8080' }
get '/array', to: ok, constraints: { :port => [8080] }
get '/regexp', to: ok, constraints: { :port => /8080/ }
end
end
include Routes.url_helpers
APP = build_app Routes
def app; APP end
def test_integer_port_constraints
get 'http://www.example.com/integer'
assert_response :not_found
get 'http://www.example.com:8080/integer'
assert_response :success
end
def test_string_port_constraints
get 'http://www.example.com/string'
assert_response :not_found
get 'http://www.example.com:8080/string'
assert_response :success
end
def test_array_port_constraints
get 'http://www.example.com/array'
assert_response :not_found
get 'http://www.example.com:8080/array'
assert_response :success
end
def test_regexp_port_constraints
get 'http://www.example.com/regexp'
assert_response :not_found
get 'http://www.example.com:8080/regexp'
assert_response :success
end
end
class TestFormatConstraints < ActionDispatch::IntegrationTest
Routes = ActionDispatch::Routing::RouteSet.new.tap do |app|
app.draw do
ok = lambda { |env| [200, { 'Content-Type' => 'text/plain' }, []] }
get '/string', to: ok, constraints: { format: 'json' }
get '/regexp', to: ok, constraints: { format: /json/ }
get '/json_only', to: ok, format: true, constraints: { format: /json/ }
get '/xml_only', to: ok, format: 'xml'
end
end
include Routes.url_helpers
APP = build_app Routes
def app; APP end
def test_string_format_constraints
get 'http://www.example.com/string'
assert_response :success
get 'http://www.example.com/string.json'
assert_response :success
get 'http://www.example.com/string.html'
assert_response :not_found
end
def test_regexp_format_constraints
get 'http://www.example.com/regexp'
assert_response :success
get 'http://www.example.com/regexp.json'
assert_response :success
get 'http://www.example.com/regexp.html'
assert_response :not_found
end
def test_enforce_with_format_true_with_constraint
get 'http://www.example.com/json_only.json'
assert_response :success
get 'http://www.example.com/json_only.html'
assert_response :not_found
get 'http://www.example.com/json_only'
assert_response :not_found
end
def test_enforce_with_string
get 'http://www.example.com/xml_only.xml'
assert_response :success
get 'http://www.example.com/xml_only'
assert_response :success
get 'http://www.example.com/xml_only.json'
assert_response :not_found
end
end
class TestCallableConstraintValidation < ActionDispatch::IntegrationTest
def test_constraint_with_object_not_callable
assert_raises(ArgumentError) do
ActionDispatch::Routing::RouteSet.new.draw do
ok = lambda { |env| [200, { 'Content-Type' => 'text/plain' }, []] }
get '/test', to: ok, constraints: Object.new
end
end
end
end
class TestRouteDefaults < ActionDispatch::IntegrationTest
stub_controllers do |routes|
Routes = routes
Routes.draw do
resources :posts, bucket_type: 'post'
resources :projects, defaults: { bucket_type: 'project' }
end
end
APP = build_app Routes
def app
APP
end
include Routes.url_helpers
def test_route_options_are_required_for_url_for
assert_raises(ActionController::UrlGenerationError) do
assert_equal '/posts/1', url_for(controller: 'posts', action: 'show', id: 1, only_path: true)
end
assert_equal '/posts/1', url_for(controller: 'posts', action: 'show', id: 1, bucket_type: 'post', only_path: true)
end
def test_route_defaults_are_not_required_for_url_for
assert_equal '/projects/1', url_for(controller: 'projects', action: 'show', id: 1, only_path: true)
end
end
class TestRackAppRouteGeneration < ActionDispatch::IntegrationTest
stub_controllers do |routes|
Routes = routes
Routes.draw do
rack_app = lambda { |env| [200, { 'Content-Type' => 'text/plain' }, []] }
mount rack_app, at: '/account', as: 'account'
mount rack_app, at: '/:locale/account', as: 'localized_account'
end
end
APP = build_app Routes
def app
APP
end
include Routes.url_helpers
def test_mounted_application_doesnt_match_unnamed_route
assert_raise(ActionController::UrlGenerationError) do
assert_equal '/account?controller=products', url_for(controller: 'products', action: 'index', only_path: true)
end
assert_raise(ActionController::UrlGenerationError) do
assert_equal '/de/account?controller=products', url_for(controller: 'products', action: 'index', :locale => 'de', only_path: true)
end
end
end
class TestRedirectRouteGeneration < ActionDispatch::IntegrationTest
stub_controllers do |routes|
Routes = routes
Routes.draw do
get '/account', to: redirect('/myaccount'), as: 'account'
get '/:locale/account', to: redirect('/%{locale}/myaccount'), as: 'localized_account'
end
end
APP = build_app Routes
def app
APP
end
include Routes.url_helpers
def test_redirect_doesnt_match_unnamed_route
assert_raise(ActionController::UrlGenerationError) do
assert_equal '/account?controller=products', url_for(controller: 'products', action: 'index', only_path: true)
end
assert_raise(ActionController::UrlGenerationError) do
assert_equal '/de/account?controller=products', url_for(controller: 'products', action: 'index', :locale => 'de', only_path: true)
end
end
end
class TestUrlGenerationErrors < ActionDispatch::IntegrationTest
Routes = ActionDispatch::Routing::RouteSet.new.tap do |app|
app.draw do
get "/products/:id" => 'products#show', :as => :product
end
end
APP = build_app Routes
def app; APP end
include Routes.url_helpers
test "url helpers raise a helpful error message when generation fails" do
url, missing = { action: 'show', controller: 'products', id: nil }, [:id]
message = "No route matches #{url.inspect} missing required keys: #{missing.inspect}"
# Optimized url helper
error = assert_raises(ActionController::UrlGenerationError){ product_path(nil) }
assert_equal message, error.message
# Non-optimized url helper
error = assert_raises(ActionController::UrlGenerationError, message){ product_path(id: nil) }
assert_equal message, error.message
end
test "url helpers raise message with mixed parameters when generation fails " do
url, missing = { action: 'show', controller: 'products', id: nil, "id"=>"url-tested"}, [:id]
message = "No route matches #{url.inspect} missing required keys: #{missing.inspect}"
# Optimized url helper
error = assert_raises(ActionController::UrlGenerationError){ product_path(nil, 'id'=>'url-tested') }
assert_equal message, error.message
# Non-optimized url helper
error = assert_raises(ActionController::UrlGenerationError, message){ product_path(id: nil, 'id'=>'url-tested') }
assert_equal message, error.message
end
end
class TestDefaultUrlOptions < ActionDispatch::IntegrationTest
class PostsController < ActionController::Base
def archive
render plain: "posts#archive"
end
end
Routes = ActionDispatch::Routing::RouteSet.new
Routes.draw do
default_url_options locale: 'en'
scope ':locale', format: false do
get '/posts/:year/:month/:day', to: 'posts#archive', as: 'archived_posts'
end
end
APP = build_app Routes
def app
APP
end
include Routes.url_helpers
def test_positional_args_with_format_false
assert_equal '/en/posts/2014/12/13', archived_posts_path(2014, 12, 13)
end
end
class TestErrorsInController < ActionDispatch::IntegrationTest
class ::PostsController < ActionController::Base
def foo
nil.i_do_not_exist
end
def bar
NonExistingClass.new
end
end
Routes = ActionDispatch::Routing::RouteSet.new
Routes.draw do
ActiveSupport::Deprecation.silence do
get '/:controller(/:action)'
end
end
APP = build_app Routes
def app
APP
end
def test_legit_no_method_errors_are_not_caught
get '/posts/foo'
assert_equal 500, response.status
end
def test_legit_name_errors_are_not_caught
get '/posts/bar'
assert_equal 500, response.status
end
def test_legit_routing_not_found_responses
get '/posts/baz'
assert_equal 404, response.status
get '/i_do_not_exist'
assert_equal 404, response.status
end
end
class TestPartialDynamicPathSegments < ActionDispatch::IntegrationTest
Routes = ActionDispatch::Routing::RouteSet.new
Routes.draw do
ok = lambda { |env| [200, { 'Content-Type' => 'text/plain' }, []] }
get '/songs/song-:song', to: ok
get '/songs/:song-song', to: ok
get '/:artist/song-:song', to: ok
get '/:artist/:song-song', to: ok
get '/optional/songs(/song-:song)', to: ok
get '/optional/songs(/:song-song)', to: ok
get '/optional/:artist(/song-:song)', to: ok
get '/optional/:artist(/:song-song)', to: ok
end
APP = build_app Routes
def app
APP
end
def test_paths_with_partial_dynamic_segments_are_recognised
get '/david-bowie/changes-song'
assert_equal 200, response.status
assert_params artist: 'david-bowie', song: 'changes'
get '/david-bowie/song-changes'
assert_equal 200, response.status
assert_params artist: 'david-bowie', song: 'changes'
get '/songs/song-changes'
assert_equal 200, response.status
assert_params song: 'changes'
get '/songs/changes-song'
assert_equal 200, response.status
assert_params song: 'changes'
get '/optional/songs/song-changes'
assert_equal 200, response.status
assert_params song: 'changes'
get '/optional/songs/changes-song'
assert_equal 200, response.status
assert_params song: 'changes'
get '/optional/david-bowie/changes-song'
assert_equal 200, response.status
assert_params artist: 'david-bowie', song: 'changes'
get '/optional/david-bowie/song-changes'
assert_equal 200, response.status
assert_params artist: 'david-bowie', song: 'changes'
end
private
def assert_params(params)
assert_equal(params, request.path_parameters)
end
end