@@ -234,6 +234,72 @@ defmodule Sentry.Opentelemetry.SpanProcessorTest do
234234 assert length ( transaction . spans ) == 0
235235 assert transaction . transaction == "child_instrumented_function_standalone"
236236 end
237+
238+ @ tag span_storage: true
239+ test "treats HTTP request spans as transaction roots even with external parents" do
240+ put_test_config ( environment_name: "test" , traces_sample_rate: 1.0 )
241+
242+ Sentry.Test . start_collecting_sentry_reports ( )
243+
244+ # Simulate an HTTP request span with external parent (like from browser tracing)
245+ require OpenTelemetry.Tracer , as: Tracer
246+ require OpenTelemetry.SemConv.Incubating.HTTPAttributes , as: HTTPAttributes
247+ require OpenTelemetry.SemConv.Incubating.URLAttributes , as: URLAttributes
248+
249+ # Create a span with HTTP attributes and an external parent span ID
250+ external_parent_span_id = "b943d7459127970c"
251+
252+ # Start a span that simulates an HTTP request from an external trace
253+ Tracer . with_span "POST /api/v1alpha" , % {
254+ attributes: % {
255+ HTTPAttributes . http_request_method ( ) => :POST ,
256+ URLAttributes . url_path ( ) => "/api/v1alpha" ,
257+ "http.route" => "/api/v1alpha" ,
258+ "server.address" => "localhost" ,
259+ "server.port" => 4000
260+ } ,
261+ parent: { :span_context , :undefined , external_parent_span_id , :undefined , :undefined , :undefined , :undefined , :undefined , :undefined , :undefined }
262+ } do
263+ # Simulate child spans (database queries, etc.) with proper DB attributes
264+ Tracer . with_span "matrix_data.repo.query" , % {
265+ attributes: % {
266+ "db.system" => :postgresql ,
267+ "db.statement" => "SELECT * FROM users"
268+ }
269+ } do
270+ Process . sleep ( 10 )
271+ end
272+
273+ Tracer . with_span "matrix_data.repo.query:agents" , % {
274+ attributes: % {
275+ "db.system" => :postgresql ,
276+ "db.statement" => "INSERT INTO agents (...) VALUES (...)"
277+ }
278+ } do
279+ Process . sleep ( 10 )
280+ end
281+ end
282+
283+ # Should capture the HTTP request span as a transaction root despite having an external parent
284+ assert [ % Sentry.Transaction { } = transaction ] = Sentry.Test . pop_sentry_transactions ( )
285+
286+ # Verify transaction properties
287+ assert transaction . transaction == "POST /api/v1alpha"
288+ assert transaction . transaction_info == % { source: :custom }
289+ assert length ( transaction . spans ) == 2
290+
291+ # Verify child spans are properly included - they should have "db" operation
292+ span_names = Enum . map ( transaction . spans , & & 1 . op ) |> Enum . sort ( )
293+ expected_names = [ "db" , "db" ]
294+ assert span_names == expected_names
295+
296+ # Verify all spans share the same trace ID
297+ trace_id = transaction . contexts . trace . trace_id
298+ Enum . each ( transaction . spans , fn span ->
299+ assert span . trace_id == trace_id
300+ end )
301+ end
302+
237303
238304 @ tag span_storage: true
239305 test "concurrent traces maintain independent sampling decisions" do
0 commit comments