Преглед на файлове

spring mvc tests to java (#11114)

Co-authored-by: Lauri Tulmin <ltulmin@splunk.com>
Andrei Chugunov преди 10 месеца
родител
ревизия
deac3971d9
променени са 33 файла, в които са добавени 1398 реда и са изтрити 1162 реда
  1. 0 61
      instrumentation/spring/spring-webmvc/spring-webmvc-3.1/javaagent/src/test/groovy/test/boot/SecurityConfig.groovy
  2. 0 20
      instrumentation/spring/spring-webmvc/spring-webmvc-3.1/javaagent/src/test/groovy/test/boot/SpringBootBasedTest.groovy
  3. 0 90
      instrumentation/spring/spring-webmvc/spring-webmvc-3.1/javaagent/src/test/groovy/test/filter/ServletFilterConfig.groovy
  4. 0 25
      instrumentation/spring/spring-webmvc/spring-webmvc-3.1/javaagent/src/test/groovy/test/filter/ServletFilterTest.groovy
  5. 67 0
      instrumentation/spring/spring-webmvc/spring-webmvc-3.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/webmvc/v3_1/boot/SecurityConfig.java
  6. 62 0
      instrumentation/spring/spring-webmvc/spring-webmvc-3.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/webmvc/v3_1/boot/SpringBootBasedTest.java
  7. 85 0
      instrumentation/spring/spring-webmvc/spring-webmvc-3.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/webmvc/v3_1/filter/ServletFilterConfig.java
  8. 50 0
      instrumentation/spring/spring-webmvc/spring-webmvc-3.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/webmvc/v3_1/filter/ServletFilterTest.java
  9. 0 57
      instrumentation/spring/spring-webmvc/spring-webmvc-6.0/javaagent/src/test/groovy/SecurityConfig.groovy
  10. 0 88
      instrumentation/spring/spring-webmvc/spring-webmvc-6.0/javaagent/src/test/groovy/ServletFilterConfig.groovy
  11. 0 57
      instrumentation/spring/spring-webmvc/spring-webmvc-6.0/javaagent/src/test/groovy/ServletFilterTest.groovy
  12. 0 39
      instrumentation/spring/spring-webmvc/spring-webmvc-6.0/javaagent/src/test/groovy/SpringBootBasedTest.groovy
  13. 57 0
      instrumentation/spring/spring-webmvc/spring-webmvc-6.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/webmvc/v6_0/boot/SecurityConfig.java
  14. 110 0
      instrumentation/spring/spring-webmvc/spring-webmvc-6.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/webmvc/v6_0/boot/SpringBootBasedTest.java
  15. 85 0
      instrumentation/spring/spring-webmvc/spring-webmvc-6.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/webmvc/v6_0/filter/ServletFilterConfig.java
  16. 101 0
      instrumentation/spring/spring-webmvc/spring-webmvc-6.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/webmvc/v6_0/filter/ServletFilterTest.java
  17. 0 2
      instrumentation/spring/spring-webmvc/spring-webmvc-common/testing/build.gradle.kts
  18. 0 212
      instrumentation/spring/spring-webmvc/spring-webmvc-common/testing/src/main/groovy/boot/AbstractSpringBootBasedTest.groovy
  19. 0 75
      instrumentation/spring/spring-webmvc/spring-webmvc-common/testing/src/main/groovy/boot/SavingAuthenticationProvider.groovy
  20. 0 108
      instrumentation/spring/spring-webmvc/spring-webmvc-common/testing/src/main/groovy/boot/TestController.groovy
  21. 0 111
      instrumentation/spring/spring-webmvc/spring-webmvc-common/testing/src/main/groovy/filter/AbstractServletFilterTest.groovy
  22. 0 132
      instrumentation/spring/spring-webmvc/spring-webmvc-common/testing/src/main/groovy/filter/FilteredAppConfig.groovy
  23. 0 76
      instrumentation/spring/spring-webmvc/spring-webmvc-common/testing/src/main/groovy/filter/TestController.groovy
  24. 229 0
      instrumentation/spring/spring-webmvc/spring-webmvc-common/testing/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/boot/AbstractSpringBootBasedTest.java
  25. 3 5
      instrumentation/spring/spring-webmvc/spring-webmvc-common/testing/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/boot/AppConfig.java
  26. 34 0
      instrumentation/spring/spring-webmvc/spring-webmvc-common/testing/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/boot/SavingAuthenticationProvider.java
  27. 108 0
      instrumentation/spring/spring-webmvc/spring-webmvc-common/testing/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/boot/TestController.java
  28. 58 0
      instrumentation/spring/spring-webmvc/spring-webmvc-common/testing/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/boot/TestUserDetails.java
  29. 116 0
      instrumentation/spring/spring-webmvc/spring-webmvc-common/testing/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/filter/AbstractServletFilterTest.java
  30. 135 0
      instrumentation/spring/spring-webmvc/spring-webmvc-common/testing/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/filter/FilteredAppConfig.java
  31. 74 0
      instrumentation/spring/spring-webmvc/spring-webmvc-common/testing/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/filter/TestController.java
  32. 17 4
      testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/http/AbstractHttpServerTest.java
  33. 7 0
      testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/http/HttpServerTestOptions.java

+ 0 - 61
instrumentation/spring/spring-webmvc/spring-webmvc-3.1/javaagent/src/test/groovy/test/boot/SecurityConfig.groovy

@@ -1,61 +0,0 @@
-/*
- * Copyright The OpenTelemetry Authors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package test.boot
-
-import boot.SavingAuthenticationProvider
-import org.springframework.context.annotation.Bean
-import org.springframework.context.annotation.Configuration
-import org.springframework.core.annotation.Order
-import org.springframework.security.config.annotation.web.builders.HttpSecurity
-import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
-import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter
-
-@Configuration
-@EnableWebSecurity
-class SecurityConfig {
-
-  @Bean
-  SavingAuthenticationProvider savingAuthenticationProvider() {
-    return new SavingAuthenticationProvider()
-  }
-
-  /**
-   * Following configuration is required for unauthorised call tests (form would redirect, we need 401)
-   */
-  @Configuration
-  @Order(1)
-  static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
-
-    protected void configure(HttpSecurity http) throws Exception {
-      http
-        .csrf().disable()
-        .antMatcher("/basicsecured/**")
-        .authorizeRequests()
-        .antMatchers("/basicsecured/**").authenticated()
-        .and()
-        .httpBasic()
-        .and().authenticationProvider(applicationContext.getBean(SavingAuthenticationProvider))
-    }
-  }
-
-  /**
-   * Following configuration is required in order to get form login, needed by password tests
-   */
-  @Configuration
-  static class FormLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
-
-    @Override
-    protected void configure(HttpSecurity http) throws Exception {
-      http
-        .csrf().disable()
-        .authorizeRequests()
-        .antMatchers("/formsecured/**").authenticated()
-        .and()
-        .formLogin()
-        .and().authenticationProvider(applicationContext.getBean(SavingAuthenticationProvider))
-    }
-  }
-}

+ 0 - 20
instrumentation/spring/spring-webmvc/spring-webmvc-3.1/javaagent/src/test/groovy/test/boot/SpringBootBasedTest.groovy

@@ -1,20 +0,0 @@
-/*
- * Copyright The OpenTelemetry Authors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package test.boot
-
-import boot.AbstractSpringBootBasedTest
-
-class SpringBootBasedTest extends AbstractSpringBootBasedTest {
-
-  Class<?> securityConfigClass() {
-    SecurityConfig
-  }
-
-  @Override
-  int getResponseCodeOnNonStandardHttpMethod() {
-    Boolean.getBoolean("testLatestDeps") ? 500 : 200
-  }
-}

+ 0 - 90
instrumentation/spring/spring-webmvc/spring-webmvc-3.1/javaagent/src/test/groovy/test/filter/ServletFilterConfig.groovy

@@ -1,90 +0,0 @@
-/*
- * Copyright The OpenTelemetry Authors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package test.filter
-
-import io.opentelemetry.instrumentation.test.base.HttpServerTest
-import io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint
-import org.springframework.context.annotation.Bean
-import org.springframework.context.annotation.Configuration
-
-import javax.servlet.Filter
-import javax.servlet.FilterChain
-import javax.servlet.FilterConfig
-import javax.servlet.ServletException
-import javax.servlet.ServletRequest
-import javax.servlet.ServletResponse
-import javax.servlet.http.HttpServletRequest
-import javax.servlet.http.HttpServletResponse
-
-import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.CAPTURE_HEADERS
-import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.ERROR
-import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.EXCEPTION
-import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.INDEXED_CHILD
-import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.PATH_PARAM
-import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.QUERY_PARAM
-import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.REDIRECT
-import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.SUCCESS
-
-@Configuration
-class ServletFilterConfig {
-
-  @Bean
-  Filter servletFilter() {
-    return new Filter() {
-
-      @Override
-      void init(FilterConfig filterConfig) throws ServletException {
-      }
-
-      @Override
-      void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
-        HttpServletRequest req = (HttpServletRequest) request
-        HttpServletResponse resp = (HttpServletResponse) response
-        ServerEndpoint endpoint = ServerEndpoint.forPath(req.servletPath)
-        HttpServerTest.controller(endpoint) {
-          resp.contentType = "text/plain"
-          switch (endpoint) {
-            case SUCCESS:
-              resp.status = endpoint.status
-              resp.writer.print(endpoint.body)
-              break
-            case QUERY_PARAM:
-              resp.status = endpoint.status
-              resp.writer.print(req.queryString)
-              break
-            case PATH_PARAM:
-              resp.status = endpoint.status
-              resp.writer.print(endpoint.body)
-              break
-            case REDIRECT:
-              resp.sendRedirect(endpoint.body)
-              break
-            case CAPTURE_HEADERS:
-              resp.setHeader("X-Test-Response", req.getHeader("X-Test-Request"))
-              resp.status = endpoint.status
-              resp.writer.print(endpoint.body)
-              break
-            case ERROR:
-              resp.sendError(endpoint.status, endpoint.body)
-              break
-            case EXCEPTION:
-              throw new Exception(endpoint.body)
-            case INDEXED_CHILD:
-              INDEXED_CHILD.collectSpanAttributes { name -> req.getParameter(name) }
-              resp.writer.print(endpoint.body)
-              break
-            default:
-              chain.doFilter(request, response)
-          }
-        }
-      }
-
-      @Override
-      void destroy() {
-      }
-    }
-  }
-}

+ 0 - 25
instrumentation/spring/spring-webmvc/spring-webmvc-3.1/javaagent/src/test/groovy/test/filter/ServletFilterTest.groovy

@@ -1,25 +0,0 @@
-/*
- * Copyright The OpenTelemetry Authors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package test.filter
-
-import filter.AbstractServletFilterTest
-import test.boot.SecurityConfig
-
-class ServletFilterTest extends AbstractServletFilterTest {
-
-  Class<?> securityConfigClass() {
-    SecurityConfig
-  }
-
-  Class<?> filterConfigClass() {
-    ServletFilterConfig
-  }
-
-  @Override
-  int getResponseCodeOnNonStandardHttpMethod() {
-    Boolean.getBoolean("testLatestDeps") ? 500 : 200
-  }
-}

+ 67 - 0
instrumentation/spring/spring-webmvc/spring-webmvc-3.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/webmvc/v3_1/boot/SecurityConfig.java

@@ -0,0 +1,67 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.javaagent.instrumentation.spring.webmvc.v3_1.boot;
+
+import io.opentelemetry.instrumentation.spring.webmvc.boot.SavingAuthenticationProvider;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.annotation.Order;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+
+@Configuration
+@EnableWebSecurity
+public class SecurityConfig {
+
+  @Bean
+  SavingAuthenticationProvider savingAuthenticationProvider() {
+    return new SavingAuthenticationProvider();
+  }
+
+  /**
+   * Following configuration is required for unauthorised call tests (form would redirect, we need
+   * 401)
+   */
+  @Configuration
+  @Order(1)
+  static class ApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
+
+    @Override
+    protected void configure(HttpSecurity http) throws Exception {
+      http.csrf()
+          .disable()
+          .antMatcher("/basicsecured/**")
+          .authorizeRequests()
+          .antMatchers("/basicsecured/**")
+          .authenticated()
+          .and()
+          .httpBasic()
+          .and()
+          .authenticationProvider(
+              getApplicationContext().getBean(SavingAuthenticationProvider.class));
+    }
+  }
+
+  /** Following configuration is required in order to get form login, needed by password tests */
+  @Configuration
+  static class FormLoginWebSecurityConfigurerAdapter extends WebSecurityConfigurerAdapter {
+
+    @Override
+    protected void configure(HttpSecurity http) throws Exception {
+      http.csrf()
+          .disable()
+          .authorizeRequests()
+          .antMatchers("/formsecured/**")
+          .authenticated()
+          .and()
+          .formLogin()
+          .and()
+          .authenticationProvider(
+              getApplicationContext().getBean(SavingAuthenticationProvider.class));
+    }
+  }
+}

