Skip to content

Commit

Permalink
Jaxrs tck tests (#108)
Browse files Browse the repository at this point in the history
* Add jaxrs test
Co-authored-by: Andrew Rouse <anrouse@uk.ibm.com>
  • Loading branch information
yasmin-aumeeruddy committed Aug 4, 2023
1 parent f05fff2 commit 2a2bee2
Show file tree
Hide file tree
Showing 11 changed files with 948 additions and 6 deletions.
16 changes: 15 additions & 1 deletion tracing/tck/README.adoc
Expand Up @@ -50,7 +50,19 @@ To enable the tests in your project you need to add the following dependency to
----

== Declaring the Tests to run
There are optional tests for the `B3` and `Jaeger` progagation formats. These tests are present in the `optional-tests` group but a MicroProfile-Telemetry implementation is not required to pass these tests in order to be considered a valid implementation. If you wish to disable them, you can exclude the group e.g. create a file `tck-suite.xml` in your project which contains the following content:
There are multiple groups of optional tests. These include:

* `optional-tests`: `B3` and `Jaeger` progagation formats.
+
These tests test the B3 and Jaeger propagation formats which are not required. If your implementation does not include support for these propagation formats, you should exclude the `optional-tests` group.

* `optional-jaxrs-tests` JAX-RS server async programming models
+
Although support for JAX-RS server async programming models is not optional, these tests depend on Jakarta Concurrency because they use `ManagedExecutorService`.
+
If you are testing in an environment which does not provide Jakarta Concurrency, you should exclude the `optional-jaxrs-tests` group.

Test groups can be excluded in the TestNG XML file. E.g. create a file `tck-suite.xml` in your project which contains the following content:

[source, xml]
----
Expand All @@ -62,6 +74,8 @@ There are optional tests for the `B3` and `Jaeger` progagation formats. These te
<run>
<!-- Exclude B3 and Jaeger propagation tests-->
<exclude name="optional-tests"/>
<!-- Exclude JaxRS server async tests-->
<exclude name="optional-jaxrs-tests"/>
</run>
</groups>
<packages>
Expand Down
11 changes: 11 additions & 0 deletions tracing/tck/pom.xml
Expand Up @@ -58,6 +58,11 @@
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-sdk-extension-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>jakarta.enterprise.concurrent</groupId>
<artifactId>jakarta.enterprise.concurrent-api</artifactId>
<version>3.0.2</version>
</dependency>
<dependency>
<groupId>io.opentelemetry.instrumentation</groupId>
<artifactId>opentelemetry-instrumentation-annotations</artifactId>
Expand Down Expand Up @@ -91,6 +96,12 @@
<artifactId>awaitility</artifactId>
<version>${version.awaitility}</version>
</dependency>
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>6.0.0</version>
<scope>provided</scope>
</dependency>
</dependencies>

<build>
Expand Down
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2022 Contributors to the Eclipse Foundation
* Copyright (c) 2022-2023 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
Expand All @@ -19,6 +19,8 @@
*/
package org.eclipse.microprofile.telemetry.tracing.tck;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URISyntaxException;
Expand Down Expand Up @@ -65,13 +67,45 @@ public int get(String path) {
try {
return connection.getResponseCode();
} catch (Exception e) {
throw new RuntimeException("Exception retriving " + spanUrl, e);
throw new RuntimeException("Exception retrieving " + spanUrl, e);
} finally {
connection.disconnect();
}
} catch (Exception e) {
throw new RuntimeException("Exception retriving path " + path, e);
throw new RuntimeException("Exception retrieving path " + path, e);
}
}

}
/**
* Makes a GET request to a path and returns the response code
*
* @param path
* the path to request, relative to the baseUrl
* @return the response message
*/
public String getResponseMessage(String path) {
if (path.startsWith("/")) {
path = path.substring(1);
}
try {
URL spanUrl = baseUri.resolve(path).toURL();
HttpURLConnection connection = (HttpURLConnection) spanUrl.openConnection();
try (BufferedReader in = new BufferedReader(new InputStreamReader(connection.getInputStream()))) {
String inputLine;
StringBuilder response = new StringBuilder();

while ((inputLine = in.readLine()) != null) {
response.append(inputLine);
}
return response.toString();
} catch (Exception e) {
throw new RuntimeException("Exception retrieving " + spanUrl, e);
} finally {
connection.disconnect();
}
} catch (Exception e) {
throw new RuntimeException("Exception retrieving path " + path, e);
}
}

}
@@ -0,0 +1,142 @@
/*
* Copyright (c) 2023 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* 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.
*
*/

