Fixed resolution of content type when working with WireMock stubs

This commit is contained in:
Marcin Grzejszczak
2019-07-09 17:48:08 +02:00
parent 7f351f8b80
commit 869e644740
5 changed files with 83 additions and 50 deletions

View File

@@ -41,7 +41,7 @@ import org.springframework.cloud.contract.verifier.util.JsonToJsonPathsConverter
import org.springframework.cloud.contract.verifier.util.MapConverter
import static org.springframework.cloud.contract.verifier.util.ContentType.XML
import static org.springframework.cloud.contract.verifier.util.ContentUtils.evaluateContentType
import static org.springframework.cloud.contract.verifier.util.ContentUtils.evaluateClientSideContentType
/**
* @author Marcin Grzejszczak
@@ -72,7 +72,7 @@ class ContractsToYaml {
if (!contract.outputMessage) {
return
}
ContentType contentType = evaluateContentType(contract.response?.headers,
ContentType contentType = evaluateClientSideContentType(contract.response?.headers,
contract.response?.body)
yamlContract.outputMessage = new YamlContract.OutputMessage()
yamlContract.outputMessage.sentTo = MapConverter.
@@ -102,7 +102,7 @@ class ContractsToYaml {
if (!contract.input) {
return
}
ContentType contentType = evaluateContentType(contract.input?.messageHeaders,
ContentType contentType = evaluateClientSideContentType(contract.input?.messageHeaders,
contract.input?.messageBody)
yamlContract.input = new YamlContract.Input()
yamlContract.input.assertThat = MapConverter.
@@ -132,7 +132,7 @@ class ContractsToYaml {
if (!contract.request) {
return
}
ContentType requestContentType = evaluateContentType(contract.request.headers,
ContentType requestContentType = evaluateClientSideContentType(contract.request.headers,
contract.request.body)
yamlContract.request = new YamlContract.Request()
yamlContract.request.with { YamlContract.Request request ->
@@ -336,7 +336,7 @@ class ContractsToYaml {
if (!contract.response) {
return
}
ContentType contentType = evaluateContentType(contract.response?.headers,
ContentType contentType = evaluateClientSideContentType(contract.response?.headers,
contract.response?.body)
yamlContract.response = new YamlContract.Response()
yamlContract.response.with { YamlContract.Response response ->

View File

@@ -39,7 +39,7 @@ import org.springframework.util.StringUtils
import static java.util.stream.Collectors.toSet
import static org.springframework.cloud.contract.verifier.util.ContentType.XML
import static org.springframework.cloud.contract.verifier.util.ContentUtils.evaluateContentType
import static org.springframework.cloud.contract.verifier.util.ContentUtils.evaluateClientSideContentType
/**
* @author Marcin Grzejszczak
* @author Olga Maciaszek-Sharma
@@ -250,7 +250,7 @@ class YamlToContracts {
yamlContract.request.matchers?.body?.
each { YamlContract.BodyStubMatcher matcher ->
ContentType contentType =
evaluateContentType(
evaluateClientSideContentType(
yamlHeadersToContractHeaders(yamlContract.request?.headers),
yamlContract.request?.body)
MatchingTypeValue value = null
@@ -367,7 +367,7 @@ class YamlToContracts {
yamlContract.response?.matchers?.body?.
each { YamlContract.BodyTestMatcher testMatcher ->
ContentType contentType =
evaluateContentType(
evaluateClientSideContentType(
yamlHeadersToContractHeaders(yamlContract.response?.headers),
yamlContract.response?.body)
MatchingTypeValue value = null
@@ -457,7 +457,7 @@ class YamlToContracts {
yamlContract.input.matchers.body?.
each { YamlContract.BodyStubMatcher matcher ->
ContentType contentType =
evaluateContentType(
evaluateClientSideContentType(
yamlHeadersToContractHeaders(yamlContract.input?.messageHeaders),
yamlContract.input?.messageBody)
MatchingTypeValue value = null
@@ -527,7 +527,7 @@ class YamlToContracts {
yamlContract.outputMessage?.matchers?.body?.
each { YamlContract.BodyTestMatcher testMatcher ->
ContentType contentType =
evaluateContentType(
evaluateClientSideContentType(
yamlHeadersToContractHeaders(yamlContract.outputMessage?.headers),
yamlContract.outputMessage?.body)
MatchingTypeValue value = null

View File

@@ -29,7 +29,6 @@ import groovy.json.JsonOutput
import groovy.json.StringEscapeUtils
import groovy.transform.PackageScope
import groovy.transform.TypeChecked
import groovy.transform.TypeCheckingMode
import groovy.util.logging.Commons
import org.springframework.cloud.contract.spec.Contract
@@ -60,6 +59,7 @@ import static org.springframework.cloud.contract.spec.internal.MatchingType.EQUA
import static org.springframework.cloud.contract.spec.internal.MatchingType.NULL
import static org.springframework.cloud.contract.spec.internal.MatchingType.TYPE
import static org.springframework.cloud.contract.verifier.util.ContentType.FORM
import static org.springframework.cloud.contract.verifier.util.ContentType.JSON
import static org.springframework.cloud.contract.verifier.util.ContentUtils.getEqualsTypeFromContentType
import static org.springframework.cloud.contract.verifier.util.RegexpBuilders.buildGStringRegexpForStubSide
import static org.springframework.cloud.contract.verifier.util.RegexpBuilders.buildJSONRegexpMatch
@@ -315,38 +315,42 @@ class WireMockRequestStubStrategy extends BaseWireMockStubStrategy {
}
}
@TypeChecked(TypeCheckingMode.SKIP)
protected ContentPattern convertToValuePattern(Object object) {
switch (object) {
case Pattern:
case RegexProperty:
return WireMock.matching(new RegexProperty(object).pattern())
case OptionalProperty:
OptionalProperty value = object as OptionalProperty
return WireMock.matching(value.optionalPattern())
case MatchingStrategy:
MatchingStrategy value = object as MatchingStrategy
switch (value.type) {
case MatchingStrategy.Type.NOT_MATCHING:
return WireMock.notMatching(value.clientValue.toString())
case MatchingStrategy.Type.ABSENT:
return WireMock.absent()
default:
try {
return WireMock."${value.type.name}"(
clientBody(value.clientValue, contentType))
}
catch (Throwable t) {
log.error("Exception occurred while trying to call WireMock.${value.type.name}(${value.clientValue})", t)
throw t
}
}
case Pattern:
case RegexProperty:
return WireMock.matching(new RegexProperty(object).pattern())
case OptionalProperty:
OptionalProperty value = object as OptionalProperty
return WireMock.matching(value.optionalPattern())
case MatchingStrategy:
MatchingStrategy value = object as MatchingStrategy
switch (value.type) {
case MatchingStrategy.Type.NOT_MATCHING:
return WireMock.notMatching(value.clientValue.toString())
case MatchingStrategy.Type.ABSENT:
return WireMock.absent()
case MatchingStrategy.Type.EQUAL_TO:
return WireMock.equalTo(clientBody(value.clientValue, contentType).toString())
case MatchingStrategy.Type.CONTAINS:
return WireMock.containing(clientBody(value.clientValue, contentType).toString())
case MatchingStrategy.Type.MATCHING:
return WireMock.matching(clientBody(value.clientValue, contentType).toString())
case MatchingStrategy.Type.EQUAL_TO_JSON:
return WireMock.equalToJson(clientBody(value.clientValue, contentType).toString())
case MatchingStrategy.Type.EQUAL_TO_XML:
return WireMock.equalToXml(clientBody(value.clientValue, contentType).toString())
case MatchingStrategy.Type.BINARY_EQUAL_TO:
return WireMock.binaryEqualTo(clientBody(value.clientValue, contentType) as byte[])
default:
return WireMock.equalTo(object.toString())
throw new UnsupportedOperationException("Unknown matching strategy " + value.type)
}
default:
return WireMock.equalTo(clientBody(object, contentType).toString())
}
}
protected static Object clientBody(Object bodyValue, ContentType contentType) {
protected Object clientBody(Object bodyValue, ContentType contentType) {
if (FORM == contentType) {
if (bodyValue instanceof Map) {
// [a:3, b:4] == "a=3&b=4"
@@ -365,6 +369,9 @@ class WireMockRequestStubStrategy extends BaseWireMockStubStrategy {
else if (bodyValue instanceof FromFileProperty) {
return bodyValue.isByte() ? bodyValue.asBytes() : bodyValue.asString()
}
else if (JSON == contentType) {
return parseBody(bodyValue, contentType)
}
return bodyValue
}
@@ -421,14 +428,14 @@ class WireMockRequestStubStrategy extends BaseWireMockStubStrategy {
private MatchingStrategy appendBodyRegexpMatchPattern(Object value, ContentType contentType) {
switch (contentType) {
case ContentType.JSON:
return new MatchingStrategy(
buildJSONRegexpMatch(value), MatchingStrategy.Type.MATCHING)
case ContentType.UNKNOWN:
return new MatchingStrategy(
buildGStringRegexpForStubSide(value), MatchingStrategy.Type.MATCHING)
case ContentType.XML:
throw new IllegalStateException("XML pattern matching is not implemented yet")
case ContentType.JSON:
return new MatchingStrategy(
buildJSONRegexpMatch(value), MatchingStrategy.Type.MATCHING)
case ContentType.UNKNOWN:
return new MatchingStrategy(
buildGStringRegexpForStubSide(value), MatchingStrategy.Type.MATCHING)
case ContentType.XML:
throw new IllegalStateException("XML pattern matching is not implemented yet")
}
}

View File

@@ -117,19 +117,37 @@ class SingleContractMetadata {
DslProperty inputBody = inputBody(currentContract)
Headers outputHeaders = outputHeaders(currentContract)
DslProperty outputBody = outputBody(currentContract)
this.evaluatedInputTestContentType = ContentUtils.evaluateContentType(inputHeaders, inputBody?.getServerValue())
this.evaluatedInputTestContentType = tryToEvaluateTestContentType(inputHeaders, inputBody)
this.inputTestContentType = inputBody != null ? this.evaluatedInputTestContentType : ContentType.UNKNOWN
this.evaluatedOutputTestContentType = ContentUtils.evaluateContentType(outputHeaders, outputBody?.getServerValue())
this.evaluatedOutputTestContentType = tryToEvaluateTestContentType(outputHeaders, outputBody)
this.outputTestContentType = outputBody != null ? this.evaluatedOutputTestContentType : ContentType.UNKNOWN
this.evaluatedInputStubContentType = ContentUtils.evaluateContentType(inputHeaders, inputBody?.getClientValue())
this.evaluatedInputStubContentType = tryToEvaluateStubContentType(inputHeaders, inputBody)
this.inputStubContentType = inputBody != null ? this.evaluatedInputStubContentType : ContentType.UNKNOWN
this.evaluatedOutputStubContentType = ContentUtils.evaluateContentType(outputHeaders, outputBody?.getClientValue())
this.evaluatedOutputStubContentType = tryToEvaluateStubContentType(outputHeaders, outputBody)
this.outputStubContentType = outputBody != null ? this.evaluatedOutputStubContentType : ContentType.UNKNOWN
this.http = currentContract.request != null
this.contractMetadata = contractMetadata
this.stubsFile = contractMetadata.getPath() != null ? contractMetadata.getPath().toFile() : null
}
private ContentType tryToEvaluateStubContentType(Headers mainHeaders, DslProperty body) {
ContentType contentType = ContentUtils.evaluateClientSideContentType(mainHeaders, body?.getClientValue())
if (contentType == ContentType.DEFINED || contentType == ContentType.UNKNOWN) {
// try to retrieve from the other side (e.g. stub side was a regex, but test side is concrete)
return ContentUtils.evaluateServerSideContentType(mainHeaders, body?.getServerValue())
}
return contentType
}
private ContentType tryToEvaluateTestContentType(Headers mainHeaders, DslProperty body) {
ContentType contentType = ContentUtils.evaluateClientSideContentType(mainHeaders, body?.getServerValue())
if (contentType == ContentType.DEFINED || contentType == ContentType.UNKNOWN) {
// try to retrieve from the other side (e.g. stub side was a regex, but test side is concrete)
return ContentUtils.evaluateServerSideContentType(mainHeaders, body?.getClientValue())
}
return contentType
}
boolean isJson() {
return this.inputTestContentType == ContentType.JSON ||
this.outputTestContentType == ContentType.JSON ||

View File

@@ -650,7 +650,7 @@ class ContentUtils {
escapeJava(property.value.serverValue.toString()) + quote + ".getBytes()"
}
static ContentType evaluateContentType(Headers contractHeaders, Object body) {
static ContentType evaluateClientSideContentType(Headers contractHeaders, Object body) {
ContentType contentType = recognizeContentTypeFromHeader(contractHeaders)
if (UNKNOWN == contentType) {
contentType = recognizeContentTypeFromContent(body)
@@ -658,6 +658,14 @@ class ContentUtils {
return contentType
}
static ContentType evaluateServerSideContentType(Headers contractHeaders, Object body) {
ContentType contentType = recognizeContentTypeFromTestHeader(contractHeaders)
if (UNKNOWN == contentType) {
contentType = recognizeContentTypeFromContent(body)
}
return contentType
}
/**
* Creates new {@link XmlSlurper} with default error handler.
*