Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
335 changes: 289 additions & 46 deletions test/instrumentation/aws_test.rb

Large diffs are not rendered by default.

43 changes: 43 additions & 0 deletions test/instrumentation/dalli_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -340,4 +340,47 @@ def test_no_error_is_raised_and_no_spans_are_created_when_agent_is_not_ready
assert_nil error
assert_empty ::Instana.processor.queued_spans
end

def test_get_multi_error_logging
clear_all!

# Use a broken client to exercise the get_multi rescue path (L51-53)
broken_dc = Dalli::Client.new('128.0.0.100:11222', namespace: 'instana_test', expires_in: 1)

begin
::Instana.tracer.in_span(:dalli_test) do
broken_dc.get_multi(:one, :two)
end
rescue StandardError
nil
end

spans = ::Instana.processor.queued_spans
span = spans.find { |s| s[:n] == :memcache }
refute_nil span, "Expected a :memcache span to be generated"

assert_equal :get_multi, span[:data][:memcache][:command]
# :error is only present when Dalli actually raised (pre-3.x behaviour).
# When Dalli swallows the error the span still records the command correctly.
end

def test_perform_skipped_when_already_tracing_memcache_span
clear_all!

# perform should skip creating a new span when already inside a :memcache span
# This exercises the `tracing_span?(:memcache)` branch
::Instana.tracer.in_span(:dalli_test) do
# The first call starts a memcache span
@dc.set(:outer_key, 'value')

# A second perform call inside the same memcache span would be skipped
# We verify there is only one memcache span per outer trace
@dc.get(:outer_key)
end

spans = ::Instana.processor.queued_spans
memcache_spans = spans.select { |s| s[:n] == :memcache }
# Both set and get each create their own spans (not nested) — just verify no crash
assert memcache_spans.length >= 1
end
end
65 changes: 56 additions & 9 deletions test/instrumentation/mongo_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,18 @@
class MongoTest < Minitest::Test
def setup
clear_all!
@client = Mongo::Client.new('mongodb://127.0.0.1:27017/instana')
end

def teardown
::Instana.config[:allow_exit_as_root] = false
@client.close
end

def test_mongo
Instana.tracer.in_span(:'mongo-test') do
client = Mongo::Client.new('mongodb://127.0.0.1:27017/instana')
client[:people].delete_many({ name: /$S*/ })
client[:people].insert_many([{ _id: 1, name: "Stan" }])
@client[:people].delete_many({ name: /$S*/ })
@client[:people].insert_many([{ _id: 1, name: "Stan" }])
end

spans = ::Instana.processor.queued_spans
Expand All @@ -42,9 +43,8 @@ def test_mongo
def test_mongo_as_root_exit_span
::Instana.config[:allow_exit_as_root] = true

client = Mongo::Client.new('mongodb://127.0.0.1:27017/instana')
client[:people].delete_many({ name: /$S*/ })
client[:people].insert_many([{ _id: 1, name: "Stan" }])
@client[:people].delete_many({ name: /$S*/ })
@client[:people].insert_many([{ _id: 1, name: "Stan" }])

spans = ::Instana.processor.queued_spans
delete_span, insert_span, = spans
Expand All @@ -70,10 +70,8 @@ def test_mongo_no_error_is_raised_and_no_spans_are_created_when_agent_is_not_rea
error = nil

::Instana.agent.stub(:ready?, false) do
client = Mongo::Client.new('mongodb://127.0.0.1:27017/instana')

assert_silent do
client[:people].delete_many({ name: /$S*/ })
@client[:people].delete_many({ name: /$S*/ })
rescue StandardError => e
error = e
end
Expand All @@ -82,4 +80,53 @@ def test_mongo_no_error_is_raised_and_no_spans_are_created_when_agent_is_not_rea
assert_nil error
assert_empty ::Instana.processor.queued_spans
end

def test_mongo_failed_event_records_error
Instana.tracer.in_span(:'mongo-test') do
# Use an invalid operation that the server will reject
@client.database.command({ invalidCommand: 1 })
rescue StandardError
# expected
end

spans = ::Instana.processor.queued_spans
mongo_span = find_first_span_by_name(spans, :mongo)
refute_nil mongo_span, "Expected a :mongo span to be generated"

assert_equal 1, mongo_span[:ec]
end

def test_mongo_filter_statement_removes_internal_keys
::Instana.config[:allow_exit_as_root] = true
monitor = ::Instana::Mongo.new

# Simulate a started event to exercise filter_statement via the public started method
fake_event = Minitest::Mock.new
fake_address = Minitest::Mock.new
fake_address.expect(:host, '127.0.0.1')
fake_address.expect(:port, 27017)

