Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -60,10 +60,9 @@ dependencies {
testLibrary("org.springframework.boot:spring-boot-starter-test:2.0.0.RELEASE")
testLibrary("org.springframework.boot:spring-boot-starter-reactor-netty:2.0.0.RELEASE")

// tests don't work with spring boot 4 yet
latestDepTestLibrary("org.springframework.boot:spring-boot-starter-webflux:3.+") // documented limitation
latestDepTestLibrary("org.springframework.boot:spring-boot-starter-test:3.+") // documented limitation
latestDepTestLibrary("org.springframework.boot:spring-boot-starter-reactor-netty:3.+") // documented limitation
latestDepTestLibrary("org.springframework.boot:spring-boot-starter-webflux:3.+") // see testing-webflux7 module
latestDepTestLibrary("org.springframework.boot:spring-boot-starter-test:3.+") // see testing-webflux7 module
latestDepTestLibrary("org.springframework.boot:spring-boot-starter-reactor-netty:3.+") // see testing-webflux7 module
}

val latestDepTest = findProperty("testLatestDeps") as Boolean
Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

package io.opentelemetry.javaagent.instrumentation.spring.webflux.v5_0.server.base;

import io.opentelemetry.instrumentation.spring.webflux.server.AbstractControllerSpringWebFluxServerTest;
import io.opentelemetry.instrumentation.spring.webflux.server.ServerTestController;
import io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint;
import java.time.Duration;
import java.util.function.Supplier;
Expand All @@ -20,7 +22,7 @@
* within a Mono map step, which follows a delay step. For exception endpoint, the exception is
* thrown within the last map step.
*/
class DelayedControllerSpringWebFluxServerTest extends ControllerSpringWebFluxServerTest {
class DelayedControllerSpringWebFluxServerTest extends AbstractControllerSpringWebFluxServerTest {
@Override
protected Class<?> getApplicationClass() {
return Application.class;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

package io.opentelemetry.javaagent.instrumentation.spring.webflux.v5_0.server.base;

import io.opentelemetry.instrumentation.spring.webflux.server.AbstractHandlerSpringWebFluxServerTest;
import io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint;
import java.time.Duration;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
Expand All @@ -20,7 +21,7 @@
* map step, which follows a delay step. For exception endpoint, the exception is thrown within the
* last map step.
*/
class DelayedHandlerSpringWebFluxServerTest extends HandlerSpringWebFluxServerTest {
class DelayedHandlerSpringWebFluxServerTest extends AbstractHandlerSpringWebFluxServerTest {
@Override
protected Class<?> getApplicationClass() {
return Application.class;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

package io.opentelemetry.javaagent.instrumentation.spring.webflux.v5_0.server.base;

import io.opentelemetry.instrumentation.spring.webflux.server.AbstractControllerSpringWebFluxServerTest;
import io.opentelemetry.instrumentation.spring.webflux.server.ServerTestController;
import io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint;
import java.util.function.Supplier;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
Expand All @@ -20,7 +22,7 @@
* <p>{@code Mono<String>} from a handler is already a fully constructed response with no deferred
* actions. For exception endpoint, the exception is thrown within controller method scope.
*/
class ImmediateControllerSpringWebFluxServerTest extends ControllerSpringWebFluxServerTest {
class ImmediateControllerSpringWebFluxServerTest extends AbstractControllerSpringWebFluxServerTest {
@Override
protected Class<?> getApplicationClass() {
return Application.class;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,8 @@

package io.opentelemetry.javaagent.instrumentation.spring.webflux.v5_0.server.base;

import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assumptions.assumeTrue;

import io.opentelemetry.instrumentation.spring.webflux.server.AbstractImmediateHandlerSpringWebFluxServerTest;
import io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint;
import io.opentelemetry.testing.internal.armeria.common.AggregatedHttpRequest;
import io.opentelemetry.testing.internal.armeria.common.AggregatedHttpResponse;
import org.junit.jupiter.api.Test;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.web.embedded.netty.NettyReactiveWebServerFactory;
import org.springframework.context.annotation.Bean;
Expand All @@ -20,14 +15,8 @@
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;

/**
* Tests the case where "controller" span is created within the route handler method scope, and the
*
* <p>{@code Mono<ServerResponse>} from a handler is already a fully constructed response with no
* deferred actions. For exception endpoint, the exception is thrown within route handler method
* scope.
*/
class ImmediateHandlerSpringWebFluxServerTest extends HandlerSpringWebFluxServerTest {
class ImmediateHandlerSpringWebFluxServerTest
extends AbstractImmediateHandlerSpringWebFluxServerTest {
@Override
protected Class<?> getApplicationClass() {
return Application.class;
Expand Down Expand Up @@ -60,18 +49,4 @@ protected Mono<ServerResponse> wrapResponse(
});
}
}

@Test
void nestedPath() {
assumeTrue(Boolean.getBoolean("testLatestDeps"));

String method = "GET";
AggregatedHttpRequest request = request(NESTED_PATH, method);
AggregatedHttpResponse response = client.execute(request).aggregate().join();
assertThat(response.status().code()).isEqualTo(NESTED_PATH.getStatus());
assertThat(response.contentUtf8()).isEqualTo(NESTED_PATH.getBody());
assertResponseHasCustomizedHeaders(response, NESTED_PATH, null);

assertTheTraces(1, null, null, null, method, NESTED_PATH);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

package io.opentelemetry.javaagent.instrumentation.spring.webflux.v5_0.server.base;

import static io.opentelemetry.javaagent.instrumentation.spring.webflux.v5_0.server.base.SpringWebFluxServerTest.NESTED_PATH;
import static io.opentelemetry.instrumentation.spring.webflux.server.AbstractSpringWebFluxServerTest.NESTED_PATH;
import static org.springframework.web.reactive.function.server.RequestPredicates.GET;
import static org.springframework.web.reactive.function.server.RequestPredicates.path;
import static org.springframework.web.reactive.function.server.RouterFunctions.nest;
Expand Down Expand Up @@ -106,12 +106,7 @@ public RouterFunction<ServerResponse> createRoutes() {
path("/nestedPath"),
nest(
path("/hello"),
route(
path("/world"),
request -> {
ServerEndpoint endpoint = NESTED_PATH;
return respond(endpoint, null, null, null);
})));
route(path("/world"), request -> respond(NESTED_PATH, null, null, null))));
}

protected Mono<ServerResponse> respond(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
plugins {
id("otel.java-conventions")
id("otel.javaagent-testing")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

javaagent-testing already depends on java-conventions

}

dependencies {
testInstrumentation(project(":instrumentation:spring:spring-core-2.0:javaagent"))
testInstrumentation(project(":instrumentation:spring:spring-webflux:spring-webflux-5.0:javaagent"))
testInstrumentation(project(":instrumentation:netty:netty-4.1:javaagent"))
testInstrumentation(project(":instrumentation:reactor:reactor-3.1:javaagent"))
testInstrumentation(project(":instrumentation:reactor:reactor-netty:reactor-netty-1.0:javaagent"))

testImplementation(project(":instrumentation:spring:spring-webflux:spring-webflux-5.0:testing"))
testImplementation(project(":instrumentation:spring:spring-webflux:spring-webflux-5.3:testing"))

testImplementation("org.springframework.boot:spring-boot-starter-webflux:4.0.0")
testImplementation("org.springframework:spring-web:7.0.0")
testImplementation("org.springframework.boot:spring-boot-starter-test:4.0.0")
testImplementation("org.springframework.boot:spring-boot-starter-reactor-netty:4.0.0")
}

otelJava {
minJavaVersionSupported.set(JavaVersion.VERSION_17)
}

tasks.withType<Test>().configureEach {
// required on jdk17
jvmArgs("--add-opens=java.base/java.lang=ALL-UNNAMED")
jvmArgs("-XX:+IgnoreUnrecognizedVMOptions")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

doesn't seem to be needed

jvmArgs("-Dotel.instrumentation.common.experimental.controller-telemetry.enabled=true")
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.javaagent.instrumentation.spring.webflux.v7_0.client;

import io.opentelemetry.instrumentation.spring.webflux.client.AbstractSpringWebfluxClientInstrumentationTest;
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
import io.opentelemetry.instrumentation.testing.junit.http.HttpClientInstrumentationExtension;
import io.opentelemetry.instrumentation.testing.junit.http.HttpClientTestOptions;
import org.junit.jupiter.api.condition.OS;
import org.junit.jupiter.api.extension.RegisterExtension;
import org.springframework.web.reactive.function.client.WebClient;

class SpringWebfluxClientInstrumentationTest
extends AbstractSpringWebfluxClientInstrumentationTest {

@RegisterExtension
static final InstrumentationExtension testing = HttpClientInstrumentationExtension.forAgent();

@Override
protected WebClient.Builder instrument(WebClient.Builder builder) {
return builder;
}

@Override
protected void configure(HttpClientTestOptions.Builder optionsBuilder) {
super.configure(optionsBuilder);

// Disable remote connection tests on Windows due to reactor-netty creating extra spans
if (OS.WINDOWS.isCurrentOs()) {
optionsBuilder.setTestRemoteConnection(false);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.javaagent.instrumentation.spring.webflux.v7_0.server;

import org.springframework.boot.reactor.netty.NettyReactiveWebServerFactory;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Bean;
import reactor.netty.resources.LoopResources;
import server.SpringWebFluxTestApplication;

/**
* Run all Webflux tests under netty event loop having only 1 thread. Some of the bugs are better
* visible in this setup because same thread is reused for different requests.
*/
@SpringBootTest(
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
classes = {
SpringWebFluxTestApplication.class,
SpringThreadedSpringWebfluxTest.ForceSingleThreadedNettyAutoConfiguration.class
})
class SpringThreadedSpringWebfluxTest extends SpringWebfluxTest {

@TestConfiguration
static class ForceSingleThreadedNettyAutoConfiguration {
@Bean
NettyReactiveWebServerFactory nettyFactory() {
NettyReactiveWebServerFactory factory = new NettyReactiveWebServerFactory();
// Configure single-threaded event loop for Spring Boot 4
factory.addServerCustomizers(
server -> server.runOn(LoopResources.create("my-http", 1, true)));
return factory;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.javaagent.instrumentation.spring.webflux.v7_0.server;

import io.opentelemetry.instrumentation.spring.webflux.server.AbstractSpringWebfluxTest;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.boot.reactor.netty.NettyReactiveWebServerFactory;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import server.SpringWebFluxTestApplication;

@ExtendWith(SpringExtension.class)
@SpringBootTest(
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
classes = {
SpringWebFluxTestApplication.class,
SpringWebfluxTest.ForceNettyAutoConfiguration.class
})
class SpringWebfluxTest extends AbstractSpringWebfluxTest {
@TestConfiguration
static class ForceNettyAutoConfiguration {
@Bean
NettyReactiveWebServerFactory nettyFactory() {
return new NettyReactiveWebServerFactory();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.javaagent.instrumentation.spring.webflux.v7_0.server.base;

import io.opentelemetry.instrumentation.spring.webflux.server.AbstractControllerSpringWebFluxServerTest;
import io.opentelemetry.instrumentation.spring.webflux.server.ServerTestController;
import io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint;
import java.time.Duration;
import java.util.function.Supplier;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.reactor.netty.NettyReactiveWebServerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatusCode;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Mono;

/**
* Tests the case which uses annotated controller methods, and where "controller" span is created
* within a Mono map step, which follows a delay step. For exception endpoint, the exception is
* thrown within the last map step.
*/
class DelayedControllerSpringWebFluxServerTest extends AbstractControllerSpringWebFluxServerTest {
@Override
protected Class<?> getApplicationClass() {
return Application.class;
}

@Configuration
@EnableAutoConfiguration
static class Application {
@Bean
Controller controller() {
return new Controller();
}

@Bean
NettyReactiveWebServerFactory nettyFactory() {
return new NettyReactiveWebServerFactory();
}
}

@RestController
static class Controller extends ServerTestController {
@Override
protected <T> Mono<T> wrapControllerMethod(ServerEndpoint endpoint, Supplier<T> handler) {
return Mono.just("")
.delayElement(Duration.ofMillis(10))
.map(unused -> controller(endpoint, handler::get));
}

@Override
protected void setStatus(ServerHttpResponse response, ServerEndpoint endpoint) {
response.setStatusCode(HttpStatusCode.valueOf(endpoint.getStatus()));
}
}
}
Loading
Loading