+ 62 - 0
instrumentation/spring/spring-webmvc/spring-webmvc-3.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/webmvc/v3_1/boot/SpringBootBasedTest.java

@@ -0,0 +1,62 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.javaagent.instrumentation.spring.webmvc.v3_1.boot;
+
+import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.EXCEPTION;
+
+import com.google.common.collect.ImmutableMap;
+import io.opentelemetry.instrumentation.spring.webmvc.boot.AbstractSpringBootBasedTest;
+import io.opentelemetry.instrumentation.spring.webmvc.boot.AppConfig;
+import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
+import io.opentelemetry.instrumentation.testing.junit.http.HttpServerInstrumentationExtension;
+import io.opentelemetry.instrumentation.testing.junit.http.HttpServerTestOptions;
+import org.junit.jupiter.api.extension.RegisterExtension;
+import org.springframework.boot.SpringApplication;
+import org.springframework.context.ConfigurableApplicationContext;
+
+class SpringBootBasedTest extends AbstractSpringBootBasedTest {
+
+  @RegisterExtension
+  private static final InstrumentationExtension testing =
+      HttpServerInstrumentationExtension.forAgent();
+
+  private ConfigurableApplicationContext context;
+
+  @Override
+  protected ConfigurableApplicationContext context() {
+    return context;
+  }
+
+  @Override
+  protected ConfigurableApplicationContext setupServer() {
+    SpringApplication app = new SpringApplication(AppConfig.class, securityConfigClass());
+    app.setDefaultProperties(
+        ImmutableMap.of(
+            "server.port",
+            port,
+            "server.context-path",
+            getContextPath(),
+            "server.servlet.contextPath",
+            getContextPath(),
+            "server.error.include-message",
+            "always"));
+    context = app.run();
+    return context;
+  }
+
+  @Override
+  public Class<?> securityConfigClass() {
+    return SecurityConfig.class;
+  }
+
+  @Override
+  protected void configure(HttpServerTestOptions options) {
+    super.configure(options);
+    options.setResponseCodeOnNonStandardHttpMethod(
+        Boolean.getBoolean("testLatestDeps") ? 500 : 200);
+    options.setExpectedException(new RuntimeException(EXCEPTION.getBody()));
+  }
+}

+ 85 - 0
instrumentation/spring/spring-webmvc/spring-webmvc-3.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/webmvc/v3_1/filter/ServletFilterConfig.java

@@ -0,0 +1,85 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.javaagent.instrumentation.spring.webmvc.v3_1.filter;
+
+import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.INDEXED_CHILD;
+
+import io.opentelemetry.instrumentation.test.base.HttpServerTest;
+import io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint;
+import java.io.IOException;
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+class ServletFilterConfig {
+
+  @Bean
+  Filter servletFilter() {
+    return new Filter() {
+
+      @Override
+      public void init(FilterConfig filterConfig) throws ServletException {}
+
+      @Override
+      public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
+          throws IOException, ServletException {
+        HttpServletRequest req = (HttpServletRequest) request;
+        HttpServletResponse resp = (HttpServletResponse) response;
+        ServerEndpoint endpoint = ServerEndpoint.forPath(req.getServletPath());
+        HttpServerTest.controller(
+            endpoint,
+            () -> {
+              resp.setContentType("text/plain");
+              switch (endpoint.name()) {
+                case "SUCCESS":
+                  resp.setStatus(endpoint.getStatus());
+                  resp.getWriter().print(endpoint.getBody());
+                  break;
+                case "QUERY_PARAM":
+                  resp.setStatus(endpoint.getStatus());
+                  resp.getWriter().print(req.getQueryString());
+                  break;
+                case "PATH_PARAM":
+                  resp.setStatus(endpoint.getStatus());
+                  resp.getWriter().print(endpoint.getBody());
+                  break;
+                case "REDIRECT":
+                  resp.sendRedirect(endpoint.getBody());
+                  break;
+                case "CAPTURE_HEADERS":
+                  resp.setHeader("X-Test-Response", req.getHeader("X-Test-Request"));
+                  resp.setStatus(endpoint.getStatus());
+                  resp.getWriter().print(endpoint.getBody());
+                  break;
+                case "ERROR":
+                  resp.sendError(endpoint.getStatus(), endpoint.getBody());
+                  break;
+                case "EXCEPTION":
+                  throw new Exception(endpoint.getBody());
+                case "INDEXED_CHILD":
+                  INDEXED_CHILD.collectSpanAttributes(req::getParameter);
+                  resp.getWriter().print(endpoint.getBody());
+                  break;
+                default:
+                  chain.doFilter(request, response);
+              }
+              return null;
+            });
+      }
+
+      @Override
+      public void destroy() {}
+    };
+  }
+}

+ 50 - 0
instrumentation/spring/spring-webmvc/spring-webmvc-3.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/webmvc/v3_1/filter/ServletFilterTest.java

@@ -0,0 +1,50 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.javaagent.instrumentation.spring.webmvc.v3_1.filter;
+
+import com.google.common.collect.ImmutableMap;
+import io.opentelemetry.instrumentation.spring.webmvc.filter.AbstractServletFilterTest;
+import io.opentelemetry.instrumentation.spring.webmvc.filter.FilteredAppConfig;
+import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
+import io.opentelemetry.instrumentation.testing.junit.http.HttpServerInstrumentationExtension;
+import io.opentelemetry.instrumentation.testing.junit.http.HttpServerTestOptions;
+import io.opentelemetry.javaagent.instrumentation.spring.webmvc.v3_1.boot.SecurityConfig;
+import org.junit.jupiter.api.extension.RegisterExtension;
+import org.springframework.boot.SpringApplication;
+import org.springframework.context.ConfigurableApplicationContext;
+
+class ServletFilterTest extends AbstractServletFilterTest {
+
+  @RegisterExtension
+  private static final InstrumentationExtension testing =
+      HttpServerInstrumentationExtension.forAgent();
+
+  @Override
+  protected Class<?> securityConfigClass() {
+    return SecurityConfig.class;
+  }
+
+  @Override
+  protected Class<?> filterConfigClass() {
+    return ServletFilterConfig.class;
+  }
+
+  @Override
+  protected ConfigurableApplicationContext setupServer() {
+    SpringApplication app =
+        new SpringApplication(FilteredAppConfig.class, securityConfigClass(), filterConfigClass());
+    app.setDefaultProperties(
+        ImmutableMap.of("server.port", port, "server.error.include-message", "always"));
+    return app.run();
+  }
+
+  @Override
+  protected void configure(HttpServerTestOptions options) {
+    super.configure(options);
+    options.setResponseCodeOnNonStandardHttpMethod(
+        Boolean.getBoolean("testLatestDeps") ? 500 : 200);
+  }
+}

+ 0 - 57
instrumentation/spring/spring-webmvc/spring-webmvc-6.0/javaagent/src/test/groovy/SecurityConfig.groovy

@@ -1,57 +0,0 @@
-/*
- * Copyright The OpenTelemetry Authors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-import boot.SavingAuthenticationProvider
-import org.springframework.context.annotation.Bean
-import org.springframework.context.annotation.Configuration
-import org.springframework.core.annotation.Order
-import org.springframework.security.config.annotation.web.builders.HttpSecurity
-import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
-import org.springframework.security.web.SecurityFilterChain
-
-@Configuration
-@EnableWebSecurity
-class SecurityConfig {
-
-  @Bean
-  SavingAuthenticationProvider savingAuthenticationProvider() {
-    return new SavingAuthenticationProvider()
-  }
-
-  /**
-   * Following configuration is required for unauthorised call tests (form would redirect, we need 401)
-   */
-  @Bean
-  @Order(1)
-  SecurityFilterChain apiWebSecurity(HttpSecurity http, SavingAuthenticationProvider savingAuthenticationProvider) {
-    return http
-      .csrf().disable()
-      .securityMatcher("/basicsecured/**")
-      .authorizeHttpRequests()
-      .requestMatchers("/basicsecured/**").authenticated()
-      .and()
-      .httpBasic()
-      .and()
-      .authenticationProvider(savingAuthenticationProvider)
-      .build()
-  }
-
-  /**
-   * Following configuration is required in order to get form login, needed by password tests
-   */
-  @Bean
-  SecurityFilterChain formLoginWebSecurity(HttpSecurity http, SavingAuthenticationProvider savingAuthenticationProvider) {
-    return http
-      .csrf().disable()
-      .authorizeHttpRequests()
-      .requestMatchers("/formsecured/**").authenticated()
-      .anyRequest().permitAll()
-      .and()
-      .formLogin()
-      .and()
-      .authenticationProvider(savingAuthenticationProvider)
-      .build()
-  }
-}

+ 0 - 88
instrumentation/spring/spring-webmvc/spring-webmvc-6.0/javaagent/src/test/groovy/ServletFilterConfig.groovy

