Skip to content

Commit cd1b746

Browse files
authored
Add smoke tests for telemetry (#7955)
1 parent 9bd1251 commit cd1b746

File tree

12 files changed

+183
-0
lines changed

12 files changed

+183
-0
lines changed

dd-smoke-tests/spring-boot-2.3-webmvc-jetty/src/test/groovy/SpringBootWebmvcIntegrationTest.groovy

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,4 +86,9 @@ class SpringBootWebmvcIntegrationTest extends AbstractServerSmokeTest {
8686
response.code() == 404
8787
waitForTraceCount(1)
8888
}
89+
90+
@Override
91+
List<String> expectedTelemetryDependencies() {
92+
['org.eclipse.jetty:jetty-client']
93+
}
8994
}

dd-smoke-tests/spring-boot-3.0-native/src/test/groovy/SpringBootNativeInstrumentationTest.groovy

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,11 @@ class SpringBootNativeInstrumentationTest extends AbstractServerSmokeTest {
5555
return ["[servlet.request[spring.handler[WebController.doHello[WebController.sayHello]]]]"]
5656
}
5757

58+
@Override
59+
boolean testTelemetry() {
60+
false
61+
}
62+
5863
def "check native instrumentation"() {
5964
setup:
6065
String url = "http://localhost:${httpPort}/hello"

dd-smoke-tests/spring-boot-3.0-webmvc/src/test/groovy/SpringBootWebmvcIntegrationTest.groovy

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,4 +78,9 @@ class SpringBootWebmvcIntegrationTest extends AbstractServerSmokeTest {
7878
responseBodyStr.contains("banana")
7979
waitForTraceCount(1)
8080
}
81+
82+
@Override
83+
List<String> expectedTelemetryDependencies() {
84+
['spring-core']
85+
}
8186
}

dd-smoke-tests/spring-boot-3.3-webmvc/src/test/groovy/SpringBootWebmvcIntegrationTest.groovy

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,4 +78,9 @@ class SpringBootWebmvcIntegrationTest extends AbstractServerSmokeTest {
7878
responseBodyStr.contains("banana")
7979
waitForTraceCount(1)
8080
}
81+
82+
@Override
83+
List<String> expectedTelemetryDependencies() {
84+
['spring-core']
85+
}
8186
}

dd-smoke-tests/springboot-openliberty-20/src/test/groovy/datadog/smoketest/SpringBootOpenLibertySmokeTest.groovy

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,11 @@ class SpringBootOpenLibertySmokeTest extends AbstractServerSmokeTest {
6161
].toSet()
6262
}
6363

64+
@Override
65+
boolean testTelemetry() {
66+
false
67+
}
68+
6469
def "Test concurrent requests to Spring Boot running Open Liberty"() {
6570
setup:
6671
def url = "http://localhost:${httpPort}/connect"

dd-smoke-tests/springboot-openliberty-20/src/test/groovy/datadog/smoketest/SpringBootOpenLibertySmokeVulnerabilityTest.groovy

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,11 @@ class SpringBootOpenLibertySmokeVulnerabilityTest extends AbstractServerSmokeTes
5959
return {} // force traces decoding
6060
}
6161

62+
@Override
63+
boolean testTelemetry() {
64+
false
65+
}
66+
6267
private static boolean contains(String s) {
6368
System.out.println("Checking span:" + s)
6469
return s.contains("MD5")

dd-smoke-tests/springboot-openliberty-23/src/test/groovy/datadog/smoketest/SpringBootOpenLibertySmokeTest.groovy

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,11 @@ class SpringBootOpenLibertySmokeTest extends AbstractServerSmokeTest {
6868
].toSet()
6969
}
7070

71+
@Override
72+
boolean testTelemetry() {
73+
false
74+
}
75+
7176
def "Test concurrent requests to Spring Boot running Open Liberty"() {
7277
setup:
7378
def url = "http://localhost:${httpPort}/connect"

dd-smoke-tests/springboot-openliberty-23/src/test/groovy/datadog/smoketest/SpringBootOpenLibertySmokeVulnerabilityTest.groovy

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,11 @@ class SpringBootOpenLibertySmokeVulnerabilityTest extends AbstractServerSmokeTes
5959
return {} // force traces decoding
6060
}
6161

62+
@Override
63+
boolean testTelemetry() {
64+
false
65+
}
66+
6267
private static boolean contains(String s) {
6368
System.out.println("Checking span:" + s)
6469
return s.contains("MD5")

dd-smoke-tests/src/main/groovy/datadog/smoketest/AbstractServerSmokeTest.groovy

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import datadog.trace.agent.test.utils.OkHttpUtils
55
import datadog.trace.agent.test.utils.PortUtils
66
import okhttp3.OkHttpClient
77
import spock.lang.Shared
8+
import static org.junit.Assume.assumeTrue
89

910
import java.util.concurrent.TimeUnit
1011
import java.util.concurrent.atomic.AtomicInteger
@@ -116,4 +117,54 @@ abstract class AbstractServerSmokeTest extends AbstractSmokeTest {
116117
}
117118
return remaining
118119
}
120+
121+
@RunLast
122+
void 'receive telemetry app-started'() {
123+
when:
124+
assumeTrue(testTelemetry())
125+
waitForTelemetryCount(1)
126+
127+
then:
128+
telemetryMessages.size() >= 1
129+
Object msg = telemetryMessages.get(0)
130+
msg['request_type'] == 'app-started'
131+
}
132+
133+
List<String> expectedTelemetryDependencies() {
134+
[]
135+
}
136+
137+
@RunLast
138+
@SuppressWarnings('UnnecessaryBooleanExpression')
139+
void 'receive telemetry app-dependencies-loaded'() {
140+
when:
141+
assumeTrue(testTelemetry())
142+
// app-started + 3 message-batch
143+
waitForTelemetryCount(4)
144+
waitForTelemetryFlat { it.get('request_type') == 'app-dependencies-loaded' }
145+
146+
then: 'received some dependencies'
147+
def dependenciesLoaded = telemetryFlatMessages.findAll { it.get('request_type') == 'app-dependencies-loaded' }
148+
def dependencies = []
149+
dependenciesLoaded.each {
150+
def payload = it.get('payload') as Map<String, Object>
151+
dependencies.addAll(payload.get('dependencies')) }
152+
dependencies.size() > 0
153+
154+
Set<String> dependencyNames = dependencies.collect {
155+
def dependency = it as Map<String, Object>
156+
dependency.get('name') as String
157+
}.toSet()
158+
159+
and: 'received tracer dependencies'
160+
// Not exhaustive list of tracer dependencies.
161+
Set<String> missingDependencyNames = ['com.github.jnr:jnr-ffi', 'net.bytebuddy:byte-buddy-agent',].toSet()
162+
missingDependencyNames.removeAll(dependencyNames) || true
163+
missingDependencyNames.isEmpty()
164+
165+
and: 'received application dependencies'
166+
Set<String> missingExtraDependencyNames = expectedTelemetryDependencies().toSet()
167+
missingExtraDependencyNames.removeAll(dependencyNames) || true
168+
missingExtraDependencyNames.isEmpty()
169+
}
119170
}

