Improved bean restarting and resetting

This commit is contained in:
Marcin Grzejszczak
2020-03-25 14:48:26 +01:00
parent 95ab9d3f2a
commit bd054e3220
17 changed files with 706 additions and 14 deletions

View File

@@ -1,5 +1,6 @@
server.port: 8097
logging:
level:
org.springframework.cloud.contract.wiremock: debug
org.springframework.web.client: debug
com.github.tomakehurst.wiremock: trace

View File

@@ -114,28 +114,58 @@ public class WireMockConfiguration implements SmartLifecycle {
updateCurrentServer();
}
void initIfNotRunning() throws IOException {
if (!this.running) {
init();
}
}
private void reRegisterBeans() {
if (!this.beanFactory.containsBean(WIREMOCK_SERVER_BEAN_NAME)) {
if (log.isDebugEnabled()) {
log.debug("Registering WireMock [" + this.server + "] instance");
}
this.beanFactory.registerSingleton(WIREMOCK_SERVER_BEAN_NAME, this.server);
}
else {
if (log.isDebugEnabled()) {
log.debug("Destroying WireMock ["
+ this.beanFactory.getBean(WIREMOCK_SERVER_BEAN_NAME)
+ "] instance");
}
this.beanFactory.destroySingleton(WIREMOCK_SERVER_BEAN_NAME);
if (log.isDebugEnabled()) {
log.debug("Registering WireMock [" + this.server + "] instance");
}
this.beanFactory.registerSingleton(WIREMOCK_SERVER_BEAN_NAME, this.server);
}
}
private void reRegisterServer() {
if (log.isDebugEnabled()) {
log.debug("Creating a new server at " + "http port ["
if (log.isTraceEnabled()) {
log.trace("Creating a new server at http port ["
+ this.wireMock.getServer().getPort() + "] and " + "https port ["
+ this.wireMock.getServer().getHttpsPort() + "]");
}
if (this.isRunning()) {
this.server.stop();
if (log.isDebugEnabled()) {
log.debug("Stopping server [" + this.server + "] at port ["
+ port(this.server) + "]");
}
stop();
}
else if (this.server == null) {
this.server = new WireMockServer(this.options);
if (log.isDebugEnabled()) {
log.debug("Created new server [" + this.server + "] at port ["
+ port(this.server) + "]");
}
}
start();
if (log.isDebugEnabled()) {
log.debug("Started server [" + this.server + "] at port [" + port(this.server)
+ "]");
}
this.server = new WireMockServer(this.options);
this.server.start();
updateCurrentServer();
logRegisteredMappings();
}
@@ -148,8 +178,8 @@ public class WireMockConfiguration implements SmartLifecycle {
void reRegisterServerWithResetMappings() {
reRegisterServer();
resetMappings();
if (this.server.isRunning()) {
resetMappings();
updateCurrentServer();
}
}
@@ -170,7 +200,7 @@ public class WireMockConfiguration implements SmartLifecycle {
private void registerStubs() {
if (log.isDebugEnabled()) {
log.debug("Will register [" + this.wireMock.getServer().getStubs().length
+ "] stubs");
+ "] stub locations");
}
for (String stubs : this.wireMock.getServer().getStubs()) {
if (StringUtils.hasText(stubs)) {
@@ -225,8 +255,10 @@ public class WireMockConfiguration implements SmartLifecycle {
@Override
public void start() {
if (isRunning()) {
int port = port(this.server);
if (log.isDebugEnabled()) {
log.debug("Server is already running");
log.debug("Server [" + this.server + "] is already running at port ["
+ port + "]");
}
return;
}
@@ -246,20 +278,25 @@ public class WireMockConfiguration implements SmartLifecycle {
@Override
public void stop() {
if (this.running) {
WireMockServer server = this.server;
int port = port(server);
this.server.stop();
this.server = null;
this.running = false;
this.options = null;
if (log.isDebugEnabled()) {
log.debug("Stopped WireMock instance");
log.debug(
"Stopped WireMock [" + server + "] instance port [" + port + "]");
}
this.beanFactory.destroySingleton(WIREMOCK_SERVER_BEAN_NAME);
}
else if (log.isDebugEnabled()) {
log.debug("Server already stopped");
}
}
private int port(WireMockServer server) {
return server.isRunning() ? (server.getOptions().httpsSettings().enabled()
? server.httpsPort() : server.port()) : -1;
}
@Override
public boolean isRunning() {
return this.running;

View File

@@ -46,7 +46,8 @@ public final class WireMockTestExecutionListener extends AbstractTestExecutionLi
if (log.isDebugEnabled()) {
log.debug("Re-registering default mappings");
}
wireMockConfig(testContext).init();
wireMockConfig(testContext).initIfNotRunning();
wireMockConfig(testContext).start();
}
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.wiremock.issues.sameConfigsDifferentTests;
public class Client {
private String pesel;
public Client() {
}
public Client(String pesel) {
this.pesel = pesel;
}
public String getPesel() {
return pesel;
}
public void setPesel(String pesel) {
this.pesel = pesel;
}
}

View File

@@ -0,0 +1,83 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.wiremock.issues.sameConfigsDifferentTests;
import java.nio.charset.Charset;
import com.github.tomakehurst.wiremock.WireMockServer;
import com.github.tomakehurst.wiremock.stubbing.StubMapping;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.contract.wiremock.AutoConfigureWireMock;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.util.StreamUtils;
import org.springframework.web.client.RestTemplate;
import static org.assertj.core.api.Assertions.assertThat;
@RunWith(SpringRunner.class)
@SpringBootTest(properties = "service.port=${wiremock.server.port}",
classes = FirstTests.Config.class)
@AutoConfigureWireMock(port = 0)
public class FirstTests {
@Value("classpath:example-mappings/shouldMarkClientAsFraud.json")
private Resource markClientAsFraud;
@Autowired
private WireMockServer server;
@Test
public void shouldBeRejectedDueToAbnormalLoanAmount() throws Exception {
server.addStubMapping(StubMapping.buildFrom(StreamUtils.copyToString(
markClientAsFraud.getInputStream(), Charset.forName("UTF-8"))));
// given:
LoanApplication loanApplication = new LoanApplication(new Client("1234567890"),
99999);
// when:
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.add(HttpHeaders.CONTENT_TYPE, "application/vnd.fraud.v1+json");
ResponseEntity<FraudServiceResponse> response = new RestTemplate().exchange(
"http://localhost:" + server.port() + "/fraudcheck", HttpMethod.PUT,
new HttpEntity<>(new FraudServiceRequest(loanApplication), httpHeaders),
FraudServiceResponse.class);
// then:
assertThat(response.getBody().getFraudCheckStatus())
.isEqualTo(FraudCheckStatus.FRAUD);
assertThat(response.getBody().getRejectionReason()).isEqualTo("Amount too high");
}
@EnableAutoConfiguration
@Configuration
static class Config {
}
}

View File

@@ -0,0 +1,23 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.wiremock.issues.sameConfigsDifferentTests;
public enum FraudCheckStatus {
OK, FRAUD
}

View File

@@ -0,0 +1,51 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.wiremock.issues.sameConfigsDifferentTests;
import java.math.BigDecimal;
public class FraudServiceRequest {
private String clientId;
private BigDecimal loanAmount;
public FraudServiceRequest() {
}
public FraudServiceRequest(LoanApplication loanApplication) {
this.clientId = loanApplication.getClient().getPesel();
this.loanAmount = loanApplication.getAmount();
}
public String getClientId() {
return clientId;
}
public void setClientId(String clientId) {
this.clientId = clientId;
}
public BigDecimal getLoanAmount() {
return loanAmount;
}
public void setLoanAmount(BigDecimal loanAmount) {
this.loanAmount = loanAmount;
}
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.wiremock.issues.sameConfigsDifferentTests;
public class FraudServiceResponse {
private FraudCheckStatus fraudCheckStatus;
private String rejectionReason;
public FraudServiceResponse() {
}
public FraudCheckStatus getFraudCheckStatus() {
return fraudCheckStatus;
}
public void setFraudCheckStatus(FraudCheckStatus fraudCheckStatus) {
this.fraudCheckStatus = fraudCheckStatus;
}
public String getRejectionReason() {
return rejectionReason;
}
public void setRejectionReason(String rejectionReason) {
this.rejectionReason = rejectionReason;
}
}

View File

@@ -0,0 +1,61 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.wiremock.issues.sameConfigsDifferentTests;
import java.math.BigDecimal;
public class LoanApplication {
private Client client;
private BigDecimal amount;
private String loanApplicationId;
public LoanApplication() {
}
public LoanApplication(Client client, double amount) {
this.client = client;
this.amount = BigDecimal.valueOf(amount);
}
public Client getClient() {
return client;
}
public void setClient(Client client) {
this.client = client;
}
public BigDecimal getAmount() {
return amount;
}
public void setAmount(BigDecimal amount) {
this.amount = amount;
}
public String getLoanApplicationId() {
return loanApplicationId;
}
public void setLoanApplicationId(String loanApplicationId) {
this.loanApplicationId = loanApplicationId;
}
}

View File

@@ -0,0 +1,82 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.wiremock.issues.sameConfigsDifferentTests;
import java.nio.charset.Charset;
import com.github.tomakehurst.wiremock.WireMockServer;
import com.github.tomakehurst.wiremock.stubbing.StubMapping;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.cloud.contract.wiremock.AutoConfigureWireMock;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.util.StreamUtils;
import org.springframework.web.client.RestTemplate;
import static org.assertj.core.api.Assertions.assertThat;
@RunWith(SpringRunner.class)
@SpringBootTest(properties = "service.port=${wiremock.server.port}",
classes = SecondTests.Config.class)
@AutoConfigureWireMock(port = 0)
public class SecondTests {
@Value("classpath:example-mappings/shouldMarkClientAsNotFraud.json")
private Resource markClientAsNotFraud;
@Autowired
private WireMockServer server;
@Test
public void shouldBeRejectedDueToAbnormalLoanAmount() throws Exception {
server.addStubMapping(StubMapping.buildFrom(StreamUtils.copyToString(
markClientAsNotFraud.getInputStream(), Charset.forName("UTF-8"))));
// given:
LoanApplication loanApplication = new LoanApplication(new Client("1234567890"),
123.123);
// when:
HttpHeaders httpHeaders = new HttpHeaders();
httpHeaders.add(HttpHeaders.CONTENT_TYPE, "application/vnd.fraud.v1+json");
ResponseEntity<FraudServiceResponse> response = new RestTemplate().exchange(
"http://localhost:" + server.port() + "/fraudcheck", HttpMethod.PUT,
new HttpEntity<>(new FraudServiceRequest(loanApplication), httpHeaders),
FraudServiceResponse.class);
// then:
assertThat(response.getBody().getFraudCheckStatus())
.isEqualTo(FraudCheckStatus.OK);
}
@EnableAutoConfiguration
@Configuration
static class Config {
}
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.wiremock.issues.staticInit;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "my-client")
public class ClientProperties {
private String url;
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.wiremock.issues.staticInit;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
@Configuration
@EnableConfigurationProperties(ClientProperties.class)
public class RestTemplateConfiguration {
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
}

View File

@@ -0,0 +1,29 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.wiremock.issues.staticInit;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class TimeoutApplication {
public static void main(String[] args) {
SpringApplication.run(TimeoutApplication.class, args);
}
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.wiremock.issues.staticInit;
import java.net.URI;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
@Component
public class WebClient {
private final RestTemplate restTemplate;
private final ClientProperties clientProperties;
public WebClient(RestTemplate restTemplate, ClientProperties clientProperties) {
this.restTemplate = restTemplate;
this.clientProperties = clientProperties;
}
public String get() {
URI url = URI.create(clientProperties.getUrl());
return restTemplate.getForObject(url, String.class);
}
}

View File

@@ -0,0 +1,82 @@
/*
* Copyright 2013-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cloud.contract.wiremock.issues.staticInit;
import com.github.tomakehurst.wiremock.WireMockServer;
import org.assertj.core.api.BDDAssertions;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.TestConfiguration;
import org.springframework.cloud.contract.wiremock.AutoConfigureWireMock;
import org.springframework.context.annotation.Bean;
import static com.github.tomakehurst.wiremock.client.WireMock.aResponse;
import static com.github.tomakehurst.wiremock.client.WireMock.get;
import static com.github.tomakehurst.wiremock.client.WireMock.stubFor;
import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
@SpringBootTest(
properties = "my-client.url=http://localhost:${wiremock.server.port}/resources")
@AutoConfigureWireMock(port = 0, httpsPort = 0, files = "src/test/resources/__files")
class WebClientTest {
private static Logger log = LoggerFactory.getLogger(WebClientTest.class);
@Autowired
WebClient underTest;
@Autowired
Stubber stubber;
@Test
void shouldRespondForGetJustFine() {
String actual = underTest.get();
BDDAssertions.then(actual).isEqualTo("Everything seems fine!");
}
@TestConfiguration
static class WebClientTestConfiguration {
@Bean
Stubber stubberWithoutDeps() {
return new Stubber();
}
}
static class Stubber {
Stubber() {
log.info("Setting up GET");
stubFor(get(urlEqualTo("/resources"))
.willReturn(aResponse().withBody("Everything seems fine!")));
}
Stubber(WireMockServer wireMockServer) {
log.info("Setting up GET");
wireMockServer.stubFor(get(urlEqualTo("/resources"))
.willReturn(aResponse().withBody("Everything seems fine!")));
}
}
}

View File

@@ -0,0 +1,26 @@
{
"id" : "6655794c-a651-4b71-a2b9-b7dc7e1c273f",
"request" : {
"url" : "/fraudcheck",
"method" : "PUT",
"headers" : {
"Content-Type" : {
"matches" : "application/vnd\\.fraud\\.v1\\+json.*"
}
},
"bodyPatterns" : [ {
"matchesJsonPath" : "$[?(@.['clientId'] =~ /[0-9]{10}/)]"
}, {
"matchesJsonPath" : "$[?(@.['loanAmount'] == 99999)]"
} ]
},
"response" : {
"status" : 200,
"body" : "{\"fraudCheckStatus\":\"FRAUD\",\"rejectionReason\":\"Amount too high\"}",
"headers" : {
"Content-Type" : "application/vnd.fraud.v1+json"
},
"transformers" : [ "response-template" ]
},
"uuid" : "6655794c-a651-4b71-a2b9-b7dc7e1c273f"
}

View File

@@ -0,0 +1,26 @@
{
"id" : "de2f8246-ae1a-4bdf-8d4c-a44af6df7a83",
"request" : {
"url" : "/fraudcheck",
"method" : "PUT",
"headers" : {
"Content-Type" : {
"matches" : "application/vnd\\.fraud\\.v1\\+json.*"
}
},
"bodyPatterns" : [ {
"matchesJsonPath" : "$[?(@.['clientId'] =~ /[0-9]{10}/)]"
}, {
"matchesJsonPath" : "$[?(@.['loanAmount'] == 123.123)]"
} ]
},
"response" : {
"status" : 200,
"body" : "{\"fraudCheckStatus\":\"OK\",\"rejectionReason\":null}",
"headers" : {
"Content-Type" : "application/vnd.fraud.v1+json"
},
"transformers" : [ "response-template" ]
},
"uuid" : "de2f8246-ae1a-4bdf-8d4c-a44af6df7a83"
}