@@ -1,88 +0,0 @@
-/*
- * Copyright The OpenTelemetry Authors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-import io.opentelemetry.instrumentation.test.base.HttpServerTest
-import io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint
-import org.springframework.context.annotation.Bean
-import org.springframework.context.annotation.Configuration
-
-import jakarta.servlet.Filter
-import jakarta.servlet.FilterChain
-import jakarta.servlet.FilterConfig
-import jakarta.servlet.ServletException
-import jakarta.servlet.ServletRequest
-import jakarta.servlet.ServletResponse
-import jakarta.servlet.http.HttpServletRequest
-import jakarta.servlet.http.HttpServletResponse
-
-import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.CAPTURE_HEADERS
-import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.ERROR
-import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.EXCEPTION
-import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.INDEXED_CHILD
-import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.PATH_PARAM
-import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.QUERY_PARAM
-import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.REDIRECT
-import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.SUCCESS
-
-@Configuration
-class ServletFilterConfig {
-
-  @Bean
-  Filter servletFilter() {
-    return new Filter() {
-
-      @Override
-      void init(FilterConfig filterConfig) throws ServletException {
-      }
-
-      @Override
-      void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
-        HttpServletRequest req = (HttpServletRequest) request
-        HttpServletResponse resp = (HttpServletResponse) response
-        ServerEndpoint endpoint = ServerEndpoint.forPath(req.servletPath)
-        HttpServerTest.controller(endpoint) {
-          resp.contentType = "text/plain"
-          switch (endpoint) {
-            case SUCCESS:
-              resp.status = endpoint.status
-              resp.writer.print(endpoint.body)
-              break
-            case QUERY_PARAM:
-              resp.status = endpoint.status
-              resp.writer.print(req.queryString)
-              break
-            case PATH_PARAM:
-              resp.status = endpoint.status
-              resp.writer.print(endpoint.body)
-              break
-            case REDIRECT:
-              resp.sendRedirect(endpoint.body)
-              break
-            case CAPTURE_HEADERS:
-              resp.setHeader("X-Test-Response", req.getHeader("X-Test-Request"))
-              resp.status = endpoint.status
-              resp.writer.print(endpoint.body)
-              break
-            case ERROR:
-              resp.sendError(endpoint.status, endpoint.body)
-              break
-            case EXCEPTION:
-              throw new Exception(endpoint.body)
-            case INDEXED_CHILD:
-              INDEXED_CHILD.collectSpanAttributes { name -> req.getParameter(name) }
-              resp.writer.print(endpoint.body)
-              break
-            default:
-              chain.doFilter(request, response)
-          }
-        }
-      }
-
-      @Override
-      void destroy() {
-      }
-    }
-  }
-}

+ 0 - 57
instrumentation/spring/spring-webmvc/spring-webmvc-6.0/javaagent/src/test/groovy/ServletFilterTest.groovy

@@ -1,57 +0,0 @@
-/*
- * Copyright The OpenTelemetry Authors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-import filter.AbstractServletFilterTest
-import io.opentelemetry.api.trace.SpanKind
-import io.opentelemetry.api.trace.StatusCode
-import io.opentelemetry.instrumentation.test.asserts.TraceAssert
-import io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint
-import io.opentelemetry.sdk.trace.data.SpanData
-
-import static io.opentelemetry.api.trace.SpanKind.INTERNAL
-
-class ServletFilterTest extends AbstractServletFilterTest {
-
-  Class<?> securityConfigClass() {
-    SecurityConfig
-  }
-
-  Class<?> filterConfigClass() {
-    ServletFilterConfig
-  }
-
-  @Override
-  void handlerSpan(TraceAssert trace, int index, Object parent, String method = "GET", ServerEndpoint endpoint) {
-    if (Boolean.getBoolean("testLatestDeps") && endpoint == ServerEndpoint.NOT_FOUND) {
-      trace.span(index) {
-        name "ResourceHttpRequestHandler.handleRequest"
-        kind INTERNAL
-        childOf((SpanData) parent)
-        status StatusCode.ERROR
-        errorEventWithAnyMessage Class.forName("org.springframework.web.servlet.resource.NoResourceFoundException")
-      }
-    } else {
-      super.handlerSpan(trace, index, parent, method, endpoint)
-    }
-  }
-
-  @Override
-  void responseSpan(TraceAssert trace, int index, Object parent, String method, ServerEndpoint endpoint) {
-    if (Boolean.getBoolean("testLatestDeps") && endpoint == ServerEndpoint.NOT_FOUND) {
-      trace.span(index) {
-        name ~/\.sendError$/
-        kind SpanKind.INTERNAL
-        // not verifying the parent span, in the latest version the responseSpan is the child of the SERVER span, not the handler span
-      }
-    } else {
-      super.responseSpan(trace, index, parent, method, endpoint)
-    }
-  }
-
-  @Override
-  int getResponseCodeOnNonStandardHttpMethod() {
-    400
-  }
-}

+ 0 - 39
instrumentation/spring/spring-webmvc/spring-webmvc-6.0/javaagent/src/test/groovy/SpringBootBasedTest.groovy

@@ -1,39 +0,0 @@
-/*
- * Copyright The OpenTelemetry Authors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-import boot.AbstractSpringBootBasedTest
-import io.opentelemetry.api.trace.StatusCode
-import io.opentelemetry.instrumentation.test.asserts.TraceAssert
-import io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint
-import io.opentelemetry.sdk.trace.data.SpanData
-
-import static io.opentelemetry.api.trace.SpanKind.INTERNAL
-
-class SpringBootBasedTest extends AbstractSpringBootBasedTest {
-
-  Class<?> securityConfigClass() {
-    SecurityConfig
-  }
-
-  @Override
-  void handlerSpan(TraceAssert trace, int index, Object parent, String method = "GET", ServerEndpoint endpoint) {
-    if (Boolean.getBoolean("testLatestDeps") && endpoint == ServerEndpoint.NOT_FOUND) {
-      trace.span(index) {
-        name "ResourceHttpRequestHandler.handleRequest"
-        kind INTERNAL
-        childOf((SpanData) parent)
-        status StatusCode.ERROR
-        errorEventWithAnyMessage Class.forName("org.springframework.web.servlet.resource.NoResourceFoundException")
-      }
-    } else {
-      super.handlerSpan(trace, index, parent, method, endpoint)
-    }
-  }
-
-  @Override
-  int getResponseCodeOnNonStandardHttpMethod() {
-    400
-  }
-}

+ 57 - 0
instrumentation/spring/spring-webmvc/spring-webmvc-6.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/webmvc/v6_0/boot/SecurityConfig.java

@@ -0,0 +1,57 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.javaagent.instrumentation.spring.webmvc.v6_0.boot;
+
+import io.opentelemetry.instrumentation.spring.webmvc.boot.SavingAuthenticationProvider;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.annotation.Order;
+import org.springframework.security.config.Customizer;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
+import org.springframework.security.web.SecurityFilterChain;
+
+@Configuration
+@EnableWebSecurity
+public class SecurityConfig {
+
+  @Bean
+  SavingAuthenticationProvider savingAuthenticationProvider() {
+    return new SavingAuthenticationProvider();
+  }
+
+  /**
+   * Following configuration is required for unauthorised call tests (form would redirect, we need
+   * 401)
+   */
+  @Bean
+  @Order(1)
+  SecurityFilterChain apiWebSecurity(
+      HttpSecurity http, SavingAuthenticationProvider savingAuthenticationProvider)
+      throws Exception {
+    return http.csrf(AbstractHttpConfigurer::disable)
+        .securityMatcher("/basicsecured/**")
+        .authorizeHttpRequests(auth -> auth.requestMatchers("/basicsecured/**").authenticated())
+        .authenticationProvider(savingAuthenticationProvider)
+        .httpBasic(Customizer.withDefaults())
+        .build();
+  }
+
+  /** Following configuration is required in order to get form login, needed by password tests */
+  @Bean
+  SecurityFilterChain formLoginWebSecurity(
+      HttpSecurity http, SavingAuthenticationProvider savingAuthenticationProvider)
+      throws Exception {
+    return http.csrf(AbstractHttpConfigurer::disable)
+        .authorizeHttpRequests(
+            auth ->
+                auth.requestMatchers("/formsecured/**").authenticated().anyRequest().permitAll())
+        .authenticationProvider(savingAuthenticationProvider)
+        .formLogin(Customizer.withDefaults())
+        .build();
+  }
+}

+ 110 - 0
instrumentation/spring/spring-webmvc/spring-webmvc-6.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/webmvc/v6_0/boot/SpringBootBasedTest.java

@@ -0,0 +1,110 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.javaagent.instrumentation.spring.webmvc.v6_0.boot;
+
+import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.EXCEPTION;
+import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo;
+import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies;
+import static io.opentelemetry.semconv.ExceptionAttributes.EXCEPTION_MESSAGE;
+import static io.opentelemetry.semconv.ExceptionAttributes.EXCEPTION_STACKTRACE;
+import static io.opentelemetry.semconv.ExceptionAttributes.EXCEPTION_TYPE;
+import static org.assertj.core.api.Assertions.assertThat;
+
+import com.google.common.collect.ImmutableMap;
+import io.opentelemetry.api.trace.SpanKind;
+import io.opentelemetry.instrumentation.spring.webmvc.boot.AbstractSpringBootBasedTest;
+import io.opentelemetry.instrumentation.spring.webmvc.boot.AppConfig;
+import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
+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.sdk.testing.assertj.SpanDataAssert;
+import io.opentelemetry.sdk.trace.data.SpanData;
+import io.opentelemetry.sdk.trace.data.StatusData;
+import org.junit.jupiter.api.extension.RegisterExtension;
+import org.springframework.boot.SpringApplication;
+import org.springframework.context.ConfigurableApplicationContext;
+
+class SpringBootBasedTest extends AbstractSpringBootBasedTest {
+
+  @RegisterExtension
+  private static final InstrumentationExtension testing =
+      HttpServerInstrumentationExtension.forAgent();
+
+  private static final boolean testLatestDeps = Boolean.getBoolean("testLatestDeps");
+
+  private ConfigurableApplicationContext context;
+
+  @Override
+  protected ConfigurableApplicationContext context() {
+    return context;
+  }
+
+  @Override
+  protected Class<?> securityConfigClass() {
+    return SecurityConfig.class;
+  }
+
+  @Override
+  protected ConfigurableApplicationContext setupServer() {
+    SpringApplication app = new SpringApplication(AppConfig.class, securityConfigClass());
+    app.setDefaultProperties(
+        ImmutableMap.of(
+            "server.port",
+            port,
+            "server.context-path",
+            getContextPath(),
+            "server.servlet.contextPath",
+            getContextPath(),
+            "server.error.include-message",
+            "always"));
+    context = app.run();
+    return context;
+  }
+
+  @Override
+  protected SpanDataAssert assertHandlerSpan(
+      SpanDataAssert span, String method, ServerEndpoint endpoint) {
+    if (testLatestDeps && endpoint == ServerEndpoint.NOT_FOUND) {
+      String handlerSpanName = "ResourceHttpRequestHandler.handleRequest";
+      span.hasName(handlerSpanName)
+          .hasKind(SpanKind.INTERNAL)
+          .hasStatus(StatusData.error())
+          .hasEventsSatisfyingExactly(
+              event ->
+                  event
+                      .hasName("exception")
+                      .hasAttributesSatisfyingExactly(
+                          equalTo(
+                              EXCEPTION_TYPE,
+                              "org.springframework.web.servlet.resource.NoResourceFoundException"),
+                          satisfies(EXCEPTION_MESSAGE, val -> assertThat(val).isNotNull()),
+                          satisfies(EXCEPTION_STACKTRACE, val -> val.isInstanceOf(String.class))));
+      return span;
+    } else {
+      return super.assertHandlerSpan(span, method, endpoint);
+    }
+  }
+
+  @Override
+  protected SpanDataAssert assertResponseSpan(
+      SpanDataAssert span, SpanData parentSpan, String method, ServerEndpoint endpoint) {
+    if (testLatestDeps && endpoint == ServerEndpoint.NOT_FOUND) {
+      // not verifying the parent span, in the latest version the responseSpan is the child of the
+      // SERVER span, not the handler span
+      return super.assertResponseSpan(span, method, endpoint);
+    } else {
+      return super.assertResponseSpan(span, parentSpan, method, endpoint);
+    }
+  }
+
+  @Override
+  protected void configure(HttpServerTestOptions options) {
+    super.configure(options);
+    options.setResponseCodeOnNonStandardHttpMethod(400);
+    options.setExpectedException(new RuntimeException(EXCEPTION.getBody()));
+  }
+}

+ 85 - 0
instrumentation/spring/spring-webmvc/spring-webmvc-6.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/webmvc/v6_0/filter/ServletFilterConfig.java