dd-smoke-tests/src/main/groovy/datadog/smoketest/AbstractSmokeTest.groovy

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import datadog.trace.test.agent.decoder.Decoder
77
import datadog.trace.test.agent.decoder.DecodedMessage
88
import datadog.trace.test.agent.decoder.DecodedTrace
99
import datadog.trace.util.Strings
10+
import groovy.json.JsonSlurper
1011

1112
import java.nio.charset.StandardCharsets
1213
import java.util.concurrent.CopyOnWriteArrayList
@@ -37,6 +38,15 @@ abstract class AbstractSmokeTest extends ProcessManager {
3738
@Shared
3839
private Throwable traceDecodingFailure = null
3940

41+
@Shared
42+
protected CopyOnWriteArrayList<Map<String, Object>> telemetryMessages = new CopyOnWriteArrayList()
43+
44+
@Shared
45+
protected CopyOnWriteArrayList<Map<String, Object>> telemetryFlatMessages = new CopyOnWriteArrayList()
46+
47+
@Shared
48+
private Throwable telemetryDecodingFailure = null
49+
4050
@Shared
4151
protected TestHttpServer.Headers lastTraceRequestHeaders = null
4252

@@ -119,6 +129,23 @@ abstract class AbstractSmokeTest extends ProcessManager {
119129
response.status(200).send(remoteConfigResponse)
120130
}
121131
prefix("/telemetry/proxy/api/v2/apmtelemetry") {
132+
try {
133+
byte[] body = request.getBody()
134+
if (body != null) {
135+
Map<String, Object> msg = new JsonSlurper().parseText(new String(body, StandardCharsets.UTF_8)) as Map<String, Object>
136+
telemetryMessages.add(msg)
137+
if (msg.get("request_type") == "message-batch") {
138+
msg.get("payload")?.each { telemetryFlatMessages.add(it as Map<String, Object>) }
139+
} else {
140+
telemetryFlatMessages.add(msg)
141+
}
142+
}
143+
} catch (Throwable t) {
144+
println("=== Failure during telemetry decoding ===")
145+
t.printStackTrace(System.out)
146+
telemetryDecodingFailure = t
147+
throw t
148+
}
122149
response.status(202).send()
123150
}
124151
}
@@ -160,6 +187,9 @@ abstract class AbstractSmokeTest extends ProcessManager {
160187
if (inferServiceName()) {
161188
ret += "-Ddd.service.name=${SERVICE_NAME}"
162189
}
190+
if (testTelemetry()) {
191+
ret += "-Ddd.telemetry.heartbeat.interval=5"
192+
}
163193
ret as String[]
164194
}
165195

@@ -172,6 +202,11 @@ abstract class AbstractSmokeTest extends ProcessManager {
172202
return !Platform.isJ9()
173203
}
174204

205+
206+
boolean testTelemetry() {
207+
return true
208+
}
209+
175210
def setup() {
176211
traceCount.set(0)
177212
decodeTraces.clear()
@@ -272,6 +307,31 @@ abstract class AbstractSmokeTest extends ProcessManager {
272307
}
273308
}
274309

310+
void waitForTelemetryCount(final int count) {
311+
def conditions = new PollingConditions(timeout: 30, initialDelay: 0, delay: 1, factor: 1)
312+
waitForTelemetryCount(conditions, count)
313+
}
314+
315+
void waitForTelemetryCount(final PollingConditions poll, final int count) {
316+
poll.eventually {
317+
telemetryMessages.size() >= count
318+
}
319+
}
320+
321+
void waitForTelemetryFlat(final Function<Map<String, Object>, Boolean> predicate) {
322+
def conditions = new PollingConditions(timeout: 30, initialDelay: 0, delay: 1, factor: 1)
323+
waitForTelemetryFlat(conditions, predicate)
324+
}
325+
326+
void waitForTelemetryFlat(final PollingConditions poll, final Function<Map<String, Object>, Boolean> predicate) {
327+
poll.eventually {
328+
if (telemetryDecodingFailure != null) {
329+
throw telemetryDecodingFailure
330+
}
331+
assert telemetryFlatMessages.find { predicate.apply(it) } != null
332+
}
333+
}
334+
275335
List<DecodedTrace> getTraces() {
276336
decodeTraces
277337
}

0 commit comments

Comments
 (0)