@@ -11,6 +11,8 @@ defmodule Commanded.OpenTelemetry.AggregatePopulateTest do
1111 alias Commanded.TestSupport.Factory
1212 alias Commanded.UUID
1313
14+ require OpenTelemetry.Tracer , as: Tracer
15+
1416 setup do
1517 detach_populate_handlers ( )
1618 AggregatePopulate . setup ( )
@@ -259,6 +261,133 @@ defmodule Commanded.OpenTelemetry.AggregatePopulateTest do
259261 end
260262 end
261263
264+ describe "trace context propagation" do
265+ setup do
266+ detach_populate_handlers ( )
267+ AggregatePopulate . setup ( )
268+ :ok
269+ end
270+
271+ test "load span becomes child of caller when traceparent is in metadata" do
272+ { parent_trace_id , parent_span_id , traceparent } =
273+ Tracer . with_span "parent.command.dispatch" do
274+ ctx = Tracer . current_span_ctx ( )
275+
276+ {
277+ :otel_span . trace_id ( ctx ) ,
278+ :otel_span . span_id ( ctx ) ,
279+ encode_traceparent ( ctx )
280+ }
281+ end
282+
283+ meta =
284+ Factory . build_aggregate_populate_metadata (
285+ metadata: % { "traceparent" => traceparent }
286+ )
287+
288+ :telemetry . execute ( [ :commanded , :aggregate , :load , :start ] , % { } , meta )
289+
290+ stop_meta =
291+ Factory . build_aggregate_load_stop_metadata ( meta ,
292+ snapshot_used: false ,
293+ aggregate_version: 0
294+ )
295+
296+ :telemetry . execute ( [ :commanded , :aggregate , :load , :stop ] , % { count: 0 } , stop_meta )
297+
298+ assert_receive { :span ,
299+ span (
300+ name: "load MockAggregate" ,
301+ trace_id: child_trace_id ,
302+ parent_span_id: received_parent_span_id
303+ ) } ,
304+ 1000
305+
306+ assert child_trace_id == parent_trace_id
307+ assert received_parent_span_id == parent_span_id
308+ end
309+
310+ test "load span is independent when no traceparent in metadata" do
311+ meta = Factory . build_aggregate_populate_metadata ( metadata: % { } )
312+
313+ :telemetry . execute ( [ :commanded , :aggregate , :load , :start ] , % { } , meta )
314+
315+ stop_meta =
316+ Factory . build_aggregate_load_stop_metadata ( meta ,
317+ snapshot_used: false ,
318+ aggregate_version: 0
319+ )
320+
321+ :telemetry . execute ( [ :commanded , :aggregate , :load , :stop ] , % { count: 0 } , stop_meta )
322+
323+ assert_receive { :span ,
324+ span (
325+ name: "load MockAggregate" ,
326+ parent_span_id: :undefined
327+ ) } ,
328+ 1000
329+ end
330+
331+ test "load span is independent when traceparent is invalid" do
332+ meta =
333+ Factory . build_aggregate_populate_metadata (
334+ metadata: % { "traceparent" => "invalid-format" }
335+ )
336+
337+ :telemetry . execute ( [ :commanded , :aggregate , :load , :start ] , % { } , meta )
338+
339+ stop_meta =
340+ Factory . build_aggregate_load_stop_metadata ( meta ,
341+ snapshot_used: false ,
342+ aggregate_version: 0
343+ )
344+
345+ :telemetry . execute ( [ :commanded , :aggregate , :load , :stop ] , % { count: 0 } , stop_meta )
346+
347+ assert_receive { :span ,
348+ span (
349+ name: "load MockAggregate" ,
350+ parent_span_id: :undefined
351+ ) } ,
352+ 1000
353+ end
354+
355+ test "load span is independent when metadata key is nil" do
356+ meta =
357+ Factory . build_aggregate_populate_metadata ( )
358+ |> Map . put ( :metadata , nil )
359+
360+ :telemetry . execute ( [ :commanded , :aggregate , :load , :start ] , % { } , meta )
361+
362+ stop_meta =
363+ Factory . build_aggregate_load_stop_metadata ( meta ,
364+ snapshot_used: false ,
365+ aggregate_version: 0
366+ )
367+
368+ :telemetry . execute ( [ :commanded , :aggregate , :load , :stop ] , % { count: 0 } , stop_meta )
369+
370+ assert_receive { :span ,
371+ span (
372+ name: "load MockAggregate" ,
373+ parent_span_id: :undefined
374+ ) } ,
375+ 1000
376+ end
377+ end
378+
379+ defp encode_traceparent ( span_ctx ) do
380+ trace_id = :otel_span . trace_id ( span_ctx )
381+ span_id = :otel_span . span_id ( span_ctx )
382+ trace_flags = span_ctx ( span_ctx , :trace_flags )
383+
384+ hex_trace_id = :io_lib . format ( "~32.16.0b" , [ trace_id ] ) |> IO . iodata_to_binary ( )
385+ hex_span_id = :io_lib . format ( "~16.16.0b" , [ span_id ] ) |> IO . iodata_to_binary ( )
386+ hex_flags = :io_lib . format ( "~2.16.0b" , [ trace_flags ] ) |> IO . iodata_to_binary ( )
387+
388+ "00-#{ hex_trace_id } -#{ hex_span_id } -#{ hex_flags } "
389+ end
390+
262391 defp detach_populate_handlers do
263392 for event <- [
264393 [ :commanded , :aggregate , :load , :start ] ,
0 commit comments