diff --git a/newrelic_rpm.gemspec b/newrelic_rpm.gemspec index 753390a..8d8d06b 100644 --- a/newrelic_rpm.gemspec +++ b/newrelic_rpm.gemspec @@ -47,6 +47,8 @@ EOS s.add_development_dependency 'yard' s.add_development_dependency 'rails', '~> 3.2.13' s.add_development_dependency 'pry', '~> 0.9.12' + s.add_development_dependency 'pry-stack_explorer' + s.add_development_dependency 'pry-byebug' s.add_development_dependency 'hometown', '~> 0.2.5' # Only let Guard run on newer Rubies diff --git a/test/multiverse/suites/rails/action_cable_test.rb b/test/multiverse/suites/rails/action_cable_test.rb deleted file mode 100644 index 0e6a85b..0000000 --- a/test/multiverse/suites/rails/action_cable_test.rb +++ /dev/null @@ -1,81 +0,0 @@ -# encoding: utf-8 -# This file is distributed under New Relic's license terms. -# See https://github.com/newrelic/rpm/blob/master/LICENSE for complete details. - -begin - require 'action_cable' -rescue LoadError -end - -if defined?(ActionCable::Channel) - -require 'stringio' -require 'logger' -require 'json' - -class ActionCableTest < Minitest::Test -include MultiverseHelpers - - class TestConnection - attr_reader :transmissions, :identifiers, :logger - - def initialize - @transmissions = [] - @identifiers = [] - @logger = Logger.new StringIO.new - end - - def transmit data - @transmissions << data - end - - def last_transmission - JSON.parse @transmissions.last - end - end - - class TestChannel < ActionCable::Channel::Base - def test_action data - transmit data['content'] - end - - def boom data - raise StandardError.new("Boom!") - end - end - - setup_and_teardown_agent do - @connection = TestConnection.new - @channel = TestChannel.new @connection, "{id: 1}" - end - - def test_creates_trace - @channel.perform_action({ 'action' => :test_action, 'content' => 'hello' }) - - last_sample = NewRelic::Agent.instance.transaction_sampler.last_sample - assert_equal('Controller/ActionCable/ActionCableTest::TestChannel/test_action', last_sample.transaction_name) - end - - def test_creates_web_transaction - @channel.perform_action({ 'action'=> :test_action, 'content' => 'hello' }) - - expected_metrics = { - 'HttpDispatcher' => { :call_count => 1 }, - 'Controller/ActionCable/ActionCableTest::TestChannel/test_action' => { :call_count => 1} - } - - assert_metrics_recorded expected_metrics - end - - def test_action_with_error_is_noticed_by_agent - @channel.perform_action({ 'action'=> :boom }) rescue nil - - error_trace = last_traced_error - - assert_equal "StandardError", error_trace.exception_class_name - assert_equal "Boom!", error_trace.message - assert_equal "Controller/ActionCable/ActionCableTest::TestChannel/boom", error_trace.path - end -end - -end diff --git a/test/multiverse/suites/rails/action_controller_live_rum_test.rb b/test/multiverse/suites/rails/action_controller_live_rum_test.rb deleted file mode 100644 index 5da5a7d..0000000 --- a/test/multiverse/suites/rails/action_controller_live_rum_test.rb +++ /dev/null @@ -1,39 +0,0 @@ -# encoding: utf-8 -# This file is distributed under New Relic's license terms. -# See https://github.com/newrelic/rpm/blob/master/LICENSE for complete details. - -require './app' - -if defined?(ActionController::Live) - -class UndeadController < ApplicationController - RESPONSE_BODY = "Brains!" - - def brains - render :inline => RESPONSE_BODY - end -end - -class LiveController < UndeadController - include ActionController::Live -end - -class ActionControllerLiveRumTest < RailsMultiverseTest - include MultiverseHelpers - - JS_LOADER = "JS LOADER IN DA HOUSE" - - setup_and_teardown_agent(:js_agent_loader => JS_LOADER, :beacon => "beacon", :browser_key => "key") - - def test_rum_instrumentation_when_not_streaming - get '/undead/brains' - assert_includes(response.body, JS_LOADER) - end - - def test_excludes_rum_instrumentation_when_streaming_with_action_controller_live - get '/live/brains' - assert_equal(LiveController::RESPONSE_BODY, response.body) - end -end - -end diff --git a/test/multiverse/suites/rails/activejob_test.rb b/test/multiverse/suites/rails/activejob_test.rb deleted file mode 100644 index 6987e52..0000000 --- a/test/multiverse/suites/rails/activejob_test.rb +++ /dev/null @@ -1,152 +0,0 @@ -# encoding: utf-8 -# This file is distributed under New Relic's license terms. -# See https://github.com/newrelic/rpm/blob/master/LICENSE for complete details. - -require File.expand_path(File.join(__FILE__, '..', 'app')) - -require 'logger' -require 'stringio' - -# ActiveJob is in Rails 4.2+, so make sure we're on an allowed version before -# we try to load. Previously just tried to require it, but that had load issues -# on Rubinius. -if Rails::VERSION::STRING >= "4.2.0" - -require 'active_job' - -ActiveJob::Base.queue_adapter = :inline - -class MyJob < ActiveJob::Base - def perform - # Nothing needed! - end -end - -class MyJobWithAlternateQueue < ActiveJob::Base - queue_as :my_jobs - - def perform - end -end - -class MyJobWithParams < ActiveJob::Base - def self.last_params - @@last_params - end - - def perform(first, last) - @@last_params = [first, last] - end -end - -class MyFailure < ActiveJob::Base - - def perform - raise ArgumentError.new("No it isn't!") - end -end - -class ActiveJobTest < Minitest::Test - - include MultiverseHelpers - - setup_and_teardown_agent do - @log = StringIO.new - ActiveJob::Base.logger = ::Logger.new(@log) - end - - def after_teardown - unless passed? - puts "\nEmitting log from failure: #{self.name}" - @log.rewind - puts @log.read - end - end - - ENQUEUE_PREFIX = "MessageBroker/ActiveJob::Inline/Queue/Produce/Named" - PERFORM_PREFIX = "MessageBroker/ActiveJob::Inline/Queue/Consume/Named" - - PERFORM_TRANSACTION_NAME = 'OtherTransaction/ActiveJob::Inline/MyJob/execute' - PERFORM_TRANSACTION_ROLLUP = 'OtherTransaction/ActiveJob::Inline/all' - - def test_record_enqueue_metrics - in_web_transaction do - MyJob.perform_later - end - - assert_metrics_recorded("#{ENQUEUE_PREFIX}/default") - end - - def test_record_enqueue_metrics_with_alternate_queue - in_web_transaction do - MyJobWithAlternateQueue.perform_later - end - - assert_metrics_recorded("#{ENQUEUE_PREFIX}/my_jobs") - end - - def test_record_perform_metrics_in_web - in_web_transaction do - MyJob.perform_later - end - - assert_metrics_recorded("#{PERFORM_PREFIX}/default") - end - - def test_record_perform_metrics_with_alternate_queue_in_web - in_web_transaction do - MyJobWithAlternateQueue.perform_later - end - - assert_metrics_recorded("#{PERFORM_PREFIX}/my_jobs") - end - - def test_doesnt_record_perform_metrics_from_background - in_background_transaction do - MyJob.perform_later - end - - assert_metrics_not_recorded("#{PERFORM_PREFIX}/default") - end - - def test_starts_transaction_if_there_isnt_one - MyJob.perform_later - assert_metrics_recorded([PERFORM_TRANSACTION_ROLLUP, - PERFORM_TRANSACTION_NAME]) - end - - def test_nests_other_transaction_if_already_running - in_background_transaction do - MyJob.perform_later - end - - assert_metrics_recorded([PERFORM_TRANSACTION_ROLLUP, - PERFORM_TRANSACTION_NAME]) - end - - # If running tasks inline, either in a dev environment or from - # misconfiguration we shouldn't accidentally rename our web transaction - def test_doesnt_nest_transactions_if_in_web - in_web_transaction do - MyJob.perform_later - end - - assert_metrics_not_recorded([PERFORM_TRANSACTION_ROLLUP, - PERFORM_TRANSACTION_NAME]) - end - - def test_doesnt_interfere_with_params_on_job - MyJobWithParams.perform_later("1", "2") - assert_equal(["1", "2"], MyJobWithParams.last_params) - end - - def test_captures_errors - # Because we're processing inline, we get the error raised here - assert_raises ArgumentError do - MyFailure.perform_later - end - assert_metrics_recorded(["Errors/all"]) - end -end - -end diff --git a/test/multiverse/suites/rails/bad_instrumentation_test.rb b/test/multiverse/suites/rails/bad_instrumentation_test.rb deleted file mode 100644 index 4aed697..0000000 --- a/test/multiverse/suites/rails/bad_instrumentation_test.rb +++ /dev/null @@ -1,29 +0,0 @@ -# encoding: utf-8 -# This file is distributed under New Relic's license terms. -# See https://github.com/newrelic/rpm/blob/master/LICENSE for complete details. - -require './app' - -class BadInstrumentationController < ApplicationController - # This action is intended to simulate a chunk of instrumentation that pushes - # a traced method frame, but then never pops it. Such a situation will break - # instrumentation of that request, but should not actually cause the request - # to fail. - # https://newrelic.atlassian.net/browse/RUBY-1158 - def failwhale - state = NewRelic::Agent::TransactionState.tl_get - stack = state.traced_method_stack - stack.push_frame(state, 'failwhale') - render :text => 'everything went great' - end -end - -class BadInstrumentationTest < RailsMultiverseTest - include MultiverseHelpers - setup_and_teardown_agent - - def test_unbalanced_tt_stack_should_not_cause_request_to_fail - rsp = get '/bad_instrumentation/failwhale' - assert_equal(200, rsp.to_i) - end -end diff --git a/test/multiverse/suites/rails/error_tracing_test.rb b/test/multiverse/suites/rails/error_tracing_test.rb index 6739916..259a125 100644 --- a/test/multiverse/suites/rails/error_tracing_test.rb +++ b/test/multiverse/suites/rails/error_tracing_test.rb @@ -1,340 +1,38 @@ # encoding: utf-8 # This file is distributed under New Relic's license terms. # See https://github.com/newrelic/rpm/blob/master/LICENSE for complete details. +require 'action_controller/railtie' +require 'active_model' +require 'rails/test_help' -# https://newrelic.atlassian.net/browse/RUBY-747 -require './app' -require 'fake_collector' -require File.expand_path(File.join(File.dirname(__FILE__), '..', '..', '..', 'helpers', 'exceptions')) - -class ErrorController < ApplicationController - include NewRelic::TestHelpers::Exceptions - - newrelic_ignore :only => :ignored_action - - def controller_error - raise 'this is an uncaught controller error' - end - - def exception_error - raise Exception.new('wobble') - end - - def view_error - render :inline => "<% raise 'this is an uncaught view error' %>" - end - - def model_error - Foo.new.raise_error - end - - def ignored_action - raise 'this error should not be noticed' - end - - def ignored_error - raise NewRelic::TestHelpers::Exceptions::IgnoredError.new('this error should not be noticed') - end - - def server_ignored_error - raise NewRelic::TestHelpers::Exceptions::ServerIgnoredError.new('this is a server ignored error') - end - - def frozen_error - e = RuntimeError.new("frozen errors make a refreshing treat on a hot summer day") - e.freeze - raise e - end - - def string_noticed_error - NewRelic::Agent.notice_error("trilobites died out millions of years ago") - render :text => 'trilobites' - end - - def noticed_error - NewRelic::Agent.notice_error(RuntimeError.new('this error should be noticed')) - render :text => "Shoulda noticed an error" - end - - def deprecated_noticed_error - newrelic_notice_error(RuntimeError.new('this error should be noticed')) - render :text => "Shoulda noticed an error" - end - - def middleware_error - render :text => 'everything went great' - end - - def error_with_custom_params - NewRelic::Agent.add_custom_attributes(:texture => 'chunky') - raise 'bad things' - end - - if Rails::VERSION::MAJOR == 2 - filter_parameter_logging(:secret) - end -end - -class ErrorsWithoutSSCTest < RailsMultiverseTest - extend Multiverse::Color - - include MultiverseHelpers - - setup_and_teardown_agent do |collector| - setup_collector_mocks - @error_collector = agent.error_collector - end - - # Let base class override this without moving where we start the agent - def setup_collector_mocks - $collector.stub('connect', {"agent_run_id" => 666 }, 200) - end - - def last_error - errors.last - end - - if Rails::VERSION::MAJOR >= 3 - def test_error_collector_should_be_enabled - assert NewRelic::Agent.config[:agent_enabled] - assert NewRelic::Agent.config[:'error_collector.enabled'] - assert @error_collector.enabled? - end - - def test_should_capture_routing_error - get '/bad_route' - assert_error_reported_once('this is an uncaught routing error', nil, nil) - end - - def test_should_capture_errors_raised_in_middleware_before_call - get '/error/middleware_error/before' - assert_error_reported_once('middleware error', nil, nil) - end - - def test_should_capture_errors_raised_in_middleware_after_call - get '/error/middleware_error/after' - assert_error_reported_once('middleware error', nil, nil) - end - - def test_should_capture_request_uri_and_params - get '/error/controller_error?eat=static' - assert_equal('/error/controller_error', attributes_for_single_error_posted("request_uri")) - - expected_params = { - 'request.parameters.eat' => 'static', - 'httpResponseCode' => '500' - } - - attributes = agent_attributes_for_single_error_posted - expected_params.each do |key, value| - assert_equal value, attributes[key] - end - end - end - - def test_should_capture_error_raised_in_view - get '/error/view_error' - assert_error_reported_once('this is an uncaught view error', - 'Controller/error/view_error') - end - - def test_should_capture_error_raised_in_controller - get '/error/controller_error' - assert_error_reported_once('this is an uncaught controller error', - 'Controller/error/controller_error') - end - - def test_should_capture_error_raised_in_model - get '/error/model_error' - assert_error_reported_once('this is an uncaught model error', - 'Controller/error/model_error') - end - - if Rails::VERSION::MAJOR < 5 - def test_should_capture_deprecated_noticed_error_in_controller - get '/error/deprecated_noticed_error' - assert_error_reported_once('this error should be noticed', - 'Controller/error/deprecated_noticed_error') - end - end - - def test_should_capture_noticed_error_in_controller - get '/error/noticed_error' - assert_error_reported_once('this error should be noticed', - 'Controller/error/noticed_error') - end - - def test_should_capture_frozen_errors - get '/error/frozen_error' - assert_error_reported_once("frozen errors make a refreshing treat on a hot summer day", - "Controller/error/frozen_error") - end - - def test_should_capture_string_noticed_errors - get '/error/string_noticed_error' - assert_error_reported_once("trilobites died out millions of years ago", - "Controller/error/string_noticed_error") - end - - # Important choice of controllor_error, since this goes through both the - # transaction and the rack error collector, so risks multiple counting! - def test_should_capture_multiple_errors - 40.times do - get '/error/controller_error' +if !defined?(MyApp) + class MyApp < Rails::Application + initialize! + routes.draw do + get '/:controller(/:action(/:id))' end - - assert_errors_reported('this is an uncaught controller error', - NewRelic::Agent::ErrorCollector::MAX_ERROR_QUEUE_LENGTH, - 40, nil, 40) - end - - def test_should_capture_manually_noticed_error - NewRelic::Agent.notice_error(RuntimeError.new('this is a noticed error')) - assert_error_reported_once('this is a noticed error', nil, nil) - end - - def test_should_apply_parameter_filtering - get '/error/controller_error?secret=shouldnotbecaptured&other=whatever' - attributes = agent_attributes_for_single_error_posted - assert_equal('[FILTERED]', attributes['request.parameters.secret']) - assert_equal('whatever', attributes['request.parameters.other']) - end - - def test_should_apply_parameter_filtering_for_non_standard_errors - get '/error/exception_error?secret=shouldnotbecaptured&other=whatever' - attributes = agent_attributes_for_single_error_posted - assert_equal('[FILTERED]', attributes['request.parameters.secret']) - assert_equal('whatever', attributes['request.parameters.other']) end - def test_should_not_notice_errors_from_ignored_action - get '/error/ignored_action' - assert(errors.empty?, - 'Noticed an error that should have been ignored') - end - - def test_should_not_notice_ignored_error_classes - get '/error/ignored_error' - assert(errors.empty?, - 'Noticed an error that should have been ignored') - end + class ApplicationController < ActionController::Base; end +end - def test_should_not_fail_apdex_for_ignored_error_class_noticed - get '/error/ignored_error' - assert_metrics_recorded({ - 'Apdex' => { :apdex_f => 0 }, - 'Apdex/error/ignored_error' => { :apdex_f => 0 } - }) +class ErrorController < ApplicationController + def controller_error + raise end +end - def test_should_not_notice_filtered_errors - filter = Proc.new do |error| - !error.kind_of?(RuntimeError) - end - - with_ignore_error_filter(filter) do +class ErrorsWithoutSSCTest < ActionDispatch::IntegrationTest + def test_1 + 100.times do get '/error/controller_error' end - - assert(errors.empty?, - 'Noticed an error that should have been ignored') - end - - def test_should_notice_server_ignored_error_if_no_server_side_config - get '/error/server_ignored_error' - assert_error_reported_once('this is a server ignored error') - end - - def test_captured_errors_should_include_custom_params - with_config(:'error_collector.attributes.enabled' => true) do - get '/error/error_with_custom_params' - assert_error_reported_once('bad things') - - attributes = user_attributes_for_single_error_posted - assert_equal({'texture' => 'chunky'}, attributes) - end - end - - def test_captured_errors_should_include_custom_params_with_legacy_setting - with_config(:'error_collector.capture_attributes' => true) do - get '/error/error_with_custom_params' - assert_error_reported_once('bad things') - - attributes = user_attributes_for_single_error_posted - assert_equal({'texture' => 'chunky'}, attributes) - end - end - - def test_captured_errors_should_not_include_custom_params_if_config_says_no - with_config(:'error_collector.attributes.enabled' => false) do - get '/error/error_with_custom_params' - assert_error_reported_once('bad things') - - attributes = user_attributes_for_single_error_posted - assert_empty attributes - end - end - - def test_captured_errors_should_not_include_custom_params_if_legacy_setting_says_no - with_config(:'error_collector.capture_attributes' => false) do - get '/error/error_with_custom_params' - assert_error_reported_once('bad things') - - attributes = user_attributes_for_single_error_posted - assert_empty attributes - end - end - - protected - - def errors - @error_collector.error_trace_aggregator.instance_variable_get :@errors end - def errors_with_message(message) - errors.select{|error| error.message == message} - end - - def assert_errors_reported(message, queued_count, total_count=queued_count, txn_name=nil, apdex_f=1) - expected = { :call_count => total_count } - assert_metrics_recorded("Errors/all" => expected) - assert_metrics_recorded("Errors/#{txn_name}" => expected) if txn_name - - unless apdex_f.nil? - assert_metrics_recorded("Apdex" => { :apdex_f => apdex_f }) - end - - assert_equal(queued_count, - errors_with_message(message).size, - "Wrong number of errors with message #{message.inspect} found") - end - - def assert_error_reported_once(message, txn_name=nil, apdex_f=1) - assert_errors_reported(message, 1, 1, txn_name, apdex_f) - end + alias test_2 test_1 end class ErrorsWithSSCTest < ErrorsWithoutSSCTest - def setup_collector_mocks - $collector.stub('connect', { - "agent_run_id" => 1, - "agent_config" => { - "error_collector.ignore_errors" => 'NewRelic::TestHelpers::Exceptions::IgnoredError,NewRelic::TestHelpers::Exceptions::ServerIgnoredError', - "error_collector.enabled" => true, - }, - "collect_errors" => true - }, 200) - end - - def test_should_notice_server_ignored_error_if_no_server_side_config - # Overrides test in base class, since doesn't apply - end - - def test_should_ignore_server_ignored_errors - get '/error/server_ignored_error' - - assert(errors.empty?, - 'Noticed an error that should have been ignored' + errors.join(', ')) - end - end +GC.disable +GC.enable diff --git a/test/multiverse/suites/rails/gc_instrumentation_test.rb b/test/multiverse/suites/rails/gc_instrumentation_test.rb deleted file mode 100644 index 2c5918c..0000000 --- a/test/multiverse/suites/rails/gc_instrumentation_test.rb +++ /dev/null @@ -1,93 +0,0 @@ -# encoding: utf-8 -# This file is distributed under New Relic's license terms. -# See https://github.com/newrelic/rpm/blob/master/LICENSE for complete details. - -require './app' - -# These tests only return consistent results for REE or MRI >= 1.9.2 -if NewRelic::LanguageSupport.ree? || - (RUBY_VERSION >= '1.9.2' && - !NewRelic::LanguageSupport.jruby? && - !NewRelic::LanguageSupport.rubinius?) - -class GcController < ApplicationController - def gc_action - begin - NewRelic::Agent::StatsEngine::GCProfiler.init - initial_gc_count = current_gc_count - - Timeout.timeout(5) do - until current_gc_count > initial_gc_count - long_string = "01234567" * 100_000 - long_string = nil - another_long_string = "01234567" * 100_000 - another_long_string = nil - end - end - rescue Timeout::Error - puts "Timed out waiting for GC..." - end - - render :text => 'ha' - end - - def current_gc_count - if NewRelic::LanguageSupport.ree? - ::GC.collections - elsif RUBY_VERSION >= '1.9.2' - ::GC.count - end - end -end - -class GCRailsInstrumentationTest < ActionController::TestCase - tests GcController - - include MultiverseHelpers - - setup_and_teardown_agent do - NewRelic::Agent.drop_buffered_data - NewRelic::Agent::StatsEngine::GCProfiler.reset - enable_gc_stats - @controller = GcController.new - end - - def test_records_accurate_time_for_gc_activity - start = Time.now - get :gc_action - elapsed = Time.now.to_f - start.to_f - - assert_in_range(elapsed, get_call_time('GC/Transaction/allWeb')) - assert_in_range(elapsed, get_call_time('GC/Transaction/allWeb', 'Controller/gc/gc_action')) - end - - def test_records_transaction_param_for_gc_activity - start = Time.now.to_f - get :gc_action - elapsed = Time.now.to_f - start - - trace = NewRelic::Agent.instance.transaction_sampler.last_sample - assert_in_range(elapsed, attributes_for(trace, :intrinsic)[:gc_time]) - end - - def assert_in_range(duration, gc_time) - assert gc_time > 0.0, "GC Time wasn't recorded!" - assert gc_time < duration, "GC Time #{gc_time} can't be more than elapsed #{duration}!" - end - - def get_call_time(name, scope=nil) - NewRelic::Agent.agent.stats_engine. - get_stats(name, true, false, scope). - total_call_time - end - - def enable_gc_stats - if NewRelic::LanguageSupport.ree? - GC.enable_stats - elsif RUBY_VERSION >= '1.9.2' - GC::Profiler.enable - end - end -end - -end diff --git a/test/multiverse/suites/rails/ignore_test.rb b/test/multiverse/suites/rails/ignore_test.rb deleted file mode 100644 index d83702f..0000000 --- a/test/multiverse/suites/rails/ignore_test.rb +++ /dev/null @@ -1,79 +0,0 @@ -# encoding: utf-8 -# This file is distributed under New Relic's license terms. -# See https://github.com/newrelic/rpm/blob/master/LICENSE for complete details. - -# https://newrelic.atlassian.net/browse/RUBY-927 - -require './app' - -class IgnoredController < ApplicationController - newrelic_ignore :only => :action_to_ignore - newrelic_ignore_apdex :only => :action_to_ignore_apdex - - def action_to_ignore - render :text => "Ignore this" - end - - def action_to_ignore_apdex - render :text => "This too" - end -end - -class ParentController < ApplicationController - newrelic_ignore_apdex - - def foo(*args); end - - add_transaction_tracer :foo -end - -class ChildController < ParentController - def bar(*args); end - - add_transaction_tracer :bar -end - - -class IgnoredActionsTest < RailsMultiverseTest - include MultiverseHelpers - - setup_and_teardown_agent(:cross_process_id => "boo", - :encoding_key => "\0", - :trusted_account_ids => [1]) - - def after_setup - # Make sure we've got a blank slate for doing easier metric comparisons - NewRelic::Agent.instance.drop_buffered_data - end - - def test_metric__ignore - get '/ignored/action_to_ignore' - assert_metrics_recorded_exclusive([]) - end - - def test_metric__ignore_apdex - get '/ignored/action_to_ignore_apdex' - assert_metrics_recorded(["Controller/ignored/action_to_ignore_apdex"]) - assert_metrics_not_recorded(["Apdex"]) - end - - def test_ignored_transaction_traces_dont_leak - get '/ignored/action_to_ignore' - get '/request_stats/stats_action' - - trace = NewRelic::Agent.instance.transaction_sampler.last_sample - assert_equal 1, trace.root_node.called_nodes.count - end - - def test_should_not_write_cat_response_headers_for_ignored_transactions - get '/ignored/action_to_ignore', nil, {'X-NewRelic-ID' => Base64.encode64('1#234')} - assert_nil @response.headers["X-NewRelic-App-Data"] - end - - def test_apdex_ignored_if_ignored_in_parent_class - get '/child/foo' - get '/child/bar' - - assert_metrics_not_recorded("Apdex") - end -end diff --git a/test/multiverse/suites/rails/middleware_instrumentation_test.rb b/test/multiverse/suites/rails/middleware_instrumentation_test.rb deleted file mode 100644 index c62e4f9..0000000 --- a/test/multiverse/suites/rails/middleware_instrumentation_test.rb +++ /dev/null @@ -1,41 +0,0 @@ -# encoding: utf-8 -# This file is distributed under New Relic's license terms. -# See https://github.com/newrelic/rpm/blob/master/LICENSE for complete details. - -# https://newrelic.atlassian.net/browse/RUBY-927 - -require './app' - -if Rails::VERSION::MAJOR.to_i >= 3 - -class MiddlewareInstrumentationTest < RailsMultiverseTest - def test_rails_middleware_records_metrics - get('/') - assert_metrics_recorded( - ['Middleware/all', 'Middleware/Rack/Rails::Rack::Logger/call'] - ) - end - - def test_rails_routeset_is_instrumented - get('/') - assert_metrics_recorded( - 'Middleware/Rack/ActionDispatch::Routing::RouteSet/call' - ) - end - - if Rails::VERSION::MAJOR >= 4 - def test_rails_middlewares_constructed_by_name - get('/') - assert response.headers['NamedMiddleware'], "NamedMiddleware should have been called, but wasn't" - assert_metrics_recorded('Middleware/Rack/NamedMiddleware/call') - end - - def test_rails_middlewares_passed_as_instances - get('/') - assert response.headers['InstanceMiddleware'], "InstanceMiddleware should have been called, but wasn't" - assert_metrics_recorded('Middleware/Rack/InstanceMiddleware/call') - end - end -end - -end diff --git a/test/multiverse/suites/rails/parameter_capture_test.rb b/test/multiverse/suites/rails/parameter_capture_test.rb deleted file mode 100644 index 1510350..0000000 --- a/test/multiverse/suites/rails/parameter_capture_test.rb +++ /dev/null @@ -1,328 +0,0 @@ -# encoding: utf-8 -# This file is distributed under New Relic's license terms. -# See https://github.com/newrelic/rpm/blob/master/LICENSE for complete details. - -require './app' - -class ParameterCaptureController < ApplicationController - def transaction - render :text => 'hi!' - end - - def create - raise 'problem' if params[:raise] - render :text => 'created' - end - - def sql - NewRelic::Agent.agent.sql_sampler.notice_sql( - 'SELECT * FROM table', - 'ActiveRecord/foos/find', - {}, - 100.0 - ) - end - - def error - raise 'wut' - end - - # For Rails 3+, this is configured globally in the config block for the app - if Rails::VERSION::MAJOR == 2 - filter_parameter_logging(:secret) - end -end - -class ParameterCaptureTest < RailsMultiverseTest - include MultiverseHelpers - setup_and_teardown_agent - - def test_no_params_captured_on_errors_when_disabled - with_config(:capture_params => false) do - get '/parameter_capture/error?other=1234&secret=4567' - refute_contains_request_params(agent_attributes_for_single_error_posted) - end - end - - def test_no_params_captured_on_tts_when_disabled - with_config(:capture_params => false) do - get '/parameter_capture/transaction?other=1234&secret=4567' - end - assert_equal({}, last_transaction_trace_request_params) - end - - def test_uri_on_traced_errors_never_contains_query_string_without_capture_params - with_config(:capture_params => false) do - get '/parameter_capture/error?other=1234&secret=4567' - assert_equal('/parameter_capture/error', attributes_for_single_error_posted("request_uri")) - end - end - - def test_uri_on_traced_errors_never_contains_query_string_with_capture_params - with_config(:capture_params => true) do - get '/parameter_capture/error?other=1234&secret=4567' - assert_equal('/parameter_capture/error', attributes_for_single_error_posted("request_uri")) - end - end - - def test_referrer_on_traced_errors_never_contains_query_string_without_capture_params - with_config(:capture_params => false) do - get '/parameter_capture/error?other=1234&secret=4567', {}, { 'HTTP_REFERER' => '/foo/bar?other=123&secret=456' } - attributes = agent_attributes_for_single_error_posted - assert_equal('/foo/bar', attributes["request.headers.referer"]) - end - end - - def test_referrer_on_traced_errors_never_contains_query_string_even_with_capture_params - with_config(:capture_params => true) do - get '/parameter_capture/error?other=1234&secret=4567', {}, { 'HTTP_REFERER' => '/foo/bar?other=123&secret=456' } - attributes = agent_attributes_for_single_error_posted - assert_equal('/foo/bar', attributes["request.headers.referer"]) - end - end - - def test_controller_and_action_excluded_from_error_parameters - with_config(:capture_params => true) do - get '/parameter_capture/error' - run_harvest - - refute_error_has_agent_attribute('request.parameters.controller') - refute_error_has_agent_attribute('request.parameters.action') - end - end - - def test_controller_and_action_excluded_from_transaction_trace_parameters - with_config(:capture_params => true, :'transaction_tracer.transaction_threshold' => -10) do - get '/parameter_capture/transaction' - run_harvest - - refute_transaction_trace_has_agent_attribute('request.parameters.controller') - refute_transaction_trace_has_agent_attribute('request.parameters.action') - end - end - - def test_uri_on_tts_never_contains_query_string - with_config(:capture_params => false) do - get '/parameter_capture/transaction?other=1234&secret=4567' - end - assert_equal('/parameter_capture/transaction', last_transaction_trace.uri) - - with_config(:capture_params => true) do - get '/parameter_capture/transaction?other=1234&secret=4567' - end - assert_equal('/parameter_capture/transaction', last_transaction_trace.uri) - end - - def test_filters_parameters_on_traced_errors - with_config(:capture_params => true) do - get '/parameter_capture/error?other=1234&secret=4567' - - captured_params = agent_attributes_for_single_error_posted - assert_equal('[FILTERED]', captured_params['request.parameters.secret']) - assert_equal('1234', captured_params['request.parameters.other']) - end - end - - def test_filters_parameters_on_transaction_traces - with_config(:capture_params => true) do - get '/parameter_capture/transaction?other=1234&secret=4567' - end - - captured_params = last_transaction_trace_request_params - assert_equal('[FILTERED]', captured_params['request.parameters.secret']) - assert_equal('1234', captured_params['request.parameters.other']) - end - - def test_no_traced_error_params_captured_when_bails_before_rails - with_config(:capture_params => false) do - get '/middleware_error/before?other=1234&secret=4567' - refute_contains_request_params(agent_attributes_for_single_error_posted) - end - end - - def test_no_transaction_trace_params_captured_when_bails_before_rails - with_config(:capture_params => false) do - get '/middleware_error/before?other=1234&secret=4567' - end - - assert_equal({}, last_transaction_trace_request_params) - end - - def test_no_params_captured_on_error_when_bails_before_rails_even_when_enabled - with_config(:capture_params => true) do - get '/middleware_error/before?other=1234&secret=4567' - refute_contains_request_params(agent_attributes_for_single_error_posted) - end - end - - def test_no_params_captured_on_tt_when_bails_before_rails_even_when_enabled - with_config(:capture_params => true) do - get '/middleware_error/before?other=1234&secret=4567' - end - assert_equal({}, last_transaction_trace_request_params) - end - - def test_uri_on_tt_should_not_contain_query_string_with_capture_params_off - with_config(:capture_params => false) do - get '/parameter_capture/transaction?param1=value1¶m2=value2' - end - assert_equal('/parameter_capture/transaction', last_transaction_trace.uri) - end - - def test_uri_on_tt_should_not_contain_query_string_with_capture_params_on - with_config(:capture_params => true) do - get '/parameter_capture/transaction?param1=value1¶m2=value2' - end - assert_equal('/parameter_capture/transaction', last_transaction_trace.uri) - end - - def test_uri_on_traced_error_should_not_contain_query_string_with_capture_params_off - with_config(:capture_params => false) do - get '/parameter_capture/error?param1=value1¶m2=value2' - assert_equal('/parameter_capture/error', attributes_for_single_error_posted("request_uri")) - end - end - - def test_uri_on_traced_error_should_not_contain_query_string_with_capture_params_on - with_config(:capture_params => true) do - get '/parameter_capture/error?param1=value1¶m2=value2' - assert_equal('/parameter_capture/error', attributes_for_single_error_posted("request_uri")) - end - end - - def test_uri_on_sql_trace_should_not_contain_query_string_with_capture_params_off - with_config(:capture_params => false) do - get '/parameter_capture/sql?param1=value1¶m2=value2' - end - assert_equal('/parameter_capture/sql', last_sql_trace.url) - end - - def test_uri_on_sql_trace_should_not_contain_query_string_with_capture_params_on - with_config(:capture_params => true) do - get '/parameter_capture/sql?param1=value1¶m2=value2' - end - assert_equal('/parameter_capture/sql', last_sql_trace.url) - end - - def test_parameters_attached_to_transaction_events_if_enabled - with_config(:'attributes.include' => 'request.parameters.*', - :'attributes.exclude' => ['request.*', 'response.*']) do - get '/parameter_capture/transaction?param1=value1¶m2=value2' - end - - actual = agent_attributes_for_single_event_posted_without_ignored_attributes - - expected = {"request.parameters.param1" => "value1", - "request.parameters.param2" => "value2" - } - - assert_equal expected, actual - end - - def test_request_and_response_attributes_recorded_as_agent_attributes - get '/parameter_capture/transaction' - - expected = { - "response.headers.contentType" => "#{response.content_type}; charset=#{response.charset}", - "request.headers.contentLength" => request.content_length.to_i, - "request.headers.contentType" => request.content_type, - "request.headers.host" => request.host, - "request.headers.accept" => request.accept - } - - # ActionController::IntegrationTest sets this header whereas ActionDispatch::IntegrationTest - # does not. Since we test using both we need to conditionally expect content_length to be set. - - unless defined?(ActionDispatch::IntegrationTest) - expected["response.headers.contentLength"] = response.content_length - end - - actual = agent_attributes_for_single_event_posted_without_ignored_attributes - - # request method may be a symbol or string based on Rails versions - request_method = actual.delete("request.method") - assert_equal request_method, request.request_method.to_s.upcase - - assert_equal(expected, actual) - end - - - if Rails::VERSION::MAJOR > 2 - def test_params_tts_should_be_filtered_when_serviced_by_rack_app - params = {"secret" => "shhhhhhh", "name" => "name"} - with_config(:capture_params => true) do - post '/filtering_test/', params - end - - expected = { - "request.parameters.secret" => "[FILTERED]", - "request.parameters.name" => "name" - } - assert_equal expected, last_transaction_trace_request_params - end - - def test_params_on_errors_should_be_filtered_when_serviced_by_rack_app - params = {"secret" => "shhhhhhh", "name" => "name"} - with_config(:capture_params => true) do - post '/filtering_test?raise=1', params - - expected = { - "request.parameters.secret" => "[FILTERED]", - "request.parameters.name" => "name", - "request.parameters.raise" => "1" - } - - attributes = agent_attributes_for_single_error_posted - expected.each do |key, value| - assert_equal value, attributes[key] - end - end - end - - def test_parameter_filtering_should_not_mutate_argument - input = { "foo" => "bar", "secret" => "baz" } - env = { "action_dispatch.parameter_filter" => ["secret"] } - filtered = NewRelic::Agent::ParameterFiltering.apply_filters(env, input) - - assert_equal({ "foo" => "bar", "secret" => "[FILTERED]" }, filtered) - assert_equal({ "foo" => "bar", "secret" => "baz" }, input) - end - end - - if Rails::VERSION::MAJOR > 2 && defined?(Sinatra) - def test_params_tts_should_be_filtered_when_serviced_by_sinatra_app - with_config(:capture_params => true) do - get '/sinatra_app/', "secret" => "shhhhhhh", "name" => "name" - end - - expected = { - "request.parameters.secret" => "[FILTERED]", - "request.parameters.name" => "name" - } - assert_equal expected, last_transaction_trace_request_params - end - - def test_params_on_errors_should_be_filtered_when_serviced_by_sinatra_app - with_config(:capture_params => true) do - get '/sinatra_app?raise=1', "secret" => "shhhhhhh", "name" => "name" - - attributes = agent_attributes_for_single_error_posted - assert_equal "[FILTERED]", attributes["request.parameters.secret"] - assert_equal "name", attributes["request.parameters.name"] - assert_equal "1", attributes["request.parameters.raise"] - end - end - - def test_file_upload_params_are_replaced_with_placeholder - with_config(:capture_params => true, :'transaction_tracer.transaction_threshold' => -10) do - post '/parameter_capture', :file => Rack::Test::UploadedFile.new(__FILE__, 'text/plain') - - run_harvest - - result = single_transaction_trace_posted - assert_equal "#", result.agent_attributes["request.parameters.file"] - end - end - end - -end diff --git a/test/multiverse/suites/rails/queue_time_test.rb b/test/multiverse/suites/rails/queue_time_test.rb deleted file mode 100644 index 157f8b9..0000000 --- a/test/multiverse/suites/rails/queue_time_test.rb +++ /dev/null @@ -1,89 +0,0 @@ -# encoding: utf-8 -# This file is distributed under New Relic's license terms. -# See https://github.com/newrelic/rpm/blob/master/LICENSE for complete details. - -# https://newrelic.atlassian.net/browse/RUBY-927 - -require './app' - -class QueueController < ApplicationController - def queued - respond_to do |format| - format.html { render :text => "Queued" } - end - end - - def nested - nested_transaction - render :text => 'whatever' - end - - def nested_transaction; end - - add_transaction_tracer :nested_transaction -end - -class QueueTimeTest < RailsMultiverseTest - - REQUEST_START_HEADER = 'HTTP_X_REQUEST_START' - - include MultiverseHelpers - - setup_and_teardown_agent(:beacon => "beacon", :browser_key => "key", :js_agent_loader => "loader") - - def test_should_track_queue_time_metric - t0 = freeze_time - t1 = advance_time(2) - get_path('/queue/queued', t0) - - assert_metrics_recorded( - 'WebFrontend/QueueTime' => { - :call_count => 1, - :total_call_time => (t1 - t0) - } - ) - end - - def test_should_see_queue_time_in_rum - t0 = freeze_time - t1 = advance_time(2) - get_path('/queue/queued', t0) - queue_time = extract_queue_time_from_response - assert_equal((t1 - t0) * 1000, queue_time) - end - - def test_should_not_track_queue_time_for_nested_transactions - t0 = freeze_time - t1 = advance_time(2) - get_path('/queue/nested', t0) - assert_metrics_recorded( - 'WebFrontend/QueueTime' => { - :call_count => 1, - :total_call_time => (t1 - t0) - } - ) - end - - def get_path(path, queue_start_time) - value = "t=#{(queue_start_time.to_f * 1_000_000).to_i}" - get(path, nil, REQUEST_START_HEADER => value) - end - - def extract_queue_time_from_response - get_last_response_body =~ /\"queueTime\":(\d+.*)/ - refute_nil $1, "Should have found queue time in #{@response.body.inspect}" - $1.to_i - end - - def get_last_response_body - if Rails::VERSION::MAJOR >= 3 - @response.body - else - # In Rails 2 integration tests, @response.body is always the response from - # the controller itself, not the middleware stack. Since we want the - # response from the middleware stack, we grab it off of the integration - # session. - @integration_session.body - end - end -end diff --git a/test/multiverse/suites/rails/request_statistics_test.rb b/test/multiverse/suites/rails/request_statistics_test.rb deleted file mode 100644 index 919d7df..0000000 --- a/test/multiverse/suites/rails/request_statistics_test.rb +++ /dev/null @@ -1,192 +0,0 @@ -# encoding: utf-8 -# This file is distributed under New Relic's license terms. -# See https://github.com/newrelic/rpm/blob/master/LICENSE for complete details. - -# https://newrelic.atlassian.net/browse/RUBY-1096 - -require './app' - -class RequestStatsController < ApplicationController - def stats_action - sleep 0.01 - render :text => 'some stuff' - end - - def cross_app_action - NewRelic::Agent::TransactionState.tl_get.is_cross_app_caller = true - render :text => 'some stuff' - end - - def stats_action_with_custom_params - ::NewRelic::Agent.add_custom_attributes('color' => 'blue', 1 => :bar, 'bad' => {}) - render :text => 'some stuff' - end -end - -class RequestStatsTest < RailsMultiverseTest - extend Multiverse::Color - - include MultiverseHelpers - setup_and_teardown_agent - - # - # Tests - # - - def test_doesnt_send_when_disabled - with_config( :'analytics_events.enabled' => false ) do - 5.times { get '/request_stats/stats_action' } - - NewRelic::Agent.agent.send(:harvest_and_send_analytic_event_data) - - assert_equal 0, $collector.calls_for('analytic_event_data').length - end - end - - def test_request_times_should_be_reported_if_enabled - with_config( :'analytics_events.enabled' => true ) do - 5.times { get '/request_stats/stats_action' } - - NewRelic::Agent.agent.send(:harvest_and_send_analytic_event_data) - - post = $collector.calls_for('analytic_event_data').first - - refute_nil( post ) - assert_kind_of Array, post.events - assert_kind_of Array, post.events.first - - assert_equal 3, post.events.first.length - - post.events.first.each do |event_chunk| - assert_kind_of Hash, event_chunk - end - - sample = post.events.first.first - assert_equal 'Controller/request_stats/stats_action', sample['name'] - assert_encoding 'utf-8', sample['name'] - assert_equal 'Transaction', sample['type'] - assert_kind_of Float, sample['duration'] - assert_kind_of Float, sample['timestamp'] - - assert_nil sample['nr.guid'] - assert_nil sample['nr.referringTransactionGuid'] - end - end - - def test_request_should_include_guid_if_cross_app - with_config( :'analytics_events.enabled' => true ) do - 5.times { get '/request_stats/cross_app_action' } - - NewRelic::Agent.agent.send(:harvest_and_send_analytic_event_data) - - post = $collector.calls_for('analytic_event_data').first - - refute_nil( post ) - assert_kind_of Array, post.events - assert_kind_of Array, post.events.first - - sample = post.events.first.first - assert_kind_of Hash, sample - - assert_kind_of String, sample['nr.guid'] - end - end - - def test_request_should_include_referring_guid_if_needed - with_config(:'analytics_events.enabled' => true, - :'cross_process_id' => 'boo', - :'encoding_key' => "\0", - :'trusted_account_ids' => [1]) do - rack_env = { - 'HTTP_X_NEWRELIC_ID' => Base64.encode64('1#234'), - 'HTTP_X_NEWRELIC_TRANSACTION' => Base64.encode64('["8badf00d",1]') - } - get '/request_stats/cross_app_action', {}, rack_env - - NewRelic::Agent.agent.send(:harvest_and_send_analytic_event_data) - - post = $collector.calls_for('analytic_event_data').first - - refute_nil( post ) - assert_kind_of Array, post.events - assert_kind_of Array, post.events.first - - sample = post.events.first.first - - assert_kind_of Hash, sample - assert_kind_of String, sample['nr.guid'] - assert_equal('8badf00d', sample['nr.referringTransactionGuid']) - end - end - - def test_custom_params_should_be_reported_with_events_and_coerced_to_safe_types - with_config( :'analytics_events.enabled' => true ) do - 5.times { get '/request_stats/stats_action_with_custom_params' } - - NewRelic::Agent.agent.send(:harvest_and_send_analytic_event_data) - - post = $collector.calls_for('analytic_event_data').first - - refute_nil( post ) - assert_kind_of Array, post.events - assert_kind_of Array, post.events.first - - sample = post.events.first[0] - assert_kind_of Hash, sample - - assert_equal 'Controller/request_stats/stats_action_with_custom_params', sample['name'] - assert_encoding 'utf-8', sample['name'] - assert_equal 'Transaction', sample['type'] - - ['blue', 'bar', 'bad'].each do |key| - assert_not_includes(sample, key) - end - - custom_params = post.events.first[1] - - assert_equal 'blue', custom_params['color'] - assert_equal 'bar', custom_params['1'] - assert_equal '{}', custom_params['bad'] - end - end - - def test_request_samples_should_be_preserved_upon_failure - with_config(:'analytics_events.enabled' => true) do - 5.times { get '/request_stats/stats_action' } - - # fail once - $collector.stub('analytic_event_data', {}, 503) - NewRelic::Agent.agent.send(:harvest_and_send_analytic_event_data) - - # recover - $collector.stub('analytic_event_data', {'return_value'=>nil}, 200) - NewRelic::Agent.agent.send(:harvest_and_send_analytic_event_data) - - post = $collector.calls_for('analytic_event_data').last - - samples = post.events - assert_equal(5, samples.size) - samples.each do |sample| - # undo the extra layer of wrapping that the collector wants - sample = sample.first - assert_kind_of Hash, sample - assert_kind_of Float, sample['duration'] - assert_kind_of Float, sample['timestamp'] - end - end - end - - - # - # Helpers - # - - def assert_encoding( encname, string ) - return unless string.respond_to?( :encoding ) - expected_encoding = Encoding.find( encname ) or raise "no such encoding #{encname.dump}" - msg = "Expected encoding of %p to be %p, but it was %p" % - [ string, expected_encoding, string.encoding ] - assert_equal( expected_encoding, string.encoding, msg ) - end - -end diff --git a/test/multiverse/suites/rails/transaction_ignoring_test.rb b/test/multiverse/suites/rails/transaction_ignoring_test.rb deleted file mode 100644 index 39285e8..0000000 --- a/test/multiverse/suites/rails/transaction_ignoring_test.rb +++ /dev/null @@ -1,41 +0,0 @@ -# encoding: utf-8 -# This file is distributed under New Relic's license terms. -# See https://github.com/newrelic/rpm/blob/master/LICENSE for complete details. - -require './app' -require 'transaction_ignoring_test_cases' - -class TransactionIgnorerController < ApplicationController - def run_transaction - state = NewRelic::Agent::TransactionState.tl_get - NewRelic::Agent.set_transaction_name(params[:txn_name]) - NewRelic::Agent.notice_error(params[:error_msg]) if params[:error_msg] - NewRelic::Agent.instance.sql_sampler.notice_sql("select * from test", - "Database/test/select", - nil, 1.5, state) if params[:slow_sql] - render :text => 'some stuff' - end - - -end - -class TransactionIgnoringTest < RailsMultiverseTest - - include MultiverseHelpers - include TransactionIgnoringTestCases - - def trigger_transaction(txn_name) - get '/transaction_ignorer/run_transaction', :txn_name => txn_name - end - - def trigger_transaction_with_error(txn_name, error_msg) - get '/transaction_ignorer/run_transaction', :txn_name => txn_name, - :error_msg => error_msg - end - - def trigger_transaction_with_slow_sql(txn_name) - get '/transaction_ignorer/run_transaction', :txn_name => txn_name, - :slow_sql => 'true' - end - -end diff --git a/test/multiverse/suites/rails/view_instrumentation_test.rb b/test/multiverse/suites/rails/view_instrumentation_test.rb deleted file mode 100644 index a7f99ce..0000000 --- a/test/multiverse/suites/rails/view_instrumentation_test.rb +++ /dev/null @@ -1,254 +0,0 @@ -# encoding: utf-8 -# This file is distributed under New Relic's license terms. -# See https://github.com/newrelic/rpm/blob/master/LICENSE for complete details. - -require './app' -require 'haml' - -ActionController::Base.view_paths = ['app/views'] - -class ViewsController < ApplicationController - def template_render_with_3_partial_renders - render 'index' - end - - def render_with_delays - freeze_time - @delay = 1 - render 'index' - end - - def deep_partial_render - render 'deep_partial' - end - - def text_render - render :text => "Yay" - end - - def json_render - render :json => {"a" => "b"} - end - - def xml_render - render :xml => {"a" => "b"} - end - - def js_render - render :js => 'alert("this is js");' - end - - def file_render - # The choice of filename is significant here: we want a dot in the filename - # in order to expose an issue on Rails 2. - file = File.expand_path(File.join(File.dirname(__FILE__), "dummy.txt")) - render :file => file, :content_type => 'text/plain', :layout => false - end - - def nothing_render - render :nothing => true - end - - def inline_render - render :inline => "<% Time.now %>

<%= Time.now %>

" - end - - def haml_render - render 'haml_view' - end - - def no_template - render [] - end - - def collection_render - render((1..3).map{|x| Foo.new }) - end - - # proc rendering isn't available in rails 3 but you can do nonsense like this - # and assign an enumerable object to the response body. - def proc_render - streamer = Class.new do - def each - 10_000.times do |i| - yield "This is line #{i}\n" - end - end - end - self.response_body = streamer.new - end - - def raise_render - raise "this is an uncaught RuntimeError" - end -end - -class ViewInstrumentationTest < RailsMultiverseTest - include MultiverseHelpers - - setup_and_teardown_agent do - # ActiveSupport testing keeps blowing away my subscribers on - # teardown for some reason. Have to keep putting it back. - if Rails::VERSION::MAJOR.to_i == 4 - NewRelic::Agent::Instrumentation::ActionViewSubscriber \ - .subscribe(/render_.+\.action_view$/) - NewRelic::Agent::Instrumentation::ActionControllerSubscriber \ - .subscribe(/^process_action.action_controller$/) - end - end - - (ViewsController.action_methods - ['raise_render']).each do |method| - - # proc rendering doesn't work on Rails 2 - next if method == 'proc_render' && Rails::VERSION::MAJOR <= 2 - - define_method("test_sanity_#{method}") do - get "/views/#{method}" - assert_equal 200, status - end - - def test_should_allow_uncaught_exception_to_propagate - get "/views/raise_render" - assert_equal 500, status - end - - def test_should_count_all_the_template_and_partial_nodes - get '/views/template_render_with_3_partial_renders' - sample = NewRelic::Agent.agent.transaction_sampler.last_sample - nodes = find_all_nodes_with_name_matching(sample, ['^Nested/Controller/views', '^View']) - nodes_list = "Found these nodes:\n #{nodes.map(&:metric_name).join("\n ")}" - - if Rails::VERSION::MAJOR <= 2 - assert_equal 8, nodes.length, "Should be a node for the controller action, the template, and 3 partials with two nodes each (8). #{nodes_list}" - else - assert_equal 5, nodes.length, "Should be a node for the controller action, the template, and 3 partials (5). #{nodes_list}" - end - end - - def test_should_have_3_nodes_with_the_correct_metric_name - get '/views/template_render_with_3_partial_renders' - - sample = NewRelic::Agent.agent.transaction_sampler.last_sample - partial_nodes = find_all_nodes_with_name_matching(sample, 'View/views/_a_partial.html.erb/Partial') - - assert_equal 3, partial_nodes.size, "sanity check" - assert_equal ['View/views/_a_partial.html.erb/Partial'], partial_nodes.map(&:metric_name).uniq - end - - # We don't capture text or inline template rendering on Rails 2 - if Rails::VERSION::MAJOR >= 3 - def test_should_create_a_metric_for_the_rendered_inline_template - get '/views/inline_render' - - sample = NewRelic::Agent.agent.transaction_sampler.last_sample - text_node = find_node_with_name(sample, 'View/inline template/Rendering') - - assert text_node, "Failed to find a node named View/inline template/Rendering" - assert_metrics_recorded('View/inline template/Rendering') - end - - # It doesn't seem worth it to get consistent behavior here. - if Rails::VERSION::MAJOR.to_i == 3 && Rails::VERSION::MINOR.to_i == 0 - def test_should_not_instrument_rendering_of_text - get '/views/text_render' - sample = NewRelic::Agent.agent.transaction_sampler.last_sample - refute find_node_with_name(sample, 'View/text template/Rendering') - end - else - def test_should_create_a_metric_for_the_rendered_text - get '/views/text_render' - - sample = NewRelic::Agent.agent.transaction_sampler.last_sample - text_node = find_node_with_name(sample, 'View/text template/Rendering') - - assert text_node, "Failed to find a node named View/text template/Rendering" - assert_metrics_recorded('View/text template/Rendering') - end - end - end - - def test_should_create_a_metric_for_the_rendered_haml_template - get '/views/haml_render' - - sample = NewRelic::Agent.agent.transaction_sampler.last_sample - text_node = find_node_with_name(sample, 'View/views/haml_view.html.haml/Rendering') - - assert text_node, "Failed to find a node named View/views/haml_view.html.haml/Rendering" - assert_metrics_recorded('View/views/haml_view.html.haml/Rendering') - end - - def test_should_create_a_proper_metric_when_the_template_is_unknown - get '/views/no_template' - sample = NewRelic::Agent.agent.transaction_sampler.last_sample - - # Different versions have significant difference in handling, but we're - # happy enough with what each of them does in the unknown case - if Rails::VERSION::MAJOR.to_i < 3 || (Rails::VERSION::MAJOR.to_i == 3 && Rails::VERSION::MINOR.to_i == 0) - refute find_node_with_name_matching(sample, /^View/) - elsif Rails::VERSION::MAJOR.to_i == 3 - assert find_node_with_name(sample, 'View/collection/Partial') - else - assert find_node_with_name(sample, 'View/(unknown)/Partial') - end - end - - def test_should_create_a_proper_metric_when_we_render_a_collection - get '/views/collection_render' - sample = NewRelic::Agent.agent.transaction_sampler.last_sample - assert find_node_with_name(sample, "View/foos/_foo.html.haml/Partial") - end - - [:js_render, :xml_render, :proc_render, :json_render ].each do |action| - next if action == :proc_render && Rails::VERSION::MAJOR <= 2 - define_method("test_should_not_instrument_rendering_of_#{action}") do - get "/views/#{action}" - sample = NewRelic::Agent.agent.transaction_sampler.last_sample - view_node = find_node_with_name_matching(sample, /^View\//) - refute view_node, "Should not instrument rendering of #{action}, found #{view_node}." - end - end - - # The Rails 2.3 view instrumentation doesn't actually pass this test, but it - # hasn't been a problem thus far, so we're letting it slide. - if Rails::VERSION::MAJOR >= 3 - def test_should_create_a_metric_for_rendered_file_that_does_not_include_the_filename_so_it_doesnt_metric_explode - get '/views/file_render' - sample = NewRelic::Agent.agent.transaction_sampler.last_sample - assert find_node_with_name(sample, 'View/file/Rendering') - refute find_node_with_name_matching(sample, 'dummy') - end - end - - def test_exclusive_time_for_template_render_metrics_should_not_include_partial_rendering_time - get '/views/render_with_delays' - - expected_stats_partial = { - :call_count => 3, - :total_call_time => 3.0, - :total_exclusive_time => 3.0 - } - - expected_stats_template = { - :call_count => 1, - :total_call_time => 4.0, - :total_exclusive_time => 1.0 # top-level template takes 1s itself - } - - scope = 'Controller/views/render_with_delays' - template_metric = 'View/views/index.html.erb/Rendering' - - if Rails::VERSION::MAJOR <= 2 - partial_metric = 'View/views/_a_partial.html.erb/Rendering' - else - partial_metric = 'View/views/_a_partial.html.erb/Partial' - end - - assert_metrics_recorded( - partial_metric => expected_stats_partial, - template_metric => expected_stats_template, - [partial_metric, scope] => expected_stats_partial, - [template_metric, scope] => expected_stats_template - ) - end - end -end