@@ -0,0 +1,85 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.javaagent.instrumentation.spring.webmvc.v6_0.filter;
+
+import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.INDEXED_CHILD;
+
+import io.opentelemetry.instrumentation.test.base.HttpServerTest;
+import io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint;
+import jakarta.servlet.Filter;
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.FilterConfig;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.ServletRequest;
+import jakarta.servlet.ServletResponse;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+class ServletFilterConfig {
+
+  @Bean
+  Filter servletFilter() {
+    return new Filter() {
+
+      @Override
+      public void init(FilterConfig filterConfig) throws ServletException {}
+
+      @Override
+      public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
+          throws IOException, ServletException {
+        HttpServletRequest req = (HttpServletRequest) request;
+        HttpServletResponse resp = (HttpServletResponse) response;
+        ServerEndpoint endpoint = ServerEndpoint.forPath(req.getServletPath());
+        HttpServerTest.controller(
+            endpoint,
+            () -> {
+              resp.setContentType("text/plain");
+              switch (endpoint.name()) {
+                case "SUCCESS":
+                  resp.setStatus(endpoint.getStatus());
+                  resp.getWriter().print(endpoint.getBody());
+                  break;
+                case "QUERY_PARAM":
+                  resp.setStatus(endpoint.getStatus());
+                  resp.getWriter().print(req.getQueryString());
+                  break;
+                case "PATH_PARAM":
+                  resp.setStatus(endpoint.getStatus());
+                  resp.getWriter().print(endpoint.getBody());
+                  break;
+                case "REDIRECT":
+                  resp.sendRedirect(endpoint.getBody());
+                  break;
+                case "CAPTURE_HEADERS":
+                  resp.setHeader("X-Test-Response", req.getHeader("X-Test-Request"));
+                  resp.setStatus(endpoint.getStatus());
+                  resp.getWriter().print(endpoint.getBody());
+                  break;
+                case "ERROR":
+                  resp.sendError(endpoint.getStatus(), endpoint.getBody());
+                  break;
+                case "EXCEPTION":
+                  throw new Exception(endpoint.getBody());
+                case "INDEXED_CHILD":
+                  INDEXED_CHILD.collectSpanAttributes(req::getParameter);
+                  resp.getWriter().print(endpoint.getBody());
+                  break;
+                default:
+                  chain.doFilter(request, response);
+              }
+              return null;
+            });
+      }
+
+      @Override
+      public void destroy() {}
+    };
+  }
+}

+ 101 - 0
instrumentation/spring/spring-webmvc/spring-webmvc-6.0/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/spring/webmvc/v6_0/filter/ServletFilterTest.java

@@ -0,0 +1,101 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.javaagent.instrumentation.spring.webmvc.v6_0.filter;
+
+import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo;
+import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies;
+import static io.opentelemetry.semconv.ExceptionAttributes.EXCEPTION_MESSAGE;
+import static io.opentelemetry.semconv.ExceptionAttributes.EXCEPTION_STACKTRACE;
+import static io.opentelemetry.semconv.ExceptionAttributes.EXCEPTION_TYPE;
+import static org.assertj.core.api.Assertions.assertThat;
+
+import com.google.common.collect.ImmutableMap;
+import io.opentelemetry.api.trace.SpanKind;
+import io.opentelemetry.instrumentation.spring.webmvc.filter.AbstractServletFilterTest;
+import io.opentelemetry.instrumentation.spring.webmvc.filter.FilteredAppConfig;
+import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension;
+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.javaagent.instrumentation.spring.webmvc.v6_0.boot.SecurityConfig;
+import io.opentelemetry.sdk.testing.assertj.SpanDataAssert;
+import io.opentelemetry.sdk.trace.data.SpanData;
+import io.opentelemetry.sdk.trace.data.StatusData;
+import org.junit.jupiter.api.extension.RegisterExtension;
+import org.springframework.boot.SpringApplication;
+import org.springframework.context.ConfigurableApplicationContext;
+
+class ServletFilterTest extends AbstractServletFilterTest {
+
+  private static final boolean testLatestDeps = Boolean.getBoolean("testLatestDeps");
+
+  @RegisterExtension
+  private static final InstrumentationExtension testing =
+      HttpServerInstrumentationExtension.forAgent();
+
+  @Override
+  protected Class<?> securityConfigClass() {
+    return SecurityConfig.class;
+  }
+
+  @Override
+  protected Class<?> filterConfigClass() {
+    return ServletFilterConfig.class;
+  }
+
+  @Override
+  protected ConfigurableApplicationContext setupServer() {
+    SpringApplication app =
+        new SpringApplication(FilteredAppConfig.class, securityConfigClass(), filterConfigClass());
+    app.setDefaultProperties(
+        ImmutableMap.of("server.port", port, "server.error.include-message", "always"));
+    return app.run();
+  }
+
+  @Override
+  protected SpanDataAssert assertHandlerSpan(
+      SpanDataAssert span, String method, ServerEndpoint endpoint) {
+    if (testLatestDeps && endpoint == ServerEndpoint.NOT_FOUND) {
+      String handlerSpanName = "ResourceHttpRequestHandler.handleRequest";
+      span.hasName(handlerSpanName)
+          .hasKind(SpanKind.INTERNAL)
+          .hasStatus(StatusData.error())
+          .hasEventsSatisfyingExactly(
+              event ->
+                  event
+                      .hasName("exception")
+                      .hasAttributesSatisfyingExactly(
+                          equalTo(
+                              EXCEPTION_TYPE,
+                              "org.springframework.web.servlet.resource.NoResourceFoundException"),
+                          satisfies(EXCEPTION_MESSAGE, val -> assertThat(val).isNotNull()),
+                          satisfies(EXCEPTION_STACKTRACE, val -> val.isInstanceOf(String.class))));
+      return span;
+    } else {
+      return super.assertHandlerSpan(span, method, endpoint);
+    }
+  }
+
+  @Override
+  protected SpanDataAssert assertResponseSpan(
+      SpanDataAssert span, SpanData parentSpan, String method, ServerEndpoint endpoint) {
+    if (testLatestDeps && endpoint == ServerEndpoint.NOT_FOUND) {
+      span.satisfies(spanData -> assertThat(spanData.getName()).endsWith(".sendError"));
+      span.hasKind(SpanKind.INTERNAL);
+      // not verifying the parent span, in the latest version the responseSpan is the child of the
+      // SERVER span, not the handler span
+      return span;
+    } else {
+      return super.assertResponseSpan(span, parentSpan, method, endpoint);
+    }
+  }
+
+  @Override
+  protected void configure(HttpServerTestOptions options) {
+    super.configure(options);
+    options.setResponseCodeOnNonStandardHttpMethod(400);
+  }
+}

+ 0 - 2
instrumentation/spring/spring-webmvc/spring-webmvc-common/testing/build.gradle.kts