package org.eclipse.microprofile.telemetry.tracing.tck.async;

import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.HTTP_METHOD;
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.HTTP_SCHEME;
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.HTTP_STATUS_CODE;
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.HTTP_TARGET;
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.HTTP_URL;
import static java.net.HttpURLConnection.HTTP_OK;

import java.net.URL;
import java.util.List;

import org.eclipse.microprofile.telemetry.tracing.tck.BasicHttpClient;
import org.eclipse.microprofile.telemetry.tracing.tck.ConfigAsset;
import org.eclipse.microprofile.telemetry.tracing.tck.TestLibraries;
import org.eclipse.microprofile.telemetry.tracing.tck.exporter.InMemorySpanExporter;
import org.eclipse.microprofile.telemetry.tracing.tck.exporter.InMemorySpanExporterProvider;
import org.jboss.arquillian.container.test.api.Deployment;
import org.jboss.arquillian.test.api.ArquillianResource;
import org.jboss.arquillian.testng.Arquillian;
import org.jboss.shrinkwrap.api.ShrinkWrap;
import org.jboss.shrinkwrap.api.asset.EmptyAsset;
import org.jboss.shrinkwrap.api.spec.WebArchive;
import org.testng.Assert;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;

import io.opentelemetry.api.trace.SpanKind;
import io.opentelemetry.sdk.autoconfigure.spi.traces.ConfigurableSpanExporterProvider;
import io.opentelemetry.sdk.trace.data.SpanData;
import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.HttpMethod;

public class JaxRsClientAsyncTest extends Arquillian {

@Deployment
public static WebArchive createDeployment() {

ConfigAsset config = new ConfigAsset()
.add("otel.bsp.schedule.delay", "100")
.add("otel.sdk.disabled", "false")
.add("otel.traces.exporter", "in-memory");

return ShrinkWrap.create(WebArchive.class)
.addClasses(InMemorySpanExporter.class, InMemorySpanExporterProvider.class, BasicHttpClient.class,
JaxRsClientAsyncTestEndpoint.class)
.addAsLibrary(TestLibraries.AWAITILITY_LIB)
.addAsServiceProvider(ConfigurableSpanExporterProvider.class, InMemorySpanExporterProvider.class)
.addAsResource(config, "META-INF/microprofile-config.properties")
.addAsManifestResource(EmptyAsset.INSTANCE, "beans.xml");
}

private BasicHttpClient basicClient;

@ArquillianResource
URL url;

public static final String TEST_PASSED = "Test Passed";

@Inject
private InMemorySpanExporter spanExporter;

@BeforeMethod
void setUp() {
// Only want to run on server
if (spanExporter != null) {
spanExporter.reset();
basicClient = new BasicHttpClient(url);
}
}

@Test
public void testIntegrationWithJaxRsClient() throws Exception {
basicClient.get("/JaxRsClientAsyncTestEndpoint/jaxrsclient");
readSpans();
}

@Test
public void testIntegrationWithJaxRsClientAsync() throws Exception {
basicClient.get("/JaxRsClientAsyncTestEndpoint/jaxrsclientasync");
readSpans();
}

public void readSpans() {

List<SpanData> spanData = spanExporter.getFinishedSpanItems(3);

List<SpanData> serverSpans = spanExporter.getSpansWithKind(SpanKind.SERVER);

SpanData firstURL = null;
SpanData secondURL = null;
for (SpanData span : serverSpans) {
if (span.getAttributes().get(HTTP_TARGET).contains("JaxRsClientAsyncTestEndpoint/jaxrsclient")) {
firstURL = span;
} else {
secondURL = span;
}
}

Assert.assertNotNull(firstURL);
Assert.assertNotNull(secondURL);

SpanData httpGet = spanExporter.getFirst(SpanKind.CLIENT);

// Assert correct parent-child links
// Shows that propagation occurred
Assert.assertEquals(httpGet.getSpanId(), secondURL.getParentSpanId());
Assert.assertEquals(firstURL.getSpanId(), httpGet.getParentSpanId());

Assert.assertEquals(HTTP_OK, firstURL.getAttributes().get(HTTP_STATUS_CODE).intValue());
Assert.assertEquals(HttpMethod.GET, firstURL.getAttributes().get(HTTP_METHOD));
Assert.assertEquals("http", firstURL.getAttributes().get(HTTP_SCHEME));

// There are many different URLs that will end up here. But all should contain "JaxRsClientAsyncTestEndpoint"
Assert.assertTrue(httpGet.getAttributes().get(HTTP_URL).contains("JaxRsClientAsyncTestEndpoint"));

Assert.assertEquals(HTTP_OK, httpGet.getAttributes().get(HTTP_STATUS_CODE).intValue());
Assert.assertEquals(HttpMethod.GET, httpGet.getAttributes().get(HTTP_METHOD));
Assert.assertTrue(httpGet.getAttributes().get(HTTP_URL).contains("JaxRsClientAsyncTestEndpoint"));
}
}
@@ -0,0 +1,135 @@
/*
* Copyright (c) 2023 Contributors to the Eclipse Foundation
*
* See the NOTICE file(s) distributed with this work for additional
* information regarding copyright ownership.
*
* 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.
*
*/
package org.eclipse.microprofile.telemetry.tracing.tck.async;

