Skip to content

Commit 561c71a

Browse files
committed
test(spring): Move the Spring jakarta WAR sample's tests into the module
Adds an integrationTest suite to sentry-spring-jakarta that runs a plain Spring MVC app, set up by an AbstractAnnotationConfigDispatcherServletInitializer as in a WAR, in an embedded Tomcat against MockSentryServer. Tomcat runs every servlet container initializer the classpath registers, as it does for a WAR, and Spring Boot stays off the classpath, so Sentry's initializer adds SentrySpringFilter the way it does in a real deployment. The suite ports the sample's Person and Metrics tests and also checks that the error carries its request. The profiler rejects a traces path with a dot anywhere in its absolute form, so the suite puts it in the temp dir. The sample has no system tests left, so it comes off the system-test runner, the backend workflow and the Spring Boot 3 matrix. That was the matrix's last runner step, so it drops the Python setup and the runner's logs too. integrationTests includes the suite. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
1 parent 16f848b commit 561c71a

16 files changed

Lines changed: 316 additions & 208 deletions

File tree

‎.github/workflows/spring-boot-3-matrix.yml‎

Lines changed: 2 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,6 @@ jobs:
2525

2626
name: Spring Boot ${{ matrix.springboot-version }}
2727
env:
28-
SENTRY_URL: http://127.0.0.1:8000
2928
GRADLE_ENCRYPTION_KEY: ${{ secrets.GRADLE_ENCRYPTION_KEY }}
3029

3130
steps:
@@ -34,16 +33,6 @@ jobs:
3433
with:
3534
submodules: 'recursive'
3635

37-
- name: Set up Python
38-
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
39-
with:
40-
python-version: '3.10'
41-
42-
- name: Install Python dependencies
43-
run: |
44-
python3 -m pip install --upgrade pip
45-
python3 -m pip install -r requirements.txt
46-
4736
- name: Set up Java
4837
uses: actions/setup-java@b843ebf9b913146b38da2eaa7aeddf76074ae1ad # v6.1.0
4938
with:
@@ -68,29 +57,17 @@ jobs:
6857
perl -0pi -e 'BEGIN { $v = shift } s/^springboot3[[:space:]]*=[[:space:]]*"\K[^"]*/$v/m or die "::error::springboot3 version entry not found in gradle/libs.versions.toml\n"' "$springboot_version" gradle/libs.versions.toml
6958
echo "Updated Spring Boot 3.x version to $springboot_version"
7059
71-
- name: Build sample artifacts
72-
run: |
73-
./gradlew \
74-
:sentry-samples:sentry-samples-spring-jakarta:war \
75-
:sentry-samples:sentry-samples-spring-jakarta:testClasses
76-
77-
- name: Test sentry-spring-boot-jakarta integration suites
60+
- name: Test integration suites
7861
run: |
7962
./gradlew \
63+
:sentry-spring-jakarta:integrationTest \
8064
:sentry-spring-boot-jakarta:integrationTest \
8165
:sentry-spring-boot-jakarta:integrationTestWebflux \
8266
:sentry-spring-boot-jakarta:integrationTestOtelNoAgent \
8367
:sentry-spring-boot-jakarta:integrationTestLog4j2 \
8468
:sentry-spring-boot-jakarta:integrationTestOtelAgent \
8569
:sentry-spring-boot-jakarta:integrationTestOtelAgentWithoutAutoInit
8670
87-
- name: Test sentry-samples-spring-jakarta
88-
run: |
89-
python3 test/system-test-runner.py test \
90-
--module "sentry-samples-spring-jakarta" \
91-
--agent false \
92-
--auto-init "true"
93-
9471
- name: Upload test results
9572
if: always()
9673
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7
@@ -99,8 +76,6 @@ jobs:
9976
path: |
10077
**/build/reports/*
10178
**/build/test-results/**/*.xml
102-
sentry-mock-server.txt
103-
spring-server.txt
10479
10580
- name: Test Report
10681
uses: phoenix-actions/test-reporting@7317eea6e13c47348dd0bb318669485157c518d6 # pin@v16