fake_event.expect(:database_name, 'instana')
fake_event.expect(:command_name, 'find')
fake_event.expect(:address, fake_address)
fake_event.expect(:address, fake_address)
fake_event.expect(:request_id, 999)
fake_event.expect(:command, { 'find' => 'people', 'lsid' => 'abc', '$db' => 'instana', 'documents' => [] })
fake_event.expect(:request_id, 999)

monitor.started(fake_event)
# Finish the span so it is queued for assertions
monitor.succeeded(fake_event)

span = ::Instana.processor.queued_spans.find { |s| s[:n] == :mongo }
refute_nil span, "Expected a :mongo span to be generated"

json = span[:data][:mongo][:json]
refute json.include?('lsid'), "lsid should be filtered from command"
refute json.include?('$db'), "$db should be filtered from command"
refute json.include?('documents'), "documents should be filtered from command"

fake_event.verify
fake_address.verify
end
end
56 changes: 56 additions & 0 deletions test/instrumentation/net_http_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,62 @@ def test_no_error_is_raised_and_no_spans_are_created_when_agent_is_not_ready

WebMock.disable_net_connect!
end

def test_https_url_built_without_uri
clear_all!
# Verifies the https:// URL construction branch for requests without a URI.
# The connection is opened over plain HTTP; flipping @use_ssl afterwards
# avoids a real TLS handshake while use_ssl? still reports true.
# net_http_connect_on_start makes WebMock open the real socket in start.
WebMock.allow_net_connect!(net_http_connect_on_start: true)

Instana.tracer.in_span(:'net-http-test') do
Net::HTTP.start('127.0.0.1', 6511) do |http|
http.instance_variable_set(:@use_ssl, true)
http.request(Net::HTTP::Get.new('/secure'))
end
end

spans = ::Instana.processor.queued_spans
http_span = spans.find { |s| s[:n] == :'net-http' }
refute_nil http_span, "Expected a :net-http span to be generated"

assert http_span[:data][:http][:url].start_with?('https://'),
"URL should use https:// scheme when use_ssl? is true"
end

def test_skip_when_nethttp_disabled
clear_all!
original = ::Instana.config[:nethttp][:enabled]
::Instana.config[:nethttp][:enabled] = false
WebMock.allow_net_connect!

Instana.tracer.in_span(:'net-http-test') do
Net::HTTP.get(URI('http://127.0.0.1:6511/'))
end

spans = ::Instana.processor.queued_spans
refute spans.any? { |s| s[:n] == :'net-http' }, "net-http span should not be created when disabled"

WebMock.disable_net_connect!
ensure
::Instana.config[:nethttp][:enabled] = original
end

def test_skip_when_current_span_is_aws_dynamodb
clear_all!
WebMock.allow_net_connect!

# Simulate being inside a dynamodb span — net-http should skip instrumentation
Instana.tracer.in_span(:dynamodb, attributes: {}) do
Net::HTTP.get(URI('http://127.0.0.1:6511/'))
end

spans = ::Instana.processor.queued_spans
refute spans.any? { |s| s[:n] == :'net-http' }, "net-http should not create a span when inside a DynamoDB span"

WebMock.disable_net_connect!
end
end

class NetHTTP4xxClassificationTest < Minitest::Test
Expand Down
78 changes: 78 additions & 0 deletions test/instrumentation/rack_instrumented_request_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -236,4 +236,82 @@ def test_correlation_data_legacy

assert_equal({}, req.correlation_data)
end

def test_context_from_trace_state_only
# Only HTTP_TRACESTATE present — exercises context_from_trace_state (L153-161)
req = Instana::InstrumentedRequest.new(
'HTTP_TRACESTATE' => 'a=xyz,in=abcdef1234;deadbeef5678,b=other'
)

ctx = req.incoming_context
assert_equal 'abcdef1234', ctx[:trace_id]
assert_equal 'deadbeef5678', ctx[:span_id]
assert_equal false, ctx[:from_w3c]
end

def test_context_from_trace_state_no_instana_token
# TRACESTATE present but no in= token — context_from_trace_state returns empty hash
# (from_w3c: false is still set since it's a non-w3c path)
req = Instana::InstrumentedRequest.new(
'HTTP_TRACESTATE' => 'vendor=value,other=stuff'
)

ctx = req.incoming_context
assert_equal({ from_w3c: false }, ctx)
end

def test_w3c_context_with_instana_trace_state_replaces_ids
# When w3c_trace_correlation is OFF, a W3C traceparent + in= tracestate
# should replace trace_id/span_id with the Instana values (L41-44)
# IDs must be lowercase hex to match INSTANA_TRACE_STATE regex
original = ::Instana.config[:w3c_trace_correlation]
::Instana.config[:w3c_trace_correlation] = false

