opentelemetry-java-instrume.../dd-trace-ot/src/test/groovy/datadog/trace/api/writer/DDAgentApiTest.groovy

244 lines
7.6 KiB
Groovy

package datadog.trace.api.writer
import com.fasterxml.jackson.core.type.TypeReference
import com.fasterxml.jackson.databind.JsonNode
import datadog.opentracing.SpanFactory
import datadog.trace.common.writer.ddagent.DDAgentApi
import datadog.trace.common.writer.ddagent.DDAgentResponseListener
import datadog.trace.util.test.DDSpecification
import java.util.concurrent.atomic.AtomicLong
import java.util.concurrent.atomic.AtomicReference
import static datadog.trace.agent.test.server.http.TestHttpServer.httpServer
class DDAgentApiTest extends DDSpecification {
static mapper = DDAgentApi.OBJECT_MAPPER
def "sending an empty list of traces returns no errors"() {
setup:
def agent = httpServer {
handlers {
put("v0.4/traces") {
if (request.contentType != "application/msgpack") {
response.status(400).send("wrong type: $request.contentType")
} else if (request.contentLength <= 0) {
response.status(400).send("no content")
} else {
response.status(200).send()
}
}
}
}
def client = new DDAgentApi("localhost", agent.address.port, null)
expect:
client.tracesUrl.toString() == "http://localhost:${agent.address.port}/v0.4/traces"
def response = client.sendTraces([])
response.success()
response.status() == 200
cleanup:
agent.close()
}
def "non-200 response"() {
setup:
def agent = httpServer {
handlers {
put("v0.4/traces") {
response.status(404).send()
}
}
}
def client = new DDAgentApi("localhost", agent.address.port, null)
expect:
client.tracesUrl.toString() == "http://localhost:${agent.address.port}/v0.3/traces"
def response = client.sendTraces([])
!response.success()
response.status() == 404
cleanup:
agent.close()
}
def "content is sent as MSGPACK"() {
setup:
def agent = httpServer {
handlers {
put("v0.4/traces") {
response.send()
}
}
}
def client = new DDAgentApi("localhost", agent.address.port, null)
expect:
client.tracesUrl.toString() == "http://localhost:${agent.address.port}/v0.4/traces"
client.sendTraces(traces).success()
agent.lastRequest.contentType == "application/msgpack"
agent.lastRequest.headers.get("Datadog-Meta-Lang") == "java"
agent.lastRequest.headers.get("Datadog-Meta-Lang-Version") == System.getProperty("java.version", "unknown")
agent.lastRequest.headers.get("Datadog-Meta-Tracer-Version") == "Stubbed-Test-Version"
agent.lastRequest.headers.get("X-Datadog-Trace-Count") == "${traces.size()}"
convertList(agent.lastRequest.body) == expectedRequestBody
cleanup:
agent.close()
// Populate thread info dynamically as it is different when run via gradle vs idea.
where:
traces | expectedRequestBody
[] | []
[[SpanFactory.newSpanOf(1L).setTag("service.name", "my-service")]] | [[new TreeMap<>([
"duration" : 0,
"error" : 0,
"meta" : ["thread.name": Thread.currentThread().getName(), "thread.id": "${Thread.currentThread().id}"],
"metrics" : [:],
"name" : "fakeOperation",
"parent_id": 0,
"resource" : "fakeResource",
"service" : "my-service",
"span_id" : 1,
"start" : 1000,
"trace_id" : 1,
"type" : "fakeType"
])]]
[[SpanFactory.newSpanOf(100L).setTag("resource.name", "my-resource")]] | [[new TreeMap<>([
"duration" : 0,
"error" : 0,
"meta" : ["thread.name": Thread.currentThread().getName(), "thread.id": "${Thread.currentThread().id}"],
"metrics" : [:],
"name" : "fakeOperation",
"parent_id": 0,
"resource" : "my-resource",
"service" : "fakeService",
"span_id" : 1,
"start" : 100000,
"trace_id" : 1,
"type" : "fakeType"
])]]
}
def "Api ResponseListeners see 200 responses"() {
setup:
def agentResponse = new AtomicReference<String>(null)
DDAgentResponseListener responseListener = { String endpoint, JsonNode responseJson ->
agentResponse.set(responseJson.toString())
}
def agent = httpServer {
handlers {
put("v0.4/traces") {
def status = request.contentLength > 0 ? 200 : 500
response.status(status).send('{"hello":"test"}')
}
}
}
def client = new DDAgentApi("localhost", agent.address.port, null)
client.addResponseListener(responseListener)
when:
client.sendTraces([[], [], []])
then:
agentResponse.get() == '{"hello":"test"}'
agent.lastRequest.headers.get("Datadog-Meta-Lang") == "java"
agent.lastRequest.headers.get("Datadog-Meta-Lang-Version") == System.getProperty("java.version", "unknown")
agent.lastRequest.headers.get("Datadog-Meta-Tracer-Version") == "Stubbed-Test-Version"
agent.lastRequest.headers.get("X-Datadog-Trace-Count") == "3" // false data shows the value provided via traceCounter.
cleanup:
agent.close()
}
def "Api Downgrades to v3 if v0.4 not available"() {
setup:
def v3Agent = httpServer {
handlers {
put("v0.3/traces") {
def status = request.contentLength > 0 ? 200 : 500
response.status(status).send()
}
}
}
def client = new DDAgentApi("localhost", v3Agent.address.port, null)
expect:
client.tracesUrl.toString() == "http://localhost:${v3Agent.address.port}/v0.3/traces"
client.sendTraces([]).success()
cleanup:
v3Agent.close()
}
def "Api Downgrades to v3 if timeout exceeded (#delayTrace, #badPort)"() {
// This test is unfortunately only exercising the read timeout, not the connect timeout.
setup:
def agent = httpServer {
handlers {
put("v0.3/traces") {
def status = request.contentLength > 0 ? 200 : 500
response.status(status).send()
}
put("v0.4/traces") {
Thread.sleep(delayTrace)
def status = request.contentLength > 0 ? 200 : 500
response.status(status).send()
}
}
}
def port = badPort ? 999 : agent.address.port
def client = new DDAgentApi("localhost", port, null)
expect:
client.tracesUrl.toString() == "http://localhost:${port}/$endpointVersion/traces"
cleanup:
agent.close()
where:
endpointVersion | delayTrace | badPort
"v0.4" | 0 | false
"v0.3" | 0 | true
"v0.4" | 500 | false
"v0.3" | 30000 | false
}
def "verify content length"() {
setup:
def receivedContentLength = new AtomicLong()
def agent = httpServer {
handlers {
put("v0.4/traces") {
receivedContentLength.set(request.contentLength)
response.status(200).send()
}
}
}
def client = new DDAgentApi("localhost", agent.address.port, null)
when:
def success = client.sendTraces(traces).success()
then:
success
receivedContentLength.get() == expectedLength
cleanup:
agent.close()
where:
expectedLength | traces
1 | []
3 | [[], []]
16 | (1..15).collect { [] }
19 | (1..16).collect { [] }
65538 | (1..((1 << 16) - 1)).collect { [] }
65541 | (1..(1 << 16)).collect { [] }
}
static List<List<TreeMap<String, Object>>> convertList(byte[] bytes) {
return mapper.readValue(bytes, new TypeReference<List<List<TreeMap<String, Object>>>>() {})
}
}