@@ -5,8 +5,6 @@ plugins {
 dependencies {
   implementation(project(":testing-common"))
 
-  implementation("org.spockframework:spock-spring")
-
   compileOnly("org.springframework.boot:spring-boot-starter-test:1.5.17.RELEASE")
   compileOnly("org.springframework.boot:spring-boot-starter-web:1.5.17.RELEASE")
   compileOnly("org.springframework.boot:spring-boot-starter-security:1.5.17.RELEASE")

+ 0 - 212
instrumentation/spring/spring-webmvc/spring-webmvc-common/testing/src/main/groovy/boot/AbstractSpringBootBasedTest.groovy

@@ -1,212 +0,0 @@
-/*
- * Copyright The OpenTelemetry Authors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package boot
-
-import io.opentelemetry.api.trace.StatusCode
-import io.opentelemetry.instrumentation.api.internal.HttpConstants
-import io.opentelemetry.instrumentation.test.AgentTestTrait
-import io.opentelemetry.instrumentation.test.asserts.TraceAssert
-import io.opentelemetry.instrumentation.test.base.HttpServerTest
-import io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint
-import io.opentelemetry.sdk.trace.data.SpanData
-import io.opentelemetry.semconv.incubating.CodeIncubatingAttributes
-import io.opentelemetry.testing.internal.armeria.common.AggregatedHttpRequest
-import io.opentelemetry.testing.internal.armeria.common.HttpData
-import io.opentelemetry.testing.internal.armeria.common.MediaType
-import io.opentelemetry.testing.internal.armeria.common.QueryParams
-import org.springframework.boot.SpringApplication
-import org.springframework.context.ConfigurableApplicationContext
-import org.springframework.security.web.util.OnCommittedResponseWrapper
-import org.springframework.web.servlet.view.RedirectView
-
-import java.util.regex.Pattern
-
-import static io.opentelemetry.api.trace.SpanKind.INTERNAL
-import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.AUTH_ERROR
-import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.EXCEPTION
-import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.LOGIN
-import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.NOT_FOUND
-import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.PATH_PARAM
-import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.REDIRECT
-import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.SUCCESS
-
-abstract class AbstractSpringBootBasedTest extends HttpServerTest<ConfigurableApplicationContext> implements AgentTestTrait {
-
-  abstract Class<?> securityConfigClass()
-
-  @Override
-  ConfigurableApplicationContext startServer(int port) {
-    def app = new SpringApplication(AppConfig, securityConfigClass())
-    app.setDefaultProperties([
-      "server.port"                 : port,
-      "server.context-path"         : getContextPath(),
-      "server.servlet.contextPath"  : getContextPath(),
-      "server.error.include-message": "always",
-    ])
-    def context = app.run()
-    return context
-  }
-
-  @Override
-  void stopServer(ConfigurableApplicationContext ctx) {
-    ctx.close()
-  }
-
-  @Override
-  String getContextPath() {
-    return "/xyz"
-  }
-
-  @Override
-  boolean hasHandlerSpan(ServerEndpoint endpoint) {
-    true
-  }
-
-  @Override
-  boolean hasRenderSpan(ServerEndpoint endpoint) {
-    endpoint == REDIRECT
-  }
-
-  @Override
-  boolean hasResponseSpan(ServerEndpoint endpoint) {
-    endpoint == REDIRECT || endpoint == NOT_FOUND
-  }
-
-  @Override
-  boolean testPathParam() {
-    true
-  }
-
-  @Override
-  boolean hasErrorPageSpans(ServerEndpoint endpoint) {
-    endpoint == NOT_FOUND
-  }
-
-  @Override
-  String expectedHttpRoute(ServerEndpoint endpoint, String method) {
-    if (method == HttpConstants._OTHER) {
-      return getContextPath() + endpoint.path
-    }
-    switch (endpoint) {
-      case PATH_PARAM:
-        return getContextPath() + "/path/{id}/param"
-      case NOT_FOUND:
-        return getContextPath() + "/**"
-      case LOGIN:
-        return getContextPath() + "/*"
-      default:
-        return super.expectedHttpRoute(endpoint, method)
-    }
-  }
-
-  def "test spans with auth error"() {
-    setup:
-    def authProvider = server.getBean(SavingAuthenticationProvider)
-    def request = request(AUTH_ERROR, "GET")
-
-    when:
-    authProvider.latestAuthentications.clear()
-    def response = client.execute(request).aggregate().join()
-
-    then:
-    response.status().code() == 401 // not secured
-
-    and:
-    assertTraces(1) {
-      trace(0, 3) {
-        serverSpan(it, 0, null, null, "GET", AUTH_ERROR)
-        sendErrorSpan(it, 1, span(0))
-        errorPageSpans(it, 2, null)
-      }
-    }
-  }
-
-  def "test character encoding of #testPassword"() {
-    setup:
-    def authProvider = server.getBean(SavingAuthenticationProvider)
-
-    QueryParams form = QueryParams.of("username", "test", "password", testPassword)
-    def request = AggregatedHttpRequest.of(
-      request(LOGIN, "POST").headers().toBuilder().contentType(MediaType.FORM_DATA).build(),
-      HttpData.ofUtf8(form.toQueryString()))
-
-    when:
-    authProvider.latestAuthentications.clear()
-    def response = client.execute(request).aggregate().join()
-
-    then:
-    response.status().code() == 302 // redirect after success
-    authProvider.latestAuthentications.get(0).password == testPassword
-
-    and:
-    assertTraces(1) {
-      trace(0, 2) {
-        serverSpan(it, 0, null, null, "POST", LOGIN)
-        redirectSpan(it, 1, span(0))
-      }
-    }
-
-    where:
-    testPassword << ["password", "dfsdföääöüüä", "🤓"]
-  }
-
-  @Override
-  void errorPageSpans(TraceAssert trace, int index, Object parent, String method = "GET", ServerEndpoint endpoint = SUCCESS) {
-    trace.span(index) {
-      name "BasicErrorController.error"
-      kind INTERNAL
-      attributes {
-      }
-    }
-  }
-
-  @Override
-  void responseSpan(TraceAssert trace, int index, Object parent, String method = "GET", ServerEndpoint endpoint = SUCCESS) {
-    def methodName = endpoint == NOT_FOUND ? "sendError" : "sendRedirect"
-    // the response wrapper class names vary depending on spring version & scenario
-    def namePattern = Pattern.compile("(OnCommittedResponseWrapper|HttpServletResponseWrapper|FirewalledResponse).$methodName")
-    trace.span(index) {
-      name namePattern
-      kind INTERNAL
-      attributes {
-        "$CodeIncubatingAttributes.CODE_NAMESPACE" {
-          it == OnCommittedResponseWrapper.name
-            || it == "org.springframework.security.web.firewall.FirewalledResponse"
-            || it == "jakarta.servlet.http.HttpServletResponseWrapper"
-        }
-        "$CodeIncubatingAttributes.CODE_FUNCTION" methodName
-      }
-    }
-  }
-
-  @Override
-  void renderSpan(TraceAssert trace, int index, Object parent, String method = "GET", ServerEndpoint endpoint = SUCCESS) {
-    trace.span(index) {
-      name "Render RedirectView"
-      kind INTERNAL
-      attributes {
-        "spring-webmvc.view.type" RedirectView.name
-      }
-    }
-  }
-
-  @Override
-  void handlerSpan(TraceAssert trace, int index, Object parent, String method = "GET", ServerEndpoint endpoint = SUCCESS) {
-    def handlerSpanName = "TestController.${endpoint.name().toLowerCase()}"
-    if (endpoint == NOT_FOUND) {
-      handlerSpanName = "ResourceHttpRequestHandler.handleRequest"
-    }
-    trace.span(index) {
-      name handlerSpanName
-      kind INTERNAL
-      if (endpoint == EXCEPTION) {
-        status StatusCode.ERROR
-        errorEvent(Exception, EXCEPTION.body)
-      }
-      childOf((SpanData) parent)
-    }
-  }
-}

+ 0 - 75
instrumentation/spring/spring-webmvc/spring-webmvc-common/testing/src/main/groovy/boot/SavingAuthenticationProvider.groovy

@@ -1,75 +0,0 @@
-/*
- * Copyright The OpenTelemetry Authors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package boot
-
-import org.springframework.security.authentication.UsernamePasswordAuthenticationToken
-import org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider
-import org.springframework.security.core.AuthenticationException
-import org.springframework.security.core.GrantedAuthority
-import org.springframework.security.core.userdetails.UserDetails
-
-class SavingAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
-  List<TestUserDetails> latestAuthentications = new ArrayList<>()
-
-  @Override
-  protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
-    // none
-  }
-
-  @Override
-  protected UserDetails retrieveUser(String username, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException {
-    def details = new TestUserDetails(username, authentication.credentials.toString())
-
-    latestAuthentications.add(details)
-
-    return details
-  }
-}
-
-class TestUserDetails implements UserDetails {
-  private final String username
-  private final String password
-
-  TestUserDetails(String username, String password) {
-    this.username = username
-    this.password = password
-  }
-
-  @Override
-  Collection<? extends GrantedAuthority> getAuthorities() {
-    return Collections.emptySet()
-  }
-
-  @Override
-  String getPassword() {
-    return password
-  }
-
-  @Override
-  String getUsername() {
-    return username
-  }
-
-  @Override
-  boolean isAccountNonExpired() {
-    return true
-  }
-
-  @Override
-  boolean isAccountNonLocked() {
-    return true
-  }
-
-  @Override
-  boolean isCredentialsNonExpired() {
-    return true
-  }
-
-  @Override
-  boolean isEnabled() {
-    return true
-  }
-}

+ 0 - 108
instrumentation/spring/spring-webmvc/spring-webmvc-common/testing/src/main/groovy/boot/TestController.groovy

@@ -1,108 +0,0 @@
-/*
- * Copyright The OpenTelemetry Authors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package boot
-
-import io.opentelemetry.instrumentation.test.base.HttpServerTest
-import org.springframework.http.HttpStatus
-import org.springframework.http.ResponseEntity
-import org.springframework.stereotype.Controller
-import org.springframework.web.bind.annotation.ExceptionHandler
-import org.springframework.web.bind.annotation.PathVariable
-import org.springframework.web.bind.annotation.RequestHeader
-import org.springframework.web.bind.annotation.RequestMapping
-import org.springframework.web.bind.annotation.RequestParam
-import org.springframework.web.bind.annotation.ResponseBody
-import org.springframework.web.servlet.view.RedirectView
-
-import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.CAPTURE_HEADERS
-import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.ERROR
-import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.EXCEPTION
-import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.INDEXED_CHILD
-import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.PATH_PARAM
-import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.QUERY_PARAM
-import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.REDIRECT
-import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.SUCCESS
-
-@Controller
-class TestController {
-
-  @RequestMapping("/basicsecured/endpoint")
-  @ResponseBody
-  String secureEndpoint() {
-    HttpServerTest.controller(SUCCESS) {
-      SUCCESS.body
-    }
-  }
-
-  @RequestMapping("/success")
-  @ResponseBody
-  String success() {
-    HttpServerTest.controller(SUCCESS) {
-      SUCCESS.body
-    }
-  }
-
-  @RequestMapping("/query")
-  @ResponseBody
-  String query_param(@RequestParam("some") String param) {
-    HttpServerTest.controller(QUERY_PARAM) {
-      "some=$param"
-    }
-  }
-
-  @RequestMapping("/redirect")
-  @ResponseBody
-  RedirectView redirect() {
-    HttpServerTest.controller(REDIRECT) {
-      new RedirectView(REDIRECT.body)
-    }
-  }
-
-  @RequestMapping("/error-status")
-  ResponseEntity error() {
-    HttpServerTest.controller(ERROR) {
-      new ResponseEntity(ERROR.body, HttpStatus.valueOf(ERROR.status))
-    }
-  }
-
-  @RequestMapping("/exception")
-  ResponseEntity exception() {
-    HttpServerTest.controller(EXCEPTION) {
-      throw new Exception(EXCEPTION.body)
-    }
-  }
-
-  @RequestMapping("/captureHeaders")
-  ResponseEntity capture_headers(@RequestHeader("X-Test-Request") String testRequestHeader) {
-    HttpServerTest.controller(CAPTURE_HEADERS) {
-      ResponseEntity.ok()
-        .header("X-Test-Response", testRequestHeader)
-        .body(CAPTURE_HEADERS.body)
-    }
-  }
-
-  @RequestMapping("/path/{id}/param")
-  @ResponseBody
-  String path_param(@PathVariable("id") int id) {
-    HttpServerTest.controller(PATH_PARAM) {
-      id
-    }
-  }
-
-  @RequestMapping("/child")
-  @ResponseBody
-  String indexed_child(@RequestParam("id") String id) {
-    HttpServerTest.controller(INDEXED_CHILD) {
-      INDEXED_CHILD.collectSpanAttributes { it == "id" ? id : null }
-      INDEXED_CHILD.body
-    }
-  }
-
-  @ExceptionHandler
-  ResponseEntity handleException(Throwable throwable) {
-    new ResponseEntity(throwable.message, HttpStatus.INTERNAL_SERVER_ERROR)
-  }
-}

+ 0 - 111
instrumentation/spring/spring-webmvc/spring-webmvc-common/testing/src/main/groovy/filter/AbstractServletFilterTest.groovy

@@ -1,111 +0,0 @@
-/*
- * Copyright The OpenTelemetry Authors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package filter
-
-import io.opentelemetry.instrumentation.api.internal.HttpConstants
-import io.opentelemetry.instrumentation.test.AgentTestTrait
-import io.opentelemetry.instrumentation.test.asserts.TraceAssert
-import io.opentelemetry.instrumentation.test.base.HttpServerTest
-import io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint
-import io.opentelemetry.sdk.trace.data.SpanData
-import org.springframework.boot.SpringApplication
-import org.springframework.context.ConfigurableApplicationContext
-
-import static io.opentelemetry.api.trace.SpanKind.INTERNAL
-import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.ERROR
-import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.EXCEPTION
-import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.NOT_FOUND
-import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.PATH_PARAM
-import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.REDIRECT
-import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.SUCCESS
-
-abstract class AbstractServletFilterTest extends HttpServerTest<ConfigurableApplicationContext> implements AgentTestTrait {
-
-  abstract Class<?> securityConfigClass()
-
-  abstract Class<?> filterConfigClass()
-
-  @Override
-  ConfigurableApplicationContext startServer(int port) {
-    def app = new SpringApplication(FilteredAppConfig, securityConfigClass(), filterConfigClass())
-    app.setDefaultProperties(["server.port": port, "server.error.include-message": "always"])
-    def context = app.run()
-    return context
-  }
-
-  @Override
-  void stopServer(ConfigurableApplicationContext ctx) {
-    ctx.close()
-  }
-
-  @Override
-  boolean hasHandlerSpan(ServerEndpoint endpoint) {
-    endpoint == NOT_FOUND
-  }
-
-  @Override
-  boolean hasErrorPageSpans(ServerEndpoint endpoint) {
-    endpoint == ERROR || endpoint == EXCEPTION || endpoint == NOT_FOUND
-  }
-
-  @Override
-  boolean hasResponseSpan(ServerEndpoint endpoint) {
-    endpoint == REDIRECT || endpoint == ERROR || endpoint == NOT_FOUND
-  }
-
-  @Override
-  void responseSpan(TraceAssert trace, int index, Object parent, String method, ServerEndpoint endpoint) {
-    switch (endpoint) {
-      case REDIRECT:
-        redirectSpan(trace, index, parent)
-        break
-      case ERROR:
-      case NOT_FOUND:
-        sendErrorSpan(trace, index, parent)
-        break
-    }
-  }
-
-  @Override
-  boolean testPathParam() {
-    true
-  }
-
-  @Override
-  void handlerSpan(TraceAssert trace, int index, Object parent, String method = "GET", ServerEndpoint endpoint) {
-    trace.span(index) {
-      name "ResourceHttpRequestHandler.handleRequest"
-      kind INTERNAL
-      childOf((SpanData) parent)
-    }
-  }
-
-  @Override
-  String expectedHttpRoute(ServerEndpoint endpoint, String method) {
-    if (method == HttpConstants._OTHER) {
-      return getContextPath() + endpoint.path
-    }
-    switch (endpoint) {
-      case PATH_PARAM:
-        return getContextPath() + "/path/{id}/param"
-      case NOT_FOUND:
-        return getContextPath() + "/**"
-      default:
-        return super.expectedHttpRoute(endpoint, method)
-    }
-  }
-
-  @Override
-  void errorPageSpans(TraceAssert trace, int index, Object parent, String method = "GET", ServerEndpoint endpoint = SUCCESS) {
-    trace.span(index) {
-      name "BasicErrorController.error"
-      kind INTERNAL
-      childOf((SpanData) parent)
-      attributes {
-      }
-    }
-  }
-}

+ 0 - 132
instrumentation/spring/spring-webmvc/spring-webmvc-common/testing/src/main/groovy/filter/FilteredAppConfig.groovy

@@ -1,132 +0,0 @@
-/*
- * Copyright The OpenTelemetry Authors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package filter
-
-import org.springframework.boot.autoconfigure.SpringBootApplication
-import org.springframework.context.annotation.Bean
-import org.springframework.format.FormatterRegistry
-import org.springframework.http.HttpInputMessage
-import org.springframework.http.HttpOutputMessage
-import org.springframework.http.MediaType
-import org.springframework.http.converter.AbstractHttpMessageConverter
-import org.springframework.http.converter.HttpMessageConverter
-import org.springframework.http.converter.HttpMessageNotReadableException
-import org.springframework.http.converter.HttpMessageNotWritableException
-import org.springframework.util.StreamUtils
-import org.springframework.validation.MessageCodesResolver
-import org.springframework.validation.Validator
-import org.springframework.web.HttpMediaTypeNotAcceptableException
-import org.springframework.web.accept.ContentNegotiationStrategy
-import org.springframework.web.context.request.NativeWebRequest
-import org.springframework.web.method.support.HandlerMethodArgumentResolver
-import org.springframework.web.method.support.HandlerMethodReturnValueHandler
-import org.springframework.web.servlet.HandlerExceptionResolver
-import org.springframework.web.servlet.config.annotation.AsyncSupportConfigurer
-import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer
-import org.springframework.web.servlet.config.annotation.CorsRegistry
-import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer
-import org.springframework.web.servlet.config.annotation.InterceptorRegistry
-import org.springframework.web.servlet.config.annotation.PathMatchConfigurer
-import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry
-import org.springframework.web.servlet.config.annotation.ViewControllerRegistry
-import org.springframework.web.servlet.config.annotation.ViewResolverRegistry
-import org.springframework.web.servlet.config.annotation.WebMvcConfigurer
-
-import java.nio.charset.StandardCharsets
-
-@SpringBootApplication
-class FilteredAppConfig implements WebMvcConfigurer {
-
-  @Override
-  void configurePathMatch(PathMatchConfigurer configurer) {}
-
-  @Override
-  void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
-    configurer.favorPathExtension(false)
-      .favorParameter(true)
-      .ignoreAcceptHeader(true)
-      .useJaf(false)
-      .defaultContentTypeStrategy(new ContentNegotiationStrategy() {
-        @Override
-        List<MediaType> resolveMediaTypes(NativeWebRequest webRequest) throws HttpMediaTypeNotAcceptableException {
-          return [MediaType.TEXT_PLAIN]
-        }
-      })
-  }
-
-  @Override
-  void configureAsyncSupport(AsyncSupportConfigurer configurer) {}
-
-  @Override
-  void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {}
-
-  @Override
-  void addFormatters(FormatterRegistry registry) {}
-
-  @Override
-  void addInterceptors(InterceptorRegistry registry) {}
-
-  @Override
-  void addResourceHandlers(ResourceHandlerRegistry registry) {}
-
-  @Override
-  void addCorsMappings(CorsRegistry registry) {}
-
-  @Override
-  void addViewControllers(ViewControllerRegistry registry) {}
-
-  @Override
-  void configureViewResolvers(ViewResolverRegistry registry) {}
-
-  @Override
-  void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {}
-
-  @Override
-  void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) {}
-
-  @Override
-  void configureMessageConverters(List<HttpMessageConverter<?>> converters) {}
-
-  @Override
-  void extendMessageConverters(List<HttpMessageConverter<?>> converters) {}
-
-  @Override
-  void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {}
-
-  @Override
-  void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {}
-
-  @Override
-  Validator getValidator() {
-    return null
-  }
-
-  @Override
-  MessageCodesResolver getMessageCodesResolver() {
-    return null
-  }
-
-  @Bean
-  HttpMessageConverter<Map<String, Object>> createPlainMapMessageConverter() {
-    return new AbstractHttpMessageConverter<Map<String, Object>>(MediaType.TEXT_PLAIN) {
-
-      @Override
-      protected boolean supports(Class<?> clazz) {
-        return Map.isAssignableFrom(clazz)
-      }
-
-      @Override
-      protected Map<String, Object> readInternal(Class<? extends Map<String, Object>> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
-        return null
-      }
-
-      @Override
-      protected void writeInternal(Map<String, Object> stringObjectMap, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException {
-        StreamUtils.copy(stringObjectMap.get("message") as String, StandardCharsets.UTF_8, outputMessage.getBody())
-      }
-    }
-  }
-}

+ 0 - 76
instrumentation/spring/spring-webmvc/spring-webmvc-common/testing/src/main/groovy/filter/TestController.groovy

@@ -1,76 +0,0 @@
-/*
- * Copyright The OpenTelemetry Authors
- * SPDX-License-Identifier: Apache-2.0
- */
-
-package filter
-
-
-import org.springframework.http.HttpStatus
-import org.springframework.http.ResponseEntity
-import org.springframework.stereotype.Controller
-import org.springframework.web.bind.annotation.ExceptionHandler
-import org.springframework.web.bind.annotation.PathVariable
-import org.springframework.web.bind.annotation.RequestMapping
-import org.springframework.web.bind.annotation.RequestParam
-import org.springframework.web.bind.annotation.ResponseBody
-import org.springframework.web.servlet.view.RedirectView
-
-/**
- * None of the methods in this controller should be called because they are intercepted
- * by the filter
- */
-@Controller
-class TestController {
-
-  @RequestMapping("/success")
-  @ResponseBody
-  String success() {
-    throw new Exception("This should not be called")
-  }
-
-  @RequestMapping("/query")
-  @ResponseBody
-  String query_param(@RequestParam("some") String param) {
-    throw new Exception("This should not be called")
-  }
-
-  @RequestMapping("/path/{id}/param")
-  @ResponseBody
-  String path_param(@PathVariable Integer id) {
-    throw new Exception("This should not be called")
-  }
-
-  @RequestMapping("/redirect")
-  @ResponseBody
-  RedirectView redirect() {
-    throw new Exception("This should not be called")
-  }
-
-  @RequestMapping("/error-status")
-  ResponseEntity error() {
-    throw new Exception("This should not be called")
-  }
-
-  @RequestMapping("/exception")
-  ResponseEntity exception() {
-    throw new Exception("This should not be called")
-  }
-
-  @RequestMapping("/captureHeaders")
-  ResponseEntity capture_headers() {
-    throw new Exception("This should not be called")
-  }
-
-  @RequestMapping("/child")
-  @ResponseBody
-  ResponseEntity indexed_child(@RequestParam("id") String id) {
-    throw new Exception("This should not be called")
-  }
-
-
-  @ExceptionHandler
-  ResponseEntity handleException(Throwable throwable) {
-    new ResponseEntity(throwable.message, HttpStatus.INTERNAL_SERVER_ERROR)
-  }
-}

+ 229 - 0
instrumentation/spring/spring-webmvc/spring-webmvc-common/testing/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/boot/AbstractSpringBootBasedTest.java

@@ -0,0 +1,229 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.instrumentation.spring.webmvc.boot;
+
+import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.AUTH_ERROR;
+import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.CAPTURE_HEADERS;
+import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.EXCEPTION;
+import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.INDEXED_CHILD;
+import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.LOGIN;
+import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.NOT_FOUND;
+import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.PATH_PARAM;
+import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.QUERY_PARAM;
+import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.REDIRECT;
+import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo;
+import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies;
+import static io.opentelemetry.semconv.ExceptionAttributes.EXCEPTION_MESSAGE;
+import static io.opentelemetry.semconv.ExceptionAttributes.EXCEPTION_STACKTRACE;
+import static io.opentelemetry.semconv.ExceptionAttributes.EXCEPTION_TYPE;
+import static io.opentelemetry.semconv.incubating.CodeIncubatingAttributes.CODE_FUNCTION;
+import static io.opentelemetry.semconv.incubating.CodeIncubatingAttributes.CODE_NAMESPACE;
+import static org.assertj.core.api.Assertions.assertThat;
+
+import io.opentelemetry.api.common.AttributeKey;
+import io.opentelemetry.api.common.Attributes;
+import io.opentelemetry.api.trace.SpanKind;
+import io.opentelemetry.instrumentation.api.internal.HttpConstants;
+import io.opentelemetry.instrumentation.testing.junit.http.AbstractHttpServerTest;
+import io.opentelemetry.instrumentation.testing.junit.http.HttpServerTestOptions;
+import io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint;
+import io.opentelemetry.sdk.testing.assertj.SpanDataAssert;
+import io.opentelemetry.sdk.trace.data.StatusData;
+import io.opentelemetry.testing.internal.armeria.common.AggregatedHttpRequest;
+import io.opentelemetry.testing.internal.armeria.common.AggregatedHttpResponse;
+import io.opentelemetry.testing.internal.armeria.common.HttpData;
+import io.opentelemetry.testing.internal.armeria.common.MediaType;
+import io.opentelemetry.testing.internal.armeria.common.QueryParams;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import java.util.function.Consumer;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.ValueSource;
+import org.springframework.context.ConfigurableApplicationContext;
+import org.springframework.security.web.util.OnCommittedResponseWrapper;
+import org.springframework.web.servlet.view.RedirectView;
+
+public abstract class AbstractSpringBootBasedTest
+    extends AbstractHttpServerTest<ConfigurableApplicationContext> {
+
+  protected abstract ConfigurableApplicationContext context();
+
+  protected abstract Class<?> securityConfigClass();
+
+  @Override
+  protected void stopServer(ConfigurableApplicationContext ctx) {
+    ctx.close();
+  }
+
+  @Override
+  protected void configure(HttpServerTestOptions options) {
+    super.configure(options);
+    options.setContextPath("/xyz");
+    options.setHasHandlerSpan(unused -> true);
+    options.setHasResponseSpan(endpoint -> endpoint == REDIRECT || endpoint == NOT_FOUND);
+    options.setTestPathParam(true);
+    options.setHasErrorPageSpans(endpoint -> endpoint == NOT_FOUND);
+    options.setHasRenderSpan(endpoint -> endpoint == REDIRECT);
+  }
+
+  @Override
+  public String expectedHttpRoute(ServerEndpoint endpoint, String method) {
+    if (HttpConstants._OTHER.equals(method)) {
+      return getContextPath() + endpoint.getPath();
+    }
+    switch (endpoint.name()) {
+      case "PATH_PARAM":
+        return getContextPath() + "/path/{id}/param";
+      case "NOT_FOUND":
+        return getContextPath() + "/**";
+      case "LOGIN":
+        return getContextPath() + "/*";
+      default:
+        return super.expectedHttpRoute(endpoint, method);
+    }
+  }
+
+  @Test
+  void testSpansWithAuthError() {
+    SavingAuthenticationProvider authProvider =
+        context().getBean(SavingAuthenticationProvider.class);
+    AggregatedHttpRequest request = request(AUTH_ERROR, "GET");
+
+    authProvider.latestAuthentications.clear();
+    AggregatedHttpResponse response = client.execute(request).aggregate().join();
+
+    assertThat(response.status().code()).isEqualTo(401); // not secured
+
+    testing()
+        .waitAndAssertTraces(
+            trace ->
+                trace.hasSpansSatisfyingExactly(
+                    span -> assertServerSpan(span, "GET", AUTH_ERROR, AUTH_ERROR.getStatus()),
+                    span ->
+                        span.satisfies(
+                                spanData -> assertThat(spanData.getName()).endsWith(".sendError"))
+                            .hasKind(SpanKind.INTERNAL),
+                    span -> errorPageSpanAssertions(null, null)));
+  }
+
+  @ParameterizedTest
+  @ValueSource(strings = {"password", "dfsdföääöüüä", "🤓"})
+  void testCharacterEncodingOfTestPassword(String testPassword) {
+    SavingAuthenticationProvider authProvider =
+        context().getBean(SavingAuthenticationProvider.class);
+
+    QueryParams form = QueryParams.of("username", "test", "password", testPassword);
+    AggregatedHttpRequest request =
+        AggregatedHttpRequest.of(
+            request(LOGIN, "POST").headers().toBuilder().contentType(MediaType.FORM_DATA).build(),
+            HttpData.ofUtf8(form.toQueryString()));
+
+    authProvider.latestAuthentications.clear();
+    AggregatedHttpResponse response = client.execute(request).aggregate().join();
+
+    assertThat(response.status().code()).isEqualTo(302); // redirect after success
+    assertThat(authProvider.latestAuthentications.get(0).getPassword()).isEqualTo(testPassword);
+
+    testing()
+        .waitAndAssertTraces(
+            trace ->
+                trace.hasSpansSatisfyingExactly(
+                    span -> assertServerSpan(span, "POST", LOGIN, LOGIN.getStatus()),
+                    span ->
+                        span.satisfies(
+                                spanData ->
+                                    assertThat(spanData.getName()).endsWith(".sendRedirect"))
+                            .hasKind(SpanKind.INTERNAL)));
+  }
+
+  @Override
+  protected List<Consumer<SpanDataAssert>> errorPageSpanAssertions(
+      String method, ServerEndpoint endpoint) {
+    List<Consumer<SpanDataAssert>> spanAssertions = new ArrayList<>();
+    spanAssertions.add(
+        span ->
+            span.hasName("BasicErrorController.error")
+                .hasKind(SpanKind.INTERNAL)
+                .hasAttributesSatisfying(Attributes::isEmpty));
+    return spanAssertions;
+  }
+
+  @Override
+  protected SpanDataAssert assertResponseSpan(
+      SpanDataAssert span, String method, ServerEndpoint endpoint) {
+    String methodName = endpoint == NOT_FOUND ? "sendError" : "sendRedirect";
+    if (endpoint == NOT_FOUND) {
+      span.satisfies(spanData -> assertThat(spanData.getName()).endsWith(".sendError"));
+    } else {
+      span.satisfies(spanData -> assertThat(spanData.getName()).endsWith(".sendRedirect"));
+    }
+
+    span.hasKind(SpanKind.INTERNAL)
+        .hasAttributesSatisfyingExactly(
+            satisfies(
+                CODE_NAMESPACE,
+                val ->
+                    val.satisfiesAnyOf(
+                        v -> assertThat(v).isEqualTo(OnCommittedResponseWrapper.class.getName()),
+                        v ->
+                            assertThat(v)
+                                .isEqualTo(
+                                    "org.springframework.security.web.firewall.FirewalledResponse"),
+                        v ->
+                            assertThat(v)
+                                .isEqualTo("jakarta.servlet.http.HttpServletResponseWrapper"))),
+            equalTo(CODE_FUNCTION, methodName));
+    return span;
+  }
+
+  @Override
+  protected SpanDataAssert assertRenderSpan(
+      SpanDataAssert span, String method, ServerEndpoint endpoint) {
+    span.hasName("Render RedirectView")
+        .hasKind(SpanKind.INTERNAL)
+        .hasAttributesSatisfyingExactly(
+            equalTo(
+                AttributeKey.stringKey("spring-webmvc.view.type"), RedirectView.class.getName()));
+    return span;
+  }
+
+  @Override
+  protected SpanDataAssert assertHandlerSpan(
+      SpanDataAssert span, String method, ServerEndpoint endpoint) {
+    String handlerSpanName = getHandlerSpanName(endpoint);
+    if (endpoint == NOT_FOUND) {
+      handlerSpanName = "ResourceHttpRequestHandler.handleRequest";
+    }
+    span.hasName(handlerSpanName).hasKind(SpanKind.INTERNAL);
+    if (endpoint == EXCEPTION) {
+      span.hasStatus(StatusData.error());
+      span.hasEventsSatisfyingExactly(
+          event ->
+              event
+                  .hasName("exception")
+                  .hasAttributesSatisfyingExactly(
+                      equalTo(EXCEPTION_TYPE, "java.lang.RuntimeException"),
+                      equalTo(EXCEPTION_MESSAGE, EXCEPTION.getBody()),
+                      satisfies(EXCEPTION_STACKTRACE, val -> val.isInstanceOf(String.class))));
+    }
+    return span;
+  }
+
+  private static String getHandlerSpanName(ServerEndpoint endpoint) {
+    if (QUERY_PARAM.equals(endpoint)) {
+      return "TestController.queryParam";
+    } else if (PATH_PARAM.equals(endpoint)) {
+      return "TestController.pathParam";
+    } else if (CAPTURE_HEADERS.equals(endpoint)) {
+      return "TestController.captureHeaders";
+    } else if (INDEXED_CHILD.equals(endpoint)) {
+      return "TestController.indexedChild";
+    }
+    return "TestController." + endpoint.name().toLowerCase(Locale.ROOT);
+  }
+}

+ 3 - 5
instrumentation/spring/spring-webmvc/spring-webmvc-common/testing/src/main/groovy/boot/AppConfig.groovy → instrumentation/spring/spring-webmvc/spring-webmvc-common/testing/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/boot/AppConfig.java

@@ -3,11 +3,9 @@
  * SPDX-License-Identifier: Apache-2.0
  */
 
-package boot
+package io.opentelemetry.instrumentation.spring.webmvc.boot;
 
-import org.springframework.boot.autoconfigure.SpringBootApplication
+import org.springframework.boot.autoconfigure.SpringBootApplication;
 
 @SpringBootApplication
-class AppConfig {
-
-}
+public class AppConfig {}

+ 34 - 0
instrumentation/spring/spring-webmvc/spring-webmvc-common/testing/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/boot/SavingAuthenticationProvider.java

@@ -0,0 +1,34 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.instrumentation.spring.webmvc.boot;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider;
+import org.springframework.security.core.userdetails.UserDetails;
+
+public class SavingAuthenticationProvider extends AbstractUserDetailsAuthenticationProvider {
+
+  List<TestUserDetails> latestAuthentications = new ArrayList<>();
+
+  @Override
+  protected void additionalAuthenticationChecks(
+      UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) {
+    // none
+  }
+
+  @Override
+  protected UserDetails retrieveUser(
+      String username, UsernamePasswordAuthenticationToken authentication) {
+    TestUserDetails details =
+        new TestUserDetails(username, authentication.getCredentials().toString());
+
+    latestAuthentications.add(details);
+
+    return details;
+  }
+}

+ 108 - 0
instrumentation/spring/spring-webmvc/spring-webmvc-common/testing/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/boot/TestController.java

@@ -0,0 +1,108 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.instrumentation.spring.webmvc.boot;
+
+import static io.opentelemetry.instrumentation.testing.junit.http.AbstractHttpServerTest.controller;
+import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.CAPTURE_HEADERS;
+import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.ERROR;
+import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.EXCEPTION;
+import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.INDEXED_CHILD;
+import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.PATH_PARAM;
+import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.QUERY_PARAM;
+import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.REDIRECT;
+import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.SUCCESS;
+
+import java.util.Objects;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestHeader;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.servlet.view.RedirectView;
+
+@Controller
+public class TestController {
+
+  @RequestMapping("/basicsecured/endpoint")
+  @ResponseBody
+  String secureEndpoint() {
+    return controller(SUCCESS, SUCCESS::getBody);
+  }
+
+  @RequestMapping("/success")
+  @ResponseBody
+  String success() {
+    return controller(SUCCESS, SUCCESS::getBody);
+  }
+
+  @RequestMapping("/query")
+  @ResponseBody
+  String queryParam(@RequestParam("some") String param) {
+    return controller(QUERY_PARAM, () -> "some=" + param);
+  }
+
+  @RequestMapping("/redirect")
+  @ResponseBody
+  RedirectView redirect() {
+    return controller(REDIRECT, () -> new RedirectView(REDIRECT.getBody()));
+  }
+
+  @RequestMapping("/error-status")
+  ResponseEntity<String> error() {
+    return controller(
+        ERROR,
+        () ->
+            ResponseEntity.status(HttpStatus.valueOf(ERROR.getStatus()).value())
+                .body(ERROR.getBody()));
+  }
+
+  @SuppressWarnings("ThrowSpecificExceptions")
+  @RequestMapping("/exception")
+  ResponseEntity<String> exception() {
+    return controller(
+        EXCEPTION,
+        () -> {
+          throw new RuntimeException(EXCEPTION.getBody());
+        });
+  }
+
+  @RequestMapping("/captureHeaders")
+  ResponseEntity<String> captureHeaders(@RequestHeader("X-Test-Request") String testRequestHeader) {
+    return controller(
+        CAPTURE_HEADERS,
+        () ->
+            ResponseEntity.ok()
+                .header("X-Test-Response", testRequestHeader)
+                .body(CAPTURE_HEADERS.getBody()));
+  }
+
+  @RequestMapping("/path/{id}/param")
+  @ResponseBody
+  String pathParam(@PathVariable("id") int id) {
+    return controller(PATH_PARAM, () -> String.valueOf(id));
+  }
+
+  @RequestMapping("/child")
+  @ResponseBody
+  String indexedChild(@RequestParam("id") String id) {
+    return controller(
+        INDEXED_CHILD,
+        () -> {
+          INDEXED_CHILD.collectSpanAttributes(it -> Objects.equals(it, "id") ? id : null);
+          return INDEXED_CHILD.getBody();
+        });
+  }
+
+  @ExceptionHandler
+  ResponseEntity<String> handleException(Throwable throwable) {
+    return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR.value())
+        .body(throwable.getMessage());
+  }
+}

+ 58 - 0
instrumentation/spring/spring-webmvc/spring-webmvc-common/testing/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/boot/TestUserDetails.java

@@ -0,0 +1,58 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.instrumentation.spring.webmvc.boot;
+
+import java.util.Collection;
+import java.util.Collections;
+import org.springframework.security.core.GrantedAuthority;
+import org.springframework.security.core.userdetails.UserDetails;
+
+public class TestUserDetails implements UserDetails {
+
+  private static final long serialVersionUID = 6470776949615799570L;
+  private final String username;
+  private final String password;
+
+  TestUserDetails(String username, String password) {
+    this.username = username;
+    this.password = password;
+  }
+
+  @Override
+  public Collection<? extends GrantedAuthority> getAuthorities() {
+    return Collections.emptySet();
+  }
+
+  @Override
+  public String getPassword() {
+    return password;
+  }
+
+  @Override
+  public String getUsername() {
+    return username;
+  }
+
+  @Override
+  public boolean isAccountNonExpired() {
+    return true;
+  }
+
+  @Override
+  public boolean isAccountNonLocked() {
+    return true;
+  }
+
+  @Override
+  public boolean isCredentialsNonExpired() {
+    return true;
+  }
+
+  @Override
+  public boolean isEnabled() {
+    return true;
+  }
+}

+ 116 - 0
instrumentation/spring/spring-webmvc/spring-webmvc-common/testing/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/filter/AbstractServletFilterTest.java

@@ -0,0 +1,116 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.instrumentation.spring.webmvc.filter;
+
+import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.CAPTURE_HEADERS;
+import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.ERROR;
+import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.EXCEPTION;
+import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.INDEXED_CHILD;
+import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.NOT_FOUND;
+import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.PATH_PARAM;
+import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.QUERY_PARAM;
+import static io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint.REDIRECT;
+import static org.assertj.core.api.Assertions.assertThat;
+
+import io.opentelemetry.api.common.Attributes;
+import io.opentelemetry.api.trace.SpanKind;
+import io.opentelemetry.instrumentation.api.internal.HttpConstants;
+import io.opentelemetry.instrumentation.testing.junit.http.AbstractHttpServerTest;
+import io.opentelemetry.instrumentation.testing.junit.http.HttpServerTestOptions;
+import io.opentelemetry.instrumentation.testing.junit.http.ServerEndpoint;
+import io.opentelemetry.sdk.testing.assertj.SpanDataAssert;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import java.util.function.Consumer;
+import org.springframework.context.ConfigurableApplicationContext;
+
+public abstract class AbstractServletFilterTest
+    extends AbstractHttpServerTest<ConfigurableApplicationContext> {
+
+  protected abstract Class<?> securityConfigClass();
+
+  protected abstract Class<?> filterConfigClass();
+
+  @Override
+  protected void stopServer(ConfigurableApplicationContext ctx) {
+    ctx.close();
+  }
+
+  @Override
+  protected void configure(HttpServerTestOptions options) {
+    super.configure(options);
+    options.setHasHandlerSpan(endpoint -> endpoint == NOT_FOUND);
+    options.setHasErrorPageSpans(
+        endpoint -> endpoint == ERROR || endpoint == EXCEPTION || endpoint == NOT_FOUND);
+    options.setHasResponseSpan(
+        endpoint -> endpoint == REDIRECT || endpoint == ERROR || endpoint == NOT_FOUND);
+    options.setTestPathParam(true);
+  }
+
+  @Override
+  protected SpanDataAssert assertResponseSpan(
+      SpanDataAssert span, String method, ServerEndpoint endpoint) {
+    if (endpoint == REDIRECT) {
+      span.satisfies(spanData -> assertThat(spanData.getName()).endsWith(".sendRedirect"));
+    } else if (endpoint == ERROR || endpoint == NOT_FOUND) {
+      span.satisfies(spanData -> assertThat(spanData.getName()).endsWith(".sendError"));
+    }
+    span.hasKind(SpanKind.INTERNAL);
+    return span;
+  }
+
+  @Override
+  protected SpanDataAssert assertHandlerSpan(
+      SpanDataAssert span, String method, ServerEndpoint endpoint) {
+    String handlerSpanName = getHandlerSpanName(endpoint);
+    if (endpoint == NOT_FOUND) {
+      handlerSpanName = "ResourceHttpRequestHandler.handleRequest";
+    }
+    span.hasName(handlerSpanName).hasKind(SpanKind.INTERNAL);
+    return span;
+  }
+
+  @Override
+  public String expectedHttpRoute(ServerEndpoint endpoint, String method) {
+    if (HttpConstants._OTHER.equals(method)) {
+      return getContextPath() + endpoint.getPath();
+    }
+    switch (endpoint.name()) {
+      case "PATH_PARAM":
+        return getContextPath() + "/path/{id}/param";
+      case "NOT_FOUND":
+        return getContextPath() + "/**";
+      default:
+        return super.expectedHttpRoute(endpoint, method);
+    }
+  }
+
+  @Override
+  protected List<Consumer<SpanDataAssert>> errorPageSpanAssertions(
+      String method, ServerEndpoint endpoint) {
+    List<Consumer<SpanDataAssert>> spanAssertions = new ArrayList<>();
+    spanAssertions.add(
+        span ->
+            span.hasName("BasicErrorController.error")
+                .hasKind(SpanKind.INTERNAL)
+                .hasAttributesSatisfying(Attributes::isEmpty));
+    return spanAssertions;
+  }
+
+  private static String getHandlerSpanName(ServerEndpoint endpoint) {
+    if (QUERY_PARAM.equals(endpoint)) {
+      return "TestController.queryParam";
+    } else if (PATH_PARAM.equals(endpoint)) {
+      return "TestController.pathParam";
+    } else if (CAPTURE_HEADERS.equals(endpoint)) {
+      return "TestController.captureHeaders";
+    } else if (INDEXED_CHILD.equals(endpoint)) {
+      return "TestController.indexedChild";
+    }
+    return "TestController." + endpoint.name().toLowerCase(Locale.ROOT);
+  }
+}

+ 135 - 0
instrumentation/spring/spring-webmvc/spring-webmvc-common/testing/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/filter/FilteredAppConfig.java

@@ -0,0 +1,135 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.instrumentation.spring.webmvc.filter;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import javax.annotation.Nullable;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.annotation.Bean;
+import org.springframework.format.FormatterRegistry;
+import org.springframework.http.HttpInputMessage;
+import org.springframework.http.HttpOutputMessage;
+import org.springframework.http.MediaType;
+import org.springframework.http.converter.AbstractHttpMessageConverter;
+import org.springframework.http.converter.HttpMessageConverter;
+import org.springframework.util.StreamUtils;
+import org.springframework.validation.MessageCodesResolver;
+import org.springframework.validation.Validator;
+import org.springframework.web.method.support.HandlerMethodArgumentResolver;
+import org.springframework.web.method.support.HandlerMethodReturnValueHandler;
+import org.springframework.web.servlet.HandlerExceptionResolver;
+import org.springframework.web.servlet.config.annotation.AsyncSupportConfigurer;
+import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
+import org.springframework.web.servlet.config.annotation.CorsRegistry;
+import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
+import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
+import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
+import org.springframework.web.servlet.config.annotation.ViewResolverRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+@SpringBootApplication
+public class FilteredAppConfig implements WebMvcConfigurer {
+
+  @Override
+  public void configurePathMatch(PathMatchConfigurer configurer) {}
+
+  @Override
+  public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
+    configurer
+        .favorPathExtension(false)
+        .favorParameter(true)
+        .ignoreAcceptHeader(true)
+        .useJaf(false)
+        .defaultContentTypeStrategy(webRequest -> Collections.singletonList(MediaType.TEXT_PLAIN));
+  }
+
+  @Override
+  public void configureAsyncSupport(AsyncSupportConfigurer configurer) {}
+
+  @Override
+  public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {}
+
+  @Override
+  public void addFormatters(FormatterRegistry registry) {}
+
+  @Override
+  public void addInterceptors(InterceptorRegistry registry) {}
+
+  @Override
+  public void addResourceHandlers(ResourceHandlerRegistry registry) {}
+
+  @Override
+  public void addCorsMappings(CorsRegistry registry) {}
+
+  @Override
+  public void addViewControllers(ViewControllerRegistry registry) {}
+
+  @Override
+  public void configureViewResolvers(ViewResolverRegistry registry) {}
+
+  @Override
+  public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {}
+
+  @Override
+  public void addReturnValueHandlers(List<HandlerMethodReturnValueHandler> returnValueHandlers) {}
+
+  @Override
+  public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {}
+
+  @Override
+  public void extendMessageConverters(List<HttpMessageConverter<?>> converters) {}
+
+  @Override
+  public void configureHandlerExceptionResolvers(
+      List<HandlerExceptionResolver> exceptionResolvers) {}
+
+  @Override
+  public void extendHandlerExceptionResolvers(List<HandlerExceptionResolver> exceptionResolvers) {}
+
+  @Override
+  public Validator getValidator() {
+    return null;
+  }
+
+  @Override
+  public MessageCodesResolver getMessageCodesResolver() {
+    return null;
+  }
+
+  @Bean
+  HttpMessageConverter<Map<String, Object>> createPlainMapMessageConverter() {
+
+    return new AbstractHttpMessageConverter<Map<String, Object>>(MediaType.TEXT_PLAIN) {
+
+      @Override
+      protected boolean supports(Class<?> clazz) {
+        return Map.class.isAssignableFrom(clazz);
+      }
+
+      @Nullable
+      @Override
+      protected Map<String, Object> readInternal(
+          Class<? extends Map<String, Object>> clazz, HttpInputMessage inputMessage) {
+        return null;
+      }
+
+      @Override
+      protected void writeInternal(
+          Map<String, Object> stringObjectMap, HttpOutputMessage outputMessage) throws IOException {
+        StreamUtils.copy(
+            (String) stringObjectMap.get("message"),
+            StandardCharsets.UTF_8,
+            outputMessage.getBody());
+      }
+    };
+  }
+}

