Skip to content

Commit d28d501

Browse files
committed
feat(testing): Add span links support
1 parent 2256d3b commit d28d501

4 files changed

Lines changed: 40 additions & 26 deletions

File tree

‎utils/test-agent-utils/decoder/src/main/java/datadog/trace/test/agent/decoder/DecodedSpan.java‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ public interface DecodedSpan {
3737

3838
/**
3939
* Returns the links.
40+
*
4041
* @return The span links, empty when the span carries none.
4142
*/
4243
List<DecodedSpanLink> getLinks();

‎utils/test-agent-utils/decoder/src/main/java/datadog/trace/test/agent/decoder/json/raw/SpanJson.java‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55

66
import com.squareup.moshi.Json;
77
import datadog.trace.test.agent.decoder.DecodedSpan;
8+
import datadog.trace.test.agent.decoder.DecodedSpanLink;
9+
import datadog.trace.test.agent.decoder.DecodedSpanLinks;
10+
import java.util.List;
811
import java.util.Map;
912

1013
/**
@@ -44,6 +47,9 @@ public final class SpanJson implements DecodedSpan {
4447

4548
Map<String, Number> metrics;
4649

50+
/** Lazily decoded from the span-links meta tag; transient so Moshi does not bind it. */
51+
private transient List<DecodedSpanLink> links;
52+
4753
@Override
4854
public String getService() {
4955
return this.service;
@@ -109,6 +115,15 @@ public String getType() {
109115
return this.type;
110116
}
111117

118+
@Override
119+
public List<DecodedSpanLink> getLinks() {
120+
List<DecodedSpanLink> links = this.links;
121+
if (links == null) {
122+
links = this.links = DecodedSpanLinks.fromMeta(getMeta());
123+
}
124+
return links;
125+
}
126+
112127
@Override
113128
public String toString() {
114129
return "SpanJson{"

‎utils/test-agent-utils/decoder/src/main/java/datadog/trace/test/agent/decoder/v04/raw/SpanV04.java‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,8 @@ public SpanV04(
263263
String type,
264264
Map<String, Number> metrics,
265265
Map<String, String> meta,
266-
Map<String, Object> metaStruct, List<DecodedSpanLink> links) {
266+
Map<String, Object> metaStruct,
267+
List<DecodedSpanLink> links) {
267268
this.service = service;
268269
this.name = name;
269270
this.resource = resource;

‎utils/test-agent-utils/decoder/src/test/java/datadog/trace/test/agent/decoder/SpanLinksDecoderTest.java‎

Lines changed: 22 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package datadog.trace.test.agent.decoder;
22

3-
import static datadog.trace.test.agent.decoder.DecodedSpanLinks.SPAN_LINKS_TAG;
3+
import static java.util.Collections.emptyMap;
44
import static java.util.Collections.singletonMap;
55
import static org.junit.jupiter.api.Assertions.assertEquals;
66
import static org.junit.jupiter.api.Assertions.assertNotNull;
@@ -13,22 +13,24 @@
1313
import org.junit.jupiter.api.Test;
1414

1515
class SpanLinksDecoderTest {
16+
// The decoder keeps the tag name private, so spell it out the way the tracer writes it.
17+
private static final String SPAN_LINKS_TAG = "_dd.span_links";
1618
private static final String TRACE_ID_128 = "11223344556677889900aabbccddeeff";
1719
private static final long TRACE_ID_LOW = 0x9900aabbccddeeffL;
1820

1921
@Test
2022
void absentOrEmptyTagYieldsNoLink() {
21-
assertTrue(DecodedSpanLinks.fromMeta(null).isEmpty(), "null tag");
22-
assertTrue(DecodedSpanLinks.fromMeta("").isEmpty(), "empty tag");
23-
assertTrue(DecodedSpanLinks.fromMeta("[]").isEmpty(), "empty array");
24-
assertTrue(DecodedSpanLinks.fromMeta("null").isEmpty(), "JSON null");
23+
assertTrue(DecodedSpanLinks.fromMeta(emptyMap()).isEmpty(), "no tag in meta");
24+
assertTrue(fromTag(null).isEmpty(), "null tag");
25+
assertTrue(fromTag("").isEmpty(), "empty tag");
26+
assertTrue(fromTag("[]").isEmpty(), "empty array");
27+
assertTrue(fromTag("null").isEmpty(), "JSON null");
2528
}
2629

2730
@Test
2831
void decodesMinimalLinkApplyingDefaults() {
2932
List<DecodedSpanLink> links =
30-
DecodedSpanLinks.fromMeta(
31-
"[{\"span_id\":\"123456789abcdef0\",\"trace_id\":\"" + TRACE_ID_128 + "\"}]");
33+
fromTag("[{\"span_id\":\"123456789abcdef0\",\"trace_id\":\"" + TRACE_ID_128 + "\"}]");
3234

3335
assertEquals(1, links.size());
3436
DecodedSpanLink link = links.get(0);
@@ -42,7 +44,7 @@ void decodesMinimalLinkApplyingDefaults() {
4244
@Test
4345
void decodesFullLink() {
4446
List<DecodedSpanLink> links =
45-
DecodedSpanLinks.fromMeta(
47+
fromTag(
4648
"[{\"trace_id\":\""
4749
+ TRACE_ID_128
4850
+ "\",\"span_id\":\"a\",\"flags\":1,"
@@ -62,7 +64,7 @@ void decodesFullLink() {
6264
@Test
6365
void preservesLinkOrderAndDecodesUnsignedSpanIds() {
6466
List<DecodedSpanLink> links =
65-
DecodedSpanLinks.fromMeta(
67+
fromTag(
6668
"[{\"trace_id\":\"0\",\"span_id\":\"1\"},"
6769
+ "{\"trace_id\":\"0\",\"span_id\":\"ffffffffffffffff\"},"
6870
+ "{\"trace_id\":\"0\",\"span_id\":\"2\"}]");
@@ -75,40 +77,31 @@ void preservesLinkOrderAndDecodesUnsignedSpanIds() {
7577

7678
@Test
7779
void decodesAShortTraceId() {
78-
DecodedSpanLink link =
79-
DecodedSpanLinks.fromMeta("[{\"trace_id\":\"abcd\",\"span_id\":\"1\"}]").get(0);
80+
DecodedSpanLink link = fromTag("[{\"trace_id\":\"abcd\",\"span_id\":\"1\"}]").get(0);
8081

8182
assertEquals(0xabcdL, link.getTraceId());
8283
}
8384

8485
@Test
8586
void unknownKeysAreIgnored() {
8687
DecodedSpanLink link =
87-
DecodedSpanLinks.fromMeta(
88-
"[{\"trace_id\":\"1\",\"span_id\":\"1\",\"something_new\":\"whatever\"}]")
89-
.get(0);
88+
fromTag("[{\"trace_id\":\"1\",\"span_id\":\"1\",\"something_new\":\"whatever\"}]").get(0);
9089

9190
assertEquals(1L, link.getTraceId());
9291
}
9392

9493
@Test
9594
void malformedTagsThrow() {
95+
assertThrows(IllegalStateException.class, () -> fromTag("not json"), "malformed JSON");
9696
assertThrows(
97-
IllegalStateException.class, () -> DecodedSpanLinks.fromMeta("not json"), "malformed JSON");
98-
assertThrows(
99-
IllegalStateException.class,
100-
() -> DecodedSpanLinks.fromMeta("[{\"span_id\":\"1\"}]"),
101-
"missing trace_id");
97+
IllegalStateException.class, () -> fromTag("[{\"span_id\":\"1\"}]"), "missing trace_id");
10298
assertThrows(
103-
IllegalStateException.class,
104-
() -> DecodedSpanLinks.fromMeta("[{\"trace_id\":\"1\"}]"),
105-
"missing span_id");
99+
IllegalStateException.class, () -> fromTag("[{\"trace_id\":\"1\"}]"), "missing span_id");
106100
assertThrows(
107101
IllegalStateException.class,
108-
() -> DecodedSpanLinks.fromMeta("[{\"trace_id\":\"1\",\"span_id\":\"zz\"}]"),
102+
() -> fromTag("[{\"trace_id\":\"1\",\"span_id\":\"zz\"}]"),
109103
"malformed span id");
110-
assertThrows(
111-
IllegalStateException.class, () -> DecodedSpanLinks.fromMeta("[null]"), "null link entry");
104+
assertThrows(IllegalStateException.class, () -> fromTag("[null]"), "null link entry");
112105
}
113106

114107
@Test
@@ -143,6 +136,10 @@ void treatsAnExplicitlyEmptyAttributeMapAsNone() {
143136
assertTrue(link.getAttributes().isEmpty());
144137
}
145138

139+
private static List<DecodedSpanLink> fromTag(String tagValue) {
140+
return DecodedSpanLinks.fromMeta(singletonMap(SPAN_LINKS_TAG, tagValue));
141+
}
142+
146143
@Test
147144
void decodedSpanExposesLinksAndKeepsTheTagVisible() {
148145
String tag = "[{\\\"trace_id\\\":\\\"" + TRACE_ID_128 + "\\\",\\\"span_id\\\":\\\"a\\\"}]";

0 commit comments

Comments
 (0)