Migrate ktor tests to kotlin (#5571)
This commit is contained in:
parent
046e4374bf
commit
90179a3441
|
@ -1,20 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright The OpenTelemetry Authors
|
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.opentelemetry.instrumentation.ktor.v1_0
|
|
||||||
|
|
||||||
import spock.lang.Specification
|
|
||||||
|
|
||||||
class IpAddressUtilTest extends Specification {
|
|
||||||
|
|
||||||
def "test ip address"() {
|
|
||||||
expect:
|
|
||||||
assert IpAddressUtilKt.isIpAddress("2001:0660:7401:0200:0000:0000:0edf:bdd7")
|
|
||||||
assert !IpAddressUtilKt.isIpAddress("2001:0660:7401:0200:0000:0000:0edf:bdd7:33")
|
|
||||||
assert IpAddressUtilKt.isIpAddress("127.0.0.1")
|
|
||||||
assert !IpAddressUtilKt.isIpAddress("127.0.0.1.1")
|
|
||||||
assert !IpAddressUtilKt.isIpAddress("localhost")
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,59 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright The OpenTelemetry Authors
|
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.opentelemetry.instrumentation.ktor.v1_0
|
|
||||||
|
|
||||||
import io.ktor.server.engine.ApplicationEngine
|
|
||||||
import io.opentelemetry.api.common.AttributeKey
|
|
||||||
import io.opentelemetry.instrumentation.test.LibraryTestTrait
|
|
||||||
import io.opentelemetry.instrumentation.test.base.HttpServerTest
|
|
||||||
import io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint
|
|
||||||
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes
|
|
||||||
|
|
||||||
import java.util.concurrent.TimeUnit
|
|
||||||
|
|
||||||
import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.PATH_PARAM
|
|
||||||
|
|
||||||
class KtorHttpServerTest extends HttpServerTest<ApplicationEngine> implements LibraryTestTrait {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
ApplicationEngine startServer(int port) {
|
|
||||||
return TestServer.startServer(port, openTelemetry)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
void stopServer(ApplicationEngine server) {
|
|
||||||
server.stop(0, 10, TimeUnit.SECONDS)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ktor does not have a controller lifecycle so the server span ends immediately when the response is sent, which is
|
|
||||||
// before the controller span finishes.
|
|
||||||
@Override
|
|
||||||
boolean verifyServerSpanEndTime() {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
boolean testPathParam() {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
Set<AttributeKey<?>> httpAttributes(ServerEndpoint endpoint) {
|
|
||||||
def attributes = super.httpAttributes(endpoint)
|
|
||||||
attributes.remove(SemanticAttributes.NET_PEER_PORT)
|
|
||||||
attributes
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
String expectedHttpRoute(ServerEndpoint endpoint) {
|
|
||||||
switch (endpoint) {
|
|
||||||
case PATH_PARAM:
|
|
||||||
return getContextPath() + "/path/{id}/param"
|
|
||||||
default:
|
|
||||||
return super.expectedHttpRoute(endpoint)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.instrumentation.ktor.v1_0
|
||||||
|
|
||||||
|
import io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.assertThat
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
|
||||||
|
class IpAddressUtilTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `test ip address`() {
|
||||||
|
assertThat(isIpAddress("2001:0660:7401:0200:0000:0000:0edf:bdd7")).isTrue()
|
||||||
|
assertThat(isIpAddress("2001:0660:7401:0200:0000:0000:0edf:bdd7:33")).isFalse()
|
||||||
|
assertThat(isIpAddress("127.0.0.1")).isTrue()
|
||||||
|
assertThat(isIpAddress("127.0.0.1.1")).isFalse()
|
||||||
|
assertThat(isIpAddress("localhost")).isFalse()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,138 @@
|
||||||
|
/*
|
||||||
|
* Copyright The OpenTelemetry Authors
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.opentelemetry.instrumentation.ktor.v1_0
|
||||||
|
|
||||||
|
import io.ktor.application.*
|
||||||
|
import io.ktor.http.*
|
||||||
|
import io.ktor.request.*
|
||||||
|
import io.ktor.response.*
|
||||||
|
import io.ktor.routing.*
|
||||||
|
import io.ktor.server.engine.*
|
||||||
|
import io.ktor.server.netty.*
|
||||||
|
import io.opentelemetry.api.trace.Span
|
||||||
|
import io.opentelemetry.api.trace.SpanKind
|
||||||
|
import io.opentelemetry.api.trace.StatusCode
|
||||||
|
import io.opentelemetry.context.Context
|
||||||
|
import io.opentelemetry.extension.kotlin.asContextElement
|
||||||
|
import io.opentelemetry.instrumentation.testing.junit.http.AbstractHttpServerTest
|
||||||
|
import io.opentelemetry.instrumentation.testing.junit.http.HttpServerInstrumentationExtension
|
||||||
|
import io.opentelemetry.instrumentation.testing.junit.http.HttpServerTestOptions
|
||||||
|
import io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint
|
||||||
|
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import org.junit.jupiter.api.extension.RegisterExtension
|
||||||
|
import java.util.concurrent.ExecutionException
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
|
class KtorHttpServerTest : AbstractHttpServerTest<ApplicationEngine>() {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
@JvmStatic
|
||||||
|
@RegisterExtension
|
||||||
|
val testing = HttpServerInstrumentationExtension.forLibrary()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setupServer(): ApplicationEngine {
|
||||||
|
return embeddedServer(Netty, port = port) {
|
||||||
|
KtorTestUtil.installOpenTelemetry(this, testing.openTelemetry)
|
||||||
|
|
||||||
|
routing {
|
||||||
|
get(ServerEndpoint.SUCCESS.path) {
|
||||||
|
controller(ServerEndpoint.SUCCESS) {
|
||||||
|
call.respondText(ServerEndpoint.SUCCESS.body, status = HttpStatusCode.fromValue(ServerEndpoint.SUCCESS.status))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get(ServerEndpoint.REDIRECT.path) {
|
||||||
|
controller(ServerEndpoint.REDIRECT) {
|
||||||
|
call.respondRedirect(ServerEndpoint.REDIRECT.body)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get(ServerEndpoint.ERROR.path) {
|
||||||
|
controller(ServerEndpoint.ERROR) {
|
||||||
|
call.respondText(ServerEndpoint.ERROR.body, status = HttpStatusCode.fromValue(ServerEndpoint.ERROR.status))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get(ServerEndpoint.EXCEPTION.path) {
|
||||||
|
controller(ServerEndpoint.EXCEPTION) {
|
||||||
|
throw Exception(ServerEndpoint.EXCEPTION.body)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get("/query") {
|
||||||
|
controller(ServerEndpoint.QUERY_PARAM) {
|
||||||
|
call.respondText("some=${call.request.queryParameters["some"]}", status = HttpStatusCode.fromValue(ServerEndpoint.QUERY_PARAM.status))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get("/path/{id}/param") {
|
||||||
|
controller(ServerEndpoint.PATH_PARAM) {
|
||||||
|
call.respondText(
|
||||||
|
call.parameters["id"]
|
||||||
|
?: "",
|
||||||
|
status = HttpStatusCode.fromValue(ServerEndpoint.PATH_PARAM.status)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get("/child") {
|
||||||
|
controller(ServerEndpoint.INDEXED_CHILD) {
|
||||||
|
ServerEndpoint.INDEXED_CHILD.collectSpanAttributes { call.request.queryParameters[it] }
|
||||||
|
call.respondText(ServerEndpoint.INDEXED_CHILD.body, status = HttpStatusCode.fromValue(ServerEndpoint.INDEXED_CHILD.status))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
get("/captureHeaders") {
|
||||||
|
controller(ServerEndpoint.CAPTURE_HEADERS) {
|
||||||
|
call.response.header("X-Test-Response", call.request.header("X-Test-Request") ?: "")
|
||||||
|
call.respondText(ServerEndpoint.CAPTURE_HEADERS.body, status = HttpStatusCode.fromValue(ServerEndpoint.CAPTURE_HEADERS.status))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun stopServer(server: ApplicationEngine) {
|
||||||
|
server.stop(0, 10, TimeUnit.SECONDS)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy in HttpServerTest.controller but make it a suspending function
|
||||||
|
private suspend fun controller(endpoint: ServerEndpoint, wrapped: suspend () -> Unit) {
|
||||||
|
assert(Span.current().spanContext.isValid, { "Controller should have a parent span. " })
|
||||||
|
if (endpoint == ServerEndpoint.NOT_FOUND) {
|
||||||
|
wrapped()
|
||||||
|
}
|
||||||
|
val span = testing.openTelemetry.getTracer("test").spanBuilder("controller").setSpanKind(SpanKind.INTERNAL).startSpan()
|
||||||
|
try {
|
||||||
|
withContext(Context.current().with(span).asContextElement()) {
|
||||||
|
wrapped()
|
||||||
|
}
|
||||||
|
span.end()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
span.setStatus(StatusCode.ERROR)
|
||||||
|
span.recordException(if (e is ExecutionException) e.cause ?: e else e)
|
||||||
|
span.end()
|
||||||
|
throw e
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun configure(options: HttpServerTestOptions) {
|
||||||
|
options.setTestPathParam(true)
|
||||||
|
|
||||||
|
options.setHttpAttributes {
|
||||||
|
HttpServerTestOptions.DEFAULT_HTTP_ATTRIBUTES - SemanticAttributes.NET_PEER_PORT
|
||||||
|
}
|
||||||
|
|
||||||
|
options.setExpectedHttpRoute {
|
||||||
|
when (it) {
|
||||||
|
ServerEndpoint.PATH_PARAM -> "/path/{id}/param"
|
||||||
|
else -> expectedHttpRoute(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,112 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright The OpenTelemetry Authors
|
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.opentelemetry.instrumentation.ktor.v1_0
|
|
||||||
|
|
||||||
import io.ktor.application.*
|
|
||||||
import io.ktor.http.*
|
|
||||||
import io.ktor.request.*
|
|
||||||
import io.ktor.response.*
|
|
||||||
import io.ktor.routing.*
|
|
||||||
import io.ktor.server.engine.*
|
|
||||||
import io.ktor.server.netty.*
|
|
||||||
import io.opentelemetry.api.GlobalOpenTelemetry
|
|
||||||
import io.opentelemetry.api.OpenTelemetry
|
|
||||||
import io.opentelemetry.api.trace.Span
|
|
||||||
import io.opentelemetry.api.trace.SpanKind
|
|
||||||
import io.opentelemetry.api.trace.StatusCode
|
|
||||||
import io.opentelemetry.context.Context
|
|
||||||
import io.opentelemetry.extension.kotlin.asContextElement
|
|
||||||
import io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint
|
|
||||||
import io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.*
|
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
import java.util.concurrent.ExecutionException
|
|
||||||
|
|
||||||
class TestServer {
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
|
|
||||||
private val tracer = GlobalOpenTelemetry.getTracer("test")
|
|
||||||
|
|
||||||
@JvmStatic
|
|
||||||
fun startServer(port: Int, openTelemetry: OpenTelemetry): ApplicationEngine {
|
|
||||||
return embeddedServer(Netty, port = port) {
|
|
||||||
KtorTestUtil.installOpenTelemetry(this, openTelemetry)
|
|
||||||
|
|
||||||
routing {
|
|
||||||
get(SUCCESS.path) {
|
|
||||||
controller(SUCCESS) {
|
|
||||||
call.respondText(SUCCESS.body, status = HttpStatusCode.fromValue(SUCCESS.status))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
get(REDIRECT.path) {
|
|
||||||
controller(REDIRECT) {
|
|
||||||
call.respondRedirect(REDIRECT.body)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
get(ERROR.path) {
|
|
||||||
controller(ERROR) {
|
|
||||||
call.respondText(ERROR.body, status = HttpStatusCode.fromValue(ERROR.status))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
get(EXCEPTION.path) {
|
|
||||||
controller(EXCEPTION) {
|
|
||||||
throw Exception(EXCEPTION.body)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
get("/query") {
|
|
||||||
controller(QUERY_PARAM) {
|
|
||||||
call.respondText("some=${call.request.queryParameters["some"]}", status = HttpStatusCode.fromValue(QUERY_PARAM.status))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
get("/path/{id}/param") {
|
|
||||||
controller(PATH_PARAM) {
|
|
||||||
call.respondText(call.parameters["id"] ?: "", status = HttpStatusCode.fromValue(PATH_PARAM.status))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
get("/child") {
|
|
||||||
controller(INDEXED_CHILD) {
|
|
||||||
INDEXED_CHILD.collectSpanAttributes { call.request.queryParameters[it] }
|
|
||||||
call.respondText(INDEXED_CHILD.body, status = HttpStatusCode.fromValue(INDEXED_CHILD.status))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
get("/captureHeaders") {
|
|
||||||
controller(CAPTURE_HEADERS) {
|
|
||||||
call.response.header("X-Test-Response", call.request.header("X-Test-Request") ?: "")
|
|
||||||
call.respondText(CAPTURE_HEADERS.body, status = HttpStatusCode.fromValue(CAPTURE_HEADERS.status))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}.start()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy in HttpServerTest.controller but make it a suspending function
|
|
||||||
private suspend fun controller(endpoint: ServerEndpoint, wrapped: suspend () -> Unit) {
|
|
||||||
assert(Span.current().spanContext.isValid, { "Controller should have a parent span. " })
|
|
||||||
if (endpoint == NOT_FOUND) {
|
|
||||||
wrapped()
|
|
||||||
}
|
|
||||||
val span = tracer.spanBuilder("controller").setSpanKind(SpanKind.INTERNAL).startSpan()
|
|
||||||
try {
|
|
||||||
withContext(Context.current().with(span).asContextElement()) {
|
|
||||||
wrapped()
|
|
||||||
}
|
|
||||||
span.end()
|
|
||||||
} catch (e: Exception) {
|
|
||||||
span.setStatus(StatusCode.ERROR)
|
|
||||||
span.recordException(if (e is ExecutionException) e.cause ?: e else e)
|
|
||||||
span.end()
|
|
||||||
throw e
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue