From 32fb96318b0308d798cfd729b5469931b1805571 Mon Sep 17 00:00:00 2001 From: Laplie Anderson Date: Thu, 23 Jul 2020 11:07:03 -0400 Subject: [PATCH] Fix finatra tests for latest dependencies (DataDog/dd-trace-java#1706) --- .../finatra-2.9/finatra-2.9.gradle | 13 ++- .../groovy/FinatraServerLatestTest.groovy | 108 ++++++++++++++++++ .../scala/FinatraController.scala | 64 +++++++++++ .../latestDepTest/scala/FinatraServer.scala | 29 +++++ .../ResponseSettingExceptionMapper.scala | 29 +++++ 5 files changed, 242 insertions(+), 1 deletion(-) create mode 100644 instrumentation/finatra-2.9/src/latestDepTest/groovy/FinatraServerLatestTest.groovy create mode 100644 instrumentation/finatra-2.9/src/latestDepTest/scala/FinatraController.scala create mode 100644 instrumentation/finatra-2.9/src/latestDepTest/scala/FinatraServer.scala create mode 100644 instrumentation/finatra-2.9/src/latestDepTest/scala/ResponseSettingExceptionMapper.scala diff --git a/instrumentation/finatra-2.9/finatra-2.9.gradle b/instrumentation/finatra-2.9/finatra-2.9.gradle index 1d60904ec8..ee78c9d9f5 100644 --- a/instrumentation/finatra-2.9/finatra-2.9.gradle +++ b/instrumentation/finatra-2.9/finatra-2.9.gradle @@ -6,6 +6,12 @@ ext { apply from: "$rootDir/gradle/instrumentation.gradle" apply from: "$rootDir/gradle/test-with-scala.gradle" +apply plugin: 'org.unbroken-dome.test-sets' + +testSets { + latestDepTest +} + muzzle { // There are some weird library issues below 2.9 so can't assert inverse pass { @@ -36,5 +42,10 @@ dependencies { // Required for older versions of finatra on JDKs >= 11 testImplementation group: 'com.sun.activation', name: 'javax.activation', version: '1.2.0' - latestDepTestLibrary group: 'com.twitter', name: 'finatra-http_2.11', version: '20.6.+' + // TODO latestDepTestLibrary doesn't work here + latestDepTestImplementation group: 'com.twitter', name: 'finatra-http_2.11', version: '+' +} + +compileLatestDepTestGroovy { + classpath += files(sourceSets.latestDepTest.scala.classesDirectory) } diff --git a/instrumentation/finatra-2.9/src/latestDepTest/groovy/FinatraServerLatestTest.groovy b/instrumentation/finatra-2.9/src/latestDepTest/groovy/FinatraServerLatestTest.groovy new file mode 100644 index 0000000000..0a68d29ab0 --- /dev/null +++ b/instrumentation/finatra-2.9/src/latestDepTest/groovy/FinatraServerLatestTest.groovy @@ -0,0 +1,108 @@ +/* + * Copyright The OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import static io.opentelemetry.auto.test.base.HttpServerTest.ServerEndpoint.SUCCESS +import static io.opentelemetry.trace.Span.Kind.INTERNAL + +import com.twitter.app.lifecycle.Event +import com.twitter.app.lifecycle.Observer +import com.twitter.finatra.http.HttpServer +import com.twitter.util.Await +import com.twitter.util.Closable +import com.twitter.util.Duration +import com.twitter.util.Promise +import io.opentelemetry.auto.test.asserts.TraceAssert +import io.opentelemetry.auto.test.base.HttpServerTest +import io.opentelemetry.sdk.trace.data.SpanData + +class FinatraServerLatestTest extends HttpServerTest { + private static final Duration TIMEOUT = Duration.fromSeconds(5) + private static final Duration STARTUP_TIMEOUT = Duration.fromSeconds(20) + + static closeAndWait(Closable closable) { + if (closable != null) { + Await.ready(closable.close(), TIMEOUT) + } + } + + @Override + HttpServer startServer(int port) { + HttpServer testServer = new FinatraServer() + + // Starting the server is blocking so start it in a separate thread + Thread startupThread = new Thread({ + testServer.main("-admin.port=:0", "-http.port=:" + port) + }) + startupThread.setDaemon(true) + startupThread.start() + + Promise startupPromise = new Promise<>() + + testServer.withObserver(new Observer() { + @Override + void onSuccess(Event event) { + if (event == testServer.startupCompletionEvent()) { + startupPromise.setValue(true) + } + } + + void onEntry(Event event) { + + } + + @Override + void onFailure(Event stage, Throwable throwable) { + if (stage != Event.Close$.MODULE$) { + startupPromise.setException(throwable) + } + } + }) + + Await.result(startupPromise, STARTUP_TIMEOUT) + + return testServer + } + + @Override + boolean hasHandlerSpan() { + return true + } + + @Override + boolean testNotFound() { + // Resource name is set to "GET /notFound" + false + } + + @Override + void stopServer(HttpServer httpServer) { + Await.ready(httpServer.close(), TIMEOUT) + } + + @Override + void handlerSpan(TraceAssert trace, int index, Object parent, String method = "GET", ServerEndpoint endpoint = SUCCESS) { + trace.span(index) { + operationName "FinatraController" + spanKind INTERNAL + childOf(parent as SpanData) + // Finatra doesn't propagate the stack trace or exception to the instrumentation + // so the normal errorAttributes() method can't be used + errored false + attributes { + } + } + } +} diff --git a/instrumentation/finatra-2.9/src/latestDepTest/scala/FinatraController.scala b/instrumentation/finatra-2.9/src/latestDepTest/scala/FinatraController.scala new file mode 100644 index 0000000000..b1b75ba6c3 --- /dev/null +++ b/instrumentation/finatra-2.9/src/latestDepTest/scala/FinatraController.scala @@ -0,0 +1,64 @@ +/* + * Copyright The OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import com.twitter.finagle.http.{Request, Response} +import com.twitter.finatra.http.Controller +import com.twitter.util.Future +import groovy.lang.Closure +import io.opentelemetry.auto.test.base.HttpServerTest.ServerEndpoint._ +import io.opentelemetry.auto.test.base.HttpServerTest.controller + +class FinatraController extends Controller { + any(SUCCESS.getPath) { request: Request => + controller(SUCCESS, new Closure[Response](null) { + override def call(): Response = { + response.ok(SUCCESS.getBody) + } + }) + } + + any(ERROR.getPath) { request: Request => + controller(ERROR, new Closure[Response](null) { + override def call(): Response = { + response.internalServerError(ERROR.getBody) + } + }) + } + + any(QUERY_PARAM.getPath) { request: Request => + controller(QUERY_PARAM, new Closure[Response](null) { + override def call(): Response = { + response.ok(QUERY_PARAM.getBody) + } + }) + } + + any(EXCEPTION.getPath) { request: Request => + controller(EXCEPTION, new Closure[Future[Response]](null) { + override def call(): Future[Response] = { + throw new Exception(EXCEPTION.getBody) + } + }) + } + + any(REDIRECT.getPath) { request: Request => + controller(REDIRECT, new Closure[Response](null) { + override def call(): Response = { + response.found.location(REDIRECT.getBody) + } + }) + } +} diff --git a/instrumentation/finatra-2.9/src/latestDepTest/scala/FinatraServer.scala b/instrumentation/finatra-2.9/src/latestDepTest/scala/FinatraServer.scala new file mode 100644 index 0000000000..94f5ec2ed7 --- /dev/null +++ b/instrumentation/finatra-2.9/src/latestDepTest/scala/FinatraServer.scala @@ -0,0 +1,29 @@ +/* + * Copyright The OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import com.twitter.finagle.http.Request +import com.twitter.finatra.http.HttpServer +import com.twitter.finatra.http.filters.ExceptionMappingFilter +import com.twitter.finatra.http.routing.HttpRouter + +class FinatraServer extends HttpServer { + override protected def configureHttp(router: HttpRouter): Unit = { + router + .filter[ExceptionMappingFilter[Request]] + .add[FinatraController] + .exceptionMapper[ResponseSettingExceptionMapper] + } +} diff --git a/instrumentation/finatra-2.9/src/latestDepTest/scala/ResponseSettingExceptionMapper.scala b/instrumentation/finatra-2.9/src/latestDepTest/scala/ResponseSettingExceptionMapper.scala new file mode 100644 index 0000000000..b14b262fc1 --- /dev/null +++ b/instrumentation/finatra-2.9/src/latestDepTest/scala/ResponseSettingExceptionMapper.scala @@ -0,0 +1,29 @@ +/* + * Copyright The OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import com.twitter.finagle.http.{Request, Response} +import com.twitter.finatra.http.exceptions.ExceptionMapper +import com.twitter.finatra.http.response.ResponseBuilder +import javax.inject.{Inject, Singleton} + +@Singleton +class ResponseSettingExceptionMapper @Inject()(response: ResponseBuilder) + extends ExceptionMapper[Exception] { + + override def toResponse(request: Request, exception: Exception): Response = { + response.internalServerError(exception.getMessage) + } +}