13
README.adoc
13
README.adoc
@@ -1,7 +1,7 @@
|
||||
// Do not edit this file (e.g. go instead to src/main/asciidoc)
|
||||
|
||||
image::https://badges.gitter.im/Join%20Chat.svg[Gitter, link="https://gitter.im/spring-cloud/spring-cloud-contract?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge"]
|
||||
image::https://codecov.io/gh/spring-cloud/spring-cloud-contract/branch/master/graph/badge.svg["codecov", link="https://codecov.io/gh/spring-cloud/spring-cloud-contract"]
|
||||
image::https://codecov.io/gh/spring-cloud/spring-cloud-contract/branch/1.0.x/graph/badge.svg["codecov", link="https://codecov.io/gh/spring-cloud/spring-cloud-contract"]
|
||||
image::https://circleci.com/gh/spring-cloud/spring-cloud-contract.svg?style=svg["CircleCI", link="https://circleci.com/gh/spring-cloud/spring-cloud-contract"]
|
||||
|
||||
== Spring Cloud Contract
|
||||
@@ -1318,7 +1318,7 @@ Example of a `pom.xml` inside the `server` folder.
|
||||
<parent>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-parent</artifactId>
|
||||
<version>1.4.2.BUILD-SNAPSHOT</version>
|
||||
<version>1.5.0.BUILD-SNAPSHOT</version>
|
||||
<relativePath />
|
||||
</parent>
|
||||
|
||||
@@ -1566,17 +1566,12 @@ follow the guidelines below.
|
||||
|
||||
=== Sign the Contributor License Agreement
|
||||
Before we accept a non-trivial patch or pull request we will need you to sign the
|
||||
https://cla.pivotal.io/sign/spring[Contributor License Agreement].
|
||||
https://support.springsource.com/spring_committer_signup[contributor's agreement].
|
||||
Signing the contributor's agreement does not grant anyone commit rights to the main
|
||||
repository, but it does mean that we can accept your contributions, and you will get an
|
||||
author credit if we do. Active contributors might be asked to join the core team, and
|
||||
given the ability to merge pull requests.
|
||||
|
||||
=== Code of Conduct
|
||||
This project adheres to the Contributor Covenant https://github.com/spring-cloud/spring-cloud-build/blob/master/docs/src/main/asciidoc/code-of-conduct.adoc[code of
|
||||
conduct]. By participating, you are expected to uphold this code. Please report
|
||||
unacceptable behavior to spring-code-of-conduct@pivotal.io.
|
||||
|
||||
=== Code Conventions and Housekeeping
|
||||
None of these is essential for a pull request, but they will all help. They can also be
|
||||
added after the original pull request but before a merge.
|
||||
@@ -1584,7 +1579,7 @@ added after the original pull request but before a merge.
|
||||
* Use the Spring Framework code format conventions. If you use Eclipse
|
||||
you can import formatter settings using the
|
||||
`eclipse-code-formatter.xml` file from the
|
||||
https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/master/spring-cloud-dependencies-parent/eclipse-code-formatter.xml[Spring
|
||||
https://github.com/spring-cloud/build/tree/master/eclipse-coding-conventions.xml[Spring
|
||||
Cloud Build] project. If using IntelliJ, you can use the
|
||||
http://plugins.jetbrains.com/plugin/6546[Eclipse Code Formatter
|
||||
Plugin] to import the same file.
|
||||
|
||||
@@ -23,18 +23,24 @@ import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import javax.xml.xpath.XPathExpressionException;
|
||||
|
||||
import org.hamcrest.BaseMatcher;
|
||||
import org.hamcrest.Description;
|
||||
import org.hamcrest.MatcherAssert;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpMethod;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.client.ClientHttpRequest;
|
||||
import org.springframework.mock.http.client.MockClientHttpRequest;
|
||||
import org.springframework.test.web.client.MockRestServiceServer;
|
||||
import org.springframework.test.web.client.MockRestServiceServer.MockRestServiceServerBuilder;
|
||||
import org.springframework.test.web.client.RequestMatcher;
|
||||
import org.springframework.test.web.client.ResponseActions;
|
||||
import org.springframework.test.web.client.match.MockRestRequestMatchers;
|
||||
import org.springframework.test.web.client.response.DefaultResponseCreator;
|
||||
import org.springframework.util.StreamUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
@@ -44,8 +50,12 @@ import com.github.tomakehurst.wiremock.common.Json;
|
||||
import com.github.tomakehurst.wiremock.http.HttpHeader;
|
||||
import com.github.tomakehurst.wiremock.http.MultiValue;
|
||||
import com.github.tomakehurst.wiremock.http.ResponseDefinition;
|
||||
import com.github.tomakehurst.wiremock.matching.MatchResult;
|
||||
import com.github.tomakehurst.wiremock.matching.MatchesJsonPathPattern;
|
||||
import com.github.tomakehurst.wiremock.matching.MatchesXPathPattern;
|
||||
import com.github.tomakehurst.wiremock.matching.MultiValuePattern;
|
||||
import com.github.tomakehurst.wiremock.matching.RequestPattern;
|
||||
import com.github.tomakehurst.wiremock.matching.StringValuePattern;
|
||||
import com.github.tomakehurst.wiremock.stubbing.StubMapping;
|
||||
|
||||
import static org.springframework.test.web.client.match.MockRestRequestMatchers.header;
|
||||
@@ -192,12 +202,47 @@ public class WireMockRestServiceServer {
|
||||
for (StubMapping mapping : mappings) {
|
||||
ResponseActions expect = server.expect(requestTo(request(mapping.getRequest())));
|
||||
expect.andExpect(method(HttpMethod.valueOf(mapping.getRequest().getMethod().getName())));
|
||||
mapping.getRequest().getBodyPatterns();
|
||||
bodyPatterns(expect, mapping.getRequest());
|
||||
requestHeaders(expect, mapping.getRequest());
|
||||
expect.andRespond(response(mapping.getResponse()));
|
||||
}
|
||||
return server;
|
||||
}
|
||||
|
||||
private void bodyPatterns(ResponseActions expect, RequestPattern request) {
|
||||
if (request.getBodyPatterns() == null) {
|
||||
return;
|
||||
}
|
||||
for (final StringValuePattern pattern : request.getBodyPatterns()) {
|
||||
if (pattern instanceof MatchesJsonPathPattern) {
|
||||
expect.andExpect(MockRestRequestMatchers.jsonPath(((MatchesJsonPathPattern) pattern).getMatchesJsonPath()).exists());
|
||||
} else if (pattern instanceof MatchesXPathPattern) {
|
||||
expect.andExpect(xpath((MatchesXPathPattern) pattern));
|
||||
}
|
||||
expect.andExpect(matchContents(pattern));
|
||||
}
|
||||
}
|
||||
|
||||
private RequestMatcher matchContents(final StringValuePattern pattern) {
|
||||
return new RequestMatcher() {
|
||||
@Override public void match(ClientHttpRequest request)
|
||||
throws IOException, AssertionError {
|
||||
MockClientHttpRequest mockRequest = (MockClientHttpRequest) request;;
|
||||
MatchResult result = pattern.match(mockRequest.getBodyAsString());
|
||||
MatcherAssert.assertThat("Request as string [" + mockRequest.getBodyAsString() + "]", result.isExactMatch());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private RequestMatcher xpath(MatchesXPathPattern pattern) {
|
||||
try {
|
||||
return MockRestRequestMatchers.xpath(pattern.getMatchesXPath()).exists();
|
||||
} catch (XPathExpressionException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private String request(RequestPattern request) {
|
||||
return this.baseUrl + (request.getUrlPath() == null ? (request.getUrl() == null ? "/" : request.getUrl())
|
||||
: request.getUrlPath());
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package org.springframework.cloud.contract.wiremock;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.http.MediaType;
|
||||
@@ -9,6 +11,8 @@ import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.test.web.client.MockRestServiceServer;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
|
||||
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.fail;
|
||||
|
||||
@@ -217,4 +221,183 @@ public class WiremockMockServerApplicationTests {
|
||||
String.class)).isEqualTo("Hello World");
|
||||
}
|
||||
|
||||
// Issue #135
|
||||
@Test
|
||||
public void postWithBodyMatchingJsonPaths() throws Exception {
|
||||
WireMockRestServiceServer.with(this.restTemplate) //
|
||||
.baseUrl("http://example.org") //
|
||||
.stubs("classpath:/mappings/body-matches-jsonpath.json").build();
|
||||
|
||||
assertThat(this.restTemplate.postForObject("http://example.org/body",
|
||||
new Things(Collections.singletonList(new Thing("RequiredThing"))), String.class))
|
||||
.isEqualTo("Hello Body");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postWithRequestThatHasNonMatchingJsonPaths() throws Exception {
|
||||
WireMockRestServiceServer.with(this.restTemplate) //
|
||||
.baseUrl("http://example.org") //
|
||||
.stubs("classpath:/mappings/body-matches-jsonpath.json").build();
|
||||
|
||||
String response;
|
||||
try {
|
||||
response = this.restTemplate.postForObject("http://example.org/body",
|
||||
new Things(Collections.singletonList(new Thing("AbsentThing"))), String.class);
|
||||
} catch (AssertionError e) {
|
||||
response = null;
|
||||
}
|
||||
if (null != response) {
|
||||
fail("There was a response for a request that shouldn't be matched : " + response);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postWithBodyMatchingXPath() throws Exception {
|
||||
WireMockRestServiceServer.with(this.restTemplate) //
|
||||
.baseUrl("http://example.org") //
|
||||
.stubs("classpath:/mappings/body-matches-xpath.json").build();
|
||||
|
||||
assertThat(this.restTemplate.exchange(
|
||||
RequestEntity.post(URI.create("http://example.org/body"))
|
||||
.contentType(MediaType.APPLICATION_XML)
|
||||
.body("<things><thing><name>RequiredThing</name></thing></things>"),
|
||||
String.class).getBody()).isEqualTo("Hello Body");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postWithRequestThatHasNonMatchingXPaths() throws Exception {
|
||||
WireMockRestServiceServer.with(this.restTemplate) //
|
||||
.baseUrl("http://example.org") //
|
||||
.stubs("classpath:/mappings/body-matches-xpath.json").build();
|
||||
|
||||
ResponseEntity<String> response;
|
||||
try {
|
||||
response = this.restTemplate.exchange(
|
||||
RequestEntity.post(URI.create("http://example.org/body"))
|
||||
.contentType(MediaType.APPLICATION_XML)
|
||||
.body("<foo/>"),
|
||||
String.class);
|
||||
} catch (AssertionError e) {
|
||||
response = null;
|
||||
}
|
||||
if (null != response) {
|
||||
fail("There was a response for a request that shouldn't be matched : " + response);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postWithBodyMatchingJson() throws Exception {
|
||||
WireMockRestServiceServer.with(this.restTemplate) //
|
||||
.baseUrl("http://example.org") //
|
||||
.stubs("classpath:/mappings/body-matches-equaltojson.json").build();
|
||||
|
||||
assertThat(this.restTemplate.postForObject("http://example.org/body",
|
||||
new Things(Collections.singletonList(new Thing("RequiredThing"))), String.class))
|
||||
.isEqualTo("Hello Body");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postWithRequestThatHasNonMatchingJson() throws Exception {
|
||||
WireMockRestServiceServer.with(this.restTemplate) //
|
||||
.baseUrl("http://example.org") //
|
||||
.stubs("classpath:/mappings/body-matches-equaltojson.json").build();
|
||||
|
||||
String response;
|
||||
try {
|
||||
response = this.restTemplate.postForObject("http://example.org/body",
|
||||
new Things(Collections.singletonList(new Thing("AbsentThing"))), String.class);
|
||||
} catch (AssertionError e) {
|
||||
response = null;
|
||||
}
|
||||
if (null != response) {
|
||||
fail("There was a response for a request that shouldn't be matched : " + response);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postWithBodyMatchingXml() throws Exception {
|
||||
WireMockRestServiceServer.with(this.restTemplate) //
|
||||
.baseUrl("http://example.org") //
|
||||
.stubs("classpath:/mappings/body-matches-equaltoxml.json").build();
|
||||
|
||||
assertThat(this.restTemplate.exchange(
|
||||
RequestEntity.post(URI.create("http://example.org/body"))
|
||||
.contentType(MediaType.APPLICATION_XML)
|
||||
.body("<things><thing><name>RequiredThing</name></thing></things>"),
|
||||
String.class).getBody()).isEqualTo("Hello Body");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postWithRequestThatHasNonMatchingXml() throws Exception {
|
||||
WireMockRestServiceServer.with(this.restTemplate) //
|
||||
.baseUrl("http://example.org") //
|
||||
.stubs("classpath:/mappings/body-matches-equaltoxml.json").build();
|
||||
|
||||
ResponseEntity<String> response;
|
||||
try {
|
||||
response = this.restTemplate.exchange(
|
||||
RequestEntity.post(URI.create("http://example.org/body"))
|
||||
.contentType(MediaType.APPLICATION_XML)
|
||||
.body("<things><thing><name>AbsentThing</name></thing></things>"),
|
||||
String.class);
|
||||
} catch (AssertionError e) {
|
||||
response = null;
|
||||
}
|
||||
if (null != response) {
|
||||
fail("There was a response for a request that shouldn't be matched : " + response);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postWithBodyMatchingRegex() throws Exception {
|
||||
WireMockRestServiceServer.with(this.restTemplate) //
|
||||
.baseUrl("http://example.org") //
|
||||
.stubs("classpath:/mappings/body-matches-regex.json").build();
|
||||
|
||||
assertThat(this.restTemplate.exchange(
|
||||
RequestEntity.post(URI.create("http://example.org/body"))
|
||||
.contentType(MediaType.APPLICATION_XML)
|
||||
.body("<things><thing><name>RequiredThing</name></thing></things>"),
|
||||
String.class).getBody()).isEqualTo("Hello Body");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void postWithRequestThatHasNonMatchingRegex() throws Exception {
|
||||
WireMockRestServiceServer.with(this.restTemplate) //
|
||||
.baseUrl("http://example.org") //
|
||||
.stubs("classpath:/mappings/body-matches-regex.json").build();
|
||||
|
||||
ResponseEntity<String> response;
|
||||
try {
|
||||
response = this.restTemplate.exchange(
|
||||
RequestEntity.post(URI.create("http://example.org/body"))
|
||||
.contentType(MediaType.APPLICATION_XML)
|
||||
.body("<things><thing><name>AbsentThing</name></thing></things>"),
|
||||
String.class);
|
||||
} catch (AssertionError e) {
|
||||
response = null;
|
||||
}
|
||||
if (null != response) {
|
||||
fail("There was a response for a request that shouldn't be matched : " + response);
|
||||
}
|
||||
}
|
||||
|
||||
public static class Things {
|
||||
public List<Thing> things;
|
||||
|
||||
@JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
|
||||
public Things(List<Thing> things) {
|
||||
this.things = things;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Thing {
|
||||
public String name;
|
||||
|
||||
@JsonCreator(mode = JsonCreator.Mode.PROPERTIES)
|
||||
public Thing(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"request" : {
|
||||
"urlPath" : "/body",
|
||||
"method" : "POST",
|
||||
"bodyPatterns" : [ {
|
||||
"equalToJson" : "{ \"things\": [ { \"name\" : \"RequiredThing\" } ] }"
|
||||
} ]
|
||||
},
|
||||
"response" : {
|
||||
"status" : 200,
|
||||
"body" : "Hello Body"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"request" : {
|
||||
"urlPath" : "/body",
|
||||
"method" : "POST",
|
||||
"bodyPatterns" : [ {
|
||||
"equalToXml" : "<things><thing><name>RequiredThing</name></thing></things>"
|
||||
} ]
|
||||
},
|
||||
"response" : {
|
||||
"status" : 200,
|
||||
"body" : "Hello Body"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"request" : {
|
||||
"urlPath" : "/body",
|
||||
"method" : "POST",
|
||||
"bodyPatterns" : [ {
|
||||
"matchesJsonPath" : "$.things[?(@.name == 'RequiredThing')]"
|
||||
} ]
|
||||
},
|
||||
"response" : {
|
||||
"status" : 200,
|
||||
"body" : "Hello Body"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"request" : {
|
||||
"urlPath" : "/body",
|
||||
"method" : "POST",
|
||||
"bodyPatterns" : [ {
|
||||
"matches" : ".*RequiredThing.*"
|
||||
} ]
|
||||
},
|
||||
"response" : {
|
||||
"status" : 200,
|
||||
"body" : "Hello Body"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"request" : {
|
||||
"urlPath" : "/body",
|
||||
"method" : "POST",
|
||||
"bodyPatterns" : [ {
|
||||
"matchesXPath" : "//things"
|
||||
} ]
|
||||
},
|
||||
"response" : {
|
||||
"status" : 200,
|
||||
"body" : "Hello Body"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user