Skip to content

Commit c51a4e8

Browse files
committed
test(spring): Move the Spring WAR sample's tests into the module
Adds sentry-spring-jakarta's embedded-Tomcat integrationTest suite to sentry-spring, with the module's packages and javax.servlet. The sample's tests were the jakarta WAR sample's, so the suite is too. Spring Boot 2's BOM picks Spring 5.3 and Tomcat 9. The sample has no system tests left, so it comes off the system-test runner, the backend workflow and the Spring Boot 2 matrix. That was the matrix's last runner step, so it drops the Python setup, the runner's logs and the exclude properties only the samples and the runner read. integrationTests includes the suite. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
1 parent 82f9173 commit c51a4e8

15 files changed

Lines changed: 314 additions & 212 deletions

File tree

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

Lines changed: 2 additions & 31 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:
@@ -65,35 +54,19 @@ jobs:
6554
- name: Update Spring Boot 2.x version
6655
run: |
6756
springboot_version="${{ matrix.springboot-version }}"
68-
if [[ ! "$springboot_version" =~ ^2\.7\. ]]; then
69-
echo "ORG_GRADLE_PROJECT_excludeGraphql=true" >> "$GITHUB_ENV"
70-
echo "ORG_GRADLE_PROJECT_excludeKafka=true" >> "$GITHUB_ENV"
71-
fi
7257
perl -0pi -e 'BEGIN { $v = shift } s/^springboot2[[:space:]]*=[[:space:]]*"\K[^"]*/$v/m or die "::error::springboot2 version entry not found in gradle/libs.versions.toml\n"' "$springboot_version" gradle/libs.versions.toml
7358
echo "Updated Spring Boot 2.x version to $springboot_version"
7459
75-
- name: Build sample artifacts
76-
run: |
77-
./gradlew \
78-
:sentry-samples:sentry-samples-spring:war \
79-
:sentry-samples:sentry-samples-spring:testClasses
80-
81-
- name: Test sentry-spring-boot integration suites
60+
- name: Test integration suites
8261
run: |
8362
./gradlew \
63+
:sentry-spring:integrationTest \
8464
:sentry-spring-boot:integrationTest \
8565
:sentry-spring-boot:integrationTestWebflux \
8666
:sentry-spring-boot:integrationTestOtelNoAgent \
8767
:sentry-spring-boot:integrationTestOtelAgent \
8868
:sentry-spring-boot:integrationTestOtelAgentWithoutAutoInit
8969
90-
- name: Test sentry-samples-spring
91-
run: |
92-
python3 test/system-test-runner.py test \
93-
--module "sentry-samples-spring" \
94-
--agent false \
95-
--auto-init "true"
96-
9770
- name: Upload test results
9871
if: always()
9972
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7
@@ -102,8 +75,6 @@ jobs:
10275
path: |
10376
**/build/reports/*
10477
**/build/test-results/**/*.xml
105-
sentry-mock-server.txt
106-
spring-server.txt
10778
10879
- name: Test Report
10980
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
@@ -40,9 +40,6 @@ jobs:
4040
- sample: "sentry-samples-spring-boot-4-otlp"
4141
agent: "false"
4242
agent-auto-init: "true"
43-
- sample: "sentry-samples-spring"
44-
agent: "false"
45-
agent-auto-init: "true"
4643
steps:
4744
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
4845
with:

‎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:integrationTest",
243244
":sentry-spring-jakarta:integrationTest",
244245
":sentry-spring-7:integrationTest",
245246
)

‎sentry-samples/sentry-samples-spring/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
// Spring scans this app's own bytecode with its repackaged ASM at startup, and the oldest version
@@ -29,9 +28,6 @@ group = "io.sentry.sample.spring"
2928

3029
version = "0.0.1-SNAPSHOT"
3130

32-
// Apollo 4.x requires coroutines 1.9.0+, override Spring Boot's managed version
33-
extra["kotlin-coroutines.version"] = "1.9.0"
34-
3531
dependencyManagement {
3632
imports {
3733
mavenBom(libs.springboot2.bom.get().toString())
@@ -58,35 +54,10 @@ dependencies {
5854

5955
implementation(libs.tomcat.catalina)
6056
implementation(libs.tomcat.embed.jasper)
61-
62-
testImplementation(projects.sentrySystemTestSupport)
63-
testImplementation(libs.kotlin.test.junit)
64-
testImplementation(libs.springboot.starter.test) {
65-
exclude(group = "org.junit.vintage", module = "junit-vintage-engine")
66-
}
6757
}
6858

6959
tasks.withType<KotlinCompile>().configureEach {
7060
kotlin {
7161
compilerOptions.freeCompilerArgs = listOf("-Xjsr305=strict")
7262
}
7363
}
74-
75-
tasks.register<Test>("systemTest").configure {
76-
group = "verification"
77-
description = "Runs the System tests"
78-
79-
val test = project.extensions.getByType<SourceSetContainer>()["test"]
80-
testClassesDirs = test.output.classesDirs
81-
classpath = test.runtimeClasspath
82-
83-
maxParallelForks = 1
84-
85-
filter { includeTestsMatching("io.sentry.systemtest*") }
86-
}
87-
88-
tasks.named("test").configure {
89-
require(this is Test)
90-
91-
filter { excludeTestsMatching("io.sentry.systemtest.*") }
92-
}

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

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

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

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

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

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

‎sentry-spring/build.gradle.kts‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,3 +95,25 @@ tasks.jar {
9595
)
9696
}
9797
}
98+
99+
testing {
100+
suites {
101+
// Runs a plain Spring MVC app in an embedded Tomcat against MockSentryServer, the way a servlet
102+
// container runs a WAR. Spring Boot isn't on its classpath, because Sentry behaves differently
103+
// with it.
104+
register<JvmTestSuite>("integrationTest") {
105+
useJUnit()
106+
dependencies {
107+
implementation(platform(libs.springboot2.bom))
108+
implementation(project())
109+
implementation(projects.sentryAsyncProfiler)
110+
implementation(projects.sentryTestSupport)
111+
implementation(Config.Libs.springWeb)
112+
implementation(libs.jackson.databind)
113+
implementation(libs.kotlin.test.junit)
114+
implementation(libs.google.truth)
115+
implementation(libs.tomcat.embed.core)
116+
}
117+
}
118+
}
119+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package io.sentry.spring.integrationtest
2+
3+
import io.sentry.test.MockSentryServer
4+
import java.nio.file.Files
5+
import java.util.ServiceLoader
6+
import javax.servlet.ServletContainerInitializer
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)