diff options
Diffstat (limited to 'actionpack')
23 files changed, 178 insertions, 145 deletions
diff --git a/actionpack/Rakefile b/actionpack/Rakefile index 37a269cffd..4a05c067a9 100644 --- a/actionpack/Rakefile +++ b/actionpack/Rakefile @@ -6,7 +6,6 @@ desc "Default Task" task :default => :test task :package -task "package:clean" # Run the unit tests Rake::TestTask.new do |t| diff --git a/actionpack/lib/action_controller/metal/conditional_get.rb b/actionpack/lib/action_controller/metal/conditional_get.rb index 480e265e44..e21449f376 100644 --- a/actionpack/lib/action_controller/metal/conditional_get.rb +++ b/actionpack/lib/action_controller/metal/conditional_get.rb @@ -129,7 +129,7 @@ module ActionController # * <tt>:etag</tt> Sets a "weak" ETag validator on the response. See the # +:weak_etag+ option. # * <tt>:weak_etag</tt> Sets a "weak" ETag validator on the response. - # requests that set If-None-Match header may return a 304 Not Modified + # Requests that set If-None-Match header may return a 304 Not Modified # response if it matches the ETag exactly. A weak ETag indicates semantic # equivalence, not byte-for-byte equality, so they're good for caching # HTML pages in browser caches. They can't be used for responses that diff --git a/actionpack/lib/action_controller/metal/request_forgery_protection.rb b/actionpack/lib/action_controller/metal/request_forgery_protection.rb index f7e8d06f10..0559fbc6ce 100644 --- a/actionpack/lib/action_controller/metal/request_forgery_protection.rb +++ b/actionpack/lib/action_controller/metal/request_forgery_protection.rb @@ -235,7 +235,9 @@ module ActionController #:nodoc: # we aren't serving an unauthorized cross-origin response. def verify_same_origin_request if marked_for_same_origin_verification? && non_xhr_javascript_response? - logger.warn CROSS_ORIGIN_JAVASCRIPT_WARNING if logger + if logger && log_warning_on_csrf_failure + logger.warn CROSS_ORIGIN_JAVASCRIPT_WARNING + end raise ActionController::InvalidCrossOriginRequest, CROSS_ORIGIN_JAVASCRIPT_WARNING end end diff --git a/actionpack/lib/action_controller/metal/rescue.rb b/actionpack/lib/action_controller/metal/rescue.rb index f1c967b982..17f4030f25 100644 --- a/actionpack/lib/action_controller/metal/rescue.rb +++ b/actionpack/lib/action_controller/metal/rescue.rb @@ -6,17 +6,6 @@ module ActionController #:nodoc: extend ActiveSupport::Concern include ActiveSupport::Rescuable - def rescue_with_handler(exception) - if exception.cause - handler_index = index_of_handler_for_rescue(exception) || Float::INFINITY - cause_handler_index = index_of_handler_for_rescue(exception.cause) - if cause_handler_index && cause_handler_index <= handler_index - exception = exception.cause - end - end - super(exception) - end - # Override this method if you want to customize when detailed # exceptions must be shown. This method is only called when # consider_all_requests_local is false. By default, it returns @@ -31,7 +20,7 @@ module ActionController #:nodoc: super rescue Exception => exception request.env['action_dispatch.show_detailed_exceptions'] ||= show_detailed_exceptions? - rescue_with_handler(exception) || raise(exception) + rescue_with_handler(exception) || raise end end end diff --git a/actionpack/lib/action_controller/metal/strong_parameters.rb b/actionpack/lib/action_controller/metal/strong_parameters.rb index 08049d7af8..46589901fd 100644 --- a/actionpack/lib/action_controller/metal/strong_parameters.rb +++ b/actionpack/lib/action_controller/metal/strong_parameters.rb @@ -441,12 +441,12 @@ module ActionController # Extracts the nested parameter from the given +keys+ by calling +dig+ # at each step. Returns +nil+ if any intermediate step is +nil+. # - # params = ActionController::Parameters.new(foo: { bar: { baz: 1 } }) - # params.dig(:foo, :bar, :baz) # => 1 - # params.dig(:foo, :zot, :xyz) # => nil + # params = ActionController::Parameters.new(foo: { bar: { baz: 1 } }) + # params.dig(:foo, :bar, :baz) # => 1 + # params.dig(:foo, :zot, :xyz) # => nil # - # params2 = ActionController::Parameters.new(foo: [10, 11, 12]) - # params2.dig(:foo, 1) # => 11 + # params2 = ActionController::Parameters.new(foo: [10, 11, 12]) + # params2.dig(:foo, 1) # => 11 def dig(*keys) convert_value_to_parameters(@parameters.dig(*keys)) end @@ -797,7 +797,7 @@ module ActionController # # class PeopleController < ActionController::Base # # Using "Person.create(params[:person])" would raise an - # # ActiveModel::ForbiddenAttributes exception because it'd + # # ActiveModel::ForbiddenAttributesError exception because it'd # # be using mass assignment without an explicit permit step. # # This is the recommended form: # def create diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb index ed2edcbe06..b1b3e87934 100644 --- a/actionpack/lib/action_controller/test_case.rb +++ b/actionpack/lib/action_controller/test_case.rb @@ -527,34 +527,37 @@ module ActionController @request.set_header k, @controller.config.relative_url_root end - @controller.recycle! - @controller.dispatch(action, @request, @response) - @request = @controller.request - @response = @controller.response - - @request.delete_header 'HTTP_COOKIE' + begin + @controller.recycle! + @controller.dispatch(action, @request, @response) + ensure + @request = @controller.request + @response = @controller.response + + @request.delete_header 'HTTP_COOKIE' + + if @request.have_cookie_jar? + unless @request.cookie_jar.committed? + @request.cookie_jar.write(@response) + self.cookies.update(@request.cookie_jar.instance_variable_get(:@cookies)) + end + end + @response.prepare! - if @request.have_cookie_jar? - unless @request.cookie_jar.committed? - @request.cookie_jar.write(@response) - self.cookies.update(@request.cookie_jar.instance_variable_get(:@cookies)) + if flash_value = @request.flash.to_session_value + @request.session['flash'] = flash_value + else + @request.session.delete('flash') end - end - @response.prepare! - if flash_value = @request.flash.to_session_value - @request.session['flash'] = flash_value - else - @request.session.delete('flash') - end + if xhr + @request.delete_header 'HTTP_X_REQUESTED_WITH' + @request.delete_header 'HTTP_ACCEPT' + end + @request.query_string = '' - if xhr - @request.delete_header 'HTTP_X_REQUESTED_WITH' - @request.delete_header 'HTTP_ACCEPT' + @response.sent! end - @request.query_string = '' - - @response.sent! @response end diff --git a/actionpack/lib/action_dispatch/http/url.rb b/actionpack/lib/action_dispatch/http/url.rb index b7a6aeee7d..7a1350a46d 100644 --- a/actionpack/lib/action_dispatch/http/url.rb +++ b/actionpack/lib/action_dispatch/http/url.rb @@ -354,6 +354,17 @@ module ActionDispatch standard_port? ? '' : ":#{port}" end + # Returns the requested port, such as 8080, based on SERVER_PORT + # + # class Request < Rack::Request + # include ActionDispatch::Http::URL + # end + # + # req = Request.new 'SERVER_PORT' => '80' + # req.server_port # => 80 + # + # req = Request.new 'SERVER_PORT' => '8080' + # req.server_port # => 8080 def server_port get_header('SERVER_PORT').to_i end diff --git a/actionpack/lib/action_dispatch/routing.rb b/actionpack/lib/action_dispatch/routing.rb index 337859feb1..61ebd0b8db 100644 --- a/actionpack/lib/action_dispatch/routing.rb +++ b/actionpack/lib/action_dispatch/routing.rb @@ -118,11 +118,11 @@ module ActionDispatch # controller :blog do # get 'blog/show' => :list # get 'blog/delete' => :delete - # get 'blog/edit/:id' => :edit + # get 'blog/edit' => :edit # end # # # provides named routes for show, delete, and edit - # link_to @article.title, show_path(id: @article.id) + # link_to @article.title, blog_show_path(id: @article.id) # # == Pretty URLs # diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 4c2a4cfeb0..8ff3b42a40 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -120,7 +120,7 @@ module ActionDispatch if options_constraints.is_a?(Hash) @defaults = Hash[options_constraints.find_all { |key, default| - URL_OPTIONS.include?(key) && (String === default || Fixnum === default) + URL_OPTIONS.include?(key) && (String === default || Integer === default) }].merge @defaults @blocks = blocks constraints.merge! options_constraints @@ -824,7 +824,7 @@ module ActionDispatch if options[:constraints].is_a?(Hash) defaults = options[:constraints].select do |k, v| - URL_OPTIONS.include?(k) && (v.is_a?(String) || v.is_a?(Fixnum)) + URL_OPTIONS.include?(k) && (v.is_a?(String) || v.is_a?(Integer)) end options[:defaults] = defaults.merge(options[:defaults] || {}) diff --git a/actionpack/lib/action_dispatch/testing/assertion_response.rb b/actionpack/lib/action_dispatch/testing/assertion_response.rb index 3fb81ff083..404b96bbcd 100644 --- a/actionpack/lib/action_dispatch/testing/assertion_response.rb +++ b/actionpack/lib/action_dispatch/testing/assertion_response.rb @@ -1,14 +1,7 @@ module ActionDispatch - # This is a class that abstracts away an asserted response. - # It purposely does not inherit from Response, because it doesn't need it. - # That means it does not have headers or a body. - # - # As an input to the initializer, we take a Fixnum, a String, or a Symbol. - # If it's a Fixnum or String, we figure out what its symbolized name. - # If it's a Symbol, we figure out what its corresponding code is. - # The resulting code will be a Fixnum, for real HTTP codes, and it will - # be a String for the pseudo-HTTP codes, such as: - # :success, :missing, :redirect and :error + # This is a class that abstracts away an asserted response. It purposely + # does not inherit from Response because it doesn't need it. That means it + # does not have headers or a body. class AssertionResponse attr_reader :code, :name @@ -19,6 +12,9 @@ module ActionDispatch error: "5XX" } + # Accepts a specific response status code as an Integer (404) or String + # ('404') or a response status range as a Symbol pseudo-code (:success, + # indicating any 200-299 status code). def initialize(code_or_name) if code_or_name.is_a?(Symbol) @name = code_or_name diff --git a/actionpack/lib/action_dispatch/testing/integration.rb b/actionpack/lib/action_dispatch/testing/integration.rb index 384254b131..8777666f9f 100644 --- a/actionpack/lib/action_dispatch/testing/integration.rb +++ b/actionpack/lib/action_dispatch/testing/integration.rb @@ -300,7 +300,7 @@ module ActionDispatch end end - REQUEST_KWARGS = %i(params headers env xhr) + REQUEST_KWARGS = %i(params headers env xhr as) def kwarg_request?(args) args[0].respond_to?(:keys) && args[0].keys.any? { |k| REQUEST_KWARGS.include?(k) } end @@ -317,7 +317,8 @@ module ActionDispatch params: { id: 1 }, headers: { 'X-Extra-Header' => '123' }, env: { 'action_dispatch.custom' => 'custom' }, - xhr: true + xhr: true, + as: :json MSG end diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb index fcbbfe8a18..1e1d6f5429 100644 --- a/actionpack/test/abstract_unit.rb +++ b/actionpack/test/abstract_unit.rb @@ -34,7 +34,6 @@ require 'action_dispatch' require 'active_support/dependencies' require 'active_model' require 'active_record' -require 'action_controller/caching' require 'pp' # require 'pp' early to prevent hidden_methods from not picking up the pretty-print methods until too late @@ -58,10 +57,6 @@ ActiveSupport::Deprecation.debug = true # Disable available locale checks to avoid warnings running the test suite. I18n.enforce_available_locales = false -# Register danish language for testing -I18n.backend.store_translations 'da', {} -I18n.backend.store_translations 'pt-BR', {} - FIXTURE_LOAD_PATH = File.join(File.dirname(__FILE__), 'fixtures') SharedTestRoutes = ActionDispatch::Routing::RouteSet.new @@ -256,24 +251,6 @@ end class ::ApplicationController < ActionController::Base end -class Workshop - extend ActiveModel::Naming - include ActiveModel::Conversion - attr_accessor :id - - def initialize(id) - @id = id - end - - def persisted? - id.present? - end - - def to_s - id.to_s - end -end - module ActionDispatch class DebugExceptions private @@ -377,37 +354,15 @@ module RoutingTestHelpers end end -class MetalRenderingController < ActionController::Metal - include AbstractController::Rendering - include ActionController::Rendering - include ActionController::Renderers -end - class ResourcesController < ActionController::Base def index() head :ok end alias_method :show, :index end -class ThreadsController < ResourcesController; end -class MessagesController < ResourcesController; end class CommentsController < ResourcesController; end -class ReviewsController < ResourcesController; end - class AccountsController < ResourcesController; end -class AdminController < ResourcesController; end -class ProductsController < ResourcesController; end class ImagesController < ResourcesController; end -module Backoffice - class ProductsController < ResourcesController; end - class ImagesController < ResourcesController; end - - module Admin - class ProductsController < ResourcesController; end - class ImagesController < ResourcesController; end - end -end - # Skips the current run on Rubinius using Minitest::Assertions#skip def rubinius_skip(message = '') skip message if RUBY_ENGINE == 'rbx' diff --git a/actionpack/test/assertions/response_assertions_test.rb b/actionpack/test/assertions/response_assertions_test.rb index 579ce0ed29..57a67a48b5 100644 --- a/actionpack/test/assertions/response_assertions_test.rb +++ b/actionpack/test/assertions/response_assertions_test.rb @@ -35,7 +35,7 @@ module ActionDispatch end end - def test_assert_response_fixnum + def test_assert_response_integer @response = FakeResponse.new 400 assert_response 400 diff --git a/actionpack/test/controller/metal/renderers_test.rb b/actionpack/test/controller/metal/renderers_test.rb index 007866a559..247e872674 100644 --- a/actionpack/test/controller/metal/renderers_test.rb +++ b/actionpack/test/controller/metal/renderers_test.rb @@ -1,6 +1,12 @@ require 'abstract_unit' require 'active_support/core_ext/hash/conversions' +class MetalRenderingController < ActionController::Metal + include AbstractController::Rendering + include ActionController::Rendering + include ActionController::Renderers +end + class MetalRenderingJsonController < MetalRenderingController class Model def to_json(options = {}) diff --git a/actionpack/test/controller/redirect_test.rb b/actionpack/test/controller/redirect_test.rb index f83248402c..4af3c628d0 100644 --- a/actionpack/test/controller/redirect_test.rb +++ b/actionpack/test/controller/redirect_test.rb @@ -1,5 +1,23 @@ require 'abstract_unit' +class Workshop + extend ActiveModel::Naming + include ActiveModel::Conversion + attr_accessor :id + + def initialize(id) + @id = id + end + + def persisted? + id.present? + end + + def to_s + id.to_s + end +end + class RedirectController < ActionController::Base # empty method not used anywhere to ensure methods like # `status` and `location` aren't called on `redirect_to` calls diff --git a/actionpack/test/controller/request_forgery_protection_test.rb b/actionpack/test/controller/request_forgery_protection_test.rb index d56241f9cd..d3f2ec6aa1 100644 --- a/actionpack/test/controller/request_forgery_protection_test.rb +++ b/actionpack/test/controller/request_forgery_protection_test.rb @@ -407,6 +407,37 @@ module RequestForgeryProtectionTests end end + def test_should_warn_on_not_same_origin_js + old_logger = ActionController::Base.logger + logger = ActiveSupport::LogSubscriber::TestHelper::MockLogger.new + ActionController::Base.logger = logger + + begin + assert_cross_origin_blocked { get :same_origin_js } + + assert_equal 1, logger.logged(:warn).size + assert_match(/<script> tag on another site requested protected JavaScript/, logger.logged(:warn).last) + ensure + ActionController::Base.logger = old_logger + end + end + + def test_should_not_warn_if_csrf_logging_disabled_and_not_same_origin_js + old_logger = ActionController::Base.logger + logger = ActiveSupport::LogSubscriber::TestHelper::MockLogger.new + ActionController::Base.logger = logger + ActionController::Base.log_warning_on_csrf_failure = false + + begin + assert_cross_origin_blocked { get :same_origin_js } + + assert_equal 0, logger.logged(:warn).size + ensure + ActionController::Base.logger = old_logger + ActionController::Base.log_warning_on_csrf_failure = true + end + end + # Allow non-GET requests since GET is all a remote <script> tag can muster. def test_should_allow_non_get_js_without_xhr_header session[:_csrf_token] = @token diff --git a/actionpack/test/controller/rescue_test.rb b/actionpack/test/controller/rescue_test.rb index ed78f859ce..c088e5a043 100644 --- a/actionpack/test/controller/rescue_test.rb +++ b/actionpack/test/controller/rescue_test.rb @@ -131,22 +131,6 @@ class RescueController < ActionController::Base def missing_template end - def io_error_in_view - begin - raise IOError.new('this is io error') - rescue - raise ActionView::TemplateError.new(nil) - end - end - - def zero_division_error_in_view - begin - raise ZeroDivisionError.new('this is zero division error') - rescue - raise ActionView::TemplateError.new(nil) - end - end - def exception_with_more_specific_handler_for_wrapper raise RecordInvalid rescue @@ -251,17 +235,6 @@ class ControllerInheritanceRescueControllerTest < ActionController::TestCase end class RescueControllerTest < ActionController::TestCase - - def test_io_error_in_view - get :io_error_in_view - assert_equal 'io error', @response.body - end - - def test_zero_division_error_in_view - get :zero_division_error_in_view - assert_equal 'action_view templater error', @response.body - end - def test_rescue_handler get :not_authorized assert_response :forbidden @@ -276,7 +249,6 @@ class RescueControllerTest < ActionController::TestCase get :record_invalid end end - def test_rescue_handler_with_argument_as_string assert_called_with @controller, :show_errors, [Exception] do get :record_invalid_raise_as_string @@ -314,7 +286,6 @@ class RescueControllerTest < ActionController::TestCase get :resource_unavailable assert_equal "RescueController::ResourceUnavailable", @response.body end - def test_block_rescue_handler_with_argument_as_string get :resource_unavailable_raise_as_string assert_equal "RescueController::ResourceUnavailableToRescueAsString", @response.body @@ -322,7 +293,7 @@ class RescueControllerTest < ActionController::TestCase test 'rescue when wrapper has more specific handler than cause' do get :exception_with_more_specific_handler_for_wrapper - assert_response :unprocessable_entity + assert_response :forbidden end test 'rescue when cause has more specific handler than wrapper' do diff --git a/actionpack/test/controller/resources_test.rb b/actionpack/test/controller/resources_test.rb index 4490abf7b2..8e38af5025 100644 --- a/actionpack/test/controller/resources_test.rb +++ b/actionpack/test/controller/resources_test.rb @@ -3,8 +3,22 @@ require 'active_support/core_ext/object/try' require 'active_support/core_ext/object/with_options' require 'active_support/core_ext/array/extract_options' -class ResourcesTest < ActionController::TestCase +class AdminController < ResourcesController; end +class MessagesController < ResourcesController; end +class ProductsController < ResourcesController; end +class ThreadsController < ResourcesController; end + +module Backoffice + class ProductsController < ResourcesController; end + class ImagesController < ResourcesController; end + + module Admin + class ProductsController < ResourcesController; end + class ImagesController < ResourcesController; end + end +end +class ResourcesTest < ActionController::TestCase def test_default_restful_routes with_restful_routing :messages do assert_simply_restful_for :messages diff --git a/actionpack/test/controller/routing_test.rb b/actionpack/test/controller/routing_test.rb index 168677829a..03bf8f8295 100644 --- a/actionpack/test/controller/routing_test.rb +++ b/actionpack/test/controller/routing_test.rb @@ -626,7 +626,7 @@ class LegacyRouteSetTests < ActiveSupport::TestCase assert_equal '/pages/boo', url_for(rs, { :controller => 'pages', :action => 'boo' }) end - def test_route_with_fixnum_default + def test_route_with_integer_default rs.draw do get 'page(/:id)' => 'content#show_page', :id => 1 diff --git a/actionpack/test/controller/test_case_test.rb b/actionpack/test/controller/test_case_test.rb index ebcdda6074..ea59156f65 100644 --- a/actionpack/test/controller/test_case_test.rb +++ b/actionpack/test/controller/test_case_test.rb @@ -154,6 +154,10 @@ XML render html: '<body class="foo"></body>'.html_safe end + def boom + raise 'boom!' + end + private def generate_url(opts) @@ -553,7 +557,7 @@ XML assert_equal 'created', flash[:notice] end - def test_params_passing_with_fixnums + def test_params_passing_with_integer get :test_params, params: { page: { name: "Page name", month: 4, year: 2004, day: 6 } } @@ -565,7 +569,7 @@ XML ) end - def test_params_passing_with_fixnums_when_not_html_request + def test_params_passing_with_integers_when_not_html_request get :test_params, params: { format: 'json', count: 999 } parsed_params = ::JSON.parse(@response.body) assert_equal( @@ -981,6 +985,26 @@ XML assert_redirected_to 'created resource' end end + + def test_exception_in_action_reaches_test + assert_raise(RuntimeError) do + process :boom, method: "GET" + end + end + + def test_request_state_is_cleared_after_exception + assert_raise(RuntimeError) do + process :boom, + method: "GET", + params: { q: 'test1' } + end + + process :test_query_string, + method: "GET", + params: { q: 'test2' } + + assert_equal "q=test2", @response.body + end end class ResponseDefaultHeadersTest < ActionController::TestCase diff --git a/actionpack/test/dispatch/prefix_generation_test.rb b/actionpack/test/dispatch/prefix_generation_test.rb index d75e31db62..b8f0ffb64a 100644 --- a/actionpack/test/dispatch/prefix_generation_test.rb +++ b/actionpack/test/dispatch/prefix_generation_test.rb @@ -166,7 +166,7 @@ module TestGenerationPrefix assert_equal "/pure-awesomeness/blog/posts/1", last_response.body end - test "[ENGINE] url_helpers from engine have higher priotity than application's url_helpers" do + test "[ENGINE] url_helpers from engine have higher priority than application's url_helpers" do get "/awesome/blog/conflicting_url" assert_equal "engine", last_response.body end @@ -319,14 +319,14 @@ module TestGenerationPrefix path = engine_object.polymorphic_url(Post.new, :host => "www.example.com") assert_equal "http://www.example.com/awesome/blog/posts/1", path end - + private def verify_redirect(url, status = 301) assert_equal status, last_response.status assert_equal url, last_response.headers["Location"] assert_equal expected_redirect_body(url), last_response.body end - + def expected_redirect_body(url) %(<html><body>You are being <a href="#{url}">redirected</a>.</body></html>) end @@ -450,14 +450,14 @@ module TestGenerationPrefix get "/absolute_custom_redirect" verify_redirect "http://example.org/foo" end - + private def verify_redirect(url, status = 301) assert_equal status, last_response.status assert_equal url, last_response.headers["Location"] assert_equal expected_redirect_body(url), last_response.body end - + def expected_redirect_body(url) %(<html><body>You are being <a href="#{url}">redirected</a>.</body></html>) end diff --git a/actionpack/test/dispatch/request_test.rb b/actionpack/test/dispatch/request_test.rb index 781f852633..8a5d85ab84 100644 --- a/actionpack/test/dispatch/request_test.rb +++ b/actionpack/test/dispatch/request_test.rb @@ -358,6 +358,17 @@ class RequestPort < BaseRequestTest request = stub_request 'HTTP_HOST' => 'www.example.org:8080' assert_equal ':8080', request.port_string end + + test "server port" do + request = stub_request 'SERVER_PORT' => '8080' + assert_equal 8080, request.server_port + + request = stub_request 'SERVER_PORT' => '80' + assert_equal 80, request.server_port + + request = stub_request 'SERVER_PORT' => '' + assert_equal 0, request.server_port + end end class RequestPath < BaseRequestTest diff --git a/actionpack/test/dispatch/routing/concerns_test.rb b/actionpack/test/dispatch/routing/concerns_test.rb index 7ef513b0c8..6934271846 100644 --- a/actionpack/test/dispatch/routing/concerns_test.rb +++ b/actionpack/test/dispatch/routing/concerns_test.rb @@ -1,5 +1,7 @@ require 'abstract_unit' +class ReviewsController < ResourcesController; end + class RoutingConcernsTest < ActionDispatch::IntegrationTest class Reviewable def self.call(mapper, options = {}) |