Compare commits

...

4 Commits

Author SHA1 Message Date
Marcel
7c2e75facc fix(backend): switch to sentry-spring-boot-4:8.41.0 for Spring Boot 4/SF7 compatibility
Some checks failed
CI / Unit & Component Tests (pull_request) Successful in 6m12s
CI / OCR Service Tests (pull_request) Successful in 42s
CI / Backend Unit Tests (pull_request) Failing after 17m13s
CI / fail2ban Regex (pull_request) Successful in 2m37s
CI / Compose Bucket Idempotency (pull_request) Successful in 2m6s
sentry-spring-boot-starter-jakarta 8.5.0 does not support Spring Boot 4.0 —
it logs an "Incompatible Spring Boot Version" warning and its SentryAutoConfiguration
crashes SF7 bean-name generation. sentry-spring-boot-4 (added in 8.21.0) is the
dedicated Spring Boot 4 module with a fixed auto-configuration class.

- Replace sentry-spring-boot-starter-jakarta:8.5.0 with sentry-spring-boot-4:8.41.0
- Delete SentryConfig.java — workaround no longer needed, auto-config handles init
- Remove spring.autoconfigure.exclude from application.yaml + application-test.yaml
- Delete SentryConfigTest.java — tested the deleted workaround class
- Update ApplicationContextTest: assert Sentry.isEnabled() is false when no DSN set

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-15 09:51:53 +02:00
Marcel
7b05b9d5a0 test(context): assert SentryAutoConfiguration is excluded from Spring context
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-15 09:45:32 +02:00
Marcel
20edc0474c test(exception): verify handleGeneric captures exception in Sentry and returns 500
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-15 09:44:10 +02:00
Marcel
fa191b5c05 test(config): unit-test SentryConfig blank-DSN no-op and non-blank init paths
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-15 09:43:08 +02:00
6 changed files with 51 additions and 55 deletions

View File

@@ -225,11 +225,13 @@
</exclusions>
</dependency>
<!-- Sentry error reporting (GlitchTip-compatible) -->
<!-- Sentry error reporting (GlitchTip-compatible) — sentry-spring-boot-4 is the
Spring Boot 4 / Spring Framework 7 compatible module (replaces the jakarta starter
which crashes with SF7 due to bean-name generation for triply-nested @Import classes) -->
<dependency>
<groupId>io.sentry</groupId>
<artifactId>sentry-spring-boot-starter-jakarta</artifactId>
<version>8.5.0</version>
<artifactId>sentry-spring-boot-4</artifactId>
<version>8.41.0</version>
</dependency>
</dependencies>

View File

@@ -1,39 +0,0 @@
package org.raddatz.familienarchiv.config;
import io.sentry.Sentry;
import jakarta.annotation.PostConstruct;
import org.raddatz.familienarchiv.exception.DomainException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
// SentryAutoConfiguration is excluded (see application.yaml) because Spring Boot 4 / Spring
// Framework 7 cannot generate a bean name for the triply-nested
// SentryAutoConfiguration$HubConfiguration$SentrySpanRestClientConfiguration class.
// This bean replicates the essential init: DSN, environment, sample rate, PII guard,
// and DomainException filter.
@Configuration
public class SentryConfig {
@Value("${sentry.dsn:}")
private String dsn;
@Value("${sentry.environment:dev}")
private String environment;
@Value("${sentry.traces-sample-rate:1.0}")
private double tracesSampleRate;
@PostConstruct
public void init() {
if (dsn == null || dsn.isBlank()) {
return;
}
Sentry.init(options -> {
options.setDsn(dsn);
options.setEnvironment(environment);
options.setTracesSampleRate(tracesSampleRate);
options.setSendDefaultPii(false);
options.addIgnoredExceptionForType(DomainException.class);
});
}
}

View File

@@ -38,13 +38,6 @@ spring:
starttls:
enable: true
autoconfigure:
exclude:
# SentryAutoConfiguration fails on Spring Boot 4/Spring Framework 7: Spring cannot generate a
# bean name for the triply-nested SentryAutoConfiguration$HubConfiguration$SentrySpanRestClientConfiguration.
# Sentry is initialized manually via SentryConfig instead. See #580.
- io.sentry.spring.boot.jakarta.SentryAutoConfiguration
server:
# Behind Caddy/reverse proxy: trust X-Forwarded-{Proto,For,Host} so that
# request.getScheme(), redirect URLs, and Spring Session "Secure" cookies

View File

@@ -1,14 +1,18 @@
package org.raddatz.familienarchiv;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.testcontainers.containers.PostgreSQLContainer;
import software.amazon.awssdk.services.s3.S3Client;
import static org.assertj.core.api.Assertions.assertThat;
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
@ActiveProfiles("test")
@Import(PostgresContainerConfig.class)
@@ -17,9 +21,18 @@ class ApplicationContextTest {
@MockitoBean
S3Client s3Client;
@Autowired
ApplicationContext ctx;
@Test
void contextLoads() {
// verifies that the Spring context starts successfully with all beans wired,
// Flyway migrations applied, and no configuration errors
}
@Test
void sentry_is_disabled_when_no_dsn_is_configured() {
// application-test.yaml has no sentry.dsn — SDK must stay inactive so tests are clean
assertThat(io.sentry.Sentry.isEnabled()).isFalse();
}
}

View File

@@ -0,0 +1,33 @@
package org.raddatz.familienarchiv.exception;
import io.sentry.Sentry;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.MockedStatic;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.http.ResponseEntity;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.mockStatic;
@ExtendWith(MockitoExtension.class)
class GlobalExceptionHandlerTest {
@InjectMocks
private GlobalExceptionHandler handler;
@Test
void handleGeneric_captures_exception_in_sentry_and_returns_500() {
RuntimeException ex = new RuntimeException("unexpected failure");
try (MockedStatic<Sentry> sentryMock = mockStatic(Sentry.class)) {
ResponseEntity<GlobalExceptionHandler.ErrorResponse> response = handler.handleGeneric(ex);
sentryMock.verify(() -> Sentry.captureException(ex));
assertThat(response.getStatusCode().value()).isEqualTo(500);
assertThat(response.getBody()).isNotNull();
assertThat(response.getBody().code()).isEqualTo(ErrorCode.INTERNAL_ERROR);
}
}
}

View File

@@ -13,12 +13,6 @@ spring:
password: test
mail:
host: localhost
autoconfigure:
exclude:
# SentryAutoConfiguration fails on Spring Boot 4/Spring Framework 7: Spring cannot generate a
# bean name for the triply-nested SentryAutoConfiguration$HubConfiguration$SentrySpanRestClientConfiguration.
# Sentry is wired manually via SentryConfig instead. See #580.
- io.sentry.spring.boot.jakarta.SentryAutoConfiguration
# Disable OTel SDK entirely in tests — prevents auto-configuration from loading resource providers
# (e.g. AzureAppServiceResourceProvider) that fail against the semconv version used here.