Merge branch '2.0.x'

This commit is contained in:
Marcin Grzejszczak
2018-10-24 22:31:35 +02:00
4 changed files with 123 additions and 11 deletions

View File

@@ -1,20 +1,20 @@
package org.springframework.cloud.contract.verifier.converter package org.springframework.cloud.contract.verifier.converter
import java.nio.file.Files
import java.util.regex.Pattern
import com.fasterxml.jackson.databind.ObjectMapper import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.dataformat.yaml.YAMLMapper import com.fasterxml.jackson.dataformat.yaml.YAMLMapper
import groovy.transform.CompileStatic import groovy.transform.CompileStatic
import groovy.transform.PackageScope import groovy.transform.PackageScope
import org.yaml.snakeyaml.Yaml
import org.springframework.cloud.contract.spec.Contract import org.springframework.cloud.contract.spec.Contract
import org.springframework.cloud.contract.spec.internal.DslProperty import org.springframework.cloud.contract.spec.internal.DslProperty
import org.springframework.cloud.contract.spec.internal.ExecutionProperty import org.springframework.cloud.contract.spec.internal.ExecutionProperty
import org.springframework.cloud.contract.spec.internal.MatchingTypeValue import org.springframework.cloud.contract.spec.internal.MatchingTypeValue
import org.springframework.cloud.contract.spec.internal.NamedProperty import org.springframework.cloud.contract.spec.internal.NamedProperty
import org.springframework.cloud.contract.spec.internal.RegexPatterns import org.springframework.cloud.contract.spec.internal.RegexPatterns
import org.springframework.cloud.contract.spec.internal.Request
import org.yaml.snakeyaml.Yaml
import java.nio.file.Files
import java.util.regex.Pattern
/** /**
* @author Marcin Grzejszczak * @author Marcin Grzejszczak
@@ -86,10 +86,10 @@ class YamlToContracts {
yamlContract.request.queryParameters.each { String key, Object value -> yamlContract.request.queryParameters.each { String key, Object value ->
if (value instanceof List) { if (value instanceof List) {
((List) value).each { ((List) value).each {
parameter(key, it) parameter(key, queryParamValue(yamlContract, key, it))
} }
} else { } else {
parameter(key, value) parameter(key, queryParamValue(yamlContract, key, value))
} }
} }
} }
@@ -202,8 +202,15 @@ class YamlToContracts {
case YamlContract.StubMatcherType.by_equality: case YamlContract.StubMatcherType.by_equality:
value = byEquality() value = byEquality()
break break
case YamlContract.StubMatcherType.by_null:
// do nothing
break
default:
throw new UnsupportedOperationException("The type [" + matcher.type + "] is unsupported. Hint: If you're using <predefined> remember to pass <type: by_regex>")
}
if (value) {
jsonPath(matcher.path, value)
} }
jsonPath(matcher.path, value)
} }
} }
} }
@@ -282,6 +289,8 @@ class YamlToContracts {
case YamlContract.TestMatcherType.by_null: case YamlContract.TestMatcherType.by_null:
value = byNull() value = byNull()
break break
default:
throw new UnsupportedOperationException("The type [" + testMatcher.type + "] is unsupported. Hint: If you're using <predefined> remember to pass <type: by_regex>")
} }
if (testMatcher.path) { if (testMatcher.path) {
jsonPath(testMatcher.path, value) jsonPath(testMatcher.path, value)
@@ -451,6 +460,33 @@ class YamlToContracts {
return clientValue return clientValue
} }
protected Object queryParamValue(YamlContract yamlContract, String key, Object value) {
Request request = new Request()
YamlContract.QueryParameterMatcher matcher = yamlContract.request.
matchers.queryParameters.find { it.key == key}
if (!matcher) {
return value
}
switch (matcher.type) {
case YamlContract.MatchingType.equal_to:
return new DslProperty(request.equalTo(matcher.value), value)
case YamlContract.MatchingType.containing:
return new DslProperty(request.containing(matcher.value), value)
case YamlContract.MatchingType.matching:
return new DslProperty(request.matching(matcher.value), value)
case YamlContract.MatchingType.not_matching:
return new DslProperty(request.notMatching(matcher.value), value)
case YamlContract.MatchingType.equal_to_json:
return new DslProperty(request.equalToJson(matcher.value), value)
case YamlContract.MatchingType.equal_to_xml:
return new DslProperty(request.equalToXml(matcher.value), value)
case YamlContract.MatchingType.absent:
return new DslProperty(request.absent(), null)
default:
throw new UnsupportedOperationException("The provided matching type [" + matcher + "] is unsupported. Use on of " + YamlContract.MatchingType.values())
}
}
protected Object serverValue(Object value, YamlContract.KeyValueMatcher matcher) { protected Object serverValue(Object value, YamlContract.KeyValueMatcher matcher) {
Object serverValue = value Object serverValue = value
if (matcher?.command) { if (matcher?.command) {
@@ -499,6 +535,8 @@ class YamlToContracts {
return patterns.nonEmpty() return patterns.nonEmpty()
case YamlContract.PredefinedRegex.non_blank: case YamlContract.PredefinedRegex.non_blank:
return patterns.nonBlank() return patterns.nonBlank()
default:
throw new UnsupportedOperationException("The predefined regex [" + predefinedRegex + "] is unsupported. Use on of " + YamlContract.PredefinedRegex.values())
} }
} }

View File

@@ -16,6 +16,8 @@
package org.springframework.cloud.contract.verifier.converter package org.springframework.cloud.contract.verifier.converter
import org.springframework.cloud.contract.spec.internal.MatchingStrategy
import org.springframework.cloud.contract.spec.internal.QueryParameters
import spock.lang.Issue import spock.lang.Issue
import java.util.regex.Pattern import java.util.regex.Pattern
@@ -187,6 +189,22 @@ class YamlContractConverterSpec extends Specification {
RegexPatterns patterns = new RegexPatterns() RegexPatterns patterns = new RegexPatterns()
contract.request.headers.entries.find { it.name == "Content-Type" && contract.request.headers.entries.find { it.name == "Content-Type" &&
((Pattern) it.clientValue).pattern == "application/json.*" && it.serverValue == "application/json" } ((Pattern) it.clientValue).pattern == "application/json.*" && it.serverValue == "application/json" }
contract.request.urlPath.queryParameters.parameters.size() == 8
QueryParameters queryParameters = contract.request.urlPath.queryParameters
assertQueryParam(queryParameters, "limit", 10,
MatchingStrategy.Type.EQUAL_TO, 20)
assertQueryParam(queryParameters, "offset", 20,
MatchingStrategy.Type.CONTAINS, 20)
assertQueryParam(queryParameters, "sort", "name",
MatchingStrategy.Type.EQUAL_TO, "name")
assertQueryParam(queryParameters, "search", 55,
MatchingStrategy.Type.NOT_MATCHING, (~/^[0-9]{2}$/).pattern())
assertQueryParam(queryParameters, "age", 99,
MatchingStrategy.Type.NOT_MATCHING, "^\\\\w*\$")
assertQueryParam(queryParameters, "name", "John.Doe",
MatchingStrategy.Type.MATCHING, "John.*")
assertQueryParam(queryParameters, "hello", true,
MatchingStrategy.Type.ABSENT, null)
contract.request.bodyMatchers.jsonPathRegexMatchers[0].path() == '$.duck' contract.request.bodyMatchers.jsonPathRegexMatchers[0].path() == '$.duck'
contract.request.bodyMatchers.jsonPathRegexMatchers[0].matchingType() == MatchingType.REGEX contract.request.bodyMatchers.jsonPathRegexMatchers[0].matchingType() == MatchingType.REGEX
contract.request.bodyMatchers.jsonPathRegexMatchers[0].value() == '[0-9]{3}' contract.request.bodyMatchers.jsonPathRegexMatchers[0].value() == '[0-9]{3}'
@@ -264,6 +282,17 @@ class YamlContractConverterSpec extends Specification {
contract.response.bodyMatchers.jsonPathRegexMatchers[15].value() == new ExecutionProperty('assertThatValueIsANumber($it)') contract.response.bodyMatchers.jsonPathRegexMatchers[15].value() == new ExecutionProperty('assertThatValueIsANumber($it)')
} }
protected Object assertQueryParam(QueryParameters queryParameters, String queryParamName, Object serverValue,
MatchingStrategy.Type clientType, Object clientValue) {
if (clientType == MatchingStrategy.Type.ABSENT) {
return ! queryParameters.parameters.find { it.name == queryParamName}
}
return queryParameters.parameters.find { it.name == queryParamName &&
it.serverValue == serverValue &&
((MatchingStrategy) it.clientValue).type == clientType &&
((MatchingStrategy) it.clientValue).clientValue == clientValue }
}
@Issue("#604") @Issue("#604")
def "should convert YAML with Message matchers to DSL"() { def "should convert YAML with Message matchers to DSL"() {
given: given:

View File

@@ -209,5 +209,10 @@ class WireMockResponseStubStrategySpec extends Specification {
subject.buildClientRequestContent() subject.buildClientRequestContent()
then: then:
noExceptionThrown() noExceptionThrown()
when:
def response = new WireMockResponseStubStrategy(contract)
response.buildClientResponseContent()
then:
noExceptionThrown()
} }
} }

View File

@@ -1,6 +1,19 @@
request: request:
method: GET method: GET
urlPath: /get urlPath: /get/1
headers:
Content-Type: application/json
cookies:
foo: 2
queryParameters:
limit: 10
offset: 20
filter: 'email'
sort: name
search: 55
age: 99
name: John.Doe
email: 'bob@email.com'
body: body:
duck: 123 duck: 123
alpha: "abc" alpha: "abc"
@@ -15,6 +28,35 @@ request:
"complex.key": 'foo' "complex.key": 'foo'
nullValue: null nullValue: null
matchers: matchers:
url:
regex: /get/[0-9]
# predefined:
# execute a method
# command:
queryParameters:
- key: limit
type: equal_to
value: 20
- key: offset
type: containing
value: 20
- key: sort
type: equal_to
value: name
- key: search
type: not_matching
value: '^[0-9]{2}$'
- key: age
type: not_matching
value: '^\\w*$'
- key: name
type: matching
value: 'John.*'
- key: hello
type: absent
cookies:
- key: foo
regex: '[0-9]'
headers: headers:
- key: Content-Type - key: Content-Type
regex: "application/json.*" regex: "application/json.*"
@@ -45,8 +87,6 @@ request:
type: by_equality type: by_equality
- path: $.nullvalue - path: $.nullvalue
type: by_null type: by_null
headers:
Content-Type: application/json
response: response:
status: 200 status: 200
body: body: