From 7ce002177e69f90ae6e1a4a0aa8560d250062eb4 Mon Sep 17 00:00:00 2001 From: Tomas Bjerre Date: Sat, 2 Nov 2024 08:41:50 +0100 Subject: [PATCH] feat: HTTP and HTTPS in same mock (refs #62) --- .../wiremock/spring/ConfigureWireMock.java | 29 ++++- .../internal/WireMockServerCreator.java | 117 ++++++++++++++---- .../src/test/java/app/HttpsAndHttpTest.java | 77 ++++++++++++ .../{HttpsTest.java => HttpsOnlyTest.java} | 22 ++-- 4 files changed, 204 insertions(+), 41 deletions(-) create mode 100644 wiremock-spring-boot-example/src/test/java/app/HttpsAndHttpTest.java rename wiremock-spring-boot-example/src/test/java/app/{HttpsTest.java => HttpsOnlyTest.java} (70%) diff --git a/src/main/java/org/wiremock/spring/ConfigureWireMock.java b/src/main/java/org/wiremock/spring/ConfigureWireMock.java index 096af1d..b8068e0 100644 --- a/src/main/java/org/wiremock/spring/ConfigureWireMock.java +++ b/src/main/java/org/wiremock/spring/ConfigureWireMock.java @@ -15,17 +15,24 @@ public @interface ConfigureWireMock { /** - * Port on which WireMock server is going to listen. {@code 0} means WireMock will pick random - * port. + * Port on which WireMock server is going to listen. + * + *

{@code -1} means disabled. + * + *

{@code 0} means WireMock will pick random available port. + * + *

{@code >0} means that static port will be used. * * @return WireMock server port */ int port() default 0; /** - * @return true for HTTPS, else false. + * Same as {@link #port()} but for HTTPS. + * + * @return HTTPS port to use. */ - boolean useHttps() default false; + int httpsPort() default -1; /** * The name of WireMock server. @@ -49,6 +56,13 @@ */ String[] portProperties() default {"wiremock.server.port"}; + /** + * Names of Spring properties to inject the {@link WireMockServer#httpsPort()} + * + * @return names of Spring properties to inject the {@link WireMockServer#httpsPort()} + */ + String[] httpsPortProperties() default {"wiremock.server.httpsPort"}; + /** * Names of Spring properties to inject the {@link WireMockServer#baseUrl()}. * @@ -56,6 +70,13 @@ */ String[] baseUrlProperties() default {"wiremock.server.baseUrl"}; + /** + * Names of Spring properties to inject the {@link WireMockServer#baseUrl()}. + * + * @return names of Spring properties to inject the {@link WireMockServer#baseUrl()}. + */ + String[] httpsBaseUrlProperties() default {"wiremock.server.httpsBaseUrl"}; + /** * Classpaths to pass to {@link WireMockConfiguration#usingFilesUnderClasspath(String)}. First one * that is found will be used. If a {@link #name()} is supplied, it will first look for {@link diff --git a/src/main/java/org/wiremock/spring/internal/WireMockServerCreator.java b/src/main/java/org/wiremock/spring/internal/WireMockServerCreator.java index 5f6c2ff..7b7ddab 100644 --- a/src/main/java/org/wiremock/spring/internal/WireMockServerCreator.java +++ b/src/main/java/org/wiremock/spring/internal/WireMockServerCreator.java @@ -23,6 +23,7 @@ import org.wiremock.spring.WireMockConfigurationCustomizer; public class WireMockServerCreator { + private static final int PORT_DISABLED = -1; private final Logger logger; public WireMockServerCreator(final String name) { @@ -31,13 +32,19 @@ public WireMockServerCreator(final String name) { public WireMockServer createWireMockServer( final ConfigurableApplicationContext context, final ConfigureWireMock options) { - final int serverPort = this.getServerProperty(context.getEnvironment(), options); final WireMockConfiguration serverOptions = options(); - if (options.useHttps()) { - serverOptions.httpsPort(serverPort); - } else { - serverOptions.port(serverPort); + + final int serverHttpsPort = this.getServerHttpsPortProperty(context.getEnvironment(), options); + final boolean httpsEnabled = serverHttpsPort != PORT_DISABLED; + if (httpsEnabled) { + serverOptions.httpsPort(serverHttpsPort); + } + + final int serverHttpPort = this.getServerHttpPortProperty(context.getEnvironment(), options); + final boolean httpEnabled = serverHttpPort != PORT_DISABLED; + if (httpEnabled) { + serverOptions.port(serverHttpPort); } serverOptions.notifier(new Slf4jNotifier(options.name())); @@ -74,9 +81,10 @@ public WireMockServer createWireMockServer( this.applyCustomizers(options, serverOptions); this.logger.info( - "Configuring WireMockServer with name '{}' on port: {}", + "Configuring WireMockServer with name '{}' on HTTP port: {} and HTTPS port: {}", options.name(), - serverOptions.portNumber()); + serverOptions.portNumber(), + serverOptions.httpsSettings().port()); final WireMockServer newServer = new WireMockServer(serverOptions); newServer.start(); @@ -96,31 +104,64 @@ public WireMockServer createWireMockServer( } }); - Arrays.stream(options.baseUrlProperties()) - .filter(StringUtils::isNotBlank) - .collect(Collectors.toList()) - .forEach( - propertyName -> { - final String property = propertyName + "=" + newServer.baseUrl(); - this.logger.info("Adding property '{}' to Spring application context", property); - TestPropertyValues.of(property).applyTo(context.getEnvironment()); - }); + if (httpEnabled) { + Arrays.stream(options.baseUrlProperties()) + .filter(StringUtils::isNotBlank) + .collect(Collectors.toList()) + .forEach( + propertyName -> { + final String property = + propertyName + "=" + String.format("http://localhost:%d", newServer.port()); + this.logger.info( + "Adding property '{}' with HTTP base URL to Spring application context", + property); + TestPropertyValues.of(property).applyTo(context.getEnvironment()); + }); - Arrays.stream(options.portProperties()) - .filter(StringUtils::isNotBlank) - .collect(Collectors.toList()) - .forEach( - propertyName -> { - final int port = options.useHttps() ? newServer.httpsPort() : newServer.port(); - final String property = propertyName + "=" + port; - this.logger.info("Adding property '{}' to Spring application context", property); - TestPropertyValues.of(property).applyTo(context.getEnvironment()); - }); + Arrays.stream(options.portProperties()) + .filter(StringUtils::isNotBlank) + .collect(Collectors.toList()) + .forEach( + propertyName -> { + final String property = propertyName + "=" + newServer.port(); + this.logger.info( + "Adding property '{}' with HTTP port to Spring application context", property); + TestPropertyValues.of(property).applyTo(context.getEnvironment()); + }); + } + + if (httpsEnabled) { + Arrays.stream(options.httpsBaseUrlProperties()) + .filter(StringUtils::isNotBlank) + .collect(Collectors.toList()) + .forEach( + propertyName -> { + final String property = + propertyName + + "=" + + String.format("https://localhost:%d", newServer.httpsPort()); + this.logger.info( + "Adding property '{}' with HTTPS base URL to Spring application context", + property); + TestPropertyValues.of(property).applyTo(context.getEnvironment()); + }); + + Arrays.stream(options.httpsPortProperties()) + .filter(StringUtils::isNotBlank) + .collect(Collectors.toList()) + .forEach( + propertyName -> { + final String property = propertyName + "=" + newServer.httpsPort(); + this.logger.info( + "Adding property '{}' with HTTPS port to Spring application context", property); + TestPropertyValues.of(property).applyTo(context.getEnvironment()); + }); + } return newServer; } - private int getServerProperty( + private int getServerHttpPortProperty( final ConfigurableEnvironment environment, final ConfigureWireMock options) { if (!options.usePortFromPredefinedPropertyIfFound()) { return options.port(); @@ -142,6 +183,28 @@ private int getServerProperty( .orElse(options.port()); } + private int getServerHttpsPortProperty( + final ConfigurableEnvironment environment, final ConfigureWireMock options) { + if (!options.usePortFromPredefinedPropertyIfFound()) { + return options.httpsPort(); + } + return Arrays.stream(options.httpsPortProperties()) + .filter(StringUtils::isNotBlank) + .filter(propertyName -> environment.containsProperty(propertyName)) + .map( + propertyName -> { + final int predefinedPropertyValue = + Integer.parseInt(environment.getProperty(propertyName)); + this.logger.info( + "Found predefined https port in property with name '{}' on port: {}", + propertyName, + predefinedPropertyValue); + return predefinedPropertyValue; + }) + .findFirst() + .orElse(options.httpsPort()); + } + private WireMockConfiguration usingFilesUnderClasspath( final WireMockConfiguration serverOptions, final String resource) { this.logger.info("Serving WireMock mappings from classpath resource: " + resource); diff --git a/wiremock-spring-boot-example/src/test/java/app/HttpsAndHttpTest.java b/wiremock-spring-boot-example/src/test/java/app/HttpsAndHttpTest.java new file mode 100644 index 0000000..113e9a2 --- /dev/null +++ b/wiremock-spring-boot-example/src/test/java/app/HttpsAndHttpTest.java @@ -0,0 +1,77 @@ +package app; + +import static com.github.tomakehurst.wiremock.client.WireMock.aResponse; +import static com.github.tomakehurst.wiremock.client.WireMock.anyRequestedFor; +import static com.github.tomakehurst.wiremock.client.WireMock.anyUrl; +import static com.github.tomakehurst.wiremock.client.WireMock.get; +import static org.assertj.core.api.Assertions.assertThat; + +import com.github.tomakehurst.wiremock.WireMockServer; +import com.github.tomakehurst.wiremock.client.WireMock; +import io.restassured.RestAssured; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.test.context.SpringBootTest; +import org.wiremock.spring.ConfigureWireMock; +import org.wiremock.spring.EnableWireMock; +import org.wiremock.spring.InjectWireMock; + +@SpringBootTest +@EnableWireMock({@ConfigureWireMock(httpsPort = 0)}) +class HttpsAndHttpTest { + + @InjectWireMock private WireMockServer wiremock; + + @Value("${wiremock.server.httpsPort}") + private int wiremockHttpsPort; + + @Value("${wiremock.server.port}") + private int wiremockHttpPort; + + @Value("${wiremock.server.httpsBaseUrl}") + private String wiremockHttpsUrl; + + @Value("${wiremock.server.baseUrl}") + private String wiremockHttpUrl; + + @BeforeEach + public void before() { + RestAssured.useRelaxedHTTPSValidation(); + } + + @Test + void testProperties() { + assertThat(this.wiremockHttpsPort).isNotNull(); + assertThat(this.wiremockHttpsUrl) + .startsWith("https://") + .contains(String.valueOf(this.wiremockHttpsPort)); + + assertThat(this.wiremockHttpPort).isNotNull(); + assertThat(this.wiremockHttpUrl) + .startsWith("http://") + .contains(String.valueOf(this.wiremockHttpPort)); + } + + @Test + void testInjectedClient() { + this.wiremock.stubFor(get("/injected-client").willReturn(aResponse().withStatus(202))); + + RestAssured.when().get(this.wiremockHttpsUrl + "/injected-client").then().statusCode(202); + assertThat(this.wiremock.findAll(anyRequestedFor(anyUrl()))).hasSize(1); + + RestAssured.when().get(this.wiremockHttpUrl + "/injected-client").then().statusCode(202); + assertThat(this.wiremock.findAll(anyRequestedFor(anyUrl()))).hasSize(2); + } + + @Test + void testDefaultClient() { + WireMock.stubFor(WireMock.get("/with-default-client").willReturn(aResponse().withStatus(202))); + + RestAssured.when().get(this.wiremockHttpsUrl + "/with-default-client").then().statusCode(202); + assertThat(WireMock.findAll(anyRequestedFor(anyUrl()))).hasSize(1); + + RestAssured.when().get(this.wiremockHttpUrl + "/with-default-client").then().statusCode(202); + assertThat(WireMock.findAll(anyRequestedFor(anyUrl()))).hasSize(2); + } +} diff --git a/wiremock-spring-boot-example/src/test/java/app/HttpsTest.java b/wiremock-spring-boot-example/src/test/java/app/HttpsOnlyTest.java similarity index 70% rename from wiremock-spring-boot-example/src/test/java/app/HttpsTest.java rename to wiremock-spring-boot-example/src/test/java/app/HttpsOnlyTest.java index e23d1ec..9004bcf 100644 --- a/wiremock-spring-boot-example/src/test/java/app/HttpsTest.java +++ b/wiremock-spring-boot-example/src/test/java/app/HttpsOnlyTest.java @@ -18,16 +18,16 @@ import org.wiremock.spring.InjectWireMock; @SpringBootTest -@EnableWireMock({@ConfigureWireMock(useHttps = true)}) -class HttpsTest { +@EnableWireMock({@ConfigureWireMock(port = -1, httpsPort = 0)}) +class HttpsOnlyTest { @InjectWireMock private WireMockServer wiremock; - @Value("${wiremock.server.port}") - private int wiremockPort; + @Value("${wiremock.server.httpsPort}") + private int wiremockHttpsPort; - @Value("${wiremock.server.baseUrl}") - private String wiremockUrl; + @Value("${wiremock.server.httpsBaseUrl}") + private String wiremockHttpsUrl; @BeforeEach public void before() { @@ -36,15 +36,17 @@ public void before() { @Test void testProperties() { - assertThat(this.wiremockPort).isNotNull(); - assertThat(this.wiremockUrl).startsWith("https://").contains(String.valueOf(this.wiremockPort)); + assertThat(this.wiremockHttpsPort).isNotNull(); + assertThat(this.wiremockHttpsUrl) + .startsWith("https://") + .contains(String.valueOf(this.wiremockHttpsPort)); } @Test void testInjectedClient() { this.wiremock.stubFor(get("/injected-client").willReturn(aResponse().withStatus(202))); - RestAssured.when().get(this.wiremockUrl + "/injected-client").then().statusCode(202); + RestAssured.when().get(this.wiremockHttpsUrl + "/injected-client").then().statusCode(202); assertThat(this.wiremock.findAll(anyRequestedFor(anyUrl()))).hasSize(1); } @@ -53,7 +55,7 @@ void testInjectedClient() { void testDefaultClient() { WireMock.stubFor(WireMock.get("/with-default-client").willReturn(aResponse().withStatus(202))); - RestAssured.when().get(this.wiremockUrl + "/with-default-client").then().statusCode(202); + RestAssured.when().get(this.wiremockHttpsUrl + "/with-default-client").then().statusCode(202); assertThat(WireMock.findAll(anyRequestedFor(anyUrl()))).hasSize(1); }