Skip to content

Commit c6049e3

Browse files
committed
Fix integration test
The com.sun.net.httpserver does not seem to be auto-instrumented by the Java Agent and @WithSpan was not producing a trace span for the handler. This commit changes the test app to use Apache HttpServer client which is auto-instrumented by the agent.
1 parent 00d4dab commit c6049e3

File tree

6 files changed

+172
-215
lines changed

6 files changed

+172
-215
lines changed

javaagent-extensions/gcp-auth/build.gradle

Lines changed: 10 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,10 @@ jar {
3434
enabled = false
3535
}
3636

37-
// For auto-instrumentation agent, used for running integration test
37+
// Custom configurations used to facilitate running the integration test
3838
configurations {
3939
agent
40-
includedDependencies
40+
includeDeps
4141
}
4242

4343
dependencies {
@@ -54,20 +54,12 @@ dependencies {
5454
implementation(libraries.google_auth)
5555

5656
// test dependencies
57-
// OTel instrumentation used in the sample app
58-
testImplementation platform(libraries.opentelemetry_instrumetation_bom)
59-
testImplementation('io.opentelemetry.instrumentation:opentelemetry-instrumentation-annotations')
60-
// logging libs - need to be included in the test app
61-
testImplementation libraries.slf4j
62-
testRuntimeOnly testLibraries.slf4j_simple
63-
includedDependencies libraries.slf4j
64-
includedDependencies testLibraries.slf4j_simple
65-
// testing library and tools
66-
testImplementation("org.testcontainers:testcontainers:1.20.4")
6757
testImplementation(testLibraries.junit5)
6858
testRuntimeOnly(testLibraries.junit5_runtime)
69-
59+
// OTel instrumentation used in the sample app to facilitate integration testing
7060
agent agentLibraries.agent
61+
testImplementation 'org.apache.httpcomponents:httpclient:4.5.14'
62+
includeDeps 'org.apache.httpcomponents:httpclient:4.5.14'
7163
}
7264

7365
// task to copy and rename the Java Auto-Instrumentation Agent into 'libs' folder
@@ -81,38 +73,23 @@ tasks.register('CopyAgent', Copy) {
8173
tasks.register('BuildTestApp', org.gradle.jvm.tasks.Jar) {
8274
dependsOn 'CopyAgent'
8375
dependsOn 'shadowJar'
84-
dependsOn 'BuildDummyBackend'
76+
8577
archiveFileName.set("auto-instrumented-test-server.jar")
86-
from(sourceSets.test.output)
87-
// include dependencies in the JAR which are required during runtime
88-
from {
89-
configurations.includedDependencies.collect {
90-
it.isDirectory() ? it : zipTree(it)
91-
}
78+
from(sourceSets.test.output) {
79+
include 'com/google/cloud/opentelemetry/extension/auth/testapp/**'
9280
}
93-
manifest {
94-
attributes.put('Main-Class', 'com.google.cloud.opentelemetry.extension.auth.testapp.InstrumentedServer')
95-
}
96-
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
97-
}
98-
99-
tasks.register('BuildDummyBackend', org.gradle.jvm.tasks.Jar) {
100-
archiveFileName.set("dummy-otlp-backend.jar")
101-
from(sourceSets.test.output)
102-
// include dependencies in the JAR which are required during runtime
10381
from {
104-
configurations.includedDependencies.collect {
82+
configurations.includeDeps.collect {
10583
it.isDirectory() ? it : zipTree(it)
10684
}
10785
}
10886
manifest {
109-
attributes.put('Main-Class', 'com.google.cloud.opentelemetry.extension.auth.testbackend.DummyOTelHttpEndpoint')
87+
attributes.put('Main-Class', 'com.google.cloud.opentelemetry.extension.auth.testapp.InstrumentedTestApp')
11088
}
11189
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
11290
}
11391

11492
test {
11593
dependsOn 'BuildTestApp'
116-
dependsOn 'BuildDummyBackend'
11794
useJUnitPlatform()
11895
}

javaagent-extensions/gcp-auth/src/test/java/com/google/cloud/opentelemetry/extension/auth/ExtensionIntegrationTest.java

Lines changed: 45 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -15,101 +15,65 @@
1515
*/
1616
package com.google.cloud.opentelemetry.extension.auth;
1717

18-
import static com.google.cloud.opentelemetry.extension.auth.testbackend.DummyOTelHttpEndpoint.HEADER_VERIFIED;
19-
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
2018
import static org.junit.jupiter.api.Assertions.assertEquals;
2119
import static org.junit.jupiter.api.Assertions.assertTrue;
2220

21+
import com.google.cloud.opentelemetry.extension.auth.testbackend.DummyOTelHttpEndpoint;
22+
import com.sun.net.httpserver.HttpServer;
23+
import java.io.BufferedReader;
2324
import java.io.File;
2425
import java.io.IOException;
25-
import java.net.URI;
26-
import java.net.http.HttpClient;
27-
import java.net.http.HttpRequest;
28-
import java.net.http.HttpResponse;
29-
import java.net.http.HttpResponse.BodyHandlers;
30-
import java.util.concurrent.atomic.AtomicReference;
31-
import org.junit.jupiter.api.AfterAll;
32-
import org.junit.jupiter.api.BeforeAll;
26+
import java.io.InputStreamReader;
27+
import java.lang.ProcessBuilder.Redirect;
3328
import org.junit.jupiter.api.Test;
34-
import org.slf4j.Logger;
35-
import org.slf4j.LoggerFactory;
36-
import org.testcontainers.containers.GenericContainer;
37-
import org.testcontainers.containers.output.Slf4jLogConsumer;
38-
import org.testcontainers.containers.wait.strategy.Wait;
39-
import org.testcontainers.utility.DockerImageName;
40-
import org.testcontainers.utility.MountableFile;
4129

4230
public class ExtensionIntegrationTest {
4331

44-
private static final String TEST_SERVER_PATH = "/doWork";
45-
private static GenericContainer<?> applicationContainer;
46-
private static Integer applicationPort;
47-
48-
private static final Logger logger = LoggerFactory.getLogger(ExtensionIntegrationTest.class);
49-
50-
@BeforeAll
51-
static void setup() {
32+
@Test
33+
public void smokeTest() throws IOException, InterruptedException {
5234
String testAppJarPath =
5335
new File("./build/libs/auto-instrumented-test-server.jar").getAbsolutePath();
54-
String dummyBackendJarPath = new File("./build/libs/dummy-otlp-backend.jar").getAbsolutePath();
36+
5537
String javaAgentJarPath = new File("./build/libs/otel-agent.jar").getAbsolutePath();
5638
String authExtensionJarPath = new File("./build/libs/gcp-auth-extension.jar").getAbsolutePath();
5739

58-
DockerImageName dockerBaseImage = DockerImageName.parse("openjdk:17-jdk-slim");
59-
int preferredApplicationPort = 8000;
60-
61-
String runJavaTestApp =
62-
"java -javaagent:/agent.jar -Dotel.javaagent.extensions=/auth-ext.jar -Dotel.java.global-autoconfigure.enabled=true -Dgoogle.cloud.project=dummy-test-project -Dotel.exporter.otlp.endpoint=http://localhost:4318/ -Dotel.traces.exporter=otlp,logging -Dotel.metrics.exporter=none -jar /test-app.jar "
63-
+ preferredApplicationPort;
64-
String runOtelBackend = "java -jar /test-backend.jar &";
65-
66-
applicationContainer =
67-
new GenericContainer<>(dockerBaseImage)
68-
.withExposedPorts(preferredApplicationPort)
69-
.withCopyFileToContainer(
70-
MountableFile.forHostPath(dummyBackendJarPath), "/test-backend.jar")
71-
.withCopyFileToContainer(MountableFile.forHostPath(testAppJarPath), "/test-app.jar")
72-
.withCopyFileToContainer(MountableFile.forHostPath(javaAgentJarPath), "/agent.jar")
73-
.withCopyFileToContainer(
74-
MountableFile.forHostPath(authExtensionJarPath), "/auth-ext.jar")
75-
.withCommand("sh", "-c", runOtelBackend + runJavaTestApp)
76-
.withLogConsumer(new Slf4jLogConsumer(logger))
77-
.waitingFor(Wait.forLogMessage(".*Waiting for requests.*", 1));
78-
applicationContainer.start();
79-
applicationPort = applicationContainer.getMappedPort(preferredApplicationPort);
80-
}
81-
82-
@AfterAll
83-
static void tearDown() {
84-
if (applicationContainer != null) {
85-
applicationContainer.stop();
40+
HttpServer backendServer;
41+
try {
42+
backendServer = DummyOTelHttpEndpoint.createTestServer();
43+
} catch (IOException e) {
44+
throw new RuntimeException(e);
8645
}
87-
}
88-
89-
@Test
90-
public void testSampleAppResponding() {
91-
AtomicReference<HttpResponse<String>> response = new AtomicReference<>();
92-
assertDoesNotThrow(() -> response.set(sendRequestToSampleApp()));
93-
assertEquals(200, response.get().statusCode());
94-
}
95-
96-
@Test
97-
public void testExtensionAttachesCorrectHeaders() throws IOException, InterruptedException {
98-
HttpResponse<String> response = sendRequestToSampleApp();
99-
assertEquals(200, response.statusCode());
100-
101-
applicationContainer.waitingFor(Wait.forLogMessage(".*Received trace data.*", 1));
102-
103-
String logs = applicationContainer.getLogs();
104-
assertTrue(logs.contains(HEADER_VERIFIED));
105-
}
106-
107-
private HttpResponse<String> sendRequestToSampleApp() throws IOException, InterruptedException {
108-
String url =
109-
"http://" + applicationContainer.getHost() + ":" + applicationPort + TEST_SERVER_PATH;
110-
HttpClient client = HttpClient.newHttpClient();
111-
HttpRequest request = HttpRequest.newBuilder().uri(URI.create(url)).build();
112-
113-
return client.send(request, BodyHandlers.ofString());
46+
backendServer.start();
47+
Process p =
48+
new ProcessBuilder(
49+
"java",
50+
"-javaagent:" + javaAgentJarPath,
51+
"-Dotel.javaagent.extensions=" + authExtensionJarPath,
52+
"-Dotel.java.global-autoconfigure.enabled=true",
53+
"-Dgoogle.cloud.project=dummy-test-project",
54+
"-Dotel.exporter.otlp.endpoint=http://localhost:4318",
55+
"-Dotel.exporter.otlp.insecure=true",
56+
"-Dotel.traces.exporter=otlp,logging",
57+
"-Dotel.metrics.exporter=none",
58+
"-Dotel.javaagent.debug=false",
59+
"-Dotel.exporter.otlp.protocol=http/protobuf",
60+
"-jar",
61+
testAppJarPath)
62+
.redirectError(
63+
Redirect.INHERIT) // Redirect stderr from this process to the current process
64+
.start();
65+
p.waitFor(); // wait for the process running the test app to finish
66+
67+
// flush logs from instrumented app process
68+
BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
69+
reader.lines().forEach(System.out::println);
70+
System.out.println("Instrumented app process finished");
71+
72+
backendServer.stop(0); // stop the mock HTTP server
73+
74+
// assert on number of requests
75+
assertEquals(1, DummyOTelHttpEndpoint.receivedRequests.size());
76+
// ensure headers were verified for all requests
77+
DummyOTelHttpEndpoint.receivedRequests.forEach((request, verified) -> assertTrue(verified));
11478
}
11579
}

javaagent-extensions/gcp-auth/src/test/java/com/google/cloud/opentelemetry/extension/auth/testapp/InstrumentedServer.java

Lines changed: 0 additions & 58 deletions
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/*
2+
* Copyright 2024 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package com.google.cloud.opentelemetry.extension.auth.testapp;
17+
18+
import com.sun.net.httpserver.HttpServer;
19+
import java.io.IOException;
20+
import java.io.OutputStream;
21+
import java.net.InetSocketAddress;
22+
import org.apache.http.HttpEntity;
23+
import org.apache.http.client.methods.CloseableHttpResponse;
24+
import org.apache.http.client.methods.HttpGet;
25+
import org.apache.http.impl.client.CloseableHttpClient;
26+
import org.apache.http.impl.client.HttpClients;
27+
import org.apache.http.util.EntityUtils;
28+
29+
public class InstrumentedTestApp {
30+
private static final String serverUrl = "http://localhost:%d/%s";
31+
private static final int defaultPort = 8080;
32+
33+
public static void main(String[] args) throws IOException, InterruptedException {
34+
int port = parsePort(args);
35+
36+
HttpServer server = HttpServer.create(new InetSocketAddress(port), 0);
37+
server.createContext("/doWork", new TestHandler());
38+
server.createContext(
39+
"/stop",
40+
exchange -> {
41+
String response = "Stopping Server";
42+
System.out.println(response);
43+
exchange.sendResponseHeaders(200, response.length());
44+
OutputStream os = exchange.getResponseBody();
45+
os.write(response.getBytes());
46+
os.close();
47+
server.stop(0);
48+
});
49+
server.setExecutor(null); // creates a default executor
50+
server.start();
51+
System.out.println("Server ready");
52+
System.out.println("Sending request to do work ...");
53+
makeCall(String.format(serverUrl, port, "doWork"));
54+
Thread.sleep(1000);
55+
System.out.println("Sending request to stop server ...");
56+
makeCall(String.format(serverUrl, port, "stop"));
57+
}
58+
59+
// Make calls to the server using Apache HttpClient
60+
private static void makeCall(String url) {
61+
try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
62+
HttpGet request = new HttpGet(url);
63+
try (CloseableHttpResponse response = httpClient.execute(request)) {
64+
HttpEntity entity = response.getEntity();
65+
if (entity != null) {
66+
String responseBody = EntityUtils.toString(entity);
67+
System.out.println("Response Body: " + responseBody);
68+
EntityUtils.consume(entity);
69+
}
70+
}
71+
} catch (IOException e) {
72+
System.err.println("Error making request: " + e.getMessage());
73+
}
74+
}
75+
76+
private static int parsePort(String[] args) {
77+
int port;
78+
if (args.length > 0) {
79+
try {
80+
port = Integer.parseInt(args[0]);
81+
if (port < 0 || port > 65535) {
82+
throw new NumberFormatException("Port number must be between 0 and 65535");
83+
}
84+
return port;
85+
} catch (NumberFormatException e) {
86+
System.err.println("Invalid port number provided: " + args[0]);
87+
System.err.println("Using default port: " + defaultPort);
88+
}
89+
}
90+
return defaultPort;
91+
}
92+
}

javaagent-extensions/gcp-auth/src/test/java/com/google/cloud/opentelemetry/extension/auth/testapp/TestHandler.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717

1818
import com.sun.net.httpserver.HttpExchange;
1919
import com.sun.net.httpserver.HttpHandler;
20-
import io.opentelemetry.instrumentation.annotations.WithSpan;
2120
import java.io.IOException;
2221
import java.io.OutputStream;
2322
import java.util.Random;
@@ -26,9 +25,9 @@ public class TestHandler implements HttpHandler {
2625
private final Random random = new Random();
2726

2827
@Override
29-
@WithSpan
3028
public void handle(HttpExchange exchange) throws IOException {
3129
int n = random.nextInt(100);
30+
System.out.println("handling");
3231
String response = n % 2 == 0 ? "Send Response A" : "Send Response B";
3332
try {
3433
Thread.sleep(1000);

0 commit comments

Comments
 (0)