# frozen_string_literal: true require 'test_helper' class StatsDInstrumentationTest < Minitest::Test module ActiveMerchant class Base extend StatsD::Instrument def ssl_post(arg) if arg 'OK' else raise 'Not OK' end end def post_with_block(&block) block.call if block_given? end end class Gateway < Base def purchase(arg) ssl_post(arg) true rescue false end def self.sync true end end class UniqueGateway < Base def ssl_post(arg) { success: arg } end def purchase(arg) ssl_post(arg) end end end class GatewaySubClass < ActiveMerchant::Gateway def metric_name 'subgateway' end end class InstrumentedClass extend StatsD::Instrument def public_and_instrumented end statsd_count :public_and_instrumented, 'InstrumentedClass.public_and_instrumented' protected def protected_and_instrumented end statsd_count :protected_and_instrumented, 'InstrumentedClass.protected_and_instrumented' private def private_and_instrumented end statsd_count :private_and_instrumented, 'InstrumentedClass.private_and_instrumented' end include StatsD::Instrument::Assertions def test_statsd_count_if ActiveMerchant::Gateway.statsd_count_if :ssl_post, 'ActiveMerchant.Gateway.if' assert_statsd_increment('ActiveMerchant.Gateway.if') do ActiveMerchant::Gateway.new.purchase(true) ActiveMerchant::Gateway.new.purchase(false) end ensure ActiveMerchant::Gateway.statsd_remove_count_if :ssl_post, 'ActiveMerchant.Gateway.if' end def test_statsd_count_if_with_method_receiving_block ActiveMerchant::Base.statsd_count_if :post_with_block, 'ActiveMerchant.Base.post_with_block' do |result| result == 'true' end assert_statsd_increment('ActiveMerchant.Base.post_with_block') do assert_equal 'true', ActiveMerchant::Base.new.post_with_block { 'true' } assert_equal 'false', ActiveMerchant::Base.new.post_with_block { 'false' } end ensure ActiveMerchant::Base.statsd_remove_count_if :post_with_block, 'ActiveMerchant.Base.post_with_block' end def test_statsd_count_if_with_block ActiveMerchant::UniqueGateway.statsd_count_if :ssl_post, 'ActiveMerchant.Gateway.block' do |result| result[:success] end assert_statsd_increment('ActiveMerchant.Gateway.block', times: 1) do ActiveMerchant::UniqueGateway.new.purchase(true) ActiveMerchant::UniqueGateway.new.purchase(false) end ensure ActiveMerchant::UniqueGateway.statsd_remove_count_if :ssl_post, 'ActiveMerchant.Gateway.block' end def test_statsd_count_success ActiveMerchant::Gateway.statsd_count_success :ssl_post, 'ActiveMerchant.Gateway', sample_rate: 0.5 assert_statsd_increment('ActiveMerchant.Gateway.success', sample_rate: 0.5, times: 1) do ActiveMerchant::Gateway.new.purchase(true) ActiveMerchant::Gateway.new.purchase(false) end assert_statsd_increment('ActiveMerchant.Gateway.failure', sample_rate: 0.5, times: 1) do ActiveMerchant::Gateway.new.purchase(false) ActiveMerchant::Gateway.new.purchase(true) end ensure ActiveMerchant::Gateway.statsd_remove_count_success :ssl_post, 'ActiveMerchant.Gateway' end def test_statsd_count_success_with_method_receiving_block ActiveMerchant::Base.statsd_count_success :post_with_block, 'ActiveMerchant.Base.post_with_block' do |result| result == 'successful' end assert_statsd_increment('ActiveMerchant.Base.post_with_block.success', times: 1) do assert_equal 'successful', ActiveMerchant::Base.new.post_with_block { 'successful' } assert_equal 'not so successful', ActiveMerchant::Base.new.post_with_block { 'not so successful' } end assert_statsd_increment('ActiveMerchant.Base.post_with_block.failure', times: 1) do assert_equal 'successful', ActiveMerchant::Base.new.post_with_block { 'successful' } assert_equal 'not so successful', ActiveMerchant::Base.new.post_with_block { 'not so successful' } end ensure ActiveMerchant::Base.statsd_remove_count_success :post_with_block, 'ActiveMerchant.Base.post_with_block' end def test_statsd_count_success_with_block ActiveMerchant::UniqueGateway.statsd_count_success :ssl_post, 'ActiveMerchant.Gateway' do |result| result[:success] end assert_statsd_increment('ActiveMerchant.Gateway.success') do ActiveMerchant::UniqueGateway.new.purchase(true) end assert_statsd_increment('ActiveMerchant.Gateway.failure') do ActiveMerchant::UniqueGateway.new.purchase(false) end ensure ActiveMerchant::UniqueGateway.statsd_remove_count_success :ssl_post, 'ActiveMerchant.Gateway' end def test_statsd_count ActiveMerchant::Gateway.statsd_count :ssl_post, 'ActiveMerchant.Gateway.ssl_post' assert_statsd_increment('ActiveMerchant.Gateway.ssl_post') do ActiveMerchant::Gateway.new.purchase(true) end ensure ActiveMerchant::Gateway.statsd_remove_count :ssl_post, 'ActiveMerchant.Gateway.ssl_post' end def test_statsd_count_with_name_as_lambda metric_namer = lambda { |object, args| "#{object.metric_name}.#{args.first}" } ActiveMerchant::Gateway.statsd_count(:ssl_post, metric_namer) assert_statsd_increment('subgateway.foo') do GatewaySubClass.new.purchase('foo') end ensure ActiveMerchant::Gateway.statsd_remove_count(:ssl_post, metric_namer) end def test_statsd_count_with_name_as_proc metric_namer = proc { |object, args| "#{object.metric_name}.#{args.first}" } ActiveMerchant::Gateway.statsd_count(:ssl_post, metric_namer) assert_statsd_increment('subgateway.foo') do GatewaySubClass.new.purchase('foo') end ensure ActiveMerchant::Gateway.statsd_remove_count(:ssl_post, metric_namer) end def test_statsd_count_with_method_receiving_block ActiveMerchant::Base.statsd_count :post_with_block, 'ActiveMerchant.Base.post_with_block' assert_statsd_increment('ActiveMerchant.Base.post_with_block') do assert_equal 'block called', ActiveMerchant::Base.new.post_with_block { 'block called' } end ensure ActiveMerchant::Base.statsd_remove_count :post_with_block, 'ActiveMerchant.Base.post_with_block' end def test_statsd_measure ActiveMerchant::UniqueGateway.statsd_measure :ssl_post, 'ActiveMerchant.Gateway.ssl_post', sample_rate: 0.3 assert_statsd_measure('ActiveMerchant.Gateway.ssl_post', sample_rate: 0.3) do ActiveMerchant::UniqueGateway.new.purchase(true) end ensure ActiveMerchant::UniqueGateway.statsd_remove_measure :ssl_post, 'ActiveMerchant.Gateway.ssl_post' end def test_statsd_measure_uses_normalized_metric_name ActiveMerchant::UniqueGateway.statsd_measure :ssl_post, 'ActiveMerchant::Gateway.ssl_post' assert_statsd_measure('ActiveMerchant.Gateway.ssl_post') do ActiveMerchant::UniqueGateway.new.purchase(true) end ensure ActiveMerchant::UniqueGateway.statsd_remove_measure :ssl_post, 'ActiveMerchant::Gateway.ssl_post' end def test_statsd_measure_raises_without_a_provided_block assert_raises(LocalJumpError) do assert_statsd_measure('ActiveMerchant.Gateway.ssl_post') end end def test_statsd_measure_with_method_receiving_block ActiveMerchant::Base.statsd_measure :post_with_block, 'ActiveMerchant.Base.post_with_block' assert_statsd_measure('ActiveMerchant.Base.post_with_block') do assert_equal 'block called', ActiveMerchant::Base.new.post_with_block { 'block called' } end ensure ActiveMerchant::Base.statsd_remove_measure :post_with_block, 'ActiveMerchant.Base.post_with_block' end def test_statsd_measure_with_sample_rate ActiveMerchant::UniqueGateway.statsd_measure :ssl_post, 'ActiveMerchant.Gateway.ssl_post', sample_rate: 0.1 assert_statsd_measure('ActiveMerchant.Gateway.ssl_post', sample_rate: 0.1) do ActiveMerchant::UniqueGateway.new.purchase(true) end ensure ActiveMerchant::UniqueGateway.statsd_remove_measure :ssl_post, 'ActiveMerchant.Gateway.ssl_post' end def test_statsd_distribution ActiveMerchant::UniqueGateway.statsd_distribution :ssl_post, 'ActiveMerchant.Gateway.ssl_post', sample_rate: 0.3 assert_statsd_distribution('ActiveMerchant.Gateway.ssl_post', sample_rate: 0.3) do ActiveMerchant::UniqueGateway.new.purchase(true) end ensure ActiveMerchant::UniqueGateway.statsd_remove_distribution :ssl_post, 'ActiveMerchant.Gateway.ssl_post' end def test_statsd_distribution_uses_normalized_metric_name ActiveMerchant::UniqueGateway.statsd_distribution :ssl_post, 'ActiveMerchant::Gateway.ssl_post' assert_statsd_distribution('ActiveMerchant.Gateway.ssl_post') do ActiveMerchant::UniqueGateway.new.purchase(true) end ensure ActiveMerchant::UniqueGateway.statsd_remove_distribution :ssl_post, 'ActiveMerchant::Gateway.ssl_post' end def test_statsd_distribution_raises_without_a_provided_block assert_raises(LocalJumpError) do assert_statsd_distribution('ActiveMerchant.Gateway.ssl_post') end end def test_statsd_distribution_with_method_receiving_block ActiveMerchant::Base.statsd_distribution :post_with_block, 'ActiveMerchant.Base.post_with_block' assert_statsd_distribution('ActiveMerchant.Base.post_with_block') do assert_equal 'block called', ActiveMerchant::Base.new.post_with_block { 'block called' } end ensure ActiveMerchant::Base.statsd_remove_distribution :post_with_block, 'ActiveMerchant.Base.post_with_block' end def test_statsd_distribution_with_tags ActiveMerchant::UniqueGateway.statsd_distribution :ssl_post, 'ActiveMerchant.Gateway.ssl_post', tags: ['foo'] assert_statsd_distribution('ActiveMerchant.Gateway.ssl_post', tags: ['foo']) do ActiveMerchant::UniqueGateway.new.purchase(true) end ensure ActiveMerchant::UniqueGateway.statsd_remove_distribution :ssl_post, 'ActiveMerchant.Gateway.ssl_post' end def test_statsd_distribution_with_sample_rate ActiveMerchant::UniqueGateway.statsd_distribution :ssl_post, 'ActiveMerchant.Gateway.ssl_post', sample_rate: 0.1 assert_statsd_distribution('ActiveMerchant.Gateway.ssl_post', sample_rate: 0.1) do ActiveMerchant::UniqueGateway.new.purchase(true) end ensure ActiveMerchant::UniqueGateway.statsd_remove_distribution :ssl_post, 'ActiveMerchant.Gateway.ssl_post' end def test_instrumenting_class_method ActiveMerchant::Gateway.singleton_class.extend StatsD::Instrument ActiveMerchant::Gateway.singleton_class.statsd_count :sync, 'ActiveMerchant.Gateway.sync' assert_statsd_increment('ActiveMerchant.Gateway.sync') do ActiveMerchant::Gateway.sync end ensure ActiveMerchant::Gateway.singleton_class.statsd_remove_count :sync, 'ActiveMerchant.Gateway.sync' end def test_statsd_count_with_tags ActiveMerchant::Gateway.singleton_class.extend StatsD::Instrument ActiveMerchant::Gateway.singleton_class.statsd_count :sync, 'ActiveMerchant.Gateway.sync', tags: { key: 'value' } assert_statsd_increment('ActiveMerchant.Gateway.sync', tags: ['key:value']) do ActiveMerchant::Gateway.sync end ensure ActiveMerchant::Gateway.singleton_class.statsd_remove_count :sync, 'ActiveMerchant.Gateway.sync' end def test_statsd_respects_global_prefix_changes StatsD.prefix = 'Foo' ActiveMerchant::Gateway.singleton_class.extend StatsD::Instrument ActiveMerchant::Gateway.singleton_class.statsd_count :sync, 'ActiveMerchant.Gateway.sync' StatsD.prefix = 'Quc' statsd_calls = capture_statsd_calls { ActiveMerchant::Gateway.sync } assert_equal 1, statsd_calls.length assert_equal "Quc.ActiveMerchant.Gateway.sync", statsd_calls.first.name ensure StatsD.prefix = nil ActiveMerchant::Gateway.singleton_class.statsd_remove_count :sync, 'ActiveMerchant.Gateway.sync' end def test_statsd_macro_can_disable_prefix StatsD.prefix = 'Foo' ActiveMerchant::Gateway.singleton_class.extend StatsD::Instrument ActiveMerchant::Gateway.singleton_class.statsd_count_success :sync, 'ActiveMerchant.Gateway.sync', no_prefix: true StatsD.prefix = 'Quc' statsd_calls = capture_statsd_calls { ActiveMerchant::Gateway.sync } assert_equal 1, statsd_calls.length assert_equal "ActiveMerchant.Gateway.sync.success", statsd_calls.first.name ensure StatsD.prefix = nil ActiveMerchant::Gateway.singleton_class.statsd_remove_count_success :sync, 'ActiveMerchant.Gateway.sync' end def test_statsd_doesnt_change_method_scope_of_public_method assert_scope InstrumentedClass, :public_and_instrumented, :public assert_statsd_increment('InstrumentedClass.public_and_instrumented') do InstrumentedClass.new.send(:public_and_instrumented) end end def test_statsd_doesnt_change_method_scope_of_protected_method assert_scope InstrumentedClass, :protected_and_instrumented, :protected assert_statsd_increment('InstrumentedClass.protected_and_instrumented') do InstrumentedClass.new.send(:protected_and_instrumented) end end def test_statsd_doesnt_change_method_scope_of_private_method assert_scope InstrumentedClass, :private_and_instrumented, :private assert_statsd_increment('InstrumentedClass.private_and_instrumented') do InstrumentedClass.new.send(:private_and_instrumented) end end def test_statsd_doesnt_change_method_scope_on_removal_of_public_method assert_scope InstrumentedClass, :public_and_instrumented, :public InstrumentedClass.statsd_remove_count :public_and_instrumented, 'InstrumentedClass.public_and_instrumented' assert_scope InstrumentedClass, :public_and_instrumented, :public InstrumentedClass.statsd_count :public_and_instrumented, 'InstrumentedClass.public_and_instrumented' end def test_statsd_doesnt_change_method_scope_on_removal_of_protected_method assert_scope InstrumentedClass, :protected_and_instrumented, :protected InstrumentedClass.statsd_remove_count :protected_and_instrumented, 'InstrumentedClass.protected_and_instrumented' assert_scope InstrumentedClass, :protected_and_instrumented, :protected InstrumentedClass.statsd_count :protected_and_instrumented, 'InstrumentedClass.protected_and_instrumented' end def test_statsd_doesnt_change_method_scope_on_removal_of_private_method assert_scope InstrumentedClass, :private_and_instrumented, :private InstrumentedClass.statsd_remove_count :private_and_instrumented, 'InstrumentedClass.private_and_instrumented' assert_scope InstrumentedClass, :private_and_instrumented, :private InstrumentedClass.statsd_count :private_and_instrumented, 'InstrumentedClass.private_and_instrumented' end def test_statsd_works_with_prepended_modules mod = Module.new do define_method(:foo) { super() } end klass = Class.new do prepend mod extend StatsD::Instrument define_method(:foo) {} statsd_count :foo, "foo" end assert_statsd_increment("foo") do klass.new.foo end end def test_statsd_measure_with_new_client old_client = StatsD.singleton_client StatsD.singleton_client = StatsD::Instrument::Client.new ActiveMerchant::UniqueGateway.statsd_measure :ssl_post, 'ActiveMerchant.Gateway.ssl_post', sample_rate: 0.3 assert_statsd_measure('ActiveMerchant.Gateway.ssl_post', sample_rate: 0.3) do ActiveMerchant::UniqueGateway.new.purchase(true) end ensure ActiveMerchant::UniqueGateway.statsd_remove_measure :ssl_post, 'ActiveMerchant.Gateway.ssl_post' StatsD.singleton_client = old_client end def test_statsd_count_with_new_client old_client = StatsD.singleton_client StatsD.singleton_client = StatsD::Instrument::Client.new ActiveMerchant::Gateway.statsd_count :ssl_post, 'ActiveMerchant.Gateway.ssl_post' assert_statsd_increment('ActiveMerchant.Gateway.ssl_post') do ActiveMerchant::Gateway.new.purchase(true) end ensure ActiveMerchant::Gateway.statsd_remove_count :ssl_post, 'ActiveMerchant.Gateway.ssl_post' StatsD.singleton_client = old_client end private def assert_scope(klass, method, expected_scope) method_scope = if klass.private_method_defined?(method) :private elsif klass.protected_method_defined?(method) :protected else :public end assert_equal method_scope, expected_scope, "Expected method to be #{expected_scope}" end end