diff --git a/backend/src/test/java/org/raddatz/familienarchiv/config/ForwardHeadersConfigurationTest.java b/backend/src/test/java/org/raddatz/familienarchiv/config/ForwardHeadersConfigurationTest.java index b97f5ff0..755dad83 100644 --- a/backend/src/test/java/org/raddatz/familienarchiv/config/ForwardHeadersConfigurationTest.java +++ b/backend/src/test/java/org/raddatz/familienarchiv/config/ForwardHeadersConfigurationTest.java @@ -1,37 +1,48 @@ package org.raddatz.familienarchiv.config; import org.junit.jupiter.api.Test; -import org.raddatz.familienarchiv.PostgresContainerConfig; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.context.annotation.Import; -import org.springframework.test.context.ActiveProfiles; -import org.springframework.test.context.bean.override.mockito.MockitoBean; -import software.amazon.awssdk.services.s3.S3Client; +import org.springframework.beans.factory.config.YamlPropertiesFactoryBean; +import org.springframework.boot.web.server.autoconfigure.ServerProperties.ForwardHeadersStrategy; +import org.springframework.boot.context.properties.bind.Binder; +import org.springframework.boot.context.properties.source.ConfigurationPropertySources; +import org.springframework.core.env.PropertiesPropertySource; +import org.springframework.core.io.ClassPathResource; + +import java.util.Properties; import static org.assertj.core.api.Assertions.assertThat; -@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE) -@ActiveProfiles("test") -@Import(PostgresContainerConfig.class) +/** + * Binds {@code server.forward-headers-strategy} from {@code application.yaml} into + * Spring Boot's typed {@link ForwardHeadersStrategy} enum. The binder rejects any + * value that is not a valid enum constant ({@code BindException}), so a typo + * ({@code "nativ"}, {@code "Native"}, {@code "framework "}) or a future Spring + * rename of the property fails the test, not silently degrades to {@code NONE}. + * + *
No Spring context, no embedded server, no Testcontainers — this is the + * cheapest test that pins the contract "Caddy's X-Forwarded-Proto is trusted". + */ class ForwardHeadersConfigurationTest { - @MockitoBean - S3Client s3Client; - - @Autowired - @Value("${server.forward-headers-strategy:}") - String forwardHeadersStrategy; - @Test - void forward_headers_strategy_is_native_for_reverse_proxy_deployment() { - // Caddy terminates TLS and forwards X-Forwarded-Proto: https. - // Spring must trust those headers so that AppUser-facing redirect URLs, - // Spring Session cookies (Secure flag), and HttpServletRequest.getScheme() - // reflect the original client-facing scheme rather than the internal http hop. - assertThat(forwardHeadersStrategy) - .as("server.forward-headers-strategy must be 'native' so Jetty honours X-Forwarded-Proto behind Caddy") - .isEqualTo("native"); + void forward_headers_strategy_binds_to_NATIVE() { + YamlPropertiesFactoryBean yaml = new YamlPropertiesFactoryBean(); + yaml.setResources(new ClassPathResource("application.yaml")); + Properties props = yaml.getObject(); + assertThat(props).as("application.yaml must be on the classpath").isNotNull(); + + Binder binder = new Binder(ConfigurationPropertySources.from( + new PropertiesPropertySource("application", props))); + + ForwardHeadersStrategy strategy = binder + .bind("server.forward-headers-strategy", ForwardHeadersStrategy.class) + .orElseThrow(() -> new AssertionError( + "server.forward-headers-strategy is missing from application.yaml")); + + assertThat(strategy) + .as("Spring must trust X-Forwarded-Proto from Caddy so that " + + "request.getScheme(), redirect URLs, and the Spring Session " + + "'Secure' cookie reflect the original https client request.") + .isEqualTo(ForwardHeadersStrategy.NATIVE); } }