‎.github/workflows/system-tests-backend.yml‎

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,6 @@ jobs:
4343
- sample: "sentry-samples-spring-7"
4444
agent: "false"
4545
agent-auto-init: "true"
46-
- sample: "sentry-samples-spring-jakarta"
47-
agent: "false"
48-
agent-auto-init: "true"
4946
- sample: "sentry-samples-spring"
5047
agent: "false"
5148
agent-auto-init: "true"

‎build.gradle.kts‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,7 @@ tasks.register("integrationTests") {
240240
":sentry-spring-boot-4:integrationTestLog4j2",
241241
":sentry-spring-boot-4:integrationTestOtelAgent",
242242
":sentry-spring-boot-4:integrationTestOtelAgentWithoutAutoInit",
243+
":sentry-spring-jakarta:integrationTest",
243244
)
244245
}
245246

‎gradle/libs.versions.toml‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,8 @@ tomcat-catalina = { module = "org.apache.tomcat:tomcat-catalina", version = "9.0
256256
tomcat-embed-jasper = { module = "org.apache.tomcat.embed:tomcat-embed-jasper", version = "9.0.108" }
257257
tomcat-catalina-jakarta = { module = "org.apache.tomcat:tomcat-catalina", version = "11.0.22" }
258258
tomcat-embed-jasper-jakarta = { module = "org.apache.tomcat.embed:tomcat-embed-jasper", version = "11.0.22" }
259+
# The version comes from the Spring Boot BOM of whichever Spring line uses it.
260+
tomcat-embed-core = { module = "org.apache.tomcat.embed:tomcat-embed-core" }
259261

260262
# test libraries
261263
androidx-benchmark-macro-junit4 = { module = "androidx.benchmark:benchmark-macro-junit4", version = "1.4.1" }

‎sentry-samples/sentry-samples-spring-jakarta/build.gradle.kts‎

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ plugins {
77
alias(libs.plugins.kotlin.spring)
88
id("war")
99
alias(libs.plugins.gretty)
10-
id("io.sentry.systemtest")
1110
}
1211

1312
application { mainClass.set("io.sentry.samples.spring.jakarta.Main") }
@@ -19,9 +18,6 @@ group = "io.sentry.sample.spring-jakarta"
1918

2019
version = "0.0.1-SNAPSHOT"
2120

22-
// Apollo 4.x requires coroutines 1.9.0+, override Spring Boot's managed version
23-
extra["kotlin-coroutines.version"] = "1.9.0"
24-
2521
dependencyManagement {
2622
imports {
2723
mavenBom("org.springframework.boot:spring-boot-dependencies:${libs.versions.springboot3.get()}")
@@ -48,35 +44,10 @@ dependencies {
4844

4945
implementation(libs.tomcat.catalina.jakarta)
5046
implementation(libs.tomcat.embed.jasper.jakarta)
51-
52-
testImplementation(projects.sentrySystemTestSupport)
53-
testImplementation(libs.kotlin.test.junit)
54-
testImplementation(libs.springboot3.starter.test) {
55-
exclude(group = "org.junit.vintage", module = "junit-vintage-engine")
56-
}
5747
}
5848

5949
tasks.withType<KotlinCompile>().configureEach {
6050
kotlin {
6151
compilerOptions.freeCompilerArgs = listOf("-Xjsr305=strict")
6252
}
6353
}
64-
65-
tasks.register<Test>("systemTest").configure {
66-
group = "verification"
67-
description = "Runs the System tests"
68-
69-
val test = project.extensions.getByType<SourceSetContainer>()["test"]
70-
testClassesDirs = test.output.classesDirs
71-
classpath = test.runtimeClasspath
72-
73-
maxParallelForks = 1
74-
75-
filter { includeTestsMatching("io.sentry.systemtest*") }
76-
}
77-
78-
tasks.named("test").configure {
79-
require(this is Test)
80-
81-
filter { excludeTestsMatching("io.sentry.systemtest.*") }
82-
}

‎sentry-samples/sentry-samples-spring-jakarta/src/test/kotlin/io/sentry/DummyTest.kt‎

Lines changed: 0 additions & 12 deletions
This file was deleted.

‎sentry-samples/sentry-samples-spring-jakarta/src/test/kotlin/io/sentry/systemtest/MetricsSystemTest.kt‎

Lines changed: 0 additions & 51 deletions
This file was deleted.

‎sentry-samples/sentry-samples-spring-jakarta/src/test/kotlin/io/sentry/systemtest/PersonSystemTest.kt‎

Lines changed: 0 additions & 85 deletions
This file was deleted.

‎sentry-spring-jakarta/build.gradle.kts‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,3 +107,25 @@ tasks.jar {
107107
)
108108
}
109109
}
110+
111+
testing {
112+
suites {
113+
// Runs a plain Spring MVC app in an embedded Tomcat against MockSentryServer, the way a servlet
114+
// container runs a WAR. Spring Boot isn't on its classpath, because Sentry behaves differently
115+
// with it.
116+
register<JvmTestSuite>("integrationTest") {
117+
useJUnit()
118+
dependencies {
119+
implementation(platform(SpringBootPlugin.BOM_COORDINATES))
120+
implementation(project())
121+
implementation(projects.sentryAsyncProfiler)
122+
implementation(projects.sentryTestSupport)
123+
implementation(Config.Libs.springWeb)
124+
implementation(libs.jackson.databind)
125+
implementation(libs.kotlin.test.junit)
126+
implementation(libs.google.truth)
127+
implementation(libs.tomcat.embed.core)
128+
}
129+
}
130+
}
131+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package io.sentry.spring.jakarta.integrationtest
2+
3+
import io.sentry.test.MockSentryServer
4+
import jakarta.servlet.ServletContainerInitializer
5+
import java.nio.file.Files
6+
import java.util.ServiceLoader
7+
import org.apache.catalina.startup.Tomcat
8+
import org.junit.Before
9+
import org.springframework.http.client.ClientHttpResponse
10+
import org.springframework.web.client.DefaultResponseErrorHandler
11+
import org.springframework.web.client.RestTemplate
12+
import org.springframework.web.util.DefaultUriBuilderFactory
13+
14+
/**
15+
* Drives [AppInitializer]'s app over HTTP and asserts what reaches [server].
16+
*
17+
* Subclasses share one app and one server. Envelopes that arrive late can outlive [server]'s reset,
18+
* so tests match on values only they produce.
19+
*/
20+
abstract class IntegrationTest {
21+
val server = TomcatApp.server
22+
val restTemplate = TomcatApp.restTemplate
23+
24+
@Before
25+
fun resetServer() {
26+
server.reset()
27+
}
28+
}
29+
30+
/**
31+
* The app in an embedded Tomcat, with no Spring Boot on the classpath, so Sentry takes the same
32+
* path as in a WAR. Tomcat starts once per test JVM.
33+
*/
34+
object TomcatApp {
35+
val server = MockSentryServer()
36+
37+
init {
38+
System.setProperty("sentry.dsn", server.dsn)
39+
// The profiler rejects a traces path with a dot anywhere in it, which the module's absolute
40+
// path can have.
41+
System.setProperty(
42+
"sentry.profiling-traces-dir-path",
43+
"${System.getProperty("java.io.tmpdir")}/sentry-integration-test/profiling-traces",
44+
)
45+
}
46+
47+
private val tomcat =
48+
Tomcat().apply {
49+
setBaseDir(Files.createTempDirectory("tomcat").toString())
50+
setPort(0)
51+
// Creates the default connector.
52+
connector
53+
val context = addContext("", Files.createTempDirectory("docbase").toString())
54+
// As Tomcat does for a WAR, runs each initializer the classpath registers: Spring's starts
55+
// AppInitializer's app, and Sentry's adds SentrySpringFilter. Only Spring's uses the set.
56+
ServiceLoader.load(ServletContainerInitializer::class.java).forEach {
57+
context.addServletContainerInitializer(it, setOf(AppInitializer::class.java))
58+
}
59+
start()
60+
}
61+
62+
val restTemplate =
63+
RestTemplate().apply {
64+
uriTemplateHandler =
65+
DefaultUriBuilderFactory("http://localhost:${tomcat.connector.localPort}")
66+
// Returns error responses rather than throwing them, so tests can check a failed request.
67+
errorHandler =
68+
object : DefaultResponseErrorHandler() {
69+
override fun hasError(response: ClientHttpResponse): Boolean = false
70+
}
71+
}
72+
}

0 commit comments

Comments
 (0)