import static java.util.concurrent.TimeUnit.SECONDS;

import java.util.concurrent.Future;

import org.eclipse.microprofile.telemetry.tracing.tck.exporter.InMemorySpanExporter;
import org.testng.Assert;

import io.opentelemetry.api.baggage.Baggage;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Scope;
import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import jakarta.inject.Inject;
import jakarta.ws.rs.ApplicationPath;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.client.Client;
import jakarta.ws.rs.client.ClientBuilder;
import jakarta.ws.rs.core.Application;
import jakarta.ws.rs.core.Context;
import jakarta.ws.rs.core.MediaType;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.core.UriInfo;

@ApplicationPath("/")
@Path("JaxRsClientAsyncTestEndpoint")
public class JaxRsClientAsyncTestEndpoint extends Application {

public static final String TEST_PASSED = "Test Passed";

@Inject
private InMemorySpanExporter spanExporter;

private Client client;

@PostConstruct
private void openClient() {
client = ClientBuilder.newClient();
}

@PreDestroy
private void closeClient() {
if (client != null) {
client.close();
client = null;
}
}

@GET
@Path("/jaxrsclient")
public Response getJax(@Context UriInfo uriInfo) {
Assert.assertNotNull(Span.current());

try (Scope s = Baggage.builder().put("foo", "bar").build().makeCurrent()) {
Baggage baggage = Baggage.current();
Assert.assertEquals("bar", baggage.getEntryValue("foo"));

String url = new String(uriInfo.getAbsolutePath().toString());
url = url.replace("jaxrsclient", "jaxrstwo"); // The jaxrsclient will use the URL as given so it needs
// the final part to be provided.

String result = client.target(url)
.request(MediaType.TEXT_PLAIN)
.get(String.class);
Assert.assertEquals(TEST_PASSED, result);
} finally {
client.close();
}
return Response.ok(Span.current().getSpanContext().getTraceId()).build();
}

@GET
@Path("/jaxrsclientasync")
public Response getJaxAsync(@Context UriInfo uriInfo) {
Assert.assertNotNull(Span.current());

try (Scope s = Baggage.builder().put("foo", "bar").build().makeCurrent()) {
Baggage baggage = Baggage.current();
Assert.assertEquals("bar", baggage.getEntryValue("foo"));

String url = new String(uriInfo.getAbsolutePath().toString());
url = url.replace("jaxrsclientasync", "jaxrstwo"); // The jaxrsclient will use the URL as given so it needs
// the final part to be provided.

Client client = ClientBuilder.newClient();
Future<String> result = client.target(url)
.request(MediaType.TEXT_PLAIN)
.async()
.get(String.class);
try {
String resultValue = result.get(10, SECONDS);
Assert.assertEquals(TEST_PASSED, resultValue);
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
client.close();
}
}
return Response.ok(Span.current().getSpanContext().getTraceId()).build();
}

// A method to be called by JAX Clients
// This method triggers span creation and span propagation is automatic.
@GET
@Path("/jaxrstwo")
public Response getJaxRsTwo() {
Assert.assertNotNull(Span.current());
Baggage baggage = Baggage.current();
// Assert that Baggage is propagated from Jax Server to Jax Client
Assert.assertEquals("bar", baggage.getEntryValue("foo"));
return Response.ok(TEST_PASSED).build();
}

}

0 comments on commit 2a2bee2

Please sign in to comment.