test(config): rewrite ForwardHeadersConfigurationTest as context-less binder test
Drops @SpringBootTest + PostgresContainerConfig + @MockitoBean S3Client in favour of Spring's Binder API against application.yaml. The new test binds the property into the typed ServerProperties.ForwardHeadersStrategy enum, so typos (`nativ`, `Native`, `framework `) and future enum renames fail the build with BindException — addresses the silent-coercion concern that the YAML-string assertion missed. Verified the test goes red on a typo (BindException: Failed to convert "nativ" → ForwardHeadersStrategy) and green on `native`. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -1,37 +1,48 @@
|
|||||||
package org.raddatz.familienarchiv.config;
|
package org.raddatz.familienarchiv.config;
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.raddatz.familienarchiv.PostgresContainerConfig;
|
import org.springframework.beans.factory.config.YamlPropertiesFactoryBean;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.boot.web.server.autoconfigure.ServerProperties.ForwardHeadersStrategy;
|
||||||
import org.springframework.beans.factory.annotation.Value;
|
import org.springframework.boot.context.properties.bind.Binder;
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
import org.springframework.boot.context.properties.source.ConfigurationPropertySources;
|
||||||
import org.springframework.context.annotation.Import;
|
import org.springframework.core.env.PropertiesPropertySource;
|
||||||
import org.springframework.test.context.ActiveProfiles;
|
import org.springframework.core.io.ClassPathResource;
|
||||||
import org.springframework.test.context.bean.override.mockito.MockitoBean;
|
|
||||||
import software.amazon.awssdk.services.s3.S3Client;
|
import java.util.Properties;
|
||||||
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
|
||||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
|
/**
|
||||||
@ActiveProfiles("test")
|
* Binds {@code server.forward-headers-strategy} from {@code application.yaml} into
|
||||||
@Import(PostgresContainerConfig.class)
|
* 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}.
|
||||||
|
*
|
||||||
|
* <p>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 {
|
class ForwardHeadersConfigurationTest {
|
||||||
|
|
||||||
@MockitoBean
|
|
||||||
S3Client s3Client;
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
@Value("${server.forward-headers-strategy:}")
|
|
||||||
String forwardHeadersStrategy;
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void forward_headers_strategy_is_native_for_reverse_proxy_deployment() {
|
void forward_headers_strategy_binds_to_NATIVE() {
|
||||||
// Caddy terminates TLS and forwards X-Forwarded-Proto: https.
|
YamlPropertiesFactoryBean yaml = new YamlPropertiesFactoryBean();
|
||||||
// Spring must trust those headers so that AppUser-facing redirect URLs,
|
yaml.setResources(new ClassPathResource("application.yaml"));
|
||||||
// Spring Session cookies (Secure flag), and HttpServletRequest.getScheme()
|
Properties props = yaml.getObject();
|
||||||
// reflect the original client-facing scheme rather than the internal http hop.
|
assertThat(props).as("application.yaml must be on the classpath").isNotNull();
|
||||||
assertThat(forwardHeadersStrategy)
|
|
||||||
.as("server.forward-headers-strategy must be 'native' so Jetty honours X-Forwarded-Proto behind Caddy")
|
Binder binder = new Binder(ConfigurationPropertySources.from(
|
||||||
.isEqualTo("native");
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user