Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
258098e
chore: update scripts/update-sentry-native-ndk.sh to 0.17.4 (#6278)
github-actions[bot] Oct 20, 2026
ee24060
feat(android): Add masked screenshot capture for Hybrid SDKs (#6281)
antonis Oct 20, 2026
e5c61b8
ci: Move jobs from Bitrise back to GitHub-hosted runners (#6289)
sentry-junior[bot] Oct 20, 2026
3f3b8ef
chore(deps): bump the github-actions group across 1 directory with 4 …
dependabot[bot] Oct 20, 2026
d5802a7
docs: Require issue discussion before PRs (#6288)
sentry-junior[bot] Oct 20, 2026
ebf465f
fix(client-reports): Count spans in batched span envelope items (#6265)
0xadam-brown Oct 21, 2026
37d61d8
build(deps): Bump certifi to 2026.10.19 (#6333)
sentry-junior[bot] Oct 21, 2026
ece9581
test: Add an in-process MockSentryServer to sentry-test-support (#6266)
runningcode Oct 21, 2026
8d7b785
ci: Use the latest Python 3.10 patch in CI (#6339)
runningcode Oct 21, 2026
5e493bd
chore(build): Compile with a JDK 21 toolchain and upgrade Mockito and…
markushi Oct 21, 2026
b731c0b
test: Move the system-test envelope matchers into sentry-test-support…
runningcode Oct 21, 2026
a0ac761
fix(replay): Use software canvas for the video encoder on the Android…
sentry-junior[bot] Oct 21, 2026
814ae44
fix(replay): Keep flushed video and segment id when stop follows flus…
romtsn Oct 21, 2026
28c6417
fix(replay): Do not recycle the canvas screenshot during a pixel copy…
romtsn Oct 21, 2026
eb32cb5
fix(replay): Keep replays of this process in the startup cleanup (#6343)
romtsn Oct 21, 2026
e220ec4
release: 8.61.1
romtsn Oct 22, 2026
62b576b
Merge commit 'e220ec4c20de6eec960a343cdabc955b99152b7b'
sentry-release-bot[bot] Oct 22, 2026
58299a4
build(deps): Bump uv to 0.13.4 (#6348)
sentry-junior[bot] Oct 22, 2026
ff8c06c
test(logback): [Sample Tests 3] Move the logback system test into the…
runningcode Oct 22, 2026
1295a3d
test(otel): Add a smoke test that runs an app with the built agent (#…
runningcode Oct 22, 2026
31b2243
fix(android): Always remove FirstDrawDoneListener after the first dra…
runningcode Oct 22, 2026
c2e5997
test(otel): Test a Spring Boot app under the agent in both init modes…
runningcode Oct 22, 2026
1569178
test(spring-boot): Move the Boot 3 sample's Person test into the modu…
runningcode Oct 22, 2026
dbad316
test(android): Wait for the previous session move in InternalSentrySd…
sentry-junior[bot] Oct 23, 2026
44566d5
test(core): Allow repeated scopes storage set calls in SentryTest (#6…
sentry-junior[bot] Oct 23, 2026
aa51872
test(core): Wait for the transaction capture in idle timeout tests (#…
sentry-junior[bot] Oct 23, 2026
be877be
test(spring-boot): [Sample Tests 7] Move the Boot 3 tracing and HTTP …
runningcode Oct 23, 2026
f98c023
chore(deps): bump getsentry/craft (#6353)
dependabot[bot] Oct 23, 2026
4d96804
test(spring-boot): Move the Boot 3 cache, metrics and feature flag te…
runningcode Oct 23, 2026
798c4c5
test(spring-boot): [Sample Tests 9] Move the Boot 3 GraphQL tests int…
runningcode Oct 23, 2026
449c56d
test(spring-boot): [Sample Tests 10] Move the Boot 3 Kafka test into …
runningcode Oct 23, 2026
6535064
test(spring-boot): [Sample Tests 11] Move the Boot 4 sample's tests i…
runningcode Oct 23, 2026
0693115
test(spring-boot): Move the Boot 4 Kafka test into the module (#6298)
runningcode Oct 23, 2026
6d9e471
test(spring-boot): [Sample Tests 13] Move the Boot 2 sample's tests i…
runningcode Oct 23, 2026
e2a270b
feat(android): Nav3 support for multi-Activity and multi-Fragment app…
0xadam-brown Oct 23, 2026
b705c10
test(spring-boot): [Sample Tests 14] Move the Boot 2 GraphQL and Kafk…
runningcode Oct 23, 2026
244b092
test(spring-boot): [Sample Tests 16] Replace the log4j2 Boot samples'…
runningcode Oct 23, 2026
6beb484
chore(deps): bump the github-actions group with 4 updates (#6375)
dependabot[bot] Oct 26, 2026
b875f50
build(deps): Bump uv to 0.13.5 (#6373)
sentry-junior[bot] Oct 26, 2026
a53f1fb
build: Pin the Gradle daemon to JDK 21 (#6364)
runningcode Oct 26, 2026
c6bde41
build: Upgrade Gradle to 9.8.2 (#6376)
sentry-junior[bot] Oct 26, 2026
5257183
chore(deps): bump log4j2 from 2.20.0 to 2.26.1 (#5435)
dependabot[bot] Oct 27, 2026
28e2389
fix(spring): Prevent Reactor pool scope chain leaks (#6283)
adinauer Oct 27, 2026
72bcb18
ref(android): Split Nav3 fragment scenarios into tabs in the Nav3 sam…
0xadam-brown Oct 27, 2026
c42d51d
fix(build): Strip MethodParameters so AGP 8.0 and older can dex the S…
antonis Oct 27, 2026
0f55e39
release: 8.62.0
markushi Oct 27, 2026
243df0b
test(spring-boot): [Sample Tests 25] Move the Boot 4 OTel agent sampl…
runningcode Oct 27, 2026
dd220ac
Merge commit '0f55e39daaba7f1fd155aeb79b0c87b58c38939e'
sentry-release-bot[bot] Oct 27, 2026
b1c4bad
Merge branch '9.x.x' into sync/main-to-9.x.x-2026-10-27
sentry-junior[bot] Oct 27, 2026
0f86319
test: Drop logs.isEnabled from tests synced from main
sentry-junior[bot] Oct 27, 2026
1463a71
test: Enable logs on the appender in integration tests synced from main
sentry-junior[bot] Oct 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
38 changes: 16 additions & 22 deletions .claude/skills/test/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
---
name: test
description: Run tests for a specific SDK module. Use when asked to "run tests", "test module", "run unit tests", "run system tests", "run e2e tests", or test a specific class. Auto-detects unit vs system tests. Supports interactive mode.
description: Run tests for a specific SDK module. Use when asked to "run tests", "test module", "run unit tests", "run integration tests", "run e2e tests", or test a specific class. Auto-detects unit tests vs integration test suites. Supports interactive mode.
allowed-tools: Bash, Read, Glob, AskUserQuestion
argument-hint: [interactive] <module-name-or-file-path> [test-class-filter]
---

# Run Tests

Run tests for a specific module. Auto-detects whether to run unit tests or system tests.
Run tests for a specific module. Auto-detects whether to run unit tests or an integration test
suite.

## Step 0: Check for Interactive Mode

Expand All @@ -19,7 +20,7 @@ In interactive mode, use AskUserQuestion at decision points as described in the

The argument can be either:
- A **file path** (e.g., `@sentry/src/test/java/io/sentry/ScopesTest.kt`)
- A **module name** (e.g., `sentry-android-core`, `sentry-samples-spring-boot-4`)
- A **module name** (e.g., `sentry-android-core`, `sentry-spring-boot-4`)
- A **module name + test filter** (e.g., `sentry ScopesTest`)

Extract the module name and optional test class filter from the argument.
Expand All @@ -30,10 +31,13 @@ Extract the module name and optional test class filter from the argument.

| Signal | Test Type |
|--------|-----------|
| Path contains `sentry-samples/` | System test |
| Module name starts with `sentry-samples-` | System test |
| Path contains `/src/integrationTest*/` or `/src/agentFixture/` | Integration test suite |
| Path contains `sentry-samples/`, or module name starts with `sentry-samples-` | None: samples have no tests |
| Everything else | Unit test |

For a sample, tell the user that samples have no tests, and that the end-to-end tests for what
the sample shows live in the module it uses (see the `e2e_tests` rule).

## Step 3a: Run Unit Tests

Determine the Gradle test task:
Expand All @@ -60,30 +64,20 @@ Without a filter:
./gradlew ':<module>:<task>' --info
```

## Step 3b: Run System Tests

System tests require the Python-based test runner which manages a mock Sentry server and sample app lifecycle.

1. Ensure the Python venv exists:
```bash
test -d .venv || make setupPython
```
## Step 3b: Run an Integration Test Suite

2. Extract the sample module name. For file paths like `sentry-samples/<sample-module>/src/...`, the sample module is the directory name (e.g., `sentry-samples-spring`).
The suite's task is named after its source set directory: `src/integrationTestWebflux/...` runs
with `integrationTestWebflux`. The OpenTelemetry agent suite runs in two tasks, one per init mode,
so for `src/integrationTestOtelAgent/` or `src/agentFixture/` run both:

3. Run the system test:
```bash
.venv/bin/python test/system-test-runner.py test --module <sample-module>
./gradlew ':<module>:integrationTestOtelAgent' ':<module>:integrationTestOtelAgentWithoutAutoInit'
```

This starts the mock Sentry server, starts the sample app (Spring Boot/Tomcat/CLI), runs tests via `./gradlew :sentry-samples:<sample-module>:systemTest`, and cleans up afterwards.

To run **every** system test instead of one module, use the Makefile targets — they also create the
venv for you:
Otherwise, with or without a test class filter:

```bash
make systemTest # all system tests (--all)
make systemTestInteractive # pick the setups to run (--interactive)
./gradlew ':<module>:<suite>' --tests="*<filter>*"
```

## Step 4: Report Results
Expand Down
84 changes: 70 additions & 14 deletions .cursor/rules/e2e_tests.mdc
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,82 @@ alwaysApply: false
description: Java SDK End to End Tests
---

# Java SDK End to End Tests (System Tests)
# Java SDK End to End Tests

The samples in the `sentry-samples` directory are used to run end to end tests against them.
End-to-end tests live in the module whose code they test, not in `sentry-samples`. Samples have no
tests. A test runs the code the way an app would and asserts what reaches Sentry over the real HTTP
transport.

There is a python script (`system-test-runner.py`) that can be used to run one (using `--module SAMPLE_NAME`) or all (using `--all`) system tests.
## MockSentryServer

The script has an interactive mode (`-i`) which allows selection of test setups to execute, whether to run the tests or just prepare infrastructure for testing from IDE.
`MockSentryServer` (`sentry-test-support`) is an in-process HTTP server on a free port. Point the
SDK at `server.dsn`, then:

The tests run a mock Sentry server via `system-test-sentry-server.py`. Any system under test will then have a DSN set that reflects this local mock server like `http://502f25099c204a2fbf4cb16edc5975d1@localhost:8000/0`.
By using this local DSN, the system under test sends events to the local mock server.
The tests can then use `TestHelper` to assert envelopes that were received by the mock server.
`TestHelper` uses HTTP requests to retrieve the JSON payload of the received events and deserialize them back to objects for easier assertion.
Tests can then assert events, transactions, logs etc. similar to how they would appear in `beforeSend` and similar callbacks.
- `awaitEnvelope(timeoutMillis) { predicate }` polls for a matching envelope and fails with what it
received on timeout.
- `assertNoEnvelope(millis) { predicate }` checks that nothing matching arrives.
- `MockSentryServer { ... }` takes a hook that runs before each check while waiting, for example to
flush an OpenTelemetry tracer provider.

`TestHelper` has a lot of helper methods for asserting, e.g. by span name, log body etc.
`EnvelopeMatchers.kt` has the accessors and matchers: `event()`, `transaction()`, `logs()`,
`metrics()`, `profileChunk()`, `hasSpanWithOp`, `hasFlag`, `hasLogWithAttribute`, `hasMetric` and
so on.

The end to end tests either expect the system under test to either be running on a server or call `java -jar` to execute a CLI system under test.
Call `Sentry.flush()` before awaiting when Sentry runs in the test's JVM: logs and metrics are
batched. Envelopes that arrive late can outlive a server's `reset()`, so tests that share a server
match on values only they produce.

For Spring Boot, we spin up the Spring Boot server. The tests then send requests to that server and assert what is sent to Sentry.
## Where the tests are

End to end tests are also executed on CI using a matrix build, as defined in `.github/workflows/system-tests-backend.yml`.
- **Core and plain integrations** use their module's `src/test`. Examples: `SentryEndToEndTest`
(`sentry`), `SentryAppenderIntegrationTest` (`sentry-logback`, `sentry-log4j2`, loading the
appender from XML), `SentryHandlerIntegrationTest` (`sentry-jul`),
`ContinuousProfilerEndToEndTest` (`sentry-async-profiler`), `SentryKafkaEndToEndTest`
(`sentry-kafka`), `AgentlessEndToEndTest` and `OtlpEndToEndTest` (`sentry-opentelemetry-*`).
- **Spring Boot** (`sentry-spring-boot`, `sentry-spring-boot-jakarta`, `sentry-spring-boot-4`) has
one JVM test suite per flavor, because a flavor is a classpath: Sentry's auto-configuration and
OpenTelemetry mode switch on which classes are present.
- `integrationTest`: servlet app.
- `integrationTestWebflux`: reactive app.
- `integrationTestOtelNoAgent`: `sentry-opentelemetry-agentless-spring`.
- `integrationTestOtelAgent` and `integrationTestOtelAgentWithoutAutoInit`: the built agent.
- `integrationTestOtlp` (Boot 4): `sentry-opentelemetry-otlp-spring`.
- `integrationTestLog4j2` (Boot 3, 4): Log4j2 instead of Logback, for the auto-configured
Log4j2 appender.

Some of the samples are tested in multiple ways, e.g. with OpenTelemetry Agent auto init turned on and off.
Each suite has a base class with `@SpringBootTest(RANDOM_PORT)` and a `@DynamicPropertySource`
that sets `sentry.dsn`. Subclasses share one context and one server.
- **Plain Spring** (`sentry-spring`, `sentry-spring-jakarta`, `sentry-spring-7`) has an
`integrationTest` suite that starts the app in an embedded Tomcat, the way a WAR starts. Spring
Boot stays off its classpath, because `SentrySpringServletContainerInitializer` behaves
differently with it.

## The OpenTelemetry agent

The agent must not run in the test's JVM, because it would instrument the test's own HTTP client
and server. The Spring Boot modules have an `agentFixture` source set with the app. `AgentApp`
starts it once per test JVM in a child process, with `ChildJvmApp` and the agent jar passed through
`ChildJvmTestArguments`. The suite's task runs with the agent initializing Sentry, and
`integrationTestOtelAgentWithoutAutoInit` runs the same tests with the Spring Boot starter doing
it. Gradle runs the two in parallel.

## Running them

- The `spring-boot-{2,3,4}-matrix.yml` workflows run the suites against each supported Spring Boot
version, by setting the version in `gradle/libs.versions.toml`. The suites aren't part of
`check`, so a new suite only runs on CI once it's added to its matrix.

## Gotchas

- Don't use the context's `TestRestTemplate`. It comes from the `RestTemplateBuilder` that Sentry
customizes, and that the OpenTelemetry starter instruments, so it adds or replaces trace headers
on the requests a test sends. Build one outside the context.
- Sentry exports OpenTelemetry spans in batches every 5 seconds. In-process, flush the tracer
provider from the server's hook. With the agent this isn't possible, so each test waits for a
batch and negative checks wait at least 6 seconds.
- Sentry is global, so a second live Spring context in an OpenTelemetry suite takes it over. Keep
one live context per JVM (`@DirtiesContext` when a test needs its own).
- The async profiler rejects a traces path with a `.` anywhere in its absolute form. Put the path
in `java.io.tmpdir`.
- Kafka uses an in-JVM broker (`@EmbeddedKafka` or spring-kafka-test's broker classes), so no
Docker is needed.
4 changes: 2 additions & 2 deletions .cursor/rules/new_module.mdc
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,8 @@ static {
addIntegrationToSdkVersion("{IntegrationName}");
```

7. If adding a JVM sample, add E2E (system) tests, following the structure we have in the existing JVM examples.
The test should then be added to `test/system-test-runner.py` and `.github/workflows/system-tests-backend.yml`.
7. Samples have no tests. Add end-to-end coverage of what the sample shows to the module it uses,
against `MockSentryServer`, as described in the `e2e_tests` rule.

`sentry-bom` and the root `build.gradle.kts` need no change — they iterate over subprojects.

Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/codeql-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ jobs:
cache-encryption-key: ${{ secrets.GRADLE_ENCRYPTION_KEY }}

- name: Initialize CodeQL
uses: github/codeql-action/init@6f2ac81f5cd81fe351a197ba0b00d91d03cd2aee # pin@v2
uses: github/codeql-action/init@9b3254085494b55a0f7c4c2b3d2d52cc712ce370 # pin@v2
with:
languages: 'java'

Expand All @@ -45,4 +45,4 @@ jobs:
./gradlew buildForCodeQL --no-build-cache

- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@6f2ac81f5cd81fe351a197ba0b00d91d03cd2aee # pin@v2
uses: github/codeql-action/analyze@9b3254085494b55a0f7c4c2b3d2d52cc712ce370 # pin@v2
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ jobs:
fetch-depth: 0
submodules: 'recursive'
- name: Prepare release
uses: getsentry/craft@7a7857552e2cdc08b502de96e6076849c01fba32 # v2.35.0
uses: getsentry/craft@924b450ede4af30e30de4821a4b357d676c8df92 # v3
env:
GITHUB_TOKEN: ${{ steps.token.outputs.token }}
with:
Expand Down
78 changes: 7 additions & 71 deletions .github/workflows/spring-boot-2-matrix.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,6 @@ jobs:

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

steps:
Expand All @@ -34,16 +33,6 @@ jobs:
with:
submodules: 'recursive'

- name: Set up Python
uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
with:
python-version: '3.10'

- name: Install Python dependencies
run: |
python3 -m pip install --upgrade pip
python3 -m pip install -r requirements.txt

- name: Set up Java
uses: actions/setup-java@b843ebf9b913146b38da2eaa7aeddf76074ae1ad # v6.1.0
with:
Expand All @@ -65,69 +54,18 @@ jobs:
- name: Update Spring Boot 2.x version
run: |
springboot_version="${{ matrix.springboot-version }}"
if [[ ! "$springboot_version" =~ ^2\.7\. ]]; then
echo "ORG_GRADLE_PROJECT_excludeGraphql=true" >> "$GITHUB_ENV"
echo "ORG_GRADLE_PROJECT_excludeKafka=true" >> "$GITHUB_ENV"
fi
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
echo "Updated Spring Boot 2.x version to $springboot_version"

- name: Build sample artifacts
- name: Test integration suites
run: |
./gradlew \
:sentry-samples:sentry-samples-spring-boot:shadowJar \
:sentry-samples:sentry-samples-spring-boot:testClasses \
:sentry-samples:sentry-samples-spring-boot-webflux:shadowJar \
:sentry-samples:sentry-samples-spring-boot-webflux:testClasses \
:sentry-samples:sentry-samples-spring-boot-opentelemetry:shadowJar \
:sentry-samples:sentry-samples-spring-boot-opentelemetry:testClasses \
:sentry-samples:sentry-samples-spring-boot-opentelemetry-noagent:shadowJar \
:sentry-samples:sentry-samples-spring-boot-opentelemetry-noagent:testClasses \
:sentry-samples:sentry-samples-spring:war \
:sentry-samples:sentry-samples-spring:testClasses \
:sentry-opentelemetry:sentry-opentelemetry-agent:assemble

- name: Test sentry-samples-spring-boot
run: |
python3 test/system-test-runner.py test \
--module "sentry-samples-spring-boot" \
--agent false \
--auto-init "true"

- name: Test sentry-samples-spring-boot-webflux
run: |
python3 test/system-test-runner.py test \
--module "sentry-samples-spring-boot-webflux" \
--agent false \
--auto-init "true"

- name: Test sentry-samples-spring-boot-opentelemetry agent init true
run: |
python3 test/system-test-runner.py test \
--module "sentry-samples-spring-boot-opentelemetry" \
--agent true \
--auto-init "true"

- name: Test sentry-samples-spring-boot-opentelemetry agent init false
run: |
python3 test/system-test-runner.py test \
--module "sentry-samples-spring-boot-opentelemetry" \
--agent true \
--auto-init "false"

- name: Test sentry-samples-spring-boot-opentelemetry-noagent
run: |
python3 test/system-test-runner.py test \
--module "sentry-samples-spring-boot-opentelemetry-noagent" \
--agent false \
--auto-init "true"

- name: Test sentry-samples-spring
run: |
python3 test/system-test-runner.py test \
--module "sentry-samples-spring" \
--agent false \
--auto-init "true"
:sentry-spring:integrationTest \
:sentry-spring-boot:integrationTest \
:sentry-spring-boot:integrationTestWebflux \
:sentry-spring-boot:integrationTestOtelNoAgent \
:sentry-spring-boot:integrationTestOtelAgent \
:sentry-spring-boot:integrationTestOtelAgentWithoutAutoInit

- name: Upload test results
if: always()
Expand All @@ -137,8 +75,6 @@ jobs:
path: |
**/build/reports/*
**/build/test-results/**/*.xml
sentry-mock-server.txt
spring-server.txt

- name: Test Report
uses: phoenix-actions/test-reporting@7317eea6e13c47348dd0bb318669485157c518d6 # pin@v16
Expand Down
Loading
Loading