+ 74 - 0
instrumentation/spring/spring-webmvc/spring-webmvc-common/testing/src/main/java/io/opentelemetry/instrumentation/spring/webmvc/filter/TestController.java

@@ -0,0 +1,74 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ * SPDX-License-Identifier: Apache-2.0
+ */
+
+package io.opentelemetry.instrumentation.spring.webmvc.filter;
+
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.servlet.view.RedirectView;
+
+/**
+ * None of the methods in this controller should be called because they are intercepted by the
+ * filter
+ */
+@Controller
+public class TestController {
+
+  @RequestMapping("/success")
+  @ResponseBody
+  String success() throws Exception {
+    throw new Exception("This should not be called");
+  }
+
+  @RequestMapping("/query")
+  @ResponseBody
+  String queryParam(@RequestParam("some") String param) throws Exception {
+    throw new Exception("This should not be called");
+  }
+
+  @RequestMapping("/path/{id}/param")
+  @ResponseBody
+  String pathParam(@PathVariable Integer id) throws Exception {
+    throw new Exception("This should not be called");
+  }
+
+  @RequestMapping("/redirect")
+  @ResponseBody
+  RedirectView redirect() throws Exception {
+    throw new Exception("This should not be called");
+  }
+
+  @RequestMapping("/error-status")
+  ResponseEntity<Object> error() throws Exception {
+    throw new Exception("This should not be called");
+  }
+
+  @RequestMapping("/exception")
+  ResponseEntity<Object> exception() throws Exception {
+    throw new Exception("This should not be called");
+  }
+
+  @RequestMapping("/captureHeaders")
+  ResponseEntity<Object> captureHeaders() throws Exception {
+    throw new Exception("This should not be called");
+  }
+
+  @RequestMapping("/child")
+  @ResponseBody
+  ResponseEntity<Object> indexedChild(@RequestParam("id") String id) throws Exception {
+    throw new Exception("This should not be called");
+  }
+
+  @ExceptionHandler
+  ResponseEntity<String> handleException(Throwable throwable) {
+    return new ResponseEntity<>(throwable.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
+  }
+}

+ 17 - 4
testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/http/AbstractHttpServerTest.java

@@ -650,15 +650,15 @@ public abstract class AbstractHttpServerTest<SERVER> extends AbstractHttpServerU
                         span, endpoint == EXCEPTION ? options.expectedException : null);
                     span.hasParent(trace.getSpan(finalParentIndex));
                   });