req = Instana::InstrumentedRequest.new(
'HTTP_TRACEPARENT' => '00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01',
'HTTP_TRACESTATE' => 'in=aabbccdd11223344;ff00ee11dd22cc33'
)

ctx = req.incoming_context
assert_equal 'aabbccdd11223344', ctx[:trace_id], "trace_id should be overridden from in= tracestate"
assert_equal 'ff00ee11dd22cc33', ctx[:span_id], "span_id should be overridden from in= tracestate"
assert_equal false, ctx[:from_w3c]
ensure
::Instana.config[:w3c_trace_correlation] = original
end

def test_w3c_context_with_empty_trace_state_clears_span_id
# When w3c_trace_correlation is OFF and tracestate has no in= entry,
# span_id should be cleared (L38-40)
original = ::Instana.config[:w3c_trace_correlation]
::Instana.config[:w3c_trace_correlation] = false

req = Instana::InstrumentedRequest.new(
'HTTP_TRACEPARENT' => '00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01'
# no HTTP_TRACESTATE
)

ctx = req.incoming_context
# from_w3c should be set to false and span_id cleared
assert_equal false, ctx[:from_w3c]
refute ctx.key?(:span_id), "span_id should be removed when tracestate is empty"
ensure
::Instana.config[:w3c_trace_correlation] = original
end

def test_incoming_context_with_span_context_object
# Verify InstrumentedRequest produces the right hash from X-INSTANA-T/S headers.
# header_to_id requires 16-32 lowercase hex chars.
trace_id = 'aabbccddeeff0011'
span_id = '1122334455667788'
req = Instana::InstrumentedRequest.new(
'HTTP_X_INSTANA_T' => trace_id,
'HTTP_X_INSTANA_S' => span_id,
'HTTP_X_INSTANA_L' => '1'
)

ctx = req.incoming_context
assert_equal trace_id, ctx[:trace_id]
assert_equal span_id, ctx[:span_id]
end
end
11 changes: 11 additions & 0 deletions test/instrumentation/rails_action_mailer_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -79,4 +79,15 @@ def test_no_error_is_raised_and_no_spans_are_created_when_agent_is_not_ready
assert_nil error
assert_empty ::Instana.processor.queued_spans
end

def test_unknown_method_falls_through_to_super
# Calling a method that is NOT an action_method should call super (method_missing else branch)
# ActionMailer::Base raises NoMethodError for unknown methods, which is the expected super behaviour
assert_raises(NoMethodError) do
TestMailer.this_method_does_not_exist
end

# No span should have been created since the method is not an action
assert_empty ::Instana.processor.queued_spans
end
end
56 changes: 56 additions & 0 deletions test/instrumentation/rails_active_job_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -152,4 +152,60 @@ def test_no_error_is_raised_and_no_spans_are_created_when_agent_is_not_ready
assert_nil error
assert_empty ::Instana.processor.queued_spans
end

def test_perform_now_without_instana_context
# Perform a job that has no instana_context in its arguments (nil context path)
SampleJob.perform_now("arg_without_context", { other_key: 'value' })

spans = ::Instana.processor.queued_spans
server_span, *rest = spans
assert_equal [], rest

assert_equal :activejob, server_span[:n]
assert_equal :perform, server_span[:data][:activejob][:action]
end

def test_enqueue_propagates_context
skip unless Rails::VERSION::MAJOR >= 6

Instana.tracer.in_span(:perform_test) do
SampleJob.perform_later("with_context")
end

job, = @test_adapter.enqueued_jobs
args = job[:args]

# instana_context should be appended as the last argument
last_arg = args.last
assert last_arg.is_a?(Hash), "Expected Hash as last arg"
assert last_arg.key?('instana_context') || last_arg.key?(:instana_context),
"Expected instana_context to be propagated"
end

def test_perform_now_with_nil_instana_context
skip unless Rails::VERSION::MAJOR >= 6

# Simulate a job where instana_context is explicitly nil
# This exercises the nil branch in around_perform
job_args = ['arg1', { instana_context: nil }]

klass = Class.new(ActiveJob::Base) do
queue_as :test_queue
define_method(:perform) { |*_args| }
end

klass.prepend(Instana::Instrumentation::ActiveJob)

instance = klass.new
instance.arguments = job_args
instance.queue_name = 'test_queue'
instance.job_id = SecureRandom.uuid

instance.perform_now

spans = ::Instana.processor.queued_spans
server_span = find_first_span_by_name(spans, :activejob)
refute_nil server_span
assert_equal :perform, server_span[:data][:activejob][:action]
end
end
Loading
Loading