Allows passing of regex type (#832)
without this change if one does $(regex("[0-9]")) we have no knowledge of whether the result should be text or a number. What we do ATM is we always generate a String
with this change once can pass the type of regular expression and we will generate the concrete value of that given type
fixes gh-768
This commit is contained in:
committed by
GitHub
parent
58bf533462
commit
56fbc11f62
@@ -801,7 +801,7 @@ following matching possibilities:
|
||||
** `byEquality()`: The value taken from the consumer's request via the provided JSON Path must be
|
||||
equal to the value provided in the contract.
|
||||
** `byRegex(...)`: The value taken from the consumer's request via the provided JSON Path must
|
||||
match the regex.
|
||||
match the regex. You can also pass the type of the expected matched value (e.g. `asString()`, `asLong()` etc.)
|
||||
** `byDate()`: The value taken from the consumer's request via the provided JSON Path must
|
||||
match the regex for an ISO Date value.
|
||||
** `byTimestamp()`: The value taken from the consumer's request via the provided JSON Path must
|
||||
@@ -848,6 +848,7 @@ For YAML the structure of a matcher looks like this
|
||||
- path: $.foo
|
||||
type: by_regex
|
||||
value: bar
|
||||
regexType: as_string
|
||||
----
|
||||
|
||||
Or if you want to use one of the predefined regular expressions
|
||||
@@ -882,6 +883,16 @@ Below you can find the allowed list of `type`s.
|
||||
** `by_command`
|
||||
** `by_null`
|
||||
|
||||
You can also define which type the regular expression corresponds to via the `regexType` field. Below you can find the allowed list of regular expression types:
|
||||
|
||||
* as_integer
|
||||
* as_double
|
||||
* as_float,
|
||||
* as_long
|
||||
* as_short
|
||||
* as_boolean
|
||||
* as_string
|
||||
|
||||
Consider the following example:
|
||||
|
||||
.Groovy DSL
|
||||
|
||||
@@ -74,7 +74,7 @@ public class LoanApplicationServiceTests {
|
||||
// when:
|
||||
int count = service.countAllFrauds();
|
||||
// then:
|
||||
assertThat(count).isEqualTo(200);
|
||||
assertThat(count).isGreaterThanOrEqualTo(200);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -12,7 +12,7 @@ import org.springframework.cloud.contract.spec.Contract
|
||||
response {
|
||||
status OK()
|
||||
body([
|
||||
count: 200
|
||||
count: $(regex("[2-9][0-9][0-9]").asInteger())
|
||||
])
|
||||
headers {
|
||||
contentType("application/json")
|
||||
|
||||
@@ -21,7 +21,6 @@ import groovy.transform.Canonical
|
||||
import groovy.transform.CompileStatic
|
||||
import groovy.transform.EqualsAndHashCode
|
||||
import groovy.transform.ToString
|
||||
|
||||
/**
|
||||
* Matching strategy of dynamic parts of the body.
|
||||
*
|
||||
@@ -58,15 +57,18 @@ class BodyMatchers {
|
||||
return new MatchingTypeValue(MatchingType.TIMESTAMP, this.regexPatterns.isoDateTime())
|
||||
}
|
||||
|
||||
MatchingTypeValue byRegex(String regex) {
|
||||
assert regex
|
||||
return new MatchingTypeValue(MatchingType.REGEX, regex)
|
||||
RegexMatchingTypeValue byRegex(String regex) {
|
||||
return byRegex(Pattern.compile(regex))
|
||||
}
|
||||
|
||||
// Backward compatibility with RegexPatterns
|
||||
MatchingTypeValue byRegex(Pattern regex) {
|
||||
RegexMatchingTypeValue byRegex(RegexProperty regex) {
|
||||
assert regex
|
||||
return new MatchingTypeValue(MatchingType.REGEX, regex)
|
||||
return new RegexMatchingTypeValue(MatchingType.REGEX, regex)
|
||||
}
|
||||
|
||||
RegexMatchingTypeValue byRegex(Pattern regex) {
|
||||
assert regex
|
||||
return new RegexMatchingTypeValue(MatchingType.REGEX, new RegexProperty(regex))
|
||||
}
|
||||
|
||||
MatchingTypeValue byEquality() {
|
||||
@@ -130,6 +132,61 @@ class JsonPathBodyMatcher implements BodyMatcher {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Matching type with corresponding values
|
||||
*/
|
||||
|
||||
@ToString(includePackage = false)
|
||||
@EqualsAndHashCode
|
||||
class RegexMatchingTypeValue extends MatchingTypeValue {
|
||||
|
||||
RegexMatchingTypeValue(MatchingType type, Object value, Integer minTypeOccurrence, Integer maxTypeOccurrence) {
|
||||
super(type, value, minTypeOccurrence, maxTypeOccurrence)
|
||||
}
|
||||
|
||||
RegexMatchingTypeValue(MatchingType type, Object value) {
|
||||
super(type, value)
|
||||
}
|
||||
|
||||
RegexMatchingTypeValue asInteger() {
|
||||
return typed(Integer)
|
||||
}
|
||||
|
||||
private RegexMatchingTypeValue typed(Class clazz) {
|
||||
assert this.value instanceof RegexProperty
|
||||
RegexProperty regexProperty = (RegexProperty) this.value
|
||||
return new RegexMatchingTypeValue(
|
||||
this.type, new RegexProperty(regexProperty.clientValue,
|
||||
regexProperty.serverValue, clazz),
|
||||
this.minTypeOccurrence, this.maxTypeOccurrence
|
||||
)
|
||||
}
|
||||
|
||||
RegexMatchingTypeValue asDouble() {
|
||||
return typed(Double)
|
||||
}
|
||||
|
||||
RegexMatchingTypeValue asFloat() {
|
||||
return typed(Float)
|
||||
}
|
||||
|
||||
RegexMatchingTypeValue asLong() {
|
||||
return typed(Long)
|
||||
}
|
||||
|
||||
RegexMatchingTypeValue asShort() {
|
||||
return typed(Short)
|
||||
}
|
||||
|
||||
RegexMatchingTypeValue asString() {
|
||||
return typed(String)
|
||||
}
|
||||
|
||||
RegexMatchingTypeValue asBooleanType() {
|
||||
return typed(Boolean)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Matching type with corresponding values
|
||||
*/
|
||||
|
||||
@@ -114,19 +114,27 @@ class Common {
|
||||
return value(server, client)
|
||||
}
|
||||
|
||||
Pattern regex(String regex) {
|
||||
return Pattern.compile(regex)
|
||||
RegexProperty regex(String regex) {
|
||||
return regexProperty(Pattern.compile(regex))
|
||||
}
|
||||
|
||||
RegexProperty regex(RegexProperty regex) {
|
||||
return regex
|
||||
}
|
||||
|
||||
// Backward compatibility with RegexPatterns
|
||||
Pattern regex(Pattern regex) {
|
||||
return regex
|
||||
RegexProperty regex(Pattern regex) {
|
||||
return regexProperty(regex)
|
||||
}
|
||||
|
||||
OptionalProperty optional(Object object) {
|
||||
return new OptionalProperty(object)
|
||||
}
|
||||
|
||||
RegexProperty regexProperty(Object object) {
|
||||
return new RegexProperty(object)
|
||||
}
|
||||
|
||||
ExecutionProperty execute(String commandToExecute) {
|
||||
return new ExecutionProperty(commandToExecute)
|
||||
}
|
||||
|
||||
@@ -16,16 +16,13 @@
|
||||
|
||||
package org.springframework.cloud.contract.spec.internal
|
||||
|
||||
import groovy.util.logging.Commons
|
||||
import groovy.util.logging.Slf4j
|
||||
|
||||
import java.util.regex.Pattern
|
||||
|
||||
import groovy.transform.CompileStatic
|
||||
import groovy.transform.EqualsAndHashCode
|
||||
import groovy.transform.ToString
|
||||
import groovy.transform.TypeChecked
|
||||
import repackaged.nl.flotsam.xeger.Xeger
|
||||
import groovy.util.logging.Commons
|
||||
/**
|
||||
* Represents an input for messaging. The input can be a message or some
|
||||
* action inside the application.
|
||||
@@ -89,17 +86,31 @@ class Input extends Common {
|
||||
}
|
||||
|
||||
DslProperty value(ClientDslProperty client) {
|
||||
Object clientValue = client.clientValue
|
||||
if (client.clientValue instanceof Pattern) {
|
||||
clientValue = new Xeger(((Pattern)client.clientValue).pattern()).generate()
|
||||
Object dynamicValue = client.clientValue
|
||||
Object concreteValue = client.serverValue
|
||||
if (dynamicValue instanceof RegexProperty) {
|
||||
return dynamicValue.dynamicClientConcreteProducer()
|
||||
}
|
||||
return new DslProperty(client.clientValue, clientValue)
|
||||
return new DslProperty(dynamicValue, concreteValue)
|
||||
}
|
||||
|
||||
DslProperty value(RegexProperty prop) {
|
||||
return value(client(prop))
|
||||
}
|
||||
|
||||
DslProperty $(RegexProperty prop) {
|
||||
return value(client(prop))
|
||||
}
|
||||
|
||||
DslProperty $(ClientDslProperty client) {
|
||||
return value(client)
|
||||
}
|
||||
|
||||
@Override
|
||||
RegexProperty regexProperty(Object object) {
|
||||
return new RegexProperty(object).dynamicClientConcreteProducer()
|
||||
}
|
||||
|
||||
@EqualsAndHashCode(includeFields = true, callSuper = true)
|
||||
@ToString(includeSuper = true)
|
||||
static class BodyType extends DslProperty {
|
||||
|
||||
@@ -19,7 +19,6 @@ package org.springframework.cloud.contract.spec.internal
|
||||
import java.util.regex.Pattern
|
||||
|
||||
import groovy.transform.CompileStatic
|
||||
import groovy.transform.PackageScope
|
||||
import groovy.transform.ToString
|
||||
|
||||
/**
|
||||
@@ -41,7 +40,12 @@ class OptionalProperty implements Serializable {
|
||||
* in an optional function
|
||||
*/
|
||||
String optionalPattern() {
|
||||
return "($value)?"
|
||||
return "(${value()})?"
|
||||
}
|
||||
|
||||
String value() {
|
||||
return this.value instanceof RegexProperty ?
|
||||
((RegexProperty) this.value).pattern.pattern() : this.value
|
||||
}
|
||||
|
||||
protected Pattern optionalPatternValue() {
|
||||
|
||||
@@ -16,17 +16,13 @@
|
||||
|
||||
package org.springframework.cloud.contract.spec.internal
|
||||
|
||||
import java.util.regex.Pattern
|
||||
|
||||
import groovy.transform.CompileStatic
|
||||
import groovy.transform.EqualsAndHashCode
|
||||
import groovy.transform.ToString
|
||||
import groovy.transform.TypeChecked
|
||||
import groovy.util.logging.Commons
|
||||
import groovy.util.logging.Slf4j
|
||||
import org.apache.commons.lang3.StringEscapeUtils
|
||||
import repackaged.nl.flotsam.xeger.Xeger
|
||||
|
||||
import java.util.regex.Pattern
|
||||
|
||||
/**
|
||||
* Represents an output for messaging. Used for verifying
|
||||
* the body and headers that are sent.
|
||||
@@ -83,26 +79,57 @@ class OutputMessage extends Common {
|
||||
this.assertThat = new ExecutionProperty(assertThat)
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated - use the server dsl property
|
||||
*/
|
||||
@Deprecated
|
||||
DslProperty value(ClientDslProperty clientDslProperty) {
|
||||
Object clientValue = clientDslProperty.clientValue
|
||||
// for the output messages ran via stub runner,
|
||||
// entries have to have fixed values
|
||||
if (clientDslProperty.clientValue instanceof Pattern) {
|
||||
clientValue = StringEscapeUtils.escapeJava(new Xeger(((Pattern)clientDslProperty.clientValue).pattern()).generate())
|
||||
}
|
||||
return new DslProperty(clientValue, clientDslProperty.clientValue)
|
||||
return value(new ServerDslProperty(clientDslProperty.serverValue, clientDslProperty.clientValue))
|
||||
}
|
||||
|
||||
DslProperty value(ServerDslProperty serverDslProperty) {
|
||||
Object concreteValue = serverDslProperty.clientValue
|
||||
Object dynamicValue = serverDslProperty.serverValue
|
||||
// for the output messages ran via stub runner,
|
||||
// entries have to have fixed values
|
||||
if (dynamicValue instanceof RegexProperty) {
|
||||
return dynamicValue
|
||||
.concreteClientEscapedDynamicProducer()
|
||||
}
|
||||
return new DslProperty(concreteValue, dynamicValue)
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated - use the server dsl property
|
||||
*/
|
||||
@Deprecated
|
||||
DslProperty $(ClientDslProperty client) {
|
||||
return value(client)
|
||||
}
|
||||
|
||||
DslProperty $(ServerDslProperty property) {
|
||||
return value(property)
|
||||
}
|
||||
|
||||
DslProperty $(Pattern pattern) {
|
||||
return value(client(pattern))
|
||||
return value(new RegexProperty(pattern))
|
||||
}
|
||||
|
||||
DslProperty $(RegexProperty pattern) {
|
||||
return value(pattern)
|
||||
}
|
||||
|
||||
DslProperty value(RegexProperty pattern) {
|
||||
return value(producer(pattern))
|
||||
}
|
||||
|
||||
DslProperty $(OptionalProperty property) {
|
||||
return value(client(property.optionalPatternValue()))
|
||||
return value(producer(property.optionalPatternValue()))
|
||||
}
|
||||
|
||||
@Override
|
||||
RegexProperty regexProperty(Object object) {
|
||||
return new RegexProperty(object).concreteClientDynamicProducer()
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -60,80 +60,80 @@ class RegexPatterns {
|
||||
return Pattern.compile(values.collect({"^$it\$"}).join("|"))
|
||||
}
|
||||
|
||||
Pattern onlyAlphaUnicode() {
|
||||
return ONLY_ALPHA_UNICODE
|
||||
RegexProperty onlyAlphaUnicode() {
|
||||
return new RegexProperty(ONLY_ALPHA_UNICODE).asString()
|
||||
}
|
||||
|
||||
Pattern alphaNumeric() {
|
||||
return ALPHA_NUMERIC
|
||||
RegexProperty alphaNumeric() {
|
||||
return new RegexProperty(ALPHA_NUMERIC).asString()
|
||||
}
|
||||
|
||||
Pattern number() {
|
||||
return NUMBER
|
||||
RegexProperty number() {
|
||||
return new RegexProperty(NUMBER).asDouble()
|
||||
}
|
||||
|
||||
Pattern positiveInt() {
|
||||
return POSITIVE_INT
|
||||
RegexProperty positiveInt() {
|
||||
return new RegexProperty(POSITIVE_INT).asInteger()
|
||||
}
|
||||
|
||||
Pattern anyBoolean() {
|
||||
return TRUE_OR_FALSE
|
||||
RegexProperty anyBoolean() {
|
||||
return new RegexProperty(TRUE_OR_FALSE).asBooleanType()
|
||||
}
|
||||
|
||||
Pattern anInteger() {
|
||||
return INTEGER
|
||||
RegexProperty anInteger() {
|
||||
return new RegexProperty(INTEGER).asInteger()
|
||||
}
|
||||
|
||||
Pattern aDouble() {
|
||||
return DOUBLE
|
||||
RegexProperty aDouble() {
|
||||
return new RegexProperty(DOUBLE).asDouble()
|
||||
}
|
||||
|
||||
Pattern ipAddress() {
|
||||
return IP_ADDRESS
|
||||
RegexProperty ipAddress() {
|
||||
return new RegexProperty(IP_ADDRESS).asString()
|
||||
}
|
||||
|
||||
Pattern hostname() {
|
||||
return HOSTNAME_PATTERN
|
||||
RegexProperty hostname() {
|
||||
return new RegexProperty(HOSTNAME_PATTERN).asString()
|
||||
}
|
||||
|
||||
Pattern email() {
|
||||
return EMAIL
|
||||
RegexProperty email() {
|
||||
return new RegexProperty(EMAIL).asString()
|
||||
}
|
||||
|
||||
Pattern url() {
|
||||
return URL
|
||||
RegexProperty url() {
|
||||
return new RegexProperty(URL).asString()
|
||||
}
|
||||
|
||||
Pattern httpsUrl() {
|
||||
return HTTPS_URL
|
||||
RegexProperty httpsUrl() {
|
||||
return new RegexProperty(HTTPS_URL).asString()
|
||||
}
|
||||
|
||||
Pattern uuid(){
|
||||
return UUID
|
||||
RegexProperty uuid(){
|
||||
return new RegexProperty(UUID).asString()
|
||||
}
|
||||
|
||||
Pattern isoDate() {
|
||||
return ANY_DATE
|
||||
RegexProperty isoDate() {
|
||||
return new RegexProperty(ANY_DATE).asString()
|
||||
}
|
||||
|
||||
Pattern isoDateTime() {
|
||||
return ANY_DATE_TIME
|
||||
RegexProperty isoDateTime() {
|
||||
return new RegexProperty(ANY_DATE_TIME).asString()
|
||||
}
|
||||
|
||||
Pattern isoTime() {
|
||||
return ANY_TIME
|
||||
RegexProperty isoTime() {
|
||||
return new RegexProperty(ANY_TIME).asString()
|
||||
}
|
||||
|
||||
Pattern iso8601WithOffset() {
|
||||
return ISO8601_WITH_OFFSET
|
||||
RegexProperty iso8601WithOffset() {
|
||||
return new RegexProperty(ISO8601_WITH_OFFSET).asString()
|
||||
}
|
||||
|
||||
Pattern nonEmpty() {
|
||||
return NON_EMPTY
|
||||
RegexProperty nonEmpty() {
|
||||
return new RegexProperty(NON_EMPTY).asString()
|
||||
}
|
||||
|
||||
Pattern nonBlank() {
|
||||
return NON_BLANK
|
||||
RegexProperty nonBlank() {
|
||||
return new RegexProperty(NON_BLANK).asString()
|
||||
}
|
||||
|
||||
// end::regexps[]
|
||||
@@ -150,7 +150,7 @@ class RegexPatterns {
|
||||
if (contentType == null) {
|
||||
return '.*'
|
||||
}
|
||||
if (contentType instanceof Pattern) {
|
||||
if (contentType instanceof RegexProperty) {
|
||||
return contentType.pattern()
|
||||
}
|
||||
return contentType.toString()
|
||||
@@ -163,7 +163,7 @@ class RegexPatterns {
|
||||
class UrlHelper {
|
||||
/**
|
||||
* Example: "http". Also called 'protocol'.
|
||||
* Scheme component is optional, even though the RFC doesn't make it optional. Since this regex is validating a
|
||||
* Scheme component is optional, even though the RFC doesn't make it optional. Since ((RegexProperty) this regex is validating a
|
||||
* submitted callback url, which determines where the browser will navigate to after a successful authentication,
|
||||
* the browser will use http or https for the scheme by default.
|
||||
* Not borrowed from dperini in order to allow any scheme type.
|
||||
|
||||
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
* Copyright 2013-2018 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.spec.internal
|
||||
|
||||
import java.util.regex.Matcher
|
||||
import java.util.regex.Pattern
|
||||
|
||||
import groovy.transform.EqualsAndHashCode
|
||||
import groovy.transform.ToString
|
||||
import groovy.transform.TypeChecked
|
||||
import org.apache.commons.text.StringEscapeUtils
|
||||
import repackaged.nl.flotsam.xeger.Xeger
|
||||
/**
|
||||
* Represents a regular expression property
|
||||
*
|
||||
* @since 2.1.0
|
||||
*/
|
||||
@TypeChecked
|
||||
@EqualsAndHashCode
|
||||
@ToString(includePackage = false, includeNames = true)
|
||||
class RegexProperty extends DslProperty {
|
||||
final Pattern pattern
|
||||
private final Class clazz
|
||||
|
||||
RegexProperty(Object value) {
|
||||
this(value, value, null)
|
||||
}
|
||||
|
||||
RegexProperty(Object client, Object server) {
|
||||
this(client, server, null)
|
||||
}
|
||||
|
||||
RegexProperty(Object client, Object server, Class clazz) {
|
||||
super(client, server)
|
||||
boolean clientDynamic = client instanceof Pattern ||
|
||||
client instanceof RegexProperty
|
||||
boolean serverDynamic = server instanceof Pattern ||
|
||||
server instanceof RegexProperty
|
||||
if (!clientDynamic && !serverDynamic) {
|
||||
throw new IllegalStateException("Neither client not server side is dynamic")
|
||||
}
|
||||
Object dynamicValue = clientDynamic ? client : server
|
||||
if (dynamicValue instanceof Pattern) {
|
||||
this.pattern = dynamicValue
|
||||
this.clazz = clazz ?: String
|
||||
} else if (dynamicValue instanceof RegexProperty) {
|
||||
RegexProperty regexProperty = ((RegexProperty) dynamicValue)
|
||||
this.pattern = regexProperty.pattern
|
||||
this.clazz = clazz ?: regexProperty.clazz
|
||||
} else {
|
||||
this.clazz = clazz
|
||||
}
|
||||
}
|
||||
|
||||
Matcher matcher(CharSequence input) {
|
||||
return this.pattern.matcher(input)
|
||||
}
|
||||
|
||||
String pattern() {
|
||||
return this.pattern.pattern()
|
||||
}
|
||||
|
||||
Class clazz() {
|
||||
return this.class
|
||||
}
|
||||
|
||||
RegexProperty asInteger() {
|
||||
return new RegexProperty(this.clientValue, this.serverValue, Integer)
|
||||
}
|
||||
|
||||
RegexProperty asDouble() {
|
||||
return new RegexProperty(this.clientValue, this.serverValue, Double)
|
||||
}
|
||||
|
||||
RegexProperty asFloat() {
|
||||
return new RegexProperty(this.clientValue, this.serverValue, Float)
|
||||
}
|
||||
|
||||
RegexProperty asLong() {
|
||||
return new RegexProperty(this.clientValue, this.serverValue, Long)
|
||||
}
|
||||
|
||||
RegexProperty asShort() {
|
||||
return new RegexProperty(this.clientValue, this.serverValue, Short)
|
||||
}
|
||||
|
||||
RegexProperty asString() {
|
||||
return new RegexProperty(this.clientValue, this.serverValue, String)
|
||||
}
|
||||
|
||||
RegexProperty asBooleanType() {
|
||||
return new RegexProperty(this.clientValue, this.serverValue, Boolean)
|
||||
}
|
||||
|
||||
Object generate() {
|
||||
String generatedValue = new Xeger(this.pattern.pattern()).generate()
|
||||
switch (this.clazz) {
|
||||
case Integer: return Integer.parseInt(generatedValue)
|
||||
case Double: return Double.parseDouble(generatedValue)
|
||||
case Float: return Float.parseFloat(generatedValue)
|
||||
case Long: return Long.parseLong(generatedValue)
|
||||
case Short: return Short.parseShort(generatedValue)
|
||||
case Boolean: return Boolean.parseBoolean(generatedValue)
|
||||
default: return generatedValue
|
||||
}
|
||||
}
|
||||
|
||||
Object generateAndEscapeJavaStringIfNeeded() {
|
||||
Object generated = generate()
|
||||
if (isNumber()) {
|
||||
return generated
|
||||
}
|
||||
return StringEscapeUtils.escapeJava(generated as String)
|
||||
}
|
||||
|
||||
private boolean isNumber() {
|
||||
return Number.isAssignableFrom(this.clazz)
|
||||
}
|
||||
|
||||
RegexProperty dynamicClientConcreteProducer() {
|
||||
return new RegexProperty(this.pattern, generate(), this.clazz)
|
||||
}
|
||||
|
||||
RegexProperty concreteClientDynamicProducer() {
|
||||
return new RegexProperty(generate(), this.pattern, this.clazz)
|
||||
}
|
||||
|
||||
RegexProperty concreteClientEscapedDynamicProducer() {
|
||||
return new RegexProperty(generateAndEscapeJavaStringIfNeeded(), this.pattern, this.clazz)
|
||||
}
|
||||
|
||||
RegexProperty dynamicClientEscapedConcreteProducer() {
|
||||
return new RegexProperty(this.pattern, generateAndEscapeJavaStringIfNeeded(), this.clazz)
|
||||
}
|
||||
|
||||
boolean equals(o) {
|
||||
if (this.is(o)) return true
|
||||
if (getClass() != o.class) return false
|
||||
if (!super.equals(o)) return false
|
||||
RegexProperty that = (RegexProperty) o
|
||||
if (this.clazz != that.clazz) return false
|
||||
if (this.pattern != that.pattern) return false
|
||||
return true
|
||||
}
|
||||
|
||||
int hashCode() {
|
||||
int result = super.hashCode()
|
||||
result = 31 * result + (this.pattern != null ? pattern.hashCode() : 0)
|
||||
result = 31 * result + (this.clazz != null ? clazz.hashCode() : 0)
|
||||
return result
|
||||
}
|
||||
|
||||
@Override
|
||||
String toString() {
|
||||
return this.pattern()
|
||||
}
|
||||
}
|
||||
@@ -23,8 +23,6 @@ import groovy.transform.EqualsAndHashCode
|
||||
import groovy.transform.ToString
|
||||
import groovy.transform.TypeChecked
|
||||
import groovy.util.logging.Commons
|
||||
import org.apache.commons.lang3.StringEscapeUtils
|
||||
import repackaged.nl.flotsam.xeger.Xeger
|
||||
|
||||
import org.springframework.cloud.contract.spec.util.RegexpUtils
|
||||
/**
|
||||
@@ -208,13 +206,22 @@ class Request extends Common {
|
||||
}
|
||||
|
||||
DslProperty value(ClientDslProperty client) {
|
||||
Object clientValue = client.clientValue
|
||||
if (client.clientValue instanceof Pattern && client.isSingleValue()) {
|
||||
clientValue = StringEscapeUtils.escapeJava(new Xeger(((Pattern)client.clientValue).pattern()).generate())
|
||||
} else if (client.clientValue instanceof Pattern && !client.isSingleValue()) {
|
||||
clientValue = client.serverValue
|
||||
Object concreteValue = client.serverValue
|
||||
Object dynamicValue = client.clientValue
|
||||
if (dynamicValue instanceof RegexProperty && client.isSingleValue()) {
|
||||
return dynamicValue.dynamicClientEscapedConcreteProducer()
|
||||
} else if (concreteValue instanceof RegexProperty && !client.isSingleValue()) {
|
||||
concreteValue = dynamicValue
|
||||
}
|
||||
return new DslProperty(client.clientValue, clientValue)
|
||||
return new DslProperty(dynamicValue, concreteValue)
|
||||
}
|
||||
|
||||
DslProperty $(RegexProperty property) {
|
||||
return value(property)
|
||||
}
|
||||
|
||||
DslProperty value(RegexProperty property) {
|
||||
return value(client(property))
|
||||
}
|
||||
|
||||
DslProperty $(ClientDslProperty client) {
|
||||
@@ -222,13 +229,18 @@ class Request extends Common {
|
||||
}
|
||||
|
||||
DslProperty value(Pattern client) {
|
||||
return value(new ClientDslProperty(client))
|
||||
return value(new RegexProperty(client))
|
||||
}
|
||||
|
||||
DslProperty $(Pattern client) {
|
||||
return value(client)
|
||||
}
|
||||
|
||||
@Override
|
||||
RegexProperty regexProperty(Object object) {
|
||||
return new RegexProperty(object).dynamicClientConcreteProducer()
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Deprecated in favor of bodyMatchers to support other future bodyMatchers too
|
||||
*/
|
||||
@@ -246,7 +258,7 @@ class Request extends Common {
|
||||
|
||||
@Override
|
||||
DslProperty value(ClientDslProperty client, ServerDslProperty server) {
|
||||
if (server.clientValue instanceof Pattern) {
|
||||
if (server.clientValue instanceof RegexProperty) {
|
||||
throw new IllegalStateException("You can't have a regular expression for the request on the server side")
|
||||
}
|
||||
return super.value(client, server)
|
||||
@@ -254,7 +266,7 @@ class Request extends Common {
|
||||
|
||||
@Override
|
||||
DslProperty value(ServerDslProperty server, ClientDslProperty client) {
|
||||
if (server.clientValue instanceof Pattern) {
|
||||
if (server.clientValue instanceof RegexProperty) {
|
||||
throw new IllegalStateException("You can't have a regular expression for the request on the server side")
|
||||
}
|
||||
return super.value(server, client)
|
||||
|
||||
@@ -23,7 +23,6 @@ import groovy.transform.EqualsAndHashCode
|
||||
import groovy.transform.ToString
|
||||
import groovy.transform.TypeChecked
|
||||
import groovy.util.logging.Commons
|
||||
import repackaged.nl.flotsam.xeger.Xeger
|
||||
|
||||
import org.springframework.cloud.contract.spec.util.RegexpUtils
|
||||
/**
|
||||
@@ -109,13 +108,12 @@ class Response extends Common {
|
||||
}
|
||||
|
||||
DslProperty value(ServerDslProperty server) {
|
||||
Object value = server.clientValue
|
||||
if (server.clientValue instanceof Pattern && server.isSingleValue()) {
|
||||
value = new Xeger(((Pattern)server.clientValue).pattern()).generate()
|
||||
} else if (server.clientValue instanceof Pattern && !server.isSingleValue()) {
|
||||
value = server.serverValue
|
||||
Object dynamicValue = server.serverValue
|
||||
Object concreteValue = server.clientValue
|
||||
if (dynamicValue instanceof RegexProperty && server.isSingleValue()) {
|
||||
return ((RegexProperty) dynamicValue).concreteClientDynamicProducer()
|
||||
}
|
||||
return new DslProperty(value, server.serverValue)
|
||||
return new DslProperty(concreteValue, dynamicValue)
|
||||
}
|
||||
|
||||
DslProperty $(ServerDslProperty server) {
|
||||
@@ -123,13 +121,26 @@ class Response extends Common {
|
||||
}
|
||||
|
||||
DslProperty value(Pattern server) {
|
||||
return value(new RegexProperty(server))
|
||||
}
|
||||
|
||||
DslProperty value(RegexProperty server) {
|
||||
return value(new ServerDslProperty(server))
|
||||
}
|
||||
|
||||
DslProperty $(Pattern server) {
|
||||
DslProperty $(RegexProperty server) {
|
||||
return value(server)
|
||||
}
|
||||
|
||||
DslProperty $(Pattern server) {
|
||||
return value(new RegexProperty(server))
|
||||
}
|
||||
|
||||
@Override
|
||||
RegexProperty regexProperty(Object object) {
|
||||
return new RegexProperty(object).concreteClientDynamicProducer()
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated Deprecated in favor of bodyMatchers to support other future bodyMatchers too
|
||||
*/
|
||||
@@ -151,7 +162,7 @@ class Response extends Common {
|
||||
|
||||
@Override
|
||||
DslProperty value(ClientDslProperty client, ServerDslProperty server) {
|
||||
if (client.clientValue instanceof Pattern) {
|
||||
if (client.clientValue instanceof RegexProperty) {
|
||||
throw new IllegalStateException("You can't have a regular expression for the response on the client side")
|
||||
}
|
||||
return super.value(client, server)
|
||||
@@ -159,7 +170,7 @@ class Response extends Common {
|
||||
|
||||
@Override
|
||||
DslProperty value(ServerDslProperty server, ClientDslProperty client) {
|
||||
if (client.clientValue instanceof Pattern) {
|
||||
if (client.clientValue instanceof RegexProperty) {
|
||||
throw new IllegalStateException("You can't have a regular expression for the response on the client side")
|
||||
}
|
||||
return super.value(server, client)
|
||||
|
||||
@@ -16,16 +16,12 @@
|
||||
|
||||
package org.springframework.cloud.contract.spec.internal
|
||||
|
||||
import java.util.regex.Pattern
|
||||
|
||||
import groovy.transform.CompileStatic
|
||||
import groovy.transform.EqualsAndHashCode
|
||||
import groovy.transform.ToString
|
||||
import org.codehaus.groovy.runtime.GStringImpl
|
||||
import repackaged.nl.flotsam.xeger.Xeger
|
||||
|
||||
import static org.springframework.cloud.contract.spec.util.ValidateUtils.validateServerValueIsAvailable
|
||||
|
||||
/**
|
||||
* Represents a URL that may contain query parameters
|
||||
*
|
||||
@@ -50,13 +46,13 @@ class Url extends DslProperty {
|
||||
|
||||
private static Object testUrl(Object url) {
|
||||
if (url instanceof GString) {
|
||||
boolean anyPattern = url.values.any { it instanceof Pattern }
|
||||
boolean anyPattern = url.values.any { it instanceof RegexProperty }
|
||||
if (!anyPattern) {
|
||||
return url
|
||||
}
|
||||
String newUrl = new GStringImpl(
|
||||
url.values.collect { it instanceof Pattern ?
|
||||
new Xeger(it.pattern()).generate() : it
|
||||
url.values.collect { it instanceof RegexProperty ?
|
||||
it.generate() : it
|
||||
} as String[],
|
||||
Arrays.copyOf(url.strings, url.strings.length) as String[]
|
||||
).toString()
|
||||
|
||||
@@ -14,7 +14,84 @@ class InputSpec extends Specification {
|
||||
input.with {
|
||||
property = $(consumer(regex("[0-9]{5}")))
|
||||
}
|
||||
def value = Integer.valueOf(property.serverValue as String)
|
||||
def generatedValue = property.serverValue
|
||||
generatedValue instanceof String
|
||||
def value = Integer.valueOf(generatedValue as String)
|
||||
then:
|
||||
value >= 0
|
||||
value <= 99_999
|
||||
}
|
||||
|
||||
def 'should set property when using the $() convenience method for Double'() {
|
||||
given:
|
||||
Input input = new Input()
|
||||
DslProperty property
|
||||
when:
|
||||
input.with {
|
||||
property = $(consumer(regex("[0-9]{5}").asDouble()))
|
||||
}
|
||||
def value = property.serverValue
|
||||
value instanceof Double
|
||||
then:
|
||||
value >= 0
|
||||
value <= 99_999
|
||||
}
|
||||
|
||||
def 'should set property when using the $() convenience method for Short'() {
|
||||
given:
|
||||
Input input = new Input()
|
||||
DslProperty property
|
||||
when:
|
||||
input.with {
|
||||
property = $(consumer(regex("[0-9]{1}").asShort()))
|
||||
}
|
||||
def value = property.serverValue
|
||||
value instanceof Short
|
||||
then:
|
||||
value >= 0
|
||||
value <= 9
|
||||
}
|
||||
|
||||
def 'should set property when using the $() convenience method for Long'() {
|
||||
given:
|
||||
Input input = new Input()
|
||||
DslProperty property
|
||||
when:
|
||||
input.with {
|
||||
property = $(consumer(regex("[0-9]{5}").asLong()))
|
||||
}
|
||||
def value = property.serverValue
|
||||
value instanceof Long
|
||||
then:
|
||||
value >= 0
|
||||
value <= 99_999
|
||||
}
|
||||
|
||||
def 'should set property when using the $() convenience method for Integer'() {
|
||||
given:
|
||||
Input input = new Input()
|
||||
DslProperty property
|
||||
when:
|
||||
input.with {
|
||||
property = $(consumer(regex("[0-9]{5}").asInteger()))
|
||||
}
|
||||
def value = property.serverValue
|
||||
value instanceof Integer
|
||||
then:
|
||||
value >= 0
|
||||
value <= 99_999
|
||||
}
|
||||
|
||||
def 'should set property when using the $() convenience method for Float'() {
|
||||
given:
|
||||
Input input = new Input()
|
||||
DslProperty property
|
||||
when:
|
||||
input.with {
|
||||
property = $(consumer(regex("[0-9]{5}").asFloat()))
|
||||
}
|
||||
def value = property.serverValue
|
||||
value instanceof Float
|
||||
then:
|
||||
value >= 0
|
||||
value <= 99_999
|
||||
|
||||
@@ -8,13 +8,90 @@ class OutputMessageSpec extends Specification {
|
||||
|
||||
def 'should set property when using the $() convenience method'() {
|
||||
given:
|
||||
Input input = new Input()
|
||||
OutputMessage contract = new OutputMessage()
|
||||
DslProperty property
|
||||
when:
|
||||
input.with {
|
||||
property = $(consumer(regex("[0-9]{5}")))
|
||||
contract.with {
|
||||
property = $(producer(regex("[0-9]{5}")))
|
||||
}
|
||||
def value = Integer.valueOf(property.serverValue as String)
|
||||
def generatedValue = property.clientValue
|
||||
generatedValue instanceof String
|
||||
def value = Integer.valueOf(generatedValue as String)
|
||||
then:
|
||||
value >= 0
|
||||
value <= 99_999
|
||||
}
|
||||
|
||||
def 'should set property when using the $() convenience method for Double'() {
|
||||
given:
|
||||
OutputMessage contract = new OutputMessage()
|
||||
DslProperty property
|
||||
when:
|
||||
contract.with {
|
||||
property = $(producer(regex("[0-9]{5}").asDouble()))
|
||||
}
|
||||
def value = property.clientValue
|
||||
value instanceof Double
|
||||
then:
|
||||
value >= 0
|
||||
value <= 99_999
|
||||
}
|
||||
|
||||
def 'should set property when using the $() convenience method for Short'() {
|
||||
given:
|
||||
OutputMessage contract = new OutputMessage()
|
||||
DslProperty property
|
||||
when:
|
||||
contract.with {
|
||||
property = $(producer(regex("[0-9]{1}").asShort()))
|
||||
}
|
||||
def value = property.clientValue
|
||||
value instanceof Short
|
||||
then:
|
||||
value >= 0
|
||||
value <= 9
|
||||
}
|
||||
|
||||
def 'should set property when using the $() convenience method for Long'() {
|
||||
given:
|
||||
OutputMessage contract = new OutputMessage()
|
||||
DslProperty property
|
||||
when:
|
||||
contract.with {
|
||||
property = $(producer(regex("[0-9]{5}").asLong()))
|
||||
}
|
||||
def value = property.clientValue
|
||||
value instanceof Long
|
||||
then:
|
||||
value >= 0
|
||||
value <= 99_999
|
||||
}
|
||||
|
||||
def 'should set property when using the $() convenience method for Integer'() {
|
||||
given:
|
||||
OutputMessage contract = new OutputMessage()
|
||||
DslProperty property
|
||||
when:
|
||||
contract.with {
|
||||
property = $(producer(regex("[0-9]{5}").asInteger()))
|
||||
}
|
||||
def value = property.clientValue
|
||||
value instanceof Integer
|
||||
then:
|
||||
value >= 0
|
||||
value <= 99_999
|
||||
}
|
||||
|
||||
def 'should set property when using the $() convenience method for Float'() {
|
||||
given:
|
||||
OutputMessage contract = new OutputMessage()
|
||||
DslProperty property
|
||||
when:
|
||||
contract.with {
|
||||
property = $(producer(regex("[0-9]{5}").asFloat()))
|
||||
}
|
||||
def value = property.clientValue
|
||||
value instanceof Float
|
||||
then:
|
||||
value >= 0
|
||||
value <= 99_999
|
||||
|
||||
@@ -8,30 +8,106 @@ class RequestSpec extends Specification {
|
||||
|
||||
def 'should throw exception when on request side a value contains regex for server'() {
|
||||
given:
|
||||
Request request = new Request()
|
||||
Request contract = new Request()
|
||||
when:
|
||||
request.with {
|
||||
contract.with {
|
||||
value(consumer("foo"), producer(regex("foo")))
|
||||
}
|
||||
then:
|
||||
thrown(IllegalStateException)
|
||||
when:
|
||||
request.with {
|
||||
contract.with {
|
||||
value(producer(regex("foo")), consumer("foo"))
|
||||
}
|
||||
then:
|
||||
thrown(IllegalStateException)
|
||||
}
|
||||
|
||||
def 'should generate a value if only regex is passed for client'() {
|
||||
def 'should set property when using the $() convenience method'() {
|
||||
given:
|
||||
Request request = new Request()
|
||||
Request contract = new Request()
|
||||
DslProperty property
|
||||
when:
|
||||
request.with {
|
||||
property = value(consumer(regex("[0-9]{5}")))
|
||||
contract.with {
|
||||
property = $(consumer(regex("[0-9]{5}")))
|
||||
}
|
||||
def value = Integer.valueOf(property.serverValue as String)
|
||||
def generatedValue = property.serverValue
|
||||
generatedValue instanceof String
|
||||
def value = Integer.valueOf(generatedValue as String)
|
||||
then:
|
||||
value >= 0
|
||||
value <= 99_999
|
||||
}
|
||||
|
||||
def 'should set property when using the $() convenience method for Double'() {
|
||||
given:
|
||||
Request contract = new Request()
|
||||
DslProperty property
|
||||
when:
|
||||
contract.with {
|
||||
property = $(consumer(regex("[0-9]{5}").asDouble()))
|
||||
}
|
||||
def value = property.serverValue
|
||||
value instanceof Double
|
||||
then:
|
||||
value >= 0
|
||||
value <= 99_999
|
||||
}
|
||||
|
||||
def 'should set property when using the $() convenience method for Short'() {
|
||||
given:
|
||||
Request contract = new Request()
|
||||
DslProperty property
|
||||
when:
|
||||
contract.with {
|
||||
property = $(consumer(regex("[0-9]{1}").asShort()))
|
||||
}
|
||||
def value = property.serverValue
|
||||
value instanceof Short
|
||||
then:
|
||||
value >= 0
|
||||
value <= 9
|
||||
}
|
||||
|
||||
def 'should set property when using the $() convenience method for Long'() {
|
||||
given:
|
||||
Request contract = new Request()
|
||||
DslProperty property
|
||||
when:
|
||||
contract.with {
|
||||
property = $(consumer(regex("[0-9]{5}").asLong()))
|
||||
}
|
||||
def value = property.serverValue
|
||||
value instanceof Long
|
||||
then:
|
||||
value >= 0
|
||||
value <= 99_999
|
||||
}
|
||||
|
||||
def 'should set property when using the $() convenience method for Integer'() {
|
||||
given:
|
||||
Request contract = new Request()
|
||||
DslProperty property
|
||||
when:
|
||||
contract.with {
|
||||
property = $(consumer(regex("[0-9]{5}").asInteger()))
|
||||
}
|
||||
def value = property.serverValue
|
||||
value instanceof Integer
|
||||
then:
|
||||
value >= 0
|
||||
value <= 99_999
|
||||
}
|
||||
|
||||
def 'should set property when using the $() convenience method for Float'() {
|
||||
given:
|
||||
Request contract = new Request()
|
||||
DslProperty property
|
||||
when:
|
||||
contract.with {
|
||||
property = $(consumer(regex("[0-9]{5}").asFloat()))
|
||||
}
|
||||
def value = property.serverValue
|
||||
value instanceof Float
|
||||
then:
|
||||
value >= 0
|
||||
value <= 99_999
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
package org.springframework.cloud.contract.spec.internal
|
||||
|
||||
import spock.lang.Specification
|
||||
|
||||
import java.util.regex.Pattern
|
||||
|
||||
import spock.lang.Specification
|
||||
/**
|
||||
* @author Marcin Grzejszczak
|
||||
*/
|
||||
@@ -26,18 +23,95 @@ class ResponseSpec extends Specification {
|
||||
thrown(IllegalStateException)
|
||||
}
|
||||
|
||||
def 'should generate a value if only regex is passed for server'() {
|
||||
|
||||
def 'should set property when using the $() convenience method'() {
|
||||
given:
|
||||
Response request = new Response()
|
||||
Response contract = new Response()
|
||||
DslProperty property
|
||||
when:
|
||||
request.with {
|
||||
property = value(producer(regex("[0-9]{5}")))
|
||||
contract.with {
|
||||
property = $(producer(regex("[0-9]{5}")))
|
||||
}
|
||||
def value = Integer.valueOf(property.clientValue as String)
|
||||
def generatedValue = property.clientValue
|
||||
generatedValue instanceof String
|
||||
def value = Integer.valueOf(generatedValue as String)
|
||||
then:
|
||||
value >= 0
|
||||
value <= 99_999
|
||||
}
|
||||
|
||||
def 'should set property when using the $() convenience method for Double'() {
|
||||
given:
|
||||
Response contract = new Response()
|
||||
DslProperty property
|
||||
when:
|
||||
contract.with {
|
||||
property = $(producer(regex("[0-9]{5}").asDouble()))
|
||||
}
|
||||
def value = property.clientValue
|
||||
value instanceof Double
|
||||
then:
|
||||
value >= 0
|
||||
value <= 99_999
|
||||
}
|
||||
|
||||
def 'should set property when using the $() convenience method for Short'() {
|
||||
given:
|
||||
Response contract = new Response()
|
||||
DslProperty property
|
||||
when:
|
||||
contract.with {
|
||||
property = $(producer(regex("[0-9]{1}").asShort()))
|
||||
}
|
||||
def value = property.clientValue
|
||||
value instanceof Short
|
||||
then:
|
||||
value >= 0
|
||||
value <= 9
|
||||
}
|
||||
|
||||
def 'should set property when using the $() convenience method for Long'() {
|
||||
given:
|
||||
Response contract = new Response()
|
||||
DslProperty property
|
||||
when:
|
||||
contract.with {
|
||||
property = $(producer(regex("[0-9]{5}").asLong()))
|
||||
}
|
||||
def value = property.clientValue
|
||||
value instanceof Long
|
||||
then:
|
||||
value >= 0
|
||||
value <= 99_999
|
||||
}
|
||||
|
||||
def 'should set property when using the $() convenience method for Integer'() {
|
||||
given:
|
||||
Response contract = new Response()
|
||||
DslProperty property
|
||||
when:
|
||||
contract.with {
|
||||
property = $(producer(regex("[0-9]{5}").asInteger()))
|
||||
}
|
||||
def value = property.clientValue
|
||||
value instanceof Integer
|
||||
then:
|
||||
value >= 0
|
||||
value <= 99_999
|
||||
}
|
||||
|
||||
def 'should set property when using the $() convenience method for Float'() {
|
||||
given:
|
||||
Response contract = new Response()
|
||||
DslProperty property
|
||||
when:
|
||||
contract.with {
|
||||
property = $(producer(regex("[0-9]{5}").asFloat()))
|
||||
}
|
||||
def value = property.clientValue
|
||||
value instanceof Float
|
||||
then:
|
||||
value >= 0
|
||||
value <= 99_999
|
||||
(property.serverValue as Pattern).pattern() == '[0-9]{5}'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@ import org.springframework.cloud.contract.spec.internal.BodyMatcher;
|
||||
import org.springframework.cloud.contract.spec.internal.BodyMatchers;
|
||||
import org.springframework.cloud.contract.spec.internal.FromFileProperty;
|
||||
import org.springframework.cloud.contract.spec.internal.Header;
|
||||
import org.springframework.cloud.contract.spec.internal.RegexProperty;
|
||||
import org.springframework.cloud.contract.verifier.messaging.internal.ContractVerifierObjectMapper;
|
||||
import org.springframework.cloud.contract.verifier.util.ContentType;
|
||||
import org.springframework.cloud.contract.verifier.util.ContentUtils;
|
||||
@@ -133,8 +134,8 @@ class StubRunnerCamelPredicate implements Predicate {
|
||||
BodyMatchers matchers = groovyDsl.getInput().getBodyMatchers();
|
||||
matches = matchesForJsonPayload(groovyDsl, inputMessage, matchers, dslBody);
|
||||
}
|
||||
else if (dslBody instanceof Pattern && inputMessage instanceof String) {
|
||||
Pattern pattern = (Pattern) dslBody;
|
||||
else if (dslBody instanceof RegexProperty && inputMessage instanceof String) {
|
||||
Pattern pattern = ((RegexProperty) dslBody).getPattern();
|
||||
matches = pattern.matcher((String) inputMessage).matches();
|
||||
bodyUnmatchedLog(dslBody, matches, pattern);
|
||||
} else {
|
||||
@@ -206,8 +207,8 @@ class StubRunnerCamelPredicate implements Predicate {
|
||||
Object value = it.getClientValue();
|
||||
Object valueInHeader = headers.get(name);
|
||||
boolean matches;
|
||||
if (value instanceof Pattern) {
|
||||
Pattern pattern = (Pattern) value;
|
||||
if (value instanceof RegexProperty) {
|
||||
Pattern pattern = ((RegexProperty) value).getPattern();
|
||||
matches = pattern.matcher(valueInHeader.toString()).matches();
|
||||
}
|
||||
else {
|
||||
@@ -225,8 +226,8 @@ class StubRunnerCamelPredicate implements Predicate {
|
||||
}
|
||||
|
||||
private String unmatchedText(Object expectedValue) {
|
||||
return expectedValue instanceof Pattern
|
||||
? "match pattern [" + ((Pattern) expectedValue).pattern() + "]"
|
||||
return expectedValue instanceof RegexProperty
|
||||
? "match pattern [" + ((RegexProperty) expectedValue).pattern() + "]"
|
||||
: "be equal to [" + expectedValue + "]";
|
||||
}
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ import org.springframework.cloud.contract.spec.internal.BodyMatcher;
|
||||
import org.springframework.cloud.contract.spec.internal.BodyMatchers;
|
||||
import org.springframework.cloud.contract.spec.internal.FromFileProperty;
|
||||
import org.springframework.cloud.contract.spec.internal.Header;
|
||||
import org.springframework.cloud.contract.spec.internal.RegexProperty;
|
||||
import org.springframework.cloud.contract.verifier.messaging.internal.ContractVerifierObjectMapper;
|
||||
import org.springframework.cloud.contract.verifier.util.ContentType;
|
||||
import org.springframework.cloud.contract.verifier.util.ContentUtils;
|
||||
@@ -151,8 +152,8 @@ class StubRunnerIntegrationMessageSelector implements MessageSelector {
|
||||
BodyMatchers matchers = groovyDsl.getInput().getBodyMatchers();
|
||||
matches = matchesForJsonPayload(groovyDsl, inputMessage, matchers, dslBody);
|
||||
}
|
||||
else if (dslBody instanceof Pattern && inputMessage instanceof String) {
|
||||
Pattern pattern = (Pattern) dslBody;
|
||||
else if (dslBody instanceof RegexProperty && inputMessage instanceof String) {
|
||||
Pattern pattern = ((RegexProperty) dslBody).getPattern();
|
||||
matches = pattern.matcher((String) inputMessage).matches();
|
||||
bodyUnmatchedLog(dslBody, matches, pattern);
|
||||
} else {
|
||||
@@ -224,8 +225,8 @@ class StubRunnerIntegrationMessageSelector implements MessageSelector {
|
||||
Object value = it.getClientValue();
|
||||
Object valueInHeader = headers.get(name);
|
||||
boolean matches;
|
||||
if (value instanceof Pattern) {
|
||||
Pattern pattern = (Pattern) value;
|
||||
if (value instanceof RegexProperty) {
|
||||
Pattern pattern = ((RegexProperty) value).getPattern();
|
||||
matches = pattern.matcher(valueInHeader.toString()).matches();
|
||||
}
|
||||
else {
|
||||
@@ -243,8 +244,8 @@ class StubRunnerIntegrationMessageSelector implements MessageSelector {
|
||||
}
|
||||
|
||||
private String unmatchedText(Object expectedValue) {
|
||||
return expectedValue instanceof Pattern
|
||||
? "match pattern [" + ((Pattern) expectedValue).pattern() + "]"
|
||||
return expectedValue instanceof RegexProperty
|
||||
? "match pattern [" + ((RegexProperty) expectedValue).pattern() + "]"
|
||||
: "be equal to [" + expectedValue + "]";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@ import org.springframework.cloud.contract.spec.internal.BodyMatcher;
|
||||
import org.springframework.cloud.contract.spec.internal.BodyMatchers;
|
||||
import org.springframework.cloud.contract.spec.internal.FromFileProperty;
|
||||
import org.springframework.cloud.contract.spec.internal.Header;
|
||||
import org.springframework.cloud.contract.spec.internal.RegexProperty;
|
||||
import org.springframework.cloud.contract.verifier.messaging.internal.ContractVerifierObjectMapper;
|
||||
import org.springframework.cloud.contract.verifier.util.ContentType;
|
||||
import org.springframework.cloud.contract.verifier.util.ContentUtils;
|
||||
@@ -150,8 +151,8 @@ class StubRunnerStreamMessageSelector implements MessageSelector {
|
||||
BodyMatchers matchers = groovyDsl.getInput().getBodyMatchers();
|
||||
matches = matchesForJsonPayload(groovyDsl, inputMessage, matchers, dslBody);
|
||||
}
|
||||
else if (dslBody instanceof Pattern && inputMessage instanceof String) {
|
||||
Pattern pattern = (Pattern) dslBody;
|
||||
else if (dslBody instanceof RegexProperty && inputMessage instanceof String) {
|
||||
Pattern pattern = ((RegexProperty) dslBody).getPattern();
|
||||
matches = pattern.matcher((String) inputMessage).matches();
|
||||
bodyUnmatchedLog(dslBody, matches, pattern);
|
||||
} else {
|
||||
@@ -223,8 +224,8 @@ class StubRunnerStreamMessageSelector implements MessageSelector {
|
||||
Object value = it.getClientValue();
|
||||
Object valueInHeader = headers.get(name);
|
||||
boolean matches;
|
||||
if (value instanceof Pattern) {
|
||||
Pattern pattern = (Pattern) value;
|
||||
if (value instanceof RegexProperty) {
|
||||
Pattern pattern = ((RegexProperty) value).getPattern();
|
||||
matches = pattern.matcher(valueInHeader.toString()).matches();
|
||||
}
|
||||
else {
|
||||
@@ -242,8 +243,8 @@ class StubRunnerStreamMessageSelector implements MessageSelector {
|
||||
}
|
||||
|
||||
private String unmatchedText(Object expectedValue) {
|
||||
return expectedValue instanceof Pattern
|
||||
? "match pattern [" + ((Pattern) expectedValue).pattern() + "]"
|
||||
return expectedValue instanceof RegexProperty
|
||||
? "match pattern [" + ((RegexProperty) expectedValue).pattern() + "]"
|
||||
: "be equal to [" + expectedValue + "]";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,12 +16,14 @@
|
||||
|
||||
package org.springframework.cloud.contract.verifier.wiremock
|
||||
|
||||
import org.springframework.cloud.contract.spec.Contract
|
||||
import org.springframework.cloud.contract.verifier.dsl.wiremock.WireMockStubMapping
|
||||
import org.springframework.cloud.contract.verifier.util.ContractVerifierDslConverter
|
||||
import java.util.regex.Pattern
|
||||
|
||||
import spock.lang.Specification
|
||||
|
||||
import java.util.regex.Pattern
|
||||
import org.springframework.cloud.contract.spec.Contract
|
||||
import org.springframework.cloud.contract.spec.internal.RegexProperty
|
||||
import org.springframework.cloud.contract.verifier.dsl.wiremock.WireMockStubMapping
|
||||
import org.springframework.cloud.contract.verifier.util.ContractVerifierDslConverter
|
||||
|
||||
class WireMockToDslConverterSpec extends Specification {
|
||||
|
||||
@@ -148,7 +150,7 @@ class WireMockToDslConverterSpec extends Specification {
|
||||
$groovyDsl
|
||||
}""")
|
||||
def b = expectedGroovyDsl
|
||||
(a.first().request.url.clientValue as Pattern).pattern() == (b.request.url.clientValue as Pattern).pattern()
|
||||
(a.first().request.url.clientValue as RegexProperty).pattern() == (b.request.url.clientValue as Pattern).pattern()
|
||||
}
|
||||
|
||||
def 'should convert WireMock stub with response body containing integer'() {
|
||||
@@ -401,7 +403,7 @@ class WireMockToDslConverterSpec extends Specification {
|
||||
$groovyDsl
|
||||
}""").first()
|
||||
and:
|
||||
(evaluatedGroovyDsl.request.body.clientValue as Pattern).pattern() == (expectedGroovyDsl.request.body.clientValue as Pattern).pattern()
|
||||
(evaluatedGroovyDsl.request.body.clientValue as RegexProperty).pattern() == (expectedGroovyDsl.request.body.clientValue as Pattern).pattern()
|
||||
}
|
||||
|
||||
def 'should convert WireMock stub with request body with equalToJson'() {
|
||||
@@ -522,7 +524,7 @@ class WireMockToDslConverterSpec extends Specification {
|
||||
$groovyDsl
|
||||
}""").first()
|
||||
and:
|
||||
(evaluatedGroovyDsl.request.body.clientValue as Pattern).pattern() == (expectedGroovyDsl.request.body.clientValue as Pattern).pattern()
|
||||
(evaluatedGroovyDsl.request.body.clientValue as RegexProperty).pattern() == (expectedGroovyDsl.request.body.clientValue as Pattern).pattern()
|
||||
}
|
||||
|
||||
def 'should convert WireMock stub with priorities'() {
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package org.springframework.cloud.contract.verifier.spec.pact
|
||||
|
||||
import java.util.regex.Pattern
|
||||
|
||||
import au.com.dius.pact.model.generators.Category
|
||||
import au.com.dius.pact.model.generators.DateGenerator
|
||||
import au.com.dius.pact.model.generators.DateTimeGenerator
|
||||
@@ -30,13 +32,13 @@ import au.com.dius.pact.model.generators.TimeGenerator
|
||||
import au.com.dius.pact.model.generators.UuidGenerator
|
||||
import groovy.transform.CompileStatic
|
||||
import groovy.transform.PackageScope
|
||||
|
||||
import org.springframework.cloud.contract.spec.internal.Body
|
||||
import org.springframework.cloud.contract.spec.internal.DslProperty
|
||||
import org.springframework.cloud.contract.spec.internal.OutputMessage
|
||||
import org.springframework.cloud.contract.spec.internal.RegexProperty
|
||||
import org.springframework.cloud.contract.verifier.util.ContentUtils
|
||||
|
||||
import java.util.regex.Pattern
|
||||
|
||||
/**
|
||||
* @author Tim Ysewyn
|
||||
* @Since 2.0.0
|
||||
@@ -129,8 +131,9 @@ class ValueGeneratorConverter {
|
||||
}
|
||||
} else if (v instanceof DslProperty) {
|
||||
traverse(v, dslPropertyValueProvider, path, generators, category)
|
||||
} else if (v instanceof Pattern) {
|
||||
switch (v.pattern()) {
|
||||
} else if (v instanceof RegexProperty || v instanceof Pattern) {
|
||||
RegexProperty regexProperty = new RegexProperty(v)
|
||||
switch (regexProperty.pattern()) {
|
||||
case INTEGER_PATTERN:
|
||||
generators.addGenerator(category, path, new RandomIntGenerator(0, Integer.MAX_VALUE))
|
||||
break
|
||||
@@ -159,7 +162,7 @@ class ValueGeneratorConverter {
|
||||
generators.addGenerator(category, path, RandomBooleanGenerator.INSTANCE)
|
||||
break
|
||||
default:
|
||||
generators.addGenerator(category, path, new RegexGenerator(v.pattern()))
|
||||
generators.addGenerator(category, path, new RegexGenerator(regexProperty.pattern()))
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ import org.springframework.cloud.contract.spec.internal.Header
|
||||
import org.springframework.cloud.contract.spec.internal.Input
|
||||
import org.springframework.cloud.contract.spec.internal.NamedProperty
|
||||
import org.springframework.cloud.contract.spec.internal.NotToEscapePattern
|
||||
import org.springframework.cloud.contract.spec.internal.RegexProperty
|
||||
import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties
|
||||
import org.springframework.cloud.contract.verifier.util.MapConverter
|
||||
|
||||
@@ -264,6 +265,10 @@ class JUnitMessagingMethodBodyBuilder extends MessagingMethodBodyBuilder {
|
||||
return ""
|
||||
}
|
||||
|
||||
protected String convertHeaderComparison(RegexProperty headerValue) {
|
||||
return convertHeaderComparison(headerValue.pattern)
|
||||
}
|
||||
|
||||
protected String createHeaderComparison(Object headerValue) {
|
||||
String escapedHeader = convertUnicodeEscapesIfRequired("$headerValue")
|
||||
return "isEqualTo(\"$escapedHeader\");"
|
||||
|
||||
@@ -41,6 +41,7 @@ import org.springframework.cloud.contract.spec.internal.MatchingType
|
||||
import org.springframework.cloud.contract.spec.internal.NamedProperty
|
||||
import org.springframework.cloud.contract.spec.internal.OptionalProperty
|
||||
import org.springframework.cloud.contract.spec.internal.QueryParameter
|
||||
import org.springframework.cloud.contract.spec.internal.RegexProperty
|
||||
import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties
|
||||
import org.springframework.cloud.contract.verifier.template.HandlebarsTemplateProcessor
|
||||
import org.springframework.cloud.contract.verifier.template.TemplateProcessor
|
||||
@@ -197,6 +198,13 @@ abstract class MethodBodyBuilder {
|
||||
*/
|
||||
protected abstract void processHeaderElement(BlockBuilder blockBuilder, String property, Pattern pattern)
|
||||
|
||||
/**
|
||||
* Appends to the {@link BlockBuilder} the assertion for the given header path
|
||||
*/
|
||||
protected void processHeaderElement(BlockBuilder blockBuilder, String property, RegexProperty regexProperty) {
|
||||
processHeaderElement(blockBuilder, property, regexProperty.pattern)
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends to the {@link BlockBuilder} the assertion for the given header path
|
||||
*/
|
||||
@@ -522,7 +530,7 @@ abstract class MethodBodyBuilder {
|
||||
protected void methodForEqualityCheck(BodyMatcher bodyMatcher, BlockBuilder bb, Object copiedBody) {
|
||||
String path = quotedAndEscaped(bodyMatcher.path())
|
||||
Object retrievedValue = value(copiedBody, bodyMatcher)
|
||||
retrievedValue = retrievedValue instanceof Pattern ? ((Pattern) retrievedValue).pattern() : retrievedValue
|
||||
retrievedValue = retrievedValue instanceof RegexProperty ? ((RegexProperty) retrievedValue).getPattern().pattern() : retrievedValue
|
||||
String valueAsParam = retrievedValue instanceof String ? quotedAndEscaped(retrievedValue.toString()) : retrievedValue.toString()
|
||||
if (arrayRelated(path) && MatchingType.regexRelated(bodyMatcher.matchingType())) {
|
||||
buildCustomMatchingConditionForEachElement(bb, path, valueAsParam)
|
||||
|
||||
@@ -27,6 +27,7 @@ import org.springframework.cloud.contract.spec.internal.Header
|
||||
import org.springframework.cloud.contract.spec.internal.Input
|
||||
import org.springframework.cloud.contract.spec.internal.NamedProperty
|
||||
import org.springframework.cloud.contract.spec.internal.NotToEscapePattern
|
||||
import org.springframework.cloud.contract.spec.internal.RegexProperty
|
||||
import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties
|
||||
import org.springframework.cloud.contract.verifier.util.MapConverter
|
||||
|
||||
@@ -251,6 +252,10 @@ class SpockMessagingMethodBodyBuilder extends MessagingMethodBodyBuilder {
|
||||
return " == '$headerValue'"
|
||||
}
|
||||
|
||||
protected String convertHeaderComparison(RegexProperty headerValue) {
|
||||
return convertHeaderComparison(headerValue.pattern)
|
||||
}
|
||||
|
||||
protected String convertHeaderComparison(Pattern headerValue) {
|
||||
String converted = escapeJava(convertUnicodeEscapesIfRequired(headerValue.pattern()))
|
||||
return "==~ java.util.regex.Pattern.compile('${converted}')"
|
||||
|
||||
@@ -24,6 +24,7 @@ import org.springframework.cloud.contract.spec.internal.Cookie
|
||||
import org.springframework.cloud.contract.spec.internal.FromFileProperty
|
||||
import org.springframework.cloud.contract.spec.internal.Header
|
||||
import org.springframework.cloud.contract.spec.internal.NamedProperty
|
||||
import org.springframework.cloud.contract.spec.internal.RegexProperty
|
||||
import org.springframework.cloud.contract.spec.internal.Request
|
||||
import org.springframework.cloud.contract.spec.internal.ExecutionProperty
|
||||
import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties
|
||||
@@ -187,10 +188,18 @@ abstract class SpockMethodRequestProcessingBodyBuilder extends RequestProcessing
|
||||
return patternComparison(headerValue)
|
||||
}
|
||||
|
||||
protected String convertHeaderComparison(RegexProperty headerValue) {
|
||||
return convertHeaderComparison(headerValue.pattern)
|
||||
}
|
||||
|
||||
protected String convertCookieComparison(String cookieValue) {
|
||||
return "== '$cookieValue'"
|
||||
}
|
||||
|
||||
protected String createBodyComparison(RegexProperty bodyValue) {
|
||||
return createBodyComparison(bodyValue.pattern)
|
||||
}
|
||||
|
||||
protected String createBodyComparison(Pattern bodyValue) {
|
||||
String patternAsString = bodyValue.pattern()
|
||||
return patternComparison(RegexpBuilders.buildGStringRegexpForTestSide(patternAsString)) + ";"
|
||||
|
||||
@@ -16,6 +16,8 @@ import org.springframework.cloud.contract.spec.internal.MatchingType
|
||||
import org.springframework.cloud.contract.spec.internal.Multipart
|
||||
import org.springframework.cloud.contract.spec.internal.NamedProperty
|
||||
import org.springframework.cloud.contract.spec.internal.NotToEscapePattern
|
||||
import org.springframework.cloud.contract.spec.internal.RegexProperty
|
||||
import org.springframework.cloud.contract.verifier.converter.YamlContract.RegexType
|
||||
import org.springframework.cloud.contract.verifier.util.JsonPaths
|
||||
import org.springframework.cloud.contract.verifier.util.JsonToJsonPathsConverter
|
||||
import org.springframework.cloud.contract.verifier.util.MapConverter
|
||||
@@ -141,12 +143,12 @@ class ContractsToYaml {
|
||||
)
|
||||
}
|
||||
Object url = contract.request.url?.clientValue
|
||||
request.matchers.url = url instanceof Pattern ?
|
||||
request.matchers.url = url instanceof RegexProperty ?
|
||||
new YamlContract.KeyValueMatcher(regex: url.pattern()) :
|
||||
url instanceof ExecutionProperty ?
|
||||
new YamlContract.KeyValueMatcher(command: url.toString()) : null
|
||||
Object urlPath = contract.request.urlPath?.clientValue
|
||||
request.matchers.url = urlPath instanceof Pattern ?
|
||||
request.matchers.url = urlPath instanceof RegexProperty ?
|
||||
new YamlContract.KeyValueMatcher(regex: urlPath.pattern()) :
|
||||
urlPath instanceof ExecutionProperty ?
|
||||
new YamlContract.KeyValueMatcher(command: urlPath.toString()) : null
|
||||
@@ -158,9 +160,9 @@ class ContractsToYaml {
|
||||
Object fileName = value.name?.clientValue
|
||||
Object fileContent = value.value?.clientValue
|
||||
Object contentType = value.contentType?.clientValue
|
||||
if (fileName instanceof Pattern ||
|
||||
fileContent instanceof Pattern ||
|
||||
contentType instanceof Pattern) {
|
||||
if (fileName instanceof RegexProperty ||
|
||||
fileContent instanceof RegexProperty ||
|
||||
contentType instanceof RegexProperty) {
|
||||
request.matchers.multipart.named << new YamlContract.MultipartNamedStubMatcher(
|
||||
paramName: key,
|
||||
fileName: valueMatcher(fileName),
|
||||
@@ -168,10 +170,12 @@ class ContractsToYaml {
|
||||
contentType: valueMatcher(contentType),
|
||||
)
|
||||
}
|
||||
} else if (value instanceof Pattern) {
|
||||
} else if (value instanceof RegexProperty || value instanceof Pattern) {
|
||||
RegexProperty property = new RegexProperty(value)
|
||||
request.matchers.multipart.params.add(new YamlContract.KeyValueMatcher(
|
||||
key: key,
|
||||
regex: value.pattern()
|
||||
regex: property.pattern(),
|
||||
regexType: regexType(property.clazz())
|
||||
))
|
||||
}
|
||||
}
|
||||
@@ -191,27 +195,56 @@ class ContractsToYaml {
|
||||
}
|
||||
|
||||
protected YamlContract.ValueMatcher valueMatcher(Object o) {
|
||||
return o instanceof Pattern ? new YamlContract.ValueMatcher(regex: o.pattern()) : null
|
||||
return o instanceof RegexProperty ? new YamlContract.ValueMatcher(regex: o.pattern()) : null
|
||||
}
|
||||
|
||||
protected void setInputBodyMatchers(DslProperty body, List<YamlContract.BodyStubMatcher> bodyMatchers) {
|
||||
def testSideValues = MapConverter.getTestSideValues(body)
|
||||
JsonPaths paths = new JsonToJsonPathsConverter().transformToJsonPathWithStubsSideValues(body)
|
||||
paths?.findAll { it.valueBeforeChecking() instanceof Pattern }?.each {
|
||||
Object element = JsonToJsonPathsConverter.readElement(testSideValues, it.keyBeforeChecking())
|
||||
bodyMatchers << new YamlContract.BodyStubMatcher(
|
||||
path: it.keyBeforeChecking(),
|
||||
type: YamlContract.StubMatcherType.by_regex,
|
||||
value: (it.valueBeforeChecking() as Pattern).pattern()
|
||||
value: (it.valueBeforeChecking() as Pattern).pattern(),
|
||||
regexType: regexType(element)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
protected RegexType regexType(Object from) {
|
||||
return regexType(from.class)
|
||||
}
|
||||
|
||||
protected RegexType regexType(Class clazz) {
|
||||
switch(clazz) {
|
||||
case Boolean:
|
||||
return RegexType.as_boolean
|
||||
case Long:
|
||||
return RegexType.as_long
|
||||
case Short:
|
||||
return RegexType.as_short
|
||||
case Integer:
|
||||
return RegexType.as_integer
|
||||
case Float:
|
||||
return RegexType.as_float
|
||||
case Double:
|
||||
return RegexType.as_double
|
||||
default:
|
||||
return RegexType.as_string
|
||||
}
|
||||
}
|
||||
|
||||
protected void setOutputBodyMatchers(DslProperty body, List<YamlContract.BodyTestMatcher> bodyMatchers) {
|
||||
def testSideValues = MapConverter.getTestSideValues(body)
|
||||
JsonPaths paths = new JsonToJsonPathsConverter().transformToJsonPathWithTestsSideValues(body)
|
||||
paths?.findAll { it.valueBeforeChecking() instanceof Pattern }?.each {
|
||||
Object element = JsonToJsonPathsConverter.readElement(testSideValues, it.keyBeforeChecking())
|
||||
bodyMatchers << new YamlContract.BodyTestMatcher(
|
||||
path: it.keyBeforeChecking(),
|
||||
type: YamlContract.TestMatcherType.by_regex,
|
||||
value: (it.valueBeforeChecking() as Pattern).pattern()
|
||||
value: (it.valueBeforeChecking() as Pattern).pattern(),
|
||||
regexType: regexType(element)
|
||||
)
|
||||
}
|
||||
if (body?.serverValue instanceof Pattern) {
|
||||
@@ -259,10 +292,12 @@ class ContractsToYaml {
|
||||
|
||||
protected void setInputHeadersMatchers(Headers headers, List<YamlContract.KeyValueMatcher> headerMatchers) {
|
||||
headers?.asStubSideMap()?.each { String key, Object value ->
|
||||
if (value instanceof Pattern) {
|
||||
if (value instanceof RegexProperty || value instanceof Pattern) {
|
||||
RegexProperty property = new RegexProperty(value)
|
||||
headerMatchers << new YamlContract.KeyValueMatcher(
|
||||
key: key,
|
||||
regex: value.pattern(),
|
||||
regex: property.pattern(),
|
||||
regexType: regexType(property.clazz())
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -270,10 +305,12 @@ class ContractsToYaml {
|
||||
|
||||
protected void setOutputHeadersMatchers(Headers headers, List<YamlContract.TestHeaderMatcher> headerMatchers) {
|
||||
headers?.asTestSideMap()?.each { String key, Object value ->
|
||||
if (value instanceof Pattern) {
|
||||
if (value instanceof RegexProperty || value instanceof Pattern) {
|
||||
RegexProperty property = new RegexProperty(value)
|
||||
headerMatchers << new YamlContract.TestHeaderMatcher(
|
||||
key: key,
|
||||
regex: value.pattern(),
|
||||
regex: property.pattern(),
|
||||
regexType: regexType(property.clazz())
|
||||
)
|
||||
} else if (value instanceof ExecutionProperty) {
|
||||
headerMatchers << new YamlContract.TestHeaderMatcher(
|
||||
@@ -283,7 +320,7 @@ class ContractsToYaml {
|
||||
} else if (value instanceof NotToEscapePattern) {
|
||||
headerMatchers << new YamlContract.TestHeaderMatcher(
|
||||
key: key,
|
||||
regex: value.serverValue.pattern(),
|
||||
regex: ((Pattern) value.serverValue).pattern(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,6 +107,12 @@ class YamlContract {
|
||||
public PredefinedRegex predefined
|
||||
public Integer minOccurrence
|
||||
public Integer maxOccurrence
|
||||
public RegexType regexType
|
||||
}
|
||||
|
||||
@CompileStatic
|
||||
enum RegexType {
|
||||
as_integer, as_double, as_float, as_long, as_short, as_boolean, as_string
|
||||
}
|
||||
|
||||
@CompileStatic
|
||||
@@ -145,6 +151,7 @@ class YamlContract {
|
||||
public Integer minOccurrence
|
||||
public Integer maxOccurrence
|
||||
public PredefinedRegex predefined
|
||||
public RegexType regexType
|
||||
}
|
||||
|
||||
@CompileStatic
|
||||
@@ -155,6 +162,7 @@ class YamlContract {
|
||||
public String regex
|
||||
public PredefinedRegex predefined
|
||||
public String command
|
||||
public RegexType regexType
|
||||
}
|
||||
|
||||
@CompileStatic
|
||||
@@ -181,6 +189,7 @@ class YamlContract {
|
||||
public String regex
|
||||
public String command
|
||||
public PredefinedRegex predefined
|
||||
public RegexType regexType
|
||||
}
|
||||
|
||||
@CompileStatic
|
||||
@@ -191,6 +200,7 @@ class YamlContract {
|
||||
public String regex
|
||||
public String command
|
||||
public PredefinedRegex predefined
|
||||
public RegexType regexType
|
||||
}
|
||||
|
||||
@CompileStatic
|
||||
|
||||
@@ -535,35 +535,35 @@ class YamlToContracts {
|
||||
RegexPatterns patterns = new RegexPatterns()
|
||||
switch (predefinedRegex) {
|
||||
case YamlContract.PredefinedRegex.only_alpha_unicode:
|
||||
return patterns.onlyAlphaUnicode()
|
||||
return patterns.onlyAlphaUnicode().pattern
|
||||
case YamlContract.PredefinedRegex.number:
|
||||
return patterns.number()
|
||||
return patterns.number().pattern
|
||||
case YamlContract.PredefinedRegex.any_double:
|
||||
return patterns.aDouble()
|
||||
return patterns.aDouble().pattern
|
||||
case YamlContract.PredefinedRegex.any_boolean:
|
||||
return patterns.anyBoolean()
|
||||
return patterns.anyBoolean().pattern
|
||||
case YamlContract.PredefinedRegex.ip_address:
|
||||
return patterns.ipAddress()
|
||||
return patterns.ipAddress().pattern
|
||||
case YamlContract.PredefinedRegex.hostname:
|
||||
return patterns.hostname()
|
||||
return patterns.hostname().pattern
|
||||
case YamlContract.PredefinedRegex.email:
|
||||
return patterns.email()
|
||||
return patterns.email().pattern
|
||||
case YamlContract.PredefinedRegex.url:
|
||||
return patterns.url()
|
||||
return patterns.url().pattern
|
||||
case YamlContract.PredefinedRegex.uuid:
|
||||
return patterns.uuid()
|
||||
return patterns.uuid().pattern
|
||||
case YamlContract.PredefinedRegex.iso_date:
|
||||
return patterns.isoDate()
|
||||
return patterns.isoDate().pattern
|
||||
case YamlContract.PredefinedRegex.iso_date_time:
|
||||
return patterns.isoDateTime()
|
||||
return patterns.isoDateTime().pattern
|
||||
case YamlContract.PredefinedRegex.iso_time:
|
||||
return patterns.isoTime()
|
||||
return patterns.isoTime().pattern
|
||||
case YamlContract.PredefinedRegex.iso_8601_with_offset:
|
||||
return patterns.iso8601WithOffset()
|
||||
return patterns.iso8601WithOffset().pattern
|
||||
case YamlContract.PredefinedRegex.non_empty:
|
||||
return patterns.nonEmpty()
|
||||
return patterns.nonEmpty().pattern
|
||||
case YamlContract.PredefinedRegex.non_blank:
|
||||
return patterns.nonBlank()
|
||||
return patterns.nonBlank().pattern
|
||||
default:
|
||||
throw new UnsupportedOperationException("The predefined regex [" + predefinedRegex + "] is unsupported. Use on of " + YamlContract.PredefinedRegex.values())
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@ import com.github.tomakehurst.wiremock.matching.StringValuePattern
|
||||
import com.github.tomakehurst.wiremock.matching.UrlPattern
|
||||
import groovy.json.JsonOutput
|
||||
import groovy.json.StringEscapeUtils
|
||||
import groovy.transform.CompileDynamic
|
||||
import groovy.transform.PackageScope
|
||||
import groovy.transform.TypeChecked
|
||||
import groovy.transform.TypeCheckingMode
|
||||
@@ -42,6 +41,7 @@ import org.springframework.cloud.contract.spec.internal.NamedProperty
|
||||
import org.springframework.cloud.contract.spec.internal.OptionalProperty
|
||||
import org.springframework.cloud.contract.spec.internal.QueryParameters
|
||||
import org.springframework.cloud.contract.spec.internal.RegexPatterns
|
||||
import org.springframework.cloud.contract.spec.internal.RegexProperty
|
||||
import org.springframework.cloud.contract.spec.internal.Request
|
||||
import org.springframework.cloud.contract.verifier.util.ContentType
|
||||
import org.springframework.cloud.contract.verifier.util.ContentUtils
|
||||
@@ -51,7 +51,6 @@ import org.springframework.cloud.contract.verifier.util.MapConverter
|
||||
|
||||
import static org.springframework.cloud.contract.verifier.util.RegexpBuilders.buildGStringRegexpForStubSide
|
||||
import static org.springframework.cloud.contract.verifier.util.RegexpBuilders.buildJSONRegexpMatch
|
||||
|
||||
/**
|
||||
* Converts a {@link Request} into {@link RequestPattern}
|
||||
*
|
||||
@@ -174,8 +173,8 @@ class WireMockRequestStubStrategy extends BaseWireMockStubStrategy {
|
||||
private UrlPattern urlPattern() {
|
||||
Object urlPath = urlPathOrUrlIfQueryPresent()
|
||||
if (urlPath) {
|
||||
if(urlPath instanceof Pattern) {
|
||||
return WireMock.urlPathMatching(getStubSideValue(urlPath.toString()) as String)
|
||||
if(urlPath instanceof Pattern || urlPath instanceof RegexProperty) {
|
||||
return WireMock.urlPathMatching(getStubSideValue(new RegexProperty(urlPath).pattern()) as String)
|
||||
} else {
|
||||
return WireMock.urlPathEqualTo(getStubSideValue(urlPath.toString()) as String)
|
||||
}
|
||||
@@ -184,8 +183,8 @@ class WireMockRequestStubStrategy extends BaseWireMockStubStrategy {
|
||||
throw new IllegalStateException("URL is required!")
|
||||
}
|
||||
Object url = getUrlIfGstring(request?.url?.clientValue)
|
||||
if (url instanceof Pattern) {
|
||||
return WireMock.urlMatching((url as Pattern).pattern())
|
||||
if (url instanceof Pattern || url instanceof RegexProperty) {
|
||||
return WireMock.urlMatching(new RegexProperty(url).pattern())
|
||||
}
|
||||
return WireMock.urlEqualTo(url.toString())
|
||||
}
|
||||
@@ -204,8 +203,12 @@ class WireMockRequestStubStrategy extends BaseWireMockStubStrategy {
|
||||
|
||||
private Object getUrlIfGstring(Object clientSide) {
|
||||
if (clientSide instanceof GString) {
|
||||
if (clientSide.values.any { getStubSideValue(it) instanceof Pattern }) {
|
||||
return Pattern.compile(getStubSideValue(clientSide).toString())
|
||||
if (clientSide.values.any {
|
||||
def value = getStubSideValue(it)
|
||||
return value instanceof Pattern || value instanceof RegexProperty
|
||||
}) {
|
||||
String string = getStubSideValue(clientSide).toString()
|
||||
return new RegexProperty(Pattern.compile(string))
|
||||
} else {
|
||||
return getStubSideValue(clientSide).toString()
|
||||
}
|
||||
@@ -224,8 +227,8 @@ class WireMockRequestStubStrategy extends BaseWireMockStubStrategy {
|
||||
private static ContentPattern convertToValuePattern(Object object, ContentType contentType) {
|
||||
switch (object) {
|
||||
case Pattern:
|
||||
Pattern value = object as Pattern
|
||||
return WireMock.matching(value.pattern())
|
||||
case RegexProperty:
|
||||
return WireMock.matching(new RegexProperty(object).pattern())
|
||||
case OptionalProperty:
|
||||
OptionalProperty value = object as OptionalProperty
|
||||
return WireMock.matching(value.optionalPattern())
|
||||
@@ -339,9 +342,8 @@ class WireMockRequestStubStrategy extends BaseWireMockStubStrategy {
|
||||
return containsPattern(map.entrySet())
|
||||
}
|
||||
|
||||
@CompileDynamic
|
||||
private boolean containsPattern(Collection collection) {
|
||||
return collection.collect(this.&containsPattern).inject('') { a, b -> a || b }
|
||||
return collection.collect(this.&containsPattern).inject(false) { a, b -> a || b }
|
||||
}
|
||||
|
||||
private boolean containsPattern(Object[] objects) {
|
||||
@@ -360,6 +362,10 @@ class WireMockRequestStubStrategy extends BaseWireMockStubStrategy {
|
||||
return true
|
||||
}
|
||||
|
||||
private boolean containsPattern(RegexProperty pattern) {
|
||||
return true
|
||||
}
|
||||
|
||||
private boolean containsPattern(Object o) {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -215,7 +215,7 @@ class DelegatingJsonVerifiable implements MethodBufferingJsonVerifiable {
|
||||
return readyToCheck;
|
||||
}
|
||||
|
||||
@Override public JsonVerifiable isInstanceOf(Class clazz)
|
||||
@Override public MethodBufferingJsonVerifiable isInstanceOf(Class clazz)
|
||||
throws IllegalStateException {
|
||||
DelegatingJsonVerifiable readyToCheck = new FinishedDelegatingJsonVerifiable(
|
||||
this.delegate.jsonPath(), this.delegate.isInstanceOf(clazz), this.methodsBuffer);
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
|
||||
package org.springframework.cloud.contract.verifier.util
|
||||
|
||||
import java.util.regex.Pattern
|
||||
|
||||
import com.jayway.jsonpath.DocumentContext
|
||||
import com.jayway.jsonpath.JsonPath
|
||||
import com.jayway.jsonpath.PathNotFoundException
|
||||
@@ -24,17 +26,15 @@ import groovy.json.JsonOutput
|
||||
import groovy.json.JsonSlurper
|
||||
import groovy.transform.CompileStatic
|
||||
import groovy.util.logging.Commons
|
||||
import org.apache.commons.lang3.StringEscapeUtils
|
||||
|
||||
import org.springframework.cloud.contract.spec.internal.BodyMatcher
|
||||
import org.springframework.cloud.contract.spec.internal.BodyMatchers
|
||||
import org.springframework.cloud.contract.spec.internal.ExecutionProperty
|
||||
import org.springframework.cloud.contract.spec.internal.MatchingType
|
||||
import org.springframework.cloud.contract.spec.internal.OptionalProperty
|
||||
import org.springframework.cloud.contract.spec.internal.RegexProperty
|
||||
import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties
|
||||
import org.springframework.util.SerializationUtils
|
||||
import repackaged.nl.flotsam.xeger.Xeger
|
||||
|
||||
import java.util.regex.Pattern
|
||||
/**
|
||||
* I would like to apologize to anyone who is reading this class. Since JSON is a hectic structure
|
||||
* this class is also hectic. The idea is to traverse the JSON structure and build a set of
|
||||
@@ -96,6 +96,18 @@ class JsonToJsonPathsConverter {
|
||||
return jsonCopy
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the value from JSON via json path
|
||||
*
|
||||
* @param json - parsed JSON
|
||||
* @param jsonPath - json path
|
||||
* @return matching part of the json
|
||||
*/
|
||||
static def readElement(def json, String jsonPath) {
|
||||
DocumentContext context = JsonPath.parse(json)
|
||||
return context.read(jsonPath)
|
||||
}
|
||||
|
||||
/**
|
||||
* Related to #391. The converted body looks different when done via the String notation than
|
||||
* it does when done via a map notation. When working with String body and when matchers
|
||||
@@ -169,8 +181,8 @@ class JsonToJsonPathsConverter {
|
||||
|
||||
@CompileStatic
|
||||
static Object generatedValueIfNeeded(Object value) {
|
||||
if (value instanceof Pattern) {
|
||||
return StringEscapeUtils.escapeJava(new Xeger(((Pattern) value).pattern()).generate())
|
||||
if (value instanceof RegexProperty) {
|
||||
return ((RegexProperty) value).generateAndEscapeJavaStringIfNeeded()
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
@@ -65,6 +65,9 @@ public interface MethodBufferingJsonVerifiable
|
||||
@Override
|
||||
MethodBufferingJsonVerifiable matches(String value);
|
||||
|
||||
@Override
|
||||
MethodBufferingJsonVerifiable isInstanceOf(Class clazz);
|
||||
|
||||
@Override
|
||||
MethodBufferingJsonVerifiable isEqualTo(Boolean value);
|
||||
|
||||
|
||||
@@ -1333,7 +1333,8 @@ DATA
|
||||
builder.appendTo(blockBuilder)
|
||||
def test = blockBuilder.toString()
|
||||
then:
|
||||
test.contains('''.cookie("cookie-key", "[A-Za-z]+")''')
|
||||
!test.contains('''.cookie("cookie-key", "[A-Za-z]+")''')
|
||||
test.contains('''.cookie("cookie-key", "''')
|
||||
test.contains('''assertThat(response.getCookies().get("cookie-key")).isNotNull();''')
|
||||
test.contains('''assertThat(response.getCookies().get("cookie-key").getValue()).matches("[A-Za-z]+");''')
|
||||
and:
|
||||
@@ -1376,7 +1377,8 @@ DATA
|
||||
builder.appendTo(blockBuilder)
|
||||
def test = blockBuilder.toString()
|
||||
then:
|
||||
test.contains('''.cookie('cookie-key', '[A-Za-z]+')''')
|
||||
!test.contains('''.cookie('cookie-key', '[A-Za-z]+')''')
|
||||
test.contains('''.cookie('cookie-key', ''')
|
||||
test.contains('''response.getCookies().get('cookie-key') != null''')
|
||||
test.contains('''response.getCookies().get('cookie-key').getValue() ==~ java.util.regex.Pattern.compile('[A-Za-z]+')''')
|
||||
and:
|
||||
|
||||
@@ -528,7 +528,7 @@ DocumentContext parsedJson = JsonPath.parse(json);
|
||||
body(
|
||||
[
|
||||
"name" : $(consumer(~/.+/), producer('string-1')),
|
||||
"updatedTs" : $(consumer(~/\d{13}/), producer(1531916906000L)),
|
||||
"updatedTs" : $(consumer(regex(~/1531916906000/).asLong())),
|
||||
"isDisabled": $(consumer(regex(anyBoolean())), producer(true))
|
||||
]
|
||||
)
|
||||
@@ -557,6 +557,7 @@ DocumentContext parsedJson = JsonPath.parse(json);
|
||||
String test = blockBuilder.toString()
|
||||
SyntaxChecker.tryToCompileWithoutCompileStatic(methodBuilderName, test)
|
||||
test.contains('''assertThatJson(parsedJson).field("['updatedTs']").isEqualTo(1531916906000L)''')
|
||||
!test.contains('''"updatedTs":"1531916906000"''')
|
||||
and:
|
||||
stubMappingIsValidWireMockStub(contractDsl)
|
||||
where:
|
||||
|
||||
@@ -62,12 +62,12 @@ class MockMvcMethodBodyBuilderWithMatchersSpec extends Specification implements
|
||||
]
|
||||
])
|
||||
bodyMatchers {
|
||||
jsonPath('$.duck', byRegex("[0-9]{3}"))
|
||||
jsonPath('$.duck', byRegex("[0-9]{3}").asInteger())
|
||||
jsonPath('$.duck', byEquality())
|
||||
jsonPath('$.alpha', byRegex(onlyAlphaUnicode()))
|
||||
jsonPath('$.alpha', byRegex(onlyAlphaUnicode()).asString())
|
||||
jsonPath('$.alpha', byEquality())
|
||||
jsonPath('$.number', byRegex(number()))
|
||||
jsonPath('$.aBoolean', byRegex(anyBoolean()))
|
||||
jsonPath('$.number', byRegex(number()).asInteger())
|
||||
jsonPath('$.aBoolean', byRegex(anyBoolean()).asBooleanType())
|
||||
jsonPath('$.date', byDate())
|
||||
jsonPath('$.dateTime', byTimestamp())
|
||||
jsonPath('$.time', byTime())
|
||||
@@ -111,18 +111,18 @@ class MockMvcMethodBodyBuilderWithMatchersSpec extends Specification implements
|
||||
])
|
||||
bodyMatchers {
|
||||
// asserts the jsonpath value against manual regex
|
||||
jsonPath('$.duck', byRegex("[0-9]{3}"))
|
||||
jsonPath('$.duck', byRegex("[0-9]{3}").asInteger())
|
||||
// asserts the jsonpath value against the provided value
|
||||
jsonPath('$.duck', byEquality())
|
||||
// asserts the jsonpath value against some default regex
|
||||
jsonPath('$.alpha', byRegex(onlyAlphaUnicode()))
|
||||
jsonPath('$.alpha', byRegex(onlyAlphaUnicode()).asString())
|
||||
jsonPath('$.alpha', byEquality())
|
||||
jsonPath('$.number', byRegex(number()))
|
||||
jsonPath('$.positiveInteger', byRegex(anInteger()))
|
||||
jsonPath('$.negativeInteger', byRegex(anInteger()))
|
||||
jsonPath('$.positiveDecimalNumber', byRegex(aDouble()))
|
||||
jsonPath('$.negativeDecimalNumber', byRegex(aDouble()))
|
||||
jsonPath('$.aBoolean', byRegex(anyBoolean()))
|
||||
jsonPath('$.number', byRegex(number()).asInteger())
|
||||
jsonPath('$.positiveInteger', byRegex(anInteger()).asInteger())
|
||||
jsonPath('$.negativeInteger', byRegex(anInteger()).asInteger())
|
||||
jsonPath('$.positiveDecimalNumber', byRegex(aDouble()).asDouble())
|
||||
jsonPath('$.negativeDecimalNumber', byRegex(aDouble()).asDouble())
|
||||
jsonPath('$.aBoolean', byRegex(anyBoolean()).asBooleanType())
|
||||
// asserts vs inbuilt time related regex
|
||||
jsonPath('$.date', byDate())
|
||||
jsonPath('$.dateTime', byTimestamp())
|
||||
|
||||
@@ -2794,7 +2794,8 @@ DocumentContext parsedJson = JsonPath.parse(json);
|
||||
builder.appendTo(blockBuilder)
|
||||
def test = blockBuilder.toString()
|
||||
then:
|
||||
test.contains('''.cookie("cookie-key", "[A-Za-z]+")''')
|
||||
!test.contains('''.cookie("cookie-key", "[A-Za-z]+")''')
|
||||
test.contains('''.cookie("cookie-key", "''')
|
||||
test.contains('''assertThat(response.getCookie("cookie-key")).isNotNull();''')
|
||||
test.contains('''assertThat(response.getCookie("cookie-key")).matches("[A-Za-z]+");''')
|
||||
and:
|
||||
@@ -2845,7 +2846,8 @@ DocumentContext parsedJson = JsonPath.parse(json);
|
||||
builder.appendTo(blockBuilder)
|
||||
def test = blockBuilder.toString()
|
||||
then:
|
||||
test.contains('''.cookie("cookie-key", "[A-Za-z]+")''')
|
||||
!test.contains('''.cookie("cookie-key", "[A-Za-z]+")''')
|
||||
test.contains('''.cookie("cookie-key", "''')
|
||||
test.contains('''response.cookie('cookie-key') != null''')
|
||||
test.contains('''response.cookie('cookie-key') ==~ java.util.regex.Pattern.compile('[A-Za-z]+')''')
|
||||
and:
|
||||
|
||||
@@ -540,6 +540,7 @@ response:
|
||||
- path: $.property2
|
||||
type: by_regex
|
||||
value: "[0-9]{3}"
|
||||
regexType: as_integer
|
||||
'''
|
||||
Contract contractDsl = fromYaml(contract)
|
||||
MethodBodyBuilder builder = methodBuilder(contractDsl)
|
||||
@@ -929,10 +930,12 @@ request:
|
||||
headers:
|
||||
- key: 'Content-Type'
|
||||
regex: 'application/json.*'
|
||||
regexType: as_string
|
||||
body:
|
||||
- path: $.first_name
|
||||
type: by_regex
|
||||
value: '[\\p{L}]*'
|
||||
regexType: as_string
|
||||
- path: $.last_name
|
||||
type: by_regex
|
||||
value: '[\\p{L}]*'
|
||||
@@ -1127,8 +1130,10 @@ request:
|
||||
params:
|
||||
- key: formParameter
|
||||
regex: ".+"
|
||||
regexType: as_string
|
||||
- key: someBooleanParameter
|
||||
predefined: any_boolean
|
||||
regexType: as_boolean
|
||||
named:
|
||||
- paramName: file
|
||||
fileName:
|
||||
@@ -1191,6 +1196,7 @@ response:
|
||||
- path: $.authorities[0]
|
||||
type: by_regex
|
||||
value: '^[a-zA-Z0-9_\\- ]+$'
|
||||
regexType: as_string
|
||||
'''
|
||||
Contract contractDsl = fromYaml(contract)
|
||||
MethodBodyBuilder builder = new HttpSpockMethodRequestProcessingBodyBuilder(contractDsl, properties, generatedClassDataForMethod)
|
||||
|
||||
@@ -33,7 +33,6 @@ import org.springframework.cloud.contract.spec.internal.RegexPatterns
|
||||
import org.springframework.cloud.contract.spec.internal.Url
|
||||
import org.springframework.cloud.contract.verifier.util.ContractVerifierDslConverter
|
||||
import org.springframework.cloud.contract.verifier.util.MapConverter
|
||||
|
||||
/**
|
||||
* @author Marcin Grzejszczak
|
||||
* @author Tim Ysewyn
|
||||
@@ -125,7 +124,7 @@ class YamlContractConverterSpec extends Specification {
|
||||
contract.request.body.clientValue == [foo: "bar"]
|
||||
contract.request.bodyMatchers.jsonPathRegexMatchers[0].path() == '$.foo'
|
||||
contract.request.bodyMatchers.jsonPathRegexMatchers[0].matchingType() == MatchingType.REGEX
|
||||
contract.request.bodyMatchers.jsonPathRegexMatchers[0].value() == 'bar'
|
||||
contract.request.bodyMatchers.jsonPathRegexMatchers[0].value().pattern() == 'bar'
|
||||
and:
|
||||
contract.response.status.clientValue == 200
|
||||
if (yamlFile == ymlWithRest) contract.response.delay.clientValue == 1000 else !contract.response.delay
|
||||
@@ -138,7 +137,7 @@ class YamlContractConverterSpec extends Specification {
|
||||
contract.response.body.clientValue == [foo2: "bar", foo3: "baz", nullValue: null]
|
||||
contract.response.bodyMatchers.jsonPathRegexMatchers[0].path() == '$.foo2'
|
||||
contract.response.bodyMatchers.jsonPathRegexMatchers[0].matchingType() == MatchingType.REGEX
|
||||
contract.response.bodyMatchers.jsonPathRegexMatchers[0].value() == 'bar'
|
||||
contract.response.bodyMatchers.jsonPathRegexMatchers[0].value().pattern() == 'bar'
|
||||
contract.response.bodyMatchers.jsonPathRegexMatchers[1].path() == '$.foo3'
|
||||
contract.response.bodyMatchers.jsonPathRegexMatchers[1].matchingType() == MatchingType.COMMAND
|
||||
contract.response.bodyMatchers.jsonPathRegexMatchers[1].value() == new ExecutionProperty('executeMe($it)')
|
||||
@@ -217,29 +216,29 @@ class YamlContractConverterSpec extends Specification {
|
||||
MatchingStrategy.Type.ABSENT, null)
|
||||
contract.request.bodyMatchers.jsonPathRegexMatchers[0].path() == '$.duck'
|
||||
contract.request.bodyMatchers.jsonPathRegexMatchers[0].matchingType() == MatchingType.REGEX
|
||||
contract.request.bodyMatchers.jsonPathRegexMatchers[0].value() == '[0-9]{3}'
|
||||
contract.request.bodyMatchers.jsonPathRegexMatchers[0].value().pattern() == '[0-9]{3}'
|
||||
contract.request.bodyMatchers.jsonPathRegexMatchers[1].path() == '$.duck'
|
||||
contract.request.bodyMatchers.jsonPathRegexMatchers[1].matchingType() == MatchingType.EQUALITY
|
||||
contract.request.bodyMatchers.jsonPathRegexMatchers[2].path() == '$.alpha'
|
||||
contract.request.bodyMatchers.jsonPathRegexMatchers[2].matchingType() == MatchingType.REGEX
|
||||
contract.request.bodyMatchers.jsonPathRegexMatchers[2].value() == patterns.onlyAlphaUnicode().pattern()
|
||||
contract.request.bodyMatchers.jsonPathRegexMatchers[2].value().pattern() == patterns.onlyAlphaUnicode().pattern()
|
||||
contract.request.bodyMatchers.jsonPathRegexMatchers[3].path() == '$.alpha'
|
||||
contract.request.bodyMatchers.jsonPathRegexMatchers[3].matchingType() == MatchingType.EQUALITY
|
||||
contract.request.bodyMatchers.jsonPathRegexMatchers[4].path() == '$.number'
|
||||
contract.request.bodyMatchers.jsonPathRegexMatchers[4].matchingType() == MatchingType.REGEX
|
||||
contract.request.bodyMatchers.jsonPathRegexMatchers[4].value() == patterns.number().pattern()
|
||||
contract.request.bodyMatchers.jsonPathRegexMatchers[4].value().pattern() == patterns.number().pattern()
|
||||
contract.request.bodyMatchers.jsonPathRegexMatchers[5].path() == '$.aBoolean'
|
||||
contract.request.bodyMatchers.jsonPathRegexMatchers[5].matchingType() == MatchingType.REGEX
|
||||
contract.request.bodyMatchers.jsonPathRegexMatchers[5].value() == patterns.anyBoolean().pattern()
|
||||
contract.request.bodyMatchers.jsonPathRegexMatchers[5].value().pattern() == patterns.anyBoolean().pattern()
|
||||
contract.request.bodyMatchers.jsonPathRegexMatchers[6].path() == '$.date'
|
||||
contract.request.bodyMatchers.jsonPathRegexMatchers[6].matchingType() == MatchingType.DATE
|
||||
contract.request.bodyMatchers.jsonPathRegexMatchers[6].value() == patterns.isoDate()
|
||||
contract.request.bodyMatchers.jsonPathRegexMatchers[6].value().pattern() == patterns.isoDate().pattern()
|
||||
contract.request.bodyMatchers.jsonPathRegexMatchers[7].path() == '$.dateTime'
|
||||
contract.request.bodyMatchers.jsonPathRegexMatchers[7].matchingType() == MatchingType.TIMESTAMP
|
||||
contract.request.bodyMatchers.jsonPathRegexMatchers[7].value() == patterns.isoDateTime()
|
||||
contract.request.bodyMatchers.jsonPathRegexMatchers[7].value().pattern() == patterns.isoDateTime().pattern()
|
||||
contract.request.bodyMatchers.jsonPathRegexMatchers[8].path() == '$.time'
|
||||
contract.request.bodyMatchers.jsonPathRegexMatchers[8].matchingType() == MatchingType.TIME
|
||||
contract.request.bodyMatchers.jsonPathRegexMatchers[8].value() == patterns.isoTime()
|
||||
contract.request.bodyMatchers.jsonPathRegexMatchers[8].value().pattern() == patterns.isoTime().pattern()
|
||||
contract.request.bodyMatchers.jsonPathRegexMatchers[9].path() == "\$.['key'].['complex.key']"
|
||||
contract.request.bodyMatchers.jsonPathRegexMatchers[9].matchingType() == MatchingType.EQUALITY
|
||||
contract.request.bodyMatchers.jsonPathRegexMatchers[10].path() == '$.valueWithMin'
|
||||
@@ -258,29 +257,29 @@ class YamlContractConverterSpec extends Specification {
|
||||
contract.response.status.clientValue == 200
|
||||
contract.response.bodyMatchers.jsonPathRegexMatchers[0].path() == '$.duck'
|
||||
contract.response.bodyMatchers.jsonPathRegexMatchers[0].matchingType() == MatchingType.REGEX
|
||||
contract.response.bodyMatchers.jsonPathRegexMatchers[0].value() == '[0-9]{3}'
|
||||
contract.response.bodyMatchers.jsonPathRegexMatchers[0].value().pattern() == '[0-9]{3}'
|
||||
contract.response.bodyMatchers.jsonPathRegexMatchers[1].path() == '$.duck'
|
||||
contract.response.bodyMatchers.jsonPathRegexMatchers[1].matchingType() == MatchingType.EQUALITY
|
||||
contract.response.bodyMatchers.jsonPathRegexMatchers[2].path() == '$.alpha'
|
||||
contract.response.bodyMatchers.jsonPathRegexMatchers[2].matchingType() == MatchingType.REGEX
|
||||
contract.response.bodyMatchers.jsonPathRegexMatchers[2].value() == patterns.onlyAlphaUnicode().pattern()
|
||||
contract.response.bodyMatchers.jsonPathRegexMatchers[2].value().pattern() == patterns.onlyAlphaUnicode().pattern()
|
||||
contract.response.bodyMatchers.jsonPathRegexMatchers[3].path() == '$.alpha'
|
||||
contract.response.bodyMatchers.jsonPathRegexMatchers[3].matchingType() == MatchingType.EQUALITY
|
||||
contract.response.bodyMatchers.jsonPathRegexMatchers[4].path() == '$.number'
|
||||
contract.response.bodyMatchers.jsonPathRegexMatchers[4].matchingType() == MatchingType.REGEX
|
||||
contract.response.bodyMatchers.jsonPathRegexMatchers[4].value() == patterns.number().pattern()
|
||||
contract.response.bodyMatchers.jsonPathRegexMatchers[4].value().pattern() == patterns.number().pattern()
|
||||
contract.response.bodyMatchers.jsonPathRegexMatchers[5].path() == '$.aBoolean'
|
||||
contract.response.bodyMatchers.jsonPathRegexMatchers[5].matchingType() == MatchingType.REGEX
|
||||
contract.response.bodyMatchers.jsonPathRegexMatchers[5].value() == patterns.anyBoolean().pattern()
|
||||
contract.response.bodyMatchers.jsonPathRegexMatchers[5].value().pattern() == patterns.anyBoolean().pattern()
|
||||
contract.response.bodyMatchers.jsonPathRegexMatchers[6].path() == '$.date'
|
||||
contract.response.bodyMatchers.jsonPathRegexMatchers[6].matchingType() == MatchingType.DATE
|
||||
contract.response.bodyMatchers.jsonPathRegexMatchers[6].value() == patterns.isoDate()
|
||||
contract.response.bodyMatchers.jsonPathRegexMatchers[6].value().pattern() == patterns.isoDate().pattern()
|
||||
contract.response.bodyMatchers.jsonPathRegexMatchers[7].path() == '$.dateTime'
|
||||
contract.response.bodyMatchers.jsonPathRegexMatchers[7].matchingType() == MatchingType.TIMESTAMP
|
||||
contract.response.bodyMatchers.jsonPathRegexMatchers[7].value() == patterns.isoDateTime()
|
||||
contract.response.bodyMatchers.jsonPathRegexMatchers[7].value().pattern() == patterns.isoDateTime().pattern()
|
||||
contract.response.bodyMatchers.jsonPathRegexMatchers[8].path() == '$.time'
|
||||
contract.response.bodyMatchers.jsonPathRegexMatchers[8].matchingType() == MatchingType.TIME
|
||||
contract.response.bodyMatchers.jsonPathRegexMatchers[8].value() == patterns.isoTime()
|
||||
contract.response.bodyMatchers.jsonPathRegexMatchers[8].value().pattern() == patterns.isoTime().pattern()
|
||||
contract.response.bodyMatchers.jsonPathRegexMatchers[9].path() == '$.valueWithTypeMatch'
|
||||
contract.response.bodyMatchers.jsonPathRegexMatchers[9].matchingType() == MatchingType.TYPE
|
||||
contract.response.bodyMatchers.jsonPathRegexMatchers[10].path() == '$.valueWithMin'
|
||||
@@ -329,57 +328,57 @@ class YamlContractConverterSpec extends Specification {
|
||||
((Pattern) it.clientValue).pattern == "application/json.*" && it.serverValue == "application/json" }
|
||||
contract.input.bodyMatchers.jsonPathRegexMatchers[0].path() == '$.duck'
|
||||
contract.input.bodyMatchers.jsonPathRegexMatchers[0].matchingType() == MatchingType.REGEX
|
||||
contract.input.bodyMatchers.jsonPathRegexMatchers[0].value() == '[0-9]{3}'
|
||||
contract.input.bodyMatchers.jsonPathRegexMatchers[0].value().pattern() == '[0-9]{3}'
|
||||
contract.input.bodyMatchers.jsonPathRegexMatchers[1].path() == '$.duck'
|
||||
contract.input.bodyMatchers.jsonPathRegexMatchers[1].matchingType() == MatchingType.EQUALITY
|
||||
contract.input.bodyMatchers.jsonPathRegexMatchers[2].path() == '$.alpha'
|
||||
contract.input.bodyMatchers.jsonPathRegexMatchers[2].matchingType() == MatchingType.REGEX
|
||||
contract.input.bodyMatchers.jsonPathRegexMatchers[2].value() == patterns.onlyAlphaUnicode().pattern()
|
||||
contract.input.bodyMatchers.jsonPathRegexMatchers[2].value().pattern() == patterns.onlyAlphaUnicode().pattern()
|
||||
contract.input.bodyMatchers.jsonPathRegexMatchers[3].path() == '$.alpha'
|
||||
contract.input.bodyMatchers.jsonPathRegexMatchers[3].matchingType() == MatchingType.EQUALITY
|
||||
contract.input.bodyMatchers.jsonPathRegexMatchers[4].path() == '$.number'
|
||||
contract.input.bodyMatchers.jsonPathRegexMatchers[4].matchingType() == MatchingType.REGEX
|
||||
contract.input.bodyMatchers.jsonPathRegexMatchers[4].value() == patterns.number().pattern()
|
||||
contract.input.bodyMatchers.jsonPathRegexMatchers[4].value().pattern() == patterns.number().pattern()
|
||||
contract.input.bodyMatchers.jsonPathRegexMatchers[5].path() == '$.aBoolean'
|
||||
contract.input.bodyMatchers.jsonPathRegexMatchers[5].matchingType() == MatchingType.REGEX
|
||||
contract.input.bodyMatchers.jsonPathRegexMatchers[5].value() == patterns.anyBoolean().pattern()
|
||||
contract.input.bodyMatchers.jsonPathRegexMatchers[5].value().pattern() == patterns.anyBoolean().pattern()
|
||||
contract.input.bodyMatchers.jsonPathRegexMatchers[6].path() == '$.date'
|
||||
contract.input.bodyMatchers.jsonPathRegexMatchers[6].matchingType() == MatchingType.DATE
|
||||
contract.input.bodyMatchers.jsonPathRegexMatchers[6].value() == patterns.isoDate()
|
||||
contract.input.bodyMatchers.jsonPathRegexMatchers[6].value().pattern() == patterns.isoDate().pattern()
|
||||
contract.input.bodyMatchers.jsonPathRegexMatchers[7].path() == '$.dateTime'
|
||||
contract.input.bodyMatchers.jsonPathRegexMatchers[7].matchingType() == MatchingType.TIMESTAMP
|
||||
contract.input.bodyMatchers.jsonPathRegexMatchers[7].value() == patterns.isoDateTime()
|
||||
contract.input.bodyMatchers.jsonPathRegexMatchers[7].value().pattern() == patterns.isoDateTime().pattern()
|
||||
contract.input.bodyMatchers.jsonPathRegexMatchers[8].path() == '$.time'
|
||||
contract.input.bodyMatchers.jsonPathRegexMatchers[8].matchingType() == MatchingType.TIME
|
||||
contract.input.bodyMatchers.jsonPathRegexMatchers[8].value() == patterns.isoTime()
|
||||
contract.input.bodyMatchers.jsonPathRegexMatchers[8].value().pattern() == patterns.isoTime().pattern()
|
||||
contract.input.bodyMatchers.jsonPathRegexMatchers[9].path() == "\$.['key'].['complex.key']"
|
||||
contract.input.bodyMatchers.jsonPathRegexMatchers[9].matchingType() == MatchingType.EQUALITY
|
||||
and:
|
||||
contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[0].path() == '$.duck'
|
||||
contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[0].matchingType() == MatchingType.REGEX
|
||||
contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[0].value() == '[0-9]{3}'
|
||||
contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[0].value().pattern() == '[0-9]{3}'
|
||||
contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[1].path() == '$.duck'
|
||||
contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[1].matchingType() == MatchingType.EQUALITY
|
||||
contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[2].path() == '$.alpha'
|
||||
contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[2].matchingType() == MatchingType.REGEX
|
||||
contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[2].value() == patterns.onlyAlphaUnicode().pattern()
|
||||
contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[2].value().pattern() == patterns.onlyAlphaUnicode().pattern()
|
||||
contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[3].path() == '$.alpha'
|
||||
contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[3].matchingType() == MatchingType.EQUALITY
|
||||
contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[4].path() == '$.number'
|
||||
contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[4].matchingType() == MatchingType.REGEX
|
||||
contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[4].value() == patterns.number().pattern()
|
||||
contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[4].value().pattern() == patterns.number().pattern()
|
||||
contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[5].path() == '$.aBoolean'
|
||||
contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[5].matchingType() == MatchingType.REGEX
|
||||
contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[5].value() == patterns.anyBoolean().pattern()
|
||||
contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[5].value().pattern() == patterns.anyBoolean().pattern()
|
||||
contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[6].path() == '$.date'
|
||||
contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[6].matchingType() == MatchingType.DATE
|
||||
contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[6].value() == patterns.isoDate()
|
||||
contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[6].value().pattern() == patterns.isoDate().pattern()
|
||||
contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[7].path() == '$.dateTime'
|
||||
contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[7].matchingType() == MatchingType.TIMESTAMP
|
||||
contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[7].value() == patterns.isoDateTime()
|
||||
contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[7].value().pattern() == patterns.isoDateTime().pattern()
|
||||
contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[8].path() == '$.time'
|
||||
contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[8].matchingType() == MatchingType.TIME
|
||||
contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[8].value() == patterns.isoTime()
|
||||
contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[8].value().pattern() == patterns.isoTime().pattern()
|
||||
contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[9].path() == '$.valueWithTypeMatch'
|
||||
contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[9].matchingType() == MatchingType.TYPE
|
||||
contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[10].path() == '$.valueWithMin'
|
||||
@@ -462,7 +461,7 @@ class YamlContractConverterSpec extends Specification {
|
||||
contract.input.messageBody.clientValue == [foo: "bar"]
|
||||
contract.input.bodyMatchers.jsonPathRegexMatchers[0].path() == '$.bar'
|
||||
contract.input.bodyMatchers.jsonPathRegexMatchers[0].matchingType() == MatchingType.REGEX
|
||||
contract.input.bodyMatchers.jsonPathRegexMatchers[0].value() == 'bar'
|
||||
contract.input.bodyMatchers.jsonPathRegexMatchers[0].value().pattern() == 'bar'
|
||||
and:
|
||||
contract.outputMessage.assertThat.toString() == "baz()"
|
||||
contract.outputMessage.headers.entries.find { it.name == "foo2" &&
|
||||
@@ -474,7 +473,7 @@ class YamlContractConverterSpec extends Specification {
|
||||
contract.outputMessage.body.clientValue == [foo2: "bar", foo3: "baz"]
|
||||
contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[0].path() == '$.foo2'
|
||||
contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[0].matchingType() == MatchingType.REGEX
|
||||
contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[0].value() == 'bar'
|
||||
contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[0].value().pattern() == 'bar'
|
||||
contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[1].path() == '$.foo3'
|
||||
contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[1].matchingType() == MatchingType.COMMAND
|
||||
contract.outputMessage.bodyMatchers.jsonPathRegexMatchers[1].value() == new ExecutionProperty('executeMe($it)')
|
||||
|
||||
@@ -906,7 +906,7 @@ class WireMockGroovyDslSpec extends Specification implements WireMockStubVerifie
|
||||
stubMappingIsValidWireMockStub(json)
|
||||
}
|
||||
|
||||
def "should not allow regexp in url for server value"() {
|
||||
def "should not allow not matching query param for server value"() {
|
||||
when:
|
||||
org.springframework.cloud.contract.spec.Contract.make {
|
||||
request {
|
||||
@@ -924,7 +924,7 @@ class WireMockGroovyDslSpec extends Specification implements WireMockStubVerifie
|
||||
}
|
||||
then:
|
||||
def e = thrown(IllegalStateException)
|
||||
e.message.contains "Url can't be a pattern for the server side"
|
||||
e.message.contains "Query parameter 'age' can't be of a matching type: NOT_MATCHING for the server side"
|
||||
}
|
||||
|
||||
def "should not allow regexp in query parameter for server value"() {
|
||||
|
||||
Reference in New Issue
Block a user