22
33import static datadog .smoketest .trace .SmokeTraceAssertions .IGNORE_ADDITIONAL_TRACES ;
44import static datadog .smoketest .trace .SmokeTraceAssertions .assertTraces ;
5+ import static datadog .smoketest .trace .SpanEventMatcher .event ;
56import static datadog .smoketest .trace .SpanLinkMatcher .any ;
67import static datadog .smoketest .trace .SpanLinkMatcher .to ;
78import static datadog .smoketest .trace .SpanLinkMatcher .toIndex ;
1213import static datadog .trace .test .junit .utils .assertions .Matchers .isNonNull ;
1314import static datadog .trace .test .junit .utils .assertions .Matchers .validates ;
1415import static java .util .Collections .singletonMap ;
16+ import static java .util .concurrent .TimeUnit .NANOSECONDS ;
1517import static org .junit .jupiter .api .Assertions .assertThrows ;
1618
1719import datadog .trace .test .agent .decoder .DecodedTrace ;
1820import datadog .trace .test .agent .decoder .Decoder ;
21+ import java .util .LinkedHashMap ;
1922import java .util .List ;
2023import java .util .Map ;
2124import org .junit .jupiter .api .Test ;
@@ -54,7 +57,7 @@ class SmokeMatcherTest {
5457 // One span carrying meta_struct: an IAST-style nested "_dd.stack" plus a request-body entry.
5558 private static final String META_STRUCT_TRACE =
5659 "[[{"
57- + "\" service\" :\" s \" ,\" name\" :\" servlet.request\" ,\" resource\" :\" r\" ,"
60+ + "\" service\" :\" test-service \" ,\" name\" :\" servlet.request\" ,\" resource\" :\" r\" ,"
5861 + "\" trace_id\" :1,\" span_id\" :1,\" parent_id\" :0,\" start\" :0,\" duration\" :1,\" error\" :0,"
5962 + "\" meta\" :{},\" metrics\" :{},"
6063 + "\" meta_struct\" :{"
@@ -242,12 +245,14 @@ void defaultConstraintsRequireDefinedServiceNoTypeAndNoError() {
242245 trace (span ().root ()));
243246
244247 // A non-null span type fails the default unless acknowledged with type(...).
245- List <DecodedTrace > typed = Decoder .decodeJson (spanTraceJson ("s" , "web" , 0 )).getTraces ();
248+ List <DecodedTrace > typed =
249+ Decoder .decodeJson (spanTraceJson ("test-service" , "web" , 0 )).getTraces ();
246250 assertThrows (AssertionError .class , () -> assertTraces (typed , trace (span ().root ())));
247251 assertTraces (typed , trace (span ().root ().type ("web" )));
248252
249253 // An errored span fails the default unless acknowledged with error(true).
250- List <DecodedTrace > errored = Decoder .decodeJson (spanTraceJson ("s" , null , 1 )).getTraces ();
254+ List <DecodedTrace > errored =
255+ Decoder .decodeJson (spanTraceJson ("test-service" , null , 1 )).getTraces ();
251256 assertThrows (AssertionError .class , () -> assertTraces (errored , trace (span ().root ())));
252257 assertTraces (errored , trace (span ().root ().error (true )));
253258
@@ -410,6 +415,77 @@ void linkTargetsDoNotLeakAcrossCandidates() {
410415 span ().operationName ("root" ).root (), span ().operationName ("merge" ).links (toIndex (0 ))));
411416 }
412417
418+ // --- Span events ---
419+
420+ @ Test
421+ void matchesEventsPositionally () {
422+ List <DecodedTrace > traces =
423+ Decoder .decodeJson (
424+ eventTraceJson (
425+ "[{\" time_unix_nano\" :110,\" name\" :\" first\" },"
426+ + "{\" time_unix_nano\" :120,\" name\" :\" second\" ,"
427+ + "\" attributes\" :{\" key\" :\" value\" ,\" count\" :3}}]" ))
428+ .getTraces ();
429+ Map <String , Object > attributes = new LinkedHashMap <>();
430+ attributes .put ("key" , "value" );
431+ attributes .put ("count" , 3L );
432+
433+ assertTraces (
434+ traces ,
435+ trace (
436+ span ()
437+ .operationName ("event" )
438+ .events (event ("first" ), event ("second" ).attributes (attributes ))));
439+ assertThrows (
440+ AssertionError .class ,
441+ () -> assertTraces (traces , trace (span ().events (event ("second" ), event ("first" )))),
442+ "events are matched positionally" );
443+ assertThrows (
444+ AssertionError .class ,
445+ () -> assertTraces (traces , trace (span ().events (event ("first" ), event ("second" )))),
446+ "missing attributes" );
447+ }
448+
449+ @ Test
450+ void eventTimeDefaultsToSpanBounds () {
451+ List <DecodedTrace > traces =
452+ Decoder .decodeJson (eventTraceJson ("[{\" time_unix_nano\" :42,\" name\" :\" early\" }]" ))
453+ .getTraces ();
454+
455+ assertThrows (
456+ AssertionError .class ,
457+ () -> assertTraces (traces , trace (span ().events (event ("early" )))),
458+ "event time outside the span bounds" );
459+ assertTraces (traces , trace (span ().events (event ("early" ).time (42 , NANOSECONDS ))));
460+ }
461+
462+ @ Test
463+ void emptyEventsAssertsNoEventAndAbsentEventsAssertNothing () {
464+ List <DecodedTrace > withEvents =
465+ Decoder .decodeJson (eventTraceJson ("[{\" time_unix_nano\" :110,\" name\" :\" event\" }]" ))
466+ .getTraces ();
467+
468+ assertThrows (
469+ AssertionError .class ,
470+ () -> assertTraces (withEvents , trace (span ().events ())),
471+ "events() requires zero events" );
472+ // Not calling events(...) asserts nothing, even though the span does carry events.
473+ assertTraces (withEvents , trace (span ().operationName ("event" )));
474+ // A span with no event satisfies events().
475+ List <DecodedTrace > withoutEvents =
476+ Decoder .decodeJson ("[[" + spanJson ("plain" , 10 , 0 , 10 ) + "]]" ).getTraces ();
477+ assertTraces (withoutEvents , trace (span ().operationName ("plain" ).events ()));
478+ }
479+
480+ /** A single-span trace, from 100 to 200 ns, carrying the given events meta tag. */
481+ private static String eventTraceJson (String eventsTag ) {
482+ return "[[{\" service\" :\" test-service\" ,\" name\" :\" event\" ,\" resource\" :\" event\" ,"
483+ + "\" trace_id\" :1,\" span_id\" :1,\" parent_id\" :0,\" start\" :100,\" duration\" :100,"
484+ + "\" error\" :0,\" metrics\" :{},\" meta\" :{\" events\" :\" "
485+ + eventsTag .replace ("\" " , "\\ \" " )
486+ + "\" }}]]" ;
487+ }
488+
413489 /** A single-span trace whose root {@code merge} span links to the given span ids. */
414490 private static String mergeOnlyTraceJson (long ... linkedSpanIds ) {
415491 return "[[" + linkingSpanJson (40 , 0 , linkedSpanIds ) + "]]" ;
@@ -430,7 +506,7 @@ private static String linkedTraceJson(long shardA, long shardB) {
430506
431507 /** A single-span trace whose link carries flags, a trace state and attributes. */
432508 private static String refinedLinkTraceJson () {
433- return "[[{\" service\" :\" s \" ,\" name\" :\" merge\" ,\" resource\" :\" merge\" ,"
509+ return "[[{\" service\" :\" test-service \" ,\" name\" :\" merge\" ,\" resource\" :\" merge\" ,"
434510 + "\" trace_id\" :1,\" span_id\" :40,\" parent_id\" :0,\" start\" :40,\" duration\" :1,"
435511 + "\" error\" :0,\" metrics\" :{},\" meta\" :{\" _dd.span_links\" :\" "
436512 + "[{\\ \" trace_id\\ \" :\\ \" 00000000000000000000000000000063\\ \" ,"
@@ -455,7 +531,7 @@ private static String linkingSpanJson(long id, long parent, long... linkedSpanId
455531 .append ("\\ \" }" );
456532 }
457533 links .append (']' );
458- return "{\" service\" :\" s \" ,\" name\" :\" merge\" ,\" resource\" :\" merge\" ,"
534+ return "{\" service\" :\" test-service \" ,\" name\" :\" merge\" ,\" resource\" :\" merge\" ,"
459535 + "\" trace_id\" :1,\" span_id\" :"
460536 + id
461537 + ",\" parent_id\" :"
@@ -467,7 +543,7 @@ private static String linkingSpanJson(long id, long parent, long... linkedSpanId
467543 }
468544
469545 private static String spanJson (String name , long id , long parent , long start ) {
470- return "{\" service\" :\" s \" ,\" name\" :\" "
546+ return "{\" service\" :\" test-service \" ,\" name\" :\" "
471547 + name
472548 + "\" ,\" resource\" :\" "
473549 + name
0 commit comments