+              if (options.hasRenderSpan.test(endpoint)) {
+                spanAssertions.add(span -> assertRenderSpan(span, method, endpoint));
+              }
             }
 
             if (options.hasResponseSpan.test(endpoint)) {
               int parentIndex = spanAssertions.size() - 1;
               spanAssertions.add(
-                  span -> {
-                    assertResponseSpan(span, method, endpoint);
-                    span.hasParent(trace.getSpan(parentIndex));
-                  });
+                  span -> assertResponseSpan(span, trace.getSpan(parentIndex), method, endpoint));
             }
 
             if (options.hasErrorPageSpans.test(endpoint)) {
@@ -699,12 +699,25 @@ public abstract class AbstractHttpServerTest<SERVER> extends AbstractHttpServerU
         "assertHandlerSpan not implemented in " + getClass().getName());
   }
 
+  @CanIgnoreReturnValue
+  protected SpanDataAssert assertResponseSpan(
+      SpanDataAssert span, SpanData parentSpan, String method, ServerEndpoint endpoint) {
+    span.hasParent(parentSpan);
+    return assertResponseSpan(span, method, endpoint);
+  }
+
   protected SpanDataAssert assertResponseSpan(
       SpanDataAssert span, String method, ServerEndpoint endpoint) {
     throw new UnsupportedOperationException(
         "assertResponseSpan not implemented in " + getClass().getName());
   }
 
