From 90179a3441baacfdeb308abb70e56ce1fca8993b Mon Sep 17 00:00:00 2001 From: Anuraag Agrawal Date: Mon, 14 Mar 2022 20:03:40 +0900 Subject: [PATCH] Migrate ktor tests to kotlin (#5571) --- .../ktor/v1_0/IpAddressUtilTest.groovy | 20 --- .../ktor/v1_0/KtorHttpServerTest.groovy | 59 -------- .../ktor/v1_0/IpAddressUtilTest.kt | 21 +++ .../ktor/v1_0/KtorHttpServerTest.kt | 138 ++++++++++++++++++ .../instrumentation/ktor/v1_0/TestServer.kt | 112 -------------- 5 files changed, 159 insertions(+), 191 deletions(-) delete mode 100644 instrumentation/ktor-1.0/library/src/test/groovy/io/opentelemetry/instrumentation/ktor/v1_0/IpAddressUtilTest.groovy delete mode 100644 instrumentation/ktor-1.0/library/src/test/groovy/io/opentelemetry/instrumentation/ktor/v1_0/KtorHttpServerTest.groovy create mode 100644 instrumentation/ktor-1.0/library/src/test/kotlin/io/opentelemetry/instrumentation/ktor/v1_0/IpAddressUtilTest.kt create mode 100644 instrumentation/ktor-1.0/library/src/test/kotlin/io/opentelemetry/instrumentation/ktor/v1_0/KtorHttpServerTest.kt delete mode 100644 instrumentation/ktor-1.0/library/src/test/kotlin/io/opentelemetry/instrumentation/ktor/v1_0/TestServer.kt diff --git a/instrumentation/ktor-1.0/library/src/test/groovy/io/opentelemetry/instrumentation/ktor/v1_0/IpAddressUtilTest.groovy b/instrumentation/ktor-1.0/library/src/test/groovy/io/opentelemetry/instrumentation/ktor/v1_0/IpAddressUtilTest.groovy deleted file mode 100644 index 6ee5b626e2..0000000000 --- a/instrumentation/ktor-1.0/library/src/test/groovy/io/opentelemetry/instrumentation/ktor/v1_0/IpAddressUtilTest.groovy +++ /dev/null @@ -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") - } -} diff --git a/instrumentation/ktor-1.0/library/src/test/groovy/io/opentelemetry/instrumentation/ktor/v1_0/KtorHttpServerTest.groovy b/instrumentation/ktor-1.0/library/src/test/groovy/io/opentelemetry/instrumentation/ktor/v1_0/KtorHttpServerTest.groovy deleted file mode 100644 index 9c4511d20f..0000000000 --- a/instrumentation/ktor-1.0/library/src/test/groovy/io/opentelemetry/instrumentation/ktor/v1_0/KtorHttpServerTest.groovy +++ /dev/null @@ -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 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> 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) - } - } -} diff --git a/instrumentation/ktor-1.0/library/src/test/kotlin/io/opentelemetry/instrumentation/ktor/v1_0/IpAddressUtilTest.kt b/instrumentation/ktor-1.0/library/src/test/kotlin/io/opentelemetry/instrumentation/ktor/v1_0/IpAddressUtilTest.kt new file mode 100644 index 0000000000..dbd3733a20 --- /dev/null +++ b/instrumentation/ktor-1.0/library/src/test/kotlin/io/opentelemetry/instrumentation/ktor/v1_0/IpAddressUtilTest.kt @@ -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() + } +} diff --git a/instrumentation/ktor-1.0/library/src/test/kotlin/io/opentelemetry/instrumentation/ktor/v1_0/KtorHttpServerTest.kt b/instrumentation/ktor-1.0/library/src/test/kotlin/io/opentelemetry/instrumentation/ktor/v1_0/KtorHttpServerTest.kt new file mode 100644 index 0000000000..82635521f7 --- /dev/null +++ b/instrumentation/ktor-1.0/library/src/test/kotlin/io/opentelemetry/instrumentation/ktor/v1_0/KtorHttpServerTest.kt @@ -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() { + + 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) + } + } + } +} diff --git a/instrumentation/ktor-1.0/library/src/test/kotlin/io/opentelemetry/instrumentation/ktor/v1_0/TestServer.kt b/instrumentation/ktor-1.0/library/src/test/kotlin/io/opentelemetry/instrumentation/ktor/v1_0/TestServer.kt deleted file mode 100644 index 0abee3630a..0000000000 --- a/instrumentation/ktor-1.0/library/src/test/kotlin/io/opentelemetry/instrumentation/ktor/v1_0/TestServer.kt +++ /dev/null @@ -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 - } - } - } -}