diff --git a/test/multiverse/lib/multiverse/runner.rb b/test/multiverse/lib/multiverse/runner.rb index 892a9d9005..5e4e60386d 100644 --- a/test/multiverse/lib/multiverse/runner.rb +++ b/test/multiverse/lib/multiverse/runner.rb @@ -110,7 +110,7 @@ def execute_suites(filter, opts) # these need services running in github actions, so they are separated 'services_1' => %w[mongo bunny], - 'services_2' => %w[redis sidekiq sidekiq_delay_extensions memcache], + 'services_2' => %w[redis sidekiq sidekiq_delay_extensions sidekiq_ignore_retry_errors_enabled memcache], 'services_kafka' => %w[rdkafka ruby_kafka], 'services_elasticsearch' => %w[elasticsearch], 'services_mysql_pg' => %w[active_record active_record_pg], diff --git a/test/multiverse/suites/sidekiq/sidekiq_ignore_retry_errors_test.rb b/test/multiverse/suites/sidekiq/sidekiq_ignore_retry_errors_disabled_test.rb similarity index 56% rename from test/multiverse/suites/sidekiq/sidekiq_ignore_retry_errors_test.rb rename to test/multiverse/suites/sidekiq/sidekiq_ignore_retry_errors_disabled_test.rb index 429e86747b..eb1d19afe7 100644 --- a/test/multiverse/suites/sidekiq/sidekiq_ignore_retry_errors_test.rb +++ b/test/multiverse/suites/sidekiq/sidekiq_ignore_retry_errors_disabled_test.rb @@ -4,6 +4,9 @@ require_relative 'sidekiq_test_helpers' +# On startup, Sidekiq instrumentation registers error and death handlers +# based on the value of the 'sidekiq.ignore_retry_errors'. Because of this, +# we need to have separate enabled/disabled test suites to test both cases. class SidekiqIgnoreRetryErrorsTest < Minitest::Test include SidekiqTestHelpers @@ -50,58 +53,6 @@ def test_error_handlers_registered_when_sidekiq_ignore_retry_errors_is_false 'Expected NewRelic error_handler to be registered when sidekiq.ignore_retry_errors is false' end - def test_error_handlers_not_registered_when_sidekiq_ignore_retry_errors_is_true - with_config(:'sidekiq.ignore_retry_errors' => true) do - # TODO: MAJOR VERSION - remove this when Sidekiq v5 is no longer supported - skip 'Test requires Sidekiq v6+' unless Sidekiq::VERSION.split('.').first.to_i >= 6 - - config = if Sidekiq::VERSION.split('.').first.to_i >= 7 - Sidekiq.default_configuration - else - Sidekiq - end - - error_handlers = if config.respond_to?(:error_handlers) - config.error_handlers - else - config[:error_handlers] || [] - end - - nr_error_handler_found = error_handlers.any? do |handler| - handler.is_a?(Proc) && handler.source_location&.first&.include?('newrelic') - end - - refute nr_error_handler_found, - 'Expected NewRelic error_handler to NOT be registered when sidekiq.ignore_retry_errors is true' - end - end - - def test_death_handlers_registered_when_sidekiq_ignore_retry_errors_is_true - with_config(:'sidekiq.ignore_retry_errors' => true) do - # TODO: MAJOR VERSION - remove this when Sidekiq v5 is no longer supported - skip 'Test requires Sidekiq v6+' unless Sidekiq::VERSION.split('.').first.to_i >= 6 - - config = if Sidekiq::VERSION.split('.').first.to_i >= 7 - Sidekiq.default_configuration - else - Sidekiq - end - - death_handlers = if config.respond_to?(:death_handlers) - config.death_handlers - else - config[:death_handlers] || [] - end - - nr_death_handler_found = death_handlers.any? do |handler| - handler.is_a?(Proc) && handler.source_location&.first&.include?('newrelic') - end - - assert nr_death_handler_found, - 'Expected NewRelic death_handler to be registered when sidekiq.ignore_retry_errors is true' - end - end - def test_death_handlers_not_registered_when_sidekiq_ignore_retry_errors_is_false # TODO: MAJOR VERSION - remove this when Sidekiq v5 is no longer supported skip 'Test requires Sidekiq v6+' unless Sidekiq::VERSION.split('.').first.to_i >= 6 @@ -125,14 +76,4 @@ def test_death_handlers_not_registered_when_sidekiq_ignore_retry_errors_is_false refute nr_death_handler_found, 'Expected NewRelic death_handler to NOT be registered when sidekiq.ignore_retry_errors is false' end - - def test_basic_job_execution_still_works - with_config(:'sidekiq.ignore_retry_errors' => true) do - segment = run_job - - assert_predicate segment, :finished? - assert_predicate segment, :record_metrics? - assert segment.duration.is_a?(Float) - end - end end diff --git a/test/multiverse/suites/sidekiq/sidekiq_ignore_retry_errors_enabled_test.rb b/test/multiverse/suites/sidekiq/sidekiq_ignore_retry_errors_enabled_test.rb new file mode 100644 index 0000000000..340ae24e57 --- /dev/null +++ b/test/multiverse/suites/sidekiq/sidekiq_ignore_retry_errors_enabled_test.rb @@ -0,0 +1,77 @@ +# This file is distributed under New Relic's license terms. +# See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details. +# frozen_string_literal: true + +# require_relative 'sidekiq_test_helpers' + +# # On startup, Sidekiq instrumentation registers error and death handlers +# # based on the value of the 'sidekiq.ignore_retry_errors'. Because of this, +# # we need to have separate enabled/disabled test suites to test both cases. +# class SidekiqIgnoreRetryErrorEnabledTest < Minitest::Test +# include SidekiqTestHelpers + +# def setup +# @config = {:'sidekiq.ignore_retry_errors' => true} +# NewRelic::Agent.config.add_config_for_testing(@config) +# end + +# def teardown +# NewRelic::Agent.config.reset_to_defaults +# end + +# def test_error_handlers_not_registered_when_sidekiq_ignore_retry_errors_is_true +# # TODO: MAJOR VERSION - remove this when Sidekiq v5 is no longer supported +# skip 'Test requires Sidekiq v6+' unless Sidekiq::VERSION.split('.').first.to_i >= 6 + +# config = if Sidekiq::VERSION.split('.').first.to_i >= 7 +# Sidekiq.default_configuration +# else +# Sidekiq +# end + +# error_handlers = if config.respond_to?(:error_handlers) +# config.error_handlers +# else +# config[:error_handlers] || [] +# end + +# nr_error_handler_found = error_handlers.any? do |handler| +# handler.is_a?(Proc) && handler.source_location&.first&.include?('newrelic') +# end + +# refute nr_error_handler_found, +# 'Expected NewRelic error_handler to NOT be registered when sidekiq.ignore_retry_errors is true' +# end + +# def test_death_handlers_registered_when_sidekiq_ignore_retry_errors_is_true +# # TODO: MAJOR VERSION - remove this when Sidekiq v5 is no longer supported +# skip 'Test requires Sidekiq v6+' unless Sidekiq::VERSION.split('.').first.to_i >= 6 + +# config = if Sidekiq::VERSION.split('.').first.to_i >= 7 +# Sidekiq.default_configuration +# else +# Sidekiq +# end + +# death_handlers = if config.respond_to?(:death_handlers) +# config.death_handlers +# else +# config[:death_handlers] || [] +# end + +# nr_death_handler_found = death_handlers.any? do |handler| +# handler.is_a?(Proc) && handler.source_location&.first&.include?('newrelic') +# end + +# assert nr_death_handler_found, +# 'Expected NewRelic death_handler to be registered when sidekiq.ignore_retry_errors is true' +# end + +# def test_basic_job_execution_still_works +# segment = run_job + +# assert_predicate segment, :finished? +# assert_predicate segment, :record_metrics? +# assert_kind_of Float, segment.duration +# end +# end diff --git a/test/multiverse/suites/sidekiq_ignore_retry_errors_enabled/Envfile b/test/multiverse/suites/sidekiq_ignore_retry_errors_enabled/Envfile new file mode 100644 index 0000000000..99ae775438 --- /dev/null +++ b/test/multiverse/suites/sidekiq_ignore_retry_errors_enabled/Envfile @@ -0,0 +1,19 @@ +# This file is distributed under New Relic's license terms. +# See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details. +# frozen_string_literal: true + +SIDEKIQ_VERSIONS = [ + [nil, 3.2], + ['7.3.9', 2.7], + ['6.4.0', 2.5], + ['5.0.3', 2.4, 2.5] +] + +def gem_list(sidekiq_version = nil) + <<-RB + gem 'sidekiq'#{sidekiq_version} + gem 'newrelic_rpm', :require => false, :path => File.expand_path('../../../../') + RB +end + +create_gemfiles(SIDEKIQ_VERSIONS) diff --git a/test/multiverse/suites/sidekiq_ignore_retry_errors_enabled/config/newrelic.yml b/test/multiverse/suites/sidekiq_ignore_retry_errors_enabled/config/newrelic.yml new file mode 100644 index 0000000000..2f1caa7b47 --- /dev/null +++ b/test/multiverse/suites/sidekiq_ignore_retry_errors_enabled/config/newrelic.yml @@ -0,0 +1,21 @@ +--- +development: + error_collector: + enabled: true + apdex_t: 0.5 + monitor_mode: true + license_key: bootstrap_newrelic_admin_license_key_000 + app_name: test + ca_bundle_path: ../../../config/test.cert.crt + app_name: test + host: localhost + api_host: localhost + port: <%= $collector && $collector.port %> + transaction_tracer: + record_sql: obfuscated + enabled: true + stack_trace_threshold: 0.5 + transaction_threshold: 1.0 + capture_params: false + log_file_path: log + sidekiq.ignore_retry_errors: true diff --git a/test/multiverse/suites/sidekiq_ignore_retry_errors_enabled/sidekiq_ignore_retry_errors_enabled_test.rb b/test/multiverse/suites/sidekiq_ignore_retry_errors_enabled/sidekiq_ignore_retry_errors_enabled_test.rb new file mode 100644 index 0000000000..d553d8fe2c --- /dev/null +++ b/test/multiverse/suites/sidekiq_ignore_retry_errors_enabled/sidekiq_ignore_retry_errors_enabled_test.rb @@ -0,0 +1,68 @@ +# This file is distributed under New Relic's license terms. +# See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details. +# frozen_string_literal: true + +require_relative 'sidekiq_test_helpers' + +# On startup, Sidekiq instrumentation registers error and death handlers +# based on the value of the 'sidekiq.ignore_retry_errors'. Because of this, +# we need to have separate enabled/disabled test suites to test both cases. +class SidekiqIgnoreRetryErrorEnabledTest < Minitest::Test + include SidekiqTestHelpers + + def test_error_handlers_not_registered_when_sidekiq_ignore_retry_errors_is_true + # TODO: MAJOR VERSION - remove this when Sidekiq v5 is no longer supported + skip 'Test requires Sidekiq v6+' unless Sidekiq::VERSION.split('.').first.to_i >= 6 + + config = if Sidekiq::VERSION.split('.').first.to_i >= 7 + Sidekiq.default_configuration + else + Sidekiq + end + + error_handlers = if config.respond_to?(:error_handlers) + config.error_handlers + else + config[:error_handlers] || [] + end + + nr_error_handler_found = error_handlers.any? do |handler| + handler.is_a?(Proc) && handler.source_location&.first&.include?('newrelic') + end + + refute nr_error_handler_found, + 'Expected NewRelic error_handler to NOT be registered when sidekiq.ignore_retry_errors is true' + end + + def test_death_handlers_registered_when_sidekiq_ignore_retry_errors_is_true + # TODO: MAJOR VERSION - remove this when Sidekiq v5 is no longer supported + skip 'Test requires Sidekiq v6+' unless Sidekiq::VERSION.split('.').first.to_i >= 6 + + config = if Sidekiq::VERSION.split('.').first.to_i >= 7 + Sidekiq.default_configuration + else + Sidekiq + end + + death_handlers = if config.respond_to?(:death_handlers) + config.death_handlers + else + config[:death_handlers] || [] + end + + nr_death_handler_found = death_handlers.any? do |handler| + handler.is_a?(Proc) && handler.source_location&.first&.include?('newrelic') + end + + assert nr_death_handler_found, + 'Expected NewRelic death_handler to be registered when sidekiq.ignore_retry_errors is true' + end + + def test_basic_job_execution_still_works + segment = run_job + + assert_predicate segment, :finished? + assert_predicate segment, :record_metrics? + assert_kind_of Float, segment.duration + end +end diff --git a/test/multiverse/suites/sidekiq_ignore_retry_errors_enabled/sidekiq_test_helpers.rb b/test/multiverse/suites/sidekiq_ignore_retry_errors_enabled/sidekiq_test_helpers.rb new file mode 100644 index 0000000000..0d05468530 --- /dev/null +++ b/test/multiverse/suites/sidekiq_ignore_retry_errors_enabled/sidekiq_test_helpers.rb @@ -0,0 +1,90 @@ +# This file is distributed under New Relic's license terms. +# See https://github.com/newrelic/newrelic-ruby-agent/blob/main/LICENSE for complete details. +# frozen_string_literal: true + +require 'sidekiq' +require 'sidekiq/cli' +require 'newrelic_rpm' + +class NRDeadEndJob + # TODO: MAJOR VERSION - remove this when Sidekiq v5 is no longer supported + if Sidekiq::VERSION.split('.').first.to_i >= 6 + include Sidekiq::Job + else + include Sidekiq::Worker + end + + sidekiq_options retry: 5 + + COMPLETION_VAR = :@@nr_job_complete + ERROR_MESSAGE = 'kaboom' + + def perform(*args) + raise ERROR_MESSAGE if args.first.is_a?(Hash) && args.first['raise_error'] + ensure + self.class.class_variable_set(COMPLETION_VAR, true) + end +end + +module SidekiqTestHelpers + def run_job(*args) + segments = nil + in_transaction do |txn| + NRDeadEndJob.perform_async(*args) + process_queued_jobs + segments = txn.segments.select { |s| s.name.eql?('Nested/OtherTransaction/SidekiqJob/NRDeadEndJob/perform') } + end + + assert_equal 1, segments.size, "Expected to find a single Sidekiq job segment, found #{segments.size}" + segments.first + end + + def run_job_and_get_attributes(*args) + run_job(*args).attributes.agent_attributes_for(NewRelic::Agent::AttributeFilter::DST_TRANSACTION_TRACER) + end + + def process_queued_jobs + NRDeadEndJob.class_variable_set(NRDeadEndJob::COMPLETION_VAR, false) + config = cli.instance_variable_defined?(:@config) ? cli.instance_variable_get(:@config) : Sidekiq.options + + # TODO: MAJOR VERSION - remove this when Sidekiq v5 is no longer supported + require 'sidekiq/launcher' if Sidekiq::VERSION.split('.').first.to_i < 6 + + launcher = Sidekiq::Launcher.new(config) + launcher.run + Timeout.timeout(5) do + sleep 0.01 until NRDeadEndJob.class_variable_get(NRDeadEndJob::COMPLETION_VAR) + end + + # TODO: MAJOR VERSION - Sidekiq v7 is fine with launcher.stop, but v5 and v6 + # need the Manager#quiet call + if launcher.instance_variable_defined?(:@manager) + launcher.instance_variable_get(:@manager).quiet + else + launcher.stop + end + end + + def cli + @@cli ||= begin + cli = Sidekiq::CLI.instance + cli.parse(['--require', File.absolute_path(__FILE__), '--queue', 'default,1']) + cli.logger.instance_variable_get(:@logdev).instance_variable_set(:@dev, File.new('/dev/null', 'w')) + ensure_sidekiq_config(cli) + cli + end + end + + def ensure_sidekiq_config(cli) + return unless Sidekiq::VERSION.split('.').first.to_i >= 7 + return unless cli.respond_to?(:config) + return unless cli.config.nil? + + require 'sidekiq/config' + cli.instance_variable_set(:@config, ::Sidekiq::Config.new) + end + + def flatten(object) + NewRelic::Agent::AttributeProcessing.flatten_and_coerce(object, 'job.sidekiq.args') + end +end