+  protected SpanDataAssert assertRenderSpan(
+      SpanDataAssert span, String method, ServerEndpoint endpoint) {
+    throw new UnsupportedOperationException(
+        "assertRenderSpan not implemented in " + getClass().getName());
+  }
+
   protected List<Consumer<SpanDataAssert>> errorPageSpanAssertions(
       String method, ServerEndpoint endpoint) {
     throw new UnsupportedOperationException(

+ 7 - 0
testing-common/src/main/java/io/opentelemetry/instrumentation/testing/junit/http/HttpServerTestOptions.java

@@ -48,6 +48,7 @@ public final class HttpServerTestOptions {
 
   Predicate<ServerEndpoint> hasHandlerSpan = unused -> false;
   Predicate<ServerEndpoint> hasResponseSpan = unused -> false;
+  Predicate<ServerEndpoint> hasRenderSpan = unused -> false;
   Predicate<ServerEndpoint> hasErrorPageSpans = unused -> false;
   Predicate<ServerEndpoint> hasResponseCustomizer = unused -> false;
 
@@ -132,6 +133,12 @@ public final class HttpServerTestOptions {
     return this;
   }
 
+  @CanIgnoreReturnValue
+  public HttpServerTestOptions setHasRenderSpan(Predicate<ServerEndpoint> hasRenderSpan) {
+    this.hasRenderSpan = hasRenderSpan;
+    return this;
+  }
+
   @CanIgnoreReturnValue
   public HttpServerTestOptions setHasErrorPageSpans(Predicate<ServerEndpoint> hasErrorPageSpans) {
     this.hasErrorPageSpans = hasErrorPageSpans;