Compare commits
4 Commits
2139d600f5
...
7c2e75facc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7c2e75facc | ||
|
|
7b05b9d5a0 | ||
|
|
20edc0474c | ||
|
|
fa191b5c05 |
@@ -225,11 +225,13 @@
|
|||||||
</exclusions>
|
</exclusions>
|
||||||
</dependency>
|
</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>
|
<dependency>
|
||||||
<groupId>io.sentry</groupId>
|
<groupId>io.sentry</groupId>
|
||||||
<artifactId>sentry-spring-boot-starter-jakarta</artifactId>
|
<artifactId>sentry-spring-boot-4</artifactId>
|
||||||
<version>8.5.0</version>
|
<version>8.41.0</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -38,13 +38,6 @@ spring:
|
|||||||
starttls:
|
starttls:
|
||||||
enable: true
|
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:
|
server:
|
||||||
# Behind Caddy/reverse proxy: trust X-Forwarded-{Proto,For,Host} so that
|
# Behind Caddy/reverse proxy: trust X-Forwarded-{Proto,For,Host} so that
|
||||||
# request.getScheme(), redirect URLs, and Spring Session "Secure" cookies
|
# request.getScheme(), redirect URLs, and Spring Session "Secure" cookies
|
||||||
|
|||||||
@@ -1,14 +1,18 @@
|
|||||||
package org.raddatz.familienarchiv;
|
package org.raddatz.familienarchiv;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
import org.springframework.boot.test.context.SpringBootTest;
|
||||||
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
|
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
|
||||||
|
import org.springframework.context.ApplicationContext;
|
||||||
import org.springframework.context.annotation.Import;
|
import org.springframework.context.annotation.Import;
|
||||||
import org.springframework.test.context.ActiveProfiles;
|
import org.springframework.test.context.ActiveProfiles;
|
||||||
import org.springframework.test.context.bean.override.mockito.MockitoBean;
|
import org.springframework.test.context.bean.override.mockito.MockitoBean;
|
||||||
import org.testcontainers.containers.PostgreSQLContainer;
|
import org.testcontainers.containers.PostgreSQLContainer;
|
||||||
import software.amazon.awssdk.services.s3.S3Client;
|
import software.amazon.awssdk.services.s3.S3Client;
|
||||||
|
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
|
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
|
||||||
@ActiveProfiles("test")
|
@ActiveProfiles("test")
|
||||||
@Import(PostgresContainerConfig.class)
|
@Import(PostgresContainerConfig.class)
|
||||||
@@ -17,9 +21,18 @@ class ApplicationContextTest {
|
|||||||
@MockitoBean
|
@MockitoBean
|
||||||
S3Client s3Client;
|
S3Client s3Client;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
ApplicationContext ctx;
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void contextLoads() {
|
void contextLoads() {
|
||||||
// verifies that the Spring context starts successfully with all beans wired,
|
// verifies that the Spring context starts successfully with all beans wired,
|
||||||
// Flyway migrations applied, and no configuration errors
|
// 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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,12 +13,6 @@ spring:
|
|||||||
password: test
|
password: test
|
||||||
mail:
|
mail:
|
||||||
host: localhost
|
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
|
# Disable OTel SDK entirely in tests — prevents auto-configuration from loading resource providers
|
||||||
# (e.g. AzureAppServiceResourceProvider) that fail against the semconv version used here.
|
# (e.g. AzureAppServiceResourceProvider) that fail against the semconv version used here.
|
||||||
|
|||||||
Reference in New Issue
Block a user