Merge branch 'master' into features/docs-tuning
This commit is contained in:
@@ -94,6 +94,49 @@ include::{doc_samples}/src/test/java/com/example/WiremockForDocsClassRuleTests.j
|
||||
|
||||
The use `@ClassRule` means that the server will shut down after all the methods in this class.
|
||||
|
||||
== Relaxed SSL Validation for Rest Template
|
||||
|
||||
WireMock allows you to stub a "secure" server with an "https" URL protocol. If your application wants to
|
||||
contact that stub server in an integration test, then it will find that the SSL certificates are not
|
||||
valid (it's the usual problem with self-installed certificates). The best option is often to just
|
||||
re-configure the client to use "http", but if that's not open to you then you can ask Spring to configure
|
||||
an HTTP client that ignores SSL validation errors (just for tests).
|
||||
|
||||
To make this work with minimum fuss you need to be using the Spring Boot `RestTemplateBuilder` in your app,
|
||||
e.g.
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
@Bean
|
||||
public RestTemplate restTemplate(RestTemplateBuilder builder) {
|
||||
return builder.build();
|
||||
}
|
||||
----
|
||||
|
||||
This is because the builder is passed through callbacks to initalize it, so the SSL validation can be set up
|
||||
in the client at that point. This will happen automatically in your test if you are using the
|
||||
`@AutoConfigureWireMock` annotation (or the stub runner). If you are using the JUnit `@Rule` approach you need
|
||||
to add the `@AutoConfigureHttpClient` annotation as well:
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest("app.baseUrl=https://localhost:6443")
|
||||
@AutoConfigureHttpClient
|
||||
public class WiremockHttpsServerApplicationTests {
|
||||
|
||||
@ClassRule
|
||||
public static WireMockClassRule wiremock = new WireMockClassRule(
|
||||
WireMockSpring.options().httpsPort(6443));
|
||||
...
|
||||
}
|
||||
----
|
||||
|
||||
If you are using `spring-boot-starter-test` then you will have the Apache HTTP client on the classpath and it will
|
||||
be selected by the `RestTemplateBuilder` and configured to ignore SSL errors. If you are using the default `java.net`
|
||||
client you don't need the annotation (but it won't do any harm). There is no support currently for other clients, but
|
||||
it may be added in future releases.
|
||||
|
||||
== WireMock and Spring MVC Mocks
|
||||
|
||||
Spring Cloud Contract provides a convenience class that can load JSON WireMock stubs into a
|
||||
|
||||
12
pom.xml
12
pom.xml
@@ -7,7 +7,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-build</artifactId>
|
||||
<version>1.3.1.BUILD-SNAPSHOT</version>
|
||||
<version>1.3.2.BUILD-SNAPSHOT</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
|
||||
@@ -27,12 +27,12 @@
|
||||
<camel.version>2.17.0</camel.version>
|
||||
<spring-boot.version>1.5.3.BUILD-SNAPSHOT</spring-boot.version>
|
||||
<checkstyle.version>2.17</checkstyle.version>
|
||||
<spring-cloud-build.version>1.3.1.BUILD-SNAPSHOT</spring-cloud-build.version>
|
||||
<spring-cloud-zookeeper.version>1.1.0.BUILD-SNAPSHOT</spring-cloud-zookeeper.version>
|
||||
<spring-cloud-build.version>1.3.2.BUILD-SNAPSHOT</spring-cloud-build.version>
|
||||
<spring-cloud-zookeeper.version>1.1.1.BUILD-SNAPSHOT</spring-cloud-zookeeper.version>
|
||||
<spring-cloud-stream.version>Chelsea.BUILD-SNAPSHOT</spring-cloud-stream.version>
|
||||
<spring-cloud-netflix.version>1.3.0.BUILD-SNAPSHOT</spring-cloud-netflix.version>
|
||||
<spring-cloud-consul.version>1.2.0.BUILD-SNAPSHOT</spring-cloud-consul.version>
|
||||
<spring-cloud-commons.version>1.2.0.BUILD-SNAPSHOT</spring-cloud-commons.version>
|
||||
<spring-cloud-netflix.version>1.3.1.BUILD-SNAPSHOT</spring-cloud-netflix.version>
|
||||
<spring-cloud-consul.version>1.2.1.BUILD-SNAPSHOT</spring-cloud-consul.version>
|
||||
<spring-cloud-commons.version>1.2.1.BUILD-SNAPSHOT</spring-cloud-commons.version>
|
||||
</properties>
|
||||
|
||||
<modules>
|
||||
|
||||
@@ -5,6 +5,7 @@ import org.springframework.boot.web.client.RestTemplateBuilder;
|
||||
import org.springframework.http.HttpEntity;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
@@ -20,9 +21,6 @@ import com.example.loan.model.Response;
|
||||
@Service
|
||||
public class LoanApplicationService {
|
||||
|
||||
private static final String FRAUD_SERVICE_JSON_VERSION_1 =
|
||||
"application/vnd.fraud.v1+json";
|
||||
|
||||
private final RestTemplate restTemplate;
|
||||
|
||||
private int port = 6565;
|
||||
@@ -45,7 +43,7 @@ public class LoanApplicationService {
|
||||
private FraudServiceResponse sendRequestToFraudDetectionService(
|
||||
FraudServiceRequest request) {
|
||||
HttpHeaders httpHeaders = new HttpHeaders();
|
||||
httpHeaders.add(HttpHeaders.CONTENT_TYPE, FRAUD_SERVICE_JSON_VERSION_1);
|
||||
httpHeaders.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
|
||||
|
||||
// tag::client_call_server[]
|
||||
ResponseEntity<FraudServiceResponse> response =
|
||||
@@ -70,7 +68,7 @@ public class LoanApplicationService {
|
||||
|
||||
public int countAllFrauds() {
|
||||
HttpHeaders httpHeaders = new HttpHeaders();
|
||||
httpHeaders.add(HttpHeaders.CONTENT_TYPE, FRAUD_SERVICE_JSON_VERSION_1);
|
||||
httpHeaders.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
|
||||
ResponseEntity<Response> response =
|
||||
restTemplate.exchange("http://localhost:" + port + "/frauds", HttpMethod.GET,
|
||||
new HttpEntity<>(httpHeaders),
|
||||
@@ -80,7 +78,7 @@ public class LoanApplicationService {
|
||||
|
||||
public int countDrunks() {
|
||||
HttpHeaders httpHeaders = new HttpHeaders();
|
||||
httpHeaders.add(HttpHeaders.CONTENT_TYPE, FRAUD_SERVICE_JSON_VERSION_1);
|
||||
httpHeaders.add(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE);
|
||||
ResponseEntity<Response> response =
|
||||
restTemplate.exchange("http://localhost:" + port + "/drunks", HttpMethod.GET,
|
||||
new HttpEntity<>(httpHeaders),
|
||||
|
||||
@@ -117,7 +117,7 @@
|
||||
<pluginExecutionFilter>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-contract-maven-plugin</artifactId>
|
||||
<versionRange>[1.1.0.BUILD-SNAPSHOT,)</versionRange>
|
||||
<versionRange>[1.1.1.BUILD-SNAPSHOT,)</versionRange>
|
||||
<goals>
|
||||
<goal>convert</goal>
|
||||
<goal>generateTests</goal>
|
||||
|
||||
@@ -15,17 +15,12 @@ import static org.springframework.web.bind.annotation.RequestMethod.PUT;
|
||||
@RestController
|
||||
public class FraudDetectionController {
|
||||
|
||||
private static final String FRAUD_SERVICE_JSON_VERSION_1 = "application/vnd.fraud.v1+json";
|
||||
private static final String NO_REASON = null;
|
||||
private static final String AMOUNT_TOO_HIGH = "Amount too high";
|
||||
private static final BigDecimal MAX_AMOUNT = new BigDecimal("5000");
|
||||
|
||||
// tag::server_api[]
|
||||
@RequestMapping(
|
||||
value = "/fraudcheck",
|
||||
method = PUT,
|
||||
consumes = FRAUD_SERVICE_JSON_VERSION_1,
|
||||
produces = FRAUD_SERVICE_JSON_VERSION_1)
|
||||
@RequestMapping(value = "/fraudcheck", method = PUT)
|
||||
public FraudCheckResult fraudCheck(@RequestBody FraudCheck fraudCheck) {
|
||||
// end::server_api[]
|
||||
// tag::new_impl[]
|
||||
|
||||
@@ -10,17 +10,13 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
@RestController
|
||||
class FraudNameController {
|
||||
|
||||
private static final String FRAUD_SERVICE_JSON_VERSION_1 = "application/vnd.fraud.v1+json";
|
||||
|
||||
private final FraudVerifier fraudVerifier;
|
||||
|
||||
FraudNameController(FraudVerifier fraudVerifier) {
|
||||
this.fraudVerifier = fraudVerifier;
|
||||
}
|
||||
|
||||
@PutMapping(
|
||||
value = "/frauds/name",
|
||||
produces = FRAUD_SERVICE_JSON_VERSION_1)
|
||||
@PutMapping(value = "/frauds/name")
|
||||
public NameResponse checkByName(@RequestBody NameRequest request) {
|
||||
boolean fraud = this.fraudVerifier.isFraudByName(request.getName());
|
||||
if (fraud) {
|
||||
|
||||
@@ -6,24 +6,18 @@ import org.springframework.web.bind.annotation.RestController;
|
||||
@RestController
|
||||
public class FraudStatsController {
|
||||
|
||||
private static final String FRAUD_SERVICE_JSON_VERSION_1 = "application/vnd.fraud.v1+json";
|
||||
|
||||
private final StatsProvider statsProvider;
|
||||
|
||||
public FraudStatsController(StatsProvider statsProvider) {
|
||||
this.statsProvider = statsProvider;
|
||||
}
|
||||
|
||||
@GetMapping(
|
||||
value = "/frauds",
|
||||
produces = FRAUD_SERVICE_JSON_VERSION_1)
|
||||
@GetMapping(value = "/frauds")
|
||||
public Response countAllFrauds() {
|
||||
return new Response(this.statsProvider.count(FraudType.ALL));
|
||||
}
|
||||
|
||||
@GetMapping(
|
||||
value = "/drunks",
|
||||
produces = FRAUD_SERVICE_JSON_VERSION_1)
|
||||
@GetMapping(value = "/drunks")
|
||||
public Response countAllDrunks() {
|
||||
return new Response(this.statsProvider.count(FraudType.DRUNKS));
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ org.springframework.cloud.contract.spec.Contract.make {
|
||||
loanAmount: 99999
|
||||
])
|
||||
headers { // (5)
|
||||
contentType('application/vnd.fraud.v1+json')
|
||||
contentType('application/json')
|
||||
}
|
||||
}
|
||||
response { // (6)
|
||||
@@ -19,7 +19,7 @@ org.springframework.cloud.contract.spec.Contract.make {
|
||||
rejectionReason: "Amount too high"
|
||||
])
|
||||
headers { // (9)
|
||||
contentType('application/vnd.fraud.v1+json')
|
||||
contentType('application/json')
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -33,12 +33,12 @@ From the Consumer perspective, when shooting a request in the integration test:
|
||||
(4) - with the JSON body that
|
||||
* has a field `clientId` that matches a regular expression `[0-9]{10}`
|
||||
* has a field `loanAmount` that is equal to `99999`
|
||||
(5) - with header `Content-Type` equal to `application/vnd.fraud.v1+json`
|
||||
(5) - with header `Content-Type` equal to `application/json`
|
||||
(6) - then the response will be sent with
|
||||
(7) - status equal `200`
|
||||
(8) - and JSON body equal to
|
||||
{ "fraudCheckStatus": "FRAUD", "rejectionReason": "Amount too high" }
|
||||
(9) - with header `Content-Type` equal to `application/vnd.fraud.v1+json`
|
||||
(9) - with header `Content-Type` equal to `application/json`
|
||||
|
||||
From the Producer perspective, in the autogenerated producer-side test:
|
||||
|
||||
@@ -48,10 +48,10 @@ From the Producer perspective, in the autogenerated producer-side test:
|
||||
(4) - with the JSON body that
|
||||
* has a field `clientId` that will have a generated value that matches a regular expression `[0-9]{10}`
|
||||
* has a field `loanAmount` that is equal to `99999`
|
||||
(5) - with header `Content-Type` equal to `application/vnd.fraud.v1+json`
|
||||
(5) - with header `Content-Type` equal to `application/json`
|
||||
(6) - then the test will assert if the response has been sent with
|
||||
(7) - status equal `200`
|
||||
(8) - and JSON body equal to
|
||||
{ "fraudCheckStatus": "FRAUD", "rejectionReason": "Amount too high" }
|
||||
(9) - with header `Content-Type` matching `application/vnd.fraud.v1+json.*`
|
||||
(9) - with header `Content-Type` matching `application/json.*`
|
||||
*/
|
||||
@@ -12,7 +12,7 @@ org.springframework.cloud.contract.spec.Contract.make {
|
||||
"""
|
||||
)
|
||||
headers {
|
||||
contentType("application/vnd.fraud.v1+json")
|
||||
contentType("application/json")
|
||||
}
|
||||
|
||||
}
|
||||
@@ -23,7 +23,7 @@ org.springframework.cloud.contract.spec.Contract.make {
|
||||
rejectionReason: $(consumer(null), producer(execute('assertThatRejectionReasonIsNull($it)')))
|
||||
)
|
||||
headers {
|
||||
contentType("application/vnd.fraud.v1+json")
|
||||
contentType("application/json")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ import org.springframework.cloud.contract.spec.Contract
|
||||
count: 200
|
||||
])
|
||||
headers {
|
||||
contentType("application/vnd.fraud.v1+json")
|
||||
contentType("application/json")
|
||||
}
|
||||
}
|
||||
},
|
||||
@@ -30,7 +30,7 @@ import org.springframework.cloud.contract.spec.Contract
|
||||
count: 100
|
||||
])
|
||||
headers {
|
||||
contentType("application/vnd.fraud.v1+json")
|
||||
contentType("application/json")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ org.springframework.cloud.contract.spec.Contract.make {
|
||||
name: "fraud"
|
||||
])
|
||||
headers {
|
||||
contentType("application/vnd.fraud.v1+json")
|
||||
contentType("application/json")
|
||||
}
|
||||
}
|
||||
response {
|
||||
|
||||
@@ -8,7 +8,7 @@ org.springframework.cloud.contract.spec.Contract.make {
|
||||
name: $(anyAlphaUnicode())
|
||||
])
|
||||
headers {
|
||||
contentType("application/vnd.fraud.v1+json")
|
||||
contentType("application/json")
|
||||
}
|
||||
}
|
||||
response {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-contract-samples-standalone</artifactId>
|
||||
<version>1.1.0.BUILD-SNAPSHOT</version>
|
||||
<version>1.1.1.BUILD-SNAPSHOT</version>
|
||||
<relativePath>..</relativePath>
|
||||
</parent>
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-contract-samples-standalone</artifactId>
|
||||
<version>1.1.0.BUILD-SNAPSHOT</version>
|
||||
<version>1.1.1.BUILD-SNAPSHOT</version>
|
||||
<relativePath>..</relativePath>
|
||||
</parent>
|
||||
|
||||
|
||||
@@ -156,7 +156,7 @@
|
||||
<pluginExecutionFilter>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-contract-maven-plugin</artifactId>
|
||||
<versionRange>[1.1.0.BUILD-SNAPSHOT,)</versionRange>
|
||||
<versionRange>[1.1.1.BUILD-SNAPSHOT,)</versionRange>
|
||||
<goals>
|
||||
<goal>convert</goal>
|
||||
<goal>generateTests</goal>
|
||||
|
||||
@@ -125,7 +125,7 @@
|
||||
<pluginExecutionFilter>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-contract-maven-plugin</artifactId>
|
||||
<versionRange>[1.1.0.BUILD-SNAPSHOT,)</versionRange>
|
||||
<versionRange>[1.1.1.BUILD-SNAPSHOT,)</versionRange>
|
||||
<goals>
|
||||
<goal>convert</goal>
|
||||
<goal>generateTests</goal>
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-contract-samples-standalone</artifactId>
|
||||
<version>1.1.0.BUILD-SNAPSHOT</version>
|
||||
<version>1.1.1.BUILD-SNAPSHOT</version>
|
||||
<relativePath>..</relativePath>
|
||||
</parent>
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-contract-samples</artifactId>
|
||||
<version>1.1.0.BUILD-SNAPSHOT</version>
|
||||
<version>1.1.1.BUILD-SNAPSHOT</version>
|
||||
<relativePath>..</relativePath>
|
||||
</parent>
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-contract-samples-standalone</artifactId>
|
||||
<version>1.1.0.BUILD-SNAPSHOT</version>
|
||||
<version>1.1.1.BUILD-SNAPSHOT</version>
|
||||
<relativePath>..</relativePath>
|
||||
</parent>
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.example;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.web.client.RestTemplateBuilder;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
@@ -13,8 +14,8 @@ import org.springframework.web.client.RestTemplate;
|
||||
public class WiremockTestsApplication {
|
||||
|
||||
@Bean
|
||||
public RestTemplate restTemplate() {
|
||||
return new RestTemplate();
|
||||
public RestTemplate restTemplate(RestTemplateBuilder builder) {
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
@@ -1,26 +1,28 @@
|
||||
package com.example;
|
||||
|
||||
import com.github.tomakehurst.wiremock.junit.WireMockClassRule;
|
||||
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.contract.wiremock.AutoConfigureHttpClient;
|
||||
import org.springframework.cloud.contract.wiremock.WireMockSpring;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
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;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.contract.wiremock.WireMockSpring;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import com.github.tomakehurst.wiremock.junit.WireMockClassRule;
|
||||
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest("app.baseUrl=https://localhost:8443")
|
||||
@DirtiesContext
|
||||
@AutoConfigureHttpClient
|
||||
public class WiremockHttpsServerApplicationTests {
|
||||
|
||||
@ClassRule
|
||||
@@ -32,8 +34,8 @@ public class WiremockHttpsServerApplicationTests {
|
||||
|
||||
@Test
|
||||
public void contextLoads() throws Exception {
|
||||
stubFor(get(urlEqualTo("/resource"))
|
||||
.willReturn(aResponse().withHeader("Content-Type", "text/plain").withBody("Hello World!")));
|
||||
stubFor(get(urlEqualTo("/resource")).willReturn(aResponse()
|
||||
.withHeader("Content-Type", "text/plain").withBody("Hello World!")));
|
||||
assertThat(this.service.go()).isEqualTo("Hello World!");
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
package com.example;
|
||||
|
||||
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;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import com.github.tomakehurst.wiremock.http.Fault;
|
||||
import com.github.tomakehurst.wiremock.junit.WireMockClassRule;
|
||||
|
||||
import org.apache.http.MalformedChunkCodingException;
|
||||
import org.apache.http.NoHttpResponseException;
|
||||
import org.apache.http.client.ClientProtocolException;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||
@@ -16,7 +19,12 @@ import org.springframework.cloud.contract.wiremock.WireMockSpring;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import com.github.tomakehurst.wiremock.junit.WireMockClassRule;
|
||||
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;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.hamcrest.CoreMatchers.instanceOf;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(properties="app.baseUrl=http://localhost:6061", webEnvironment=WebEnvironment.NONE)
|
||||
@@ -26,14 +34,41 @@ public class WiremockServerApplicationTests {
|
||||
@ClassRule
|
||||
public static WireMockClassRule wiremock = new WireMockClassRule(WireMockSpring.options().port(6061));
|
||||
|
||||
@Rule
|
||||
public ExpectedException expected = ExpectedException.none();
|
||||
|
||||
@Autowired
|
||||
private Service service;
|
||||
|
||||
@Test
|
||||
public void contextLoads() throws Exception {
|
||||
public void hello() throws Exception {
|
||||
stubFor(get(urlEqualTo("/resource"))
|
||||
.willReturn(aResponse().withHeader("Content-Type", "text/plain").withBody("Hello World!")));
|
||||
assertThat(this.service.go()).isEqualTo("Hello World!");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void randomData() throws Exception {
|
||||
stubFor(get(urlEqualTo("/resource"))
|
||||
.willReturn(aResponse().withFault(Fault.RANDOM_DATA_THEN_CLOSE)));
|
||||
expected.expectCause(instanceOf(ClientProtocolException.class));
|
||||
assertThat(this.service.go()).isEqualTo("Oops!");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void emptyResponse() throws Exception {
|
||||
stubFor(get(urlEqualTo("/resource"))
|
||||
.willReturn(aResponse().withFault(Fault.EMPTY_RESPONSE)));
|
||||
expected.expectCause(instanceOf(NoHttpResponseException.class));
|
||||
assertThat(this.service.go()).isEqualTo("Oops!");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void malformed() throws Exception {
|
||||
stubFor(get(urlEqualTo("/resource"))
|
||||
.willReturn(aResponse().withFault(Fault.MALFORMED_RESPONSE_CHUNK)));
|
||||
expected.expectCause(instanceOf(MalformedChunkCodingException.class));
|
||||
assertThat(this.service.go()).isEqualTo("Oops!");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ package com.example;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.boot.web.client.RestTemplateBuilder;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
@@ -13,8 +14,8 @@ import org.springframework.web.client.RestTemplate;
|
||||
public class WiremockTestsApplication {
|
||||
|
||||
@Bean
|
||||
public RestTemplate restTemplate() {
|
||||
return new RestTemplate();
|
||||
public RestTemplate restTemplate(RestTemplateBuilder builder) {
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
@@ -11,6 +11,7 @@ import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.cloud.contract.wiremock.AutoConfigureHttpClient;
|
||||
import org.springframework.cloud.contract.wiremock.WireMockSpring;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
@@ -21,6 +22,7 @@ import com.github.tomakehurst.wiremock.junit.WireMockClassRule;
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest("app.baseUrl=https://localhost:6443")
|
||||
@DirtiesContext
|
||||
@AutoConfigureHttpClient
|
||||
public class WiremockHttpsServerApplicationTests {
|
||||
|
||||
@ClassRule
|
||||
|
||||
@@ -1,14 +1,18 @@
|
||||
package com.example;
|
||||
|
||||
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;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import java.io.IOException;
|
||||
|
||||
import com.github.tomakehurst.wiremock.http.Fault;
|
||||
import com.github.tomakehurst.wiremock.junit.WireMockClassRule;
|
||||
|
||||
import org.apache.http.NoHttpResponseException;
|
||||
import org.apache.http.client.ClientProtocolException;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||
@@ -16,7 +20,12 @@ import org.springframework.cloud.contract.wiremock.WireMockSpring;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import com.github.tomakehurst.wiremock.junit.WireMockClassRule;
|
||||
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;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.hamcrest.CoreMatchers.instanceOf;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(properties="app.baseUrl=http://localhost:6067", webEnvironment=WebEnvironment.NONE)
|
||||
@@ -26,6 +35,9 @@ public class WiremockServerApplicationTests {
|
||||
@ClassRule
|
||||
public static WireMockClassRule wiremock = new WireMockClassRule(WireMockSpring.options().port(6067));
|
||||
|
||||
@Rule
|
||||
public ExpectedException expected = ExpectedException.none();
|
||||
|
||||
@Autowired
|
||||
private Service service;
|
||||
|
||||
@@ -36,4 +48,30 @@ public class WiremockServerApplicationTests {
|
||||
assertThat(this.service.go()).isEqualTo("Hello World!");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void randomData() throws Exception {
|
||||
stubFor(get(urlEqualTo("/resource"))
|
||||
.willReturn(aResponse().withFault(Fault.RANDOM_DATA_THEN_CLOSE)));
|
||||
expected.expectCause(instanceOf(ClientProtocolException.class));
|
||||
assertThat(this.service.go()).isEqualTo("Oops!");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void emptyResponse() throws Exception {
|
||||
stubFor(get(urlEqualTo("/resource"))
|
||||
.willReturn(aResponse().withFault(Fault.EMPTY_RESPONSE)));
|
||||
expected.expectCause(instanceOf(NoHttpResponseException.class));
|
||||
assertThat(this.service.go()).isEqualTo("Oops!");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void malformed() throws Exception {
|
||||
stubFor(get(urlEqualTo("/resource"))
|
||||
.willReturn(aResponse().withFault(Fault.MALFORMED_RESPONSE_CHUNK)));
|
||||
// It's a different exception type than Jetty, but it's in the right ballpark
|
||||
expected.expectCause(instanceOf(IOException.class));
|
||||
expected.expectMessage("chunk");
|
||||
assertThat(this.service.go()).isEqualTo("Oops!");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<artifactId>spring-cloud-dependencies-parent</artifactId>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<version>1.3.1.BUILD-SNAPSHOT</version>
|
||||
<version>1.3.2.BUILD-SNAPSHOT</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
<artifactId>spring-cloud-contract-dependencies</artifactId>
|
||||
|
||||
@@ -23,7 +23,7 @@ ext {
|
||||
]
|
||||
}
|
||||
|
||||
project.version = findProperty('verifierVersion') ?: '1.1.0.BUILD-SNAPSHOT'
|
||||
project.version = findProperty('verifierVersion') ?: '1.1.1.BUILD-SNAPSHOT'
|
||||
apply plugin: 'groovy'
|
||||
apply from: "$rootDir/gradle/release.gradle"
|
||||
apply plugin: 'eclipse'
|
||||
|
||||
@@ -16,5 +16,5 @@
|
||||
|
||||
nexusUsername =
|
||||
nexusPassword =
|
||||
verifierVersion=1.1.0.BUILD-SNAPSHOT
|
||||
verifierVersion=1.1.1.BUILD-SNAPSHOT
|
||||
org.gradle.daemon=false
|
||||
@@ -15,5 +15,5 @@
|
||||
#
|
||||
wiremockVersion=2.5.1
|
||||
jsonAssertVersion=0.4.8
|
||||
verifierVersion=1.1.0.BUILD-SNAPSHOT
|
||||
verifierVersion=1.1.1.BUILD-SNAPSHOT
|
||||
|
||||
|
||||
@@ -15,4 +15,4 @@
|
||||
#
|
||||
wiremockVersion=2.5.1
|
||||
jsonAssertVersion=0.4.8
|
||||
verifierVersion=1.1.0.BUILD-SNAPSHOT
|
||||
verifierVersion=1.1.1.BUILD-SNAPSHOT
|
||||
|
||||
@@ -15,4 +15,4 @@
|
||||
#
|
||||
wiremockVersion=2.5.1
|
||||
jsonAssertVersion=0.4.8
|
||||
verifierVersion=1.1.0.BUILD-SNAPSHOT
|
||||
verifierVersion=1.1.1.BUILD-SNAPSHOT
|
||||
@@ -15,5 +15,5 @@
|
||||
#
|
||||
wiremockVersion=2.5.1
|
||||
jsonAssertVersion=0.4.8
|
||||
verifierVersion=1.1.0.BUILD-SNAPSHOT
|
||||
verifierVersion=1.1.1.BUILD-SNAPSHOT
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@
|
||||
<plugin>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-contract-maven-plugin</artifactId>
|
||||
<version>1.1.0.BUILD-SNAPSHOT</version>
|
||||
<version>1.1.1.BUILD-SNAPSHOT</version>
|
||||
<configuration>
|
||||
<contractsPath>com/example/server</contractsPath>
|
||||
<contractDependency>
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
<version>0.1</version>
|
||||
|
||||
<properties>
|
||||
<spring.cloud.contract.version>1.1.0.BUILD-SNAPSHOT</spring.cloud.contract.version>
|
||||
<spring.cloud.contract.version>1.1.1.BUILD-SNAPSHOT</spring.cloud.contract.version>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
|
||||
@@ -27,7 +27,6 @@ import groovy.transform.PackageScope
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@PackageScope
|
||||
@CompileStatic
|
||||
class BlockBuilder {
|
||||
|
||||
@@ -43,21 +42,33 @@ class BlockBuilder {
|
||||
builder = new StringBuilder()
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds indents to start a new block
|
||||
*/
|
||||
BlockBuilder startBlock() {
|
||||
indents++
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Ends block by removing indents
|
||||
*/
|
||||
BlockBuilder endBlock() {
|
||||
indents--
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a block and adds indents
|
||||
*/
|
||||
BlockBuilder indent() {
|
||||
startBlock().startBlock()
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes indents and closes the block
|
||||
*/
|
||||
BlockBuilder unindent() {
|
||||
endBlock().endBlock()
|
||||
return this
|
||||
@@ -81,6 +92,7 @@ class BlockBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
@PackageScope
|
||||
BlockBuilder addBlock(MethodBuilder methodBuilder) {
|
||||
startBlock()
|
||||
methodBuilder.appendTo(this)
|
||||
@@ -89,6 +101,11 @@ class BlockBuilder {
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given text at the end of the line
|
||||
*
|
||||
* @return updated BlockBuilder
|
||||
*/
|
||||
BlockBuilder addAtTheEnd(String toAdd) {
|
||||
String lastChar = builder.charAt(builder.length() - 1) as String
|
||||
String secondLastChar = builder.length() >= 2 ? builder.charAt(builder.length() - 2) as String : ""
|
||||
@@ -114,6 +131,12 @@ class BlockBuilder {
|
||||
return character == "{" || character == toAdd
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the current text with the provided one
|
||||
*
|
||||
* @param contents - text to replace the current content with
|
||||
* @return updated Block Builder
|
||||
*/
|
||||
BlockBuilder updateContents(String contents) {
|
||||
this.builder.replace(0, this.builder.length(), contents)
|
||||
return this
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2012-2015 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
|
||||
*
|
||||
* http://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;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.boot.test.autoconfigure.properties.PropertyMapping;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
/**
|
||||
* Annotation for test classes that want to install a RestTemplateCustomizer that sets up
|
||||
* a Spring Boot app to ignore SSL errors. Use only in tests!
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Import(WireMockRestTemplateConfiguration.class)
|
||||
@PropertyMapping("wiremock.server")
|
||||
public @interface AutoConfigureHttpClient {
|
||||
}
|
||||
@@ -39,6 +39,7 @@ import org.springframework.context.annotation.Import;
|
||||
@Documented
|
||||
@Import(WireMockConfiguration.class)
|
||||
@PropertyMapping("wiremock.server")
|
||||
@AutoConfigureHttpClient
|
||||
public @interface AutoConfigureWireMock {
|
||||
|
||||
int port() default 8080;
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright 2016-2017 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
|
||||
*
|
||||
* http://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;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.channels.ByteChannel;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import com.github.tomakehurst.wiremock.core.FaultInjector;
|
||||
import com.google.common.base.Charsets;
|
||||
|
||||
import org.eclipse.jetty.io.ChannelEndPoint;
|
||||
import org.eclipse.jetty.server.HttpChannel;
|
||||
import org.eclipse.jetty.server.Response;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
|
||||
import static com.github.tomakehurst.wiremock.common.Exceptions.throwUnchecked;
|
||||
import static com.github.tomakehurst.wiremock.jetty9.JettyUtils.unwrapResponse;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class JettyFaultInjector implements FaultInjector {
|
||||
|
||||
private static final byte[] GARBAGE = "lskdu018973t09sylgasjkfg1][]'./.sdlv"
|
||||
.getBytes(Charsets.UTF_8);
|
||||
|
||||
private final Response response;
|
||||
private final ByteChannel socket;
|
||||
|
||||
public JettyFaultInjector(HttpServletResponse response) {
|
||||
this.response = unwrapResponse(response);
|
||||
this.socket = socket();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void emptyResponseAndCloseConnection() {
|
||||
try {
|
||||
this.socket.close();
|
||||
}
|
||||
catch (IOException e) {
|
||||
throwUnchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void malformedResponseChunk() {
|
||||
try {
|
||||
this.response.setStatus(200);
|
||||
this.response.flushBuffer();
|
||||
this.socket.write(BufferUtil.toBuffer(GARBAGE));
|
||||
this.socket.close();
|
||||
}
|
||||
catch (IOException e) {
|
||||
throwUnchecked(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void randomDataAndCloseConnection() {
|
||||
try {
|
||||
this.socket.write(BufferUtil.toBuffer(GARBAGE));
|
||||
this.socket.close();
|
||||
}
|
||||
catch (IOException e) {
|
||||
throwUnchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
private ByteChannel socket() {
|
||||
HttpChannel httpChannel = this.response.getHttpOutput().getHttpChannel();
|
||||
ChannelEndPoint ep = (ChannelEndPoint) httpChannel.getEndPoint();
|
||||
return ep.getChannel();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2016-2017 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
|
||||
*
|
||||
* http://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;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import com.github.tomakehurst.wiremock.core.FaultInjector;
|
||||
import com.github.tomakehurst.wiremock.servlet.FaultInjectorFactory;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class JettyFaultInjectorFactory implements FaultInjectorFactory {
|
||||
|
||||
@Override
|
||||
public FaultInjector buildFaultInjector(HttpServletRequest httpServletRequest,
|
||||
HttpServletResponse httpServletResponse) {
|
||||
return new JettyFaultInjector(httpServletResponse);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -18,11 +18,27 @@ package org.springframework.cloud.contract.wiremock;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
|
||||
import io.undertow.Undertow.Builder;
|
||||
|
||||
import com.github.tomakehurst.wiremock.common.HttpsSettings;
|
||||
import com.github.tomakehurst.wiremock.common.Notifier;
|
||||
import com.github.tomakehurst.wiremock.core.Options;
|
||||
import com.github.tomakehurst.wiremock.core.WireMockApp;
|
||||
import com.github.tomakehurst.wiremock.http.AdminRequestHandler;
|
||||
import com.github.tomakehurst.wiremock.http.HttpServer;
|
||||
import com.github.tomakehurst.wiremock.http.HttpServerFactory;
|
||||
import com.github.tomakehurst.wiremock.http.RequestHandler;
|
||||
import com.github.tomakehurst.wiremock.http.StubRequestHandler;
|
||||
import com.github.tomakehurst.wiremock.servlet.FaultInjectorFactory;
|
||||
import com.github.tomakehurst.wiremock.servlet.NoFaultInjectorFactory;
|
||||
import com.github.tomakehurst.wiremock.servlet.WireMockHandlerDispatchingServlet;
|
||||
|
||||
import org.apache.catalina.connector.Connector;
|
||||
import org.eclipse.jetty.server.ConnectionFactory;
|
||||
import org.eclipse.jetty.server.HttpConfiguration;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.ServerConnector;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
@@ -64,19 +80,6 @@ import org.springframework.context.support.GenericApplicationContext;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.context.ServletContextAware;
|
||||
|
||||
import com.github.tomakehurst.wiremock.common.HttpsSettings;
|
||||
import com.github.tomakehurst.wiremock.common.Notifier;
|
||||
import com.github.tomakehurst.wiremock.core.Options;
|
||||
import com.github.tomakehurst.wiremock.core.WireMockApp;
|
||||
import com.github.tomakehurst.wiremock.http.AdminRequestHandler;
|
||||
import com.github.tomakehurst.wiremock.http.HttpServer;
|
||||
import com.github.tomakehurst.wiremock.http.HttpServerFactory;
|
||||
import com.github.tomakehurst.wiremock.http.RequestHandler;
|
||||
import com.github.tomakehurst.wiremock.http.StubRequestHandler;
|
||||
import com.github.tomakehurst.wiremock.servlet.WireMockHandlerDispatchingServlet;
|
||||
|
||||
import io.undertow.Undertow.Builder;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
@@ -258,6 +261,8 @@ class WiremockServerConfiguration {
|
||||
@Autowired
|
||||
private StubRequestHandler stubRequestHandler;
|
||||
@Autowired
|
||||
private FaultInjectorFactory faultInjectorFactory;
|
||||
@Autowired
|
||||
private Options options;
|
||||
|
||||
@Bean(name = DispatcherServletAutoConfiguration.DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
|
||||
@@ -265,6 +270,10 @@ class WiremockServerConfiguration {
|
||||
ServletRegistrationBean reg = new ServletRegistrationBean();
|
||||
reg.addInitParameter(RequestHandler.HANDLER_CLASS_KEY,
|
||||
StubRequestHandler.class.getName());
|
||||
if (WiremockServerConfiguration.this.faultInjectorFactory != null) {
|
||||
reg.addInitParameter(FaultInjectorFactory.INJECTOR_CLASS_KEY,
|
||||
FaultInjectorFactory.class.getName());
|
||||
}
|
||||
reg.setServlet(new WireMockHandlerDispatchingServlet());
|
||||
reg.setName("stub");
|
||||
reg.addUrlMappings("/");
|
||||
@@ -293,6 +302,10 @@ class WiremockServerConfiguration {
|
||||
WiremockServerConfiguration.this.stubRequestHandler);
|
||||
servletContext.setAttribute(Notifier.KEY,
|
||||
WiremockServerConfiguration.this.options.notifier());
|
||||
if (WiremockServerConfiguration.this.faultInjectorFactory != null) {
|
||||
servletContext.setAttribute(FaultInjectorFactory.class.getName(),
|
||||
WiremockServerConfiguration.this.faultInjectorFactory);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -367,6 +380,11 @@ class ContainerConfiguration {
|
||||
return tomcat;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public FaultInjectorFactory faultInjectorFactory() {
|
||||
return new TomcatFaultInjectorFactory();
|
||||
}
|
||||
|
||||
@EventListener
|
||||
public void serverUp(EmbeddedServletContainerInitializedEvent event) {
|
||||
if (this.connector != null) {
|
||||
@@ -417,6 +435,11 @@ class ContainerConfiguration {
|
||||
return undertow;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public FaultInjectorFactory faultInjectorFactory() {
|
||||
return new NoFaultInjectorFactory();
|
||||
}
|
||||
|
||||
@EventListener
|
||||
public void serverUp(EmbeddedServletContainerInitializedEvent event) {
|
||||
if (this.port != null) {
|
||||
@@ -457,6 +480,11 @@ class ContainerConfiguration {
|
||||
return jetty;
|
||||
}
|
||||
|
||||
@Bean
|
||||
public JettyFaultInjectorFactory faultInjectorFactory() {
|
||||
return new JettyFaultInjectorFactory();
|
||||
}
|
||||
|
||||
private org.eclipse.jetty.server.Connector createStandardConnector(
|
||||
Server server) {
|
||||
ServerConnector connector = new ServerConnector(server, -1, -1);
|
||||
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright 2016-2017 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
|
||||
*
|
||||
* http://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;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import com.github.tomakehurst.wiremock.common.Exceptions;
|
||||
import com.github.tomakehurst.wiremock.core.FaultInjector;
|
||||
import com.google.common.base.Charsets;
|
||||
|
||||
import org.apache.coyote.Response;
|
||||
import org.apache.tomcat.util.net.SocketWrapperBase;
|
||||
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import static com.github.tomakehurst.wiremock.common.Exceptions.throwUnchecked;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class TomcatFaultInjector implements FaultInjector {
|
||||
|
||||
private static final byte[] GARBAGE = "lskdu018973t09sylgasjkfg1][]'./.sdlv"
|
||||
.getBytes(Charsets.UTF_8);
|
||||
private final Response response;
|
||||
private SocketWrapperBase<?> socket;
|
||||
|
||||
public TomcatFaultInjector(HttpServletResponse response) {
|
||||
this.response = ((org.apache.catalina.connector.Response) getField(response,
|
||||
"response")).getCoyoteResponse();
|
||||
this.socket = (SocketWrapperBase<?>) getField(
|
||||
getField(this.response, "outputBuffer"), "socketWrapper");
|
||||
}
|
||||
|
||||
private Object getField(Object target, String string) {
|
||||
Field field = ReflectionUtils.findField(target.getClass(), string);
|
||||
ReflectionUtils.makeAccessible(field);
|
||||
return ReflectionUtils.getField(field, target);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void emptyResponseAndCloseConnection() {
|
||||
try {
|
||||
this.socket.close();
|
||||
}
|
||||
catch (IOException e) {
|
||||
Exceptions.throwUnchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void malformedResponseChunk() {
|
||||
try {
|
||||
this.response.sendHeaders();
|
||||
this.response.doWrite(ByteBuffer.wrap(GARBAGE));
|
||||
this.socket.flush(true);
|
||||
this.socket.close();
|
||||
}
|
||||
catch (IOException e) {
|
||||
throwUnchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void randomDataAndCloseConnection() {
|
||||
try {
|
||||
this.socket.write(true, GARBAGE, 0, GARBAGE.length);
|
||||
this.socket.flush(true);
|
||||
this.socket.close();
|
||||
}
|
||||
catch (IOException e) {
|
||||
throwUnchecked(e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2016-2017 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
|
||||
*
|
||||
* http://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;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import com.github.tomakehurst.wiremock.core.FaultInjector;
|
||||
import com.github.tomakehurst.wiremock.servlet.FaultInjectorFactory;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class TomcatFaultInjectorFactory implements FaultInjectorFactory {
|
||||
|
||||
@Override
|
||||
public FaultInjector buildFaultInjector(HttpServletRequest httpServletRequest,
|
||||
HttpServletResponse httpServletResponse) {
|
||||
return new TomcatFaultInjector(httpServletResponse);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright 2016-2017 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
|
||||
*
|
||||
* http://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;
|
||||
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.conn.ssl.NoopHostnameVerifier;
|
||||
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
|
||||
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
|
||||
import org.apache.http.impl.client.HttpClients;
|
||||
import org.apache.http.ssl.SSLContextBuilder;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.web.client.RestTemplateCustomizer;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@Configuration
|
||||
public class WireMockRestTemplateConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnClass(SSLContextBuilder.class)
|
||||
public RestTemplateCustomizer restTemplateCustomizer() {
|
||||
return new RestTemplateCustomizer() {
|
||||
@Override
|
||||
public void customize(RestTemplate restTemplate) {
|
||||
HttpComponentsClientHttpRequestFactory factory = (HttpComponentsClientHttpRequestFactory) restTemplate
|
||||
.getRequestFactory();
|
||||
factory.setHttpClient(createSslHttpClient());
|
||||
}
|
||||
|
||||
private HttpClient createSslHttpClient() {
|
||||
try {
|
||||
SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(
|
||||
new SSLContextBuilder().loadTrustMaterial(null,
|
||||
TrustSelfSignedStrategy.INSTANCE).build(),
|
||||
NoopHostnameVerifier.INSTANCE);
|
||||
return HttpClients.custom().setSSLSocketFactory(socketFactory)
|
||||
.build();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new IllegalStateException("Unable to create SSL HttpClient",
|
||||
ex);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -18,13 +18,14 @@ package org.springframework.cloud.contract.wiremock;
|
||||
|
||||
import javax.net.ssl.HttpsURLConnection;
|
||||
|
||||
import com.github.tomakehurst.wiremock.core.WireMockConfiguration;
|
||||
|
||||
import org.apache.http.conn.ssl.NoopHostnameVerifier;
|
||||
import org.apache.http.conn.ssl.TrustSelfSignedStrategy;
|
||||
import org.apache.http.ssl.SSLContexts;
|
||||
import org.junit.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
import com.github.tomakehurst.wiremock.core.WireMockConfiguration;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Convenience factory class for a {@link WireMockConfiguration} that knows how to use
|
||||
|
||||
@@ -4,4 +4,4 @@ org.springframework.cloud.contract.wiremock.WireMockApplicationListener
|
||||
|
||||
# RestDocs Auto Configuration
|
||||
org.springframework.boot.test.autoconfigure.restdocs.AutoConfigureRestDocs=\
|
||||
org.springframework.cloud.contract.wiremock.restdocs.WireMockRestDocsConfiguration
|
||||
org.springframework.cloud.contract.wiremock.restdocs.WireMockRestDocsConfiguration
|
||||
|
||||
@@ -1,21 +1,22 @@
|
||||
package org.springframework.cloud.contract.wiremock;
|
||||
|
||||
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;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import com.github.tomakehurst.wiremock.junit.WireMockClassRule;
|
||||
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.junit4.SpringRunner;
|
||||
|
||||
import com.github.tomakehurst.wiremock.junit.WireMockClassRule;
|
||||
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;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(classes=WiremockTestsApplication.class, properties="app.baseUrl=http://localhost:8080", webEnvironment=WebEnvironment.NONE)
|
||||
@@ -29,7 +30,7 @@ public class WiremockServerApplicationTests {
|
||||
private Service service;
|
||||
|
||||
@Test
|
||||
public void contextLoads() throws Exception {
|
||||
public void hello() throws Exception {
|
||||
stubFor(get(urlEqualTo("/test"))
|
||||
.willReturn(aResponse().withHeader("Content-Type", "text/plain").withBody("Hello World!")));
|
||||
assertThat(this.service.go()).isEqualTo("Hello World!");
|
||||
|
||||
Reference in New Issue
Block a user