Fixed invalid escaping of characters for regex

without this change some regex contained invalid characters. Even though the code compiles the regular expressions passed to json assert are invalid.
with this change that gets fixed and tests are added. We're now checking if the code can be executed and not only compiled. We also check if WireMock fed with the stubs generated from these cases works fine and not only do we check if stub mappings are properly generated.

fixes #250
This commit is contained in:
Marcin Grzejszczak
2017-04-14 09:40:15 +02:00
parent f5d1a72359
commit 5843234fa1
12 changed files with 349 additions and 40 deletions

View File

@@ -49,7 +49,7 @@ abstract class PatternValueDslProperty<T extends DslProperty> {
}
T anyIpAddress() {
return createAndValidateProperty(RegexPatterns.IP_ADDRESS)
return createAndValidateProperty(RegexPatterns.IP_ADDRESS, "192.168.0." + this.random.nextInt(10))
}
T anyHostname() {

View File

@@ -37,9 +37,9 @@ class RegexPatterns {
protected static final Pattern ONLY_ALPHA_UNICODE = Pattern.compile(/[\p{L}]*/)
protected static final Pattern NUMBER = Pattern.compile('-?\\d*(\\.\\d+)?')
protected static final Pattern IP_ADDRESS = Pattern.compile('([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])')
protected static final Pattern HOSTNAME_PATTERN = Pattern.compile('((http[s]?|ftp):\\/)\\/?([^:\\/\\s]+)(:[0-9]{1,5})?')
protected static final Pattern EMAIL = Pattern.compile('[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,4}');
protected static final Pattern URL = Pattern.compile('((www\\.|(http|https|ftp|news|file)+\\:\\/\\/)[_.a-z0-9-]+\\.[a-z0-9\\/_:@=.+?,##%&~-]*[^.|\\\'|\\# |!|\\(|?|,| |>|<|;|\\)])')
protected static final Pattern HOSTNAME_PATTERN = Pattern.compile('((http[s]?|ftp):/)/?([^:/\\s]+)(:[0-9]{1,5})?')
protected static final Pattern EMAIL = Pattern.compile('[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,4}')
protected static final Pattern URL = UrlHelper.URL
protected static final Pattern UUID = Pattern.compile('[a-z0-9]{8}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{12}')
protected static final Pattern ANY_DATE = Pattern.compile('(\\d\\d\\d\\d)-(0[1-9]|1[012])-(0[1-9]|[12][0-9]|3[01])')
protected static final Pattern ANY_DATE_TIME = Pattern.compile('([0-9]{4})-(1[0-2]|0[1-9])-(3[01]|0[1-9]|[12][0-9])T(2[0-3]|[01][0-9]):([0-5][0-9]):([0-5][0-9])')
@@ -118,3 +118,45 @@ class RegexPatterns {
return ".*--(.*)\r\nContent-Disposition: form-data; name=\"$name\"; filename=\"$filename\"\r\n(Content-Type: .*\r\n)?(Content-Length: \\d+\r\n)?\r\n$content\r\n--\\1.*";
}
}
/**
* Taken from https://gist.github.com/skeller88/5eb73dc0090d4ff1249a
*/
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
* 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.
*/
private static final String REGEX_SCHEME = "[A-Za-z][+-.\\w^_]*:"
// Example: "//".
private static final String REGEX_AUTHORATIVE_DECLARATION = "/{2}"
// Optional component. Example: "suzie:abc123@". The use of the format "user:password" is deprecated.
private static final String REGEX_USERINFO = "(?:\\S+(?::\\S*)?@)?"
// Examples: "fitbit.com", "22.231.113.64".
private static final String REGEX_HOST = "(?:" +
// @Author = http://www.regular-expressions.info/examples.html
// IP address
"(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)" +
"|" +
// host name
"(?:(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)" +
// domain name
"(?:\\.(?:[a-z\\u00a1-\\uffff0-9]-*)*[a-z\\u00a1-\\uffff0-9]+)*" +
// TLD identifier must have >= 2 characters
"(?:\\.(?:[a-z\\u00a1-\\uffff]{2,})))"
// Example: ":8042".
private static final String REGEX_PORT = "(?::\\d{2,5})?"
//Example: "/user/heartrate?foo=bar#element1".
private static final String REGEX_RESOURCE_PATH = "(?:/\\S*)?"
protected static final Pattern URL = Pattern.compile("^(?:(?:" + REGEX_SCHEME + REGEX_AUTHORATIVE_DECLARATION + ")?" +
REGEX_USERINFO + REGEX_HOST + REGEX_PORT + REGEX_RESOURCE_PATH + ")\$")
}

View File

@@ -55,13 +55,76 @@ class RegexPatternsSpec extends Specification {
'a.b.' || false
}
// @see http://formvalidation.io/validators/uri/
def "should generate a regex for url [#textToMatch] that is a match [#shouldMatch]"() {
expect:
shouldMatch == Pattern.compile(regexPatterns.url()).matcher(textToMatch).matches()
where:
textToMatch || shouldMatch
'ftp://asd.com:9090/asd/a?a=b' || true
'a.b.' || false
textToMatch || shouldMatch
'ftp://asd.com:9090/asd/a?a=b' || true
'http://foo.com/blah_blah' || true
'http://foo.com/blah_blah/' || true
'http://foo.com/blah_blah_(wikipedia)' || true
'http://foo.com/blah_blah_(wikipedia)_(again)' || true
'http://www.example.com/wpstyle/?p=364' || true
'https://www.example.com/foo/?bar=baz&inga=42&quux' || true
'http://✪df.ws/123' || true
'http://userid:password@example.com:8080' || true
'http://userid:password@example.com:8080/' || true
'http://userid@example.com' || true
'http://userid@example.com/' || true
'http://userid@example.com:8080' || true
'http://userid@example.com:8080/' || true
'http://userid:password@example.com' || true
'http://userid:password@example.com/' || true
'http://142.42.1.1/' || true
'http://142.42.1.1:8080/' || true
'http://⌘.ws' || true
'http://⌘.ws/' || true
'http://foo.com/blah_(wikipedia)#cite-1' || true
'http://foo.com/blah_(wikipedia)_blah#cite-1' || true
'http://foo.com/unicode_(✪)_in_parens' || true
'http://foo.com/(something)?after=parens' || true
'http://☺.damowmow.com/' || true
'http://code.google.com/events/#&product=browser' || true
'http://j.mp' || true
'ftp://foo.bar/baz' || true
'http://foo.bar/?q=Test%20URL-encoded%20stuff' || true
'http://1337.net' || true
'http://a.b-c.de' || true
'http://223.255.255.254' || true
'foo.com' || true
'a.b.' || false
'http://' || false
'http://.' || false
'http://..' || false
'http://../' || false
'http://?' || false
'http://??' || false
'http://??/' || false
'http://#' || false
'http://##' || false
'http://##/' || false
'http://foo.bar?q=Spaces should be encoded' || false
'//' || false
'//a' || false
'///a' || false
'///' || false
'http:///a' || false
'rdar://1234' || false
'h://test' || false
'http:// shouldfail.com' || false
':// should fail' || false
'http://foo.bar/foo(bar)baz quux' || false
'http://-error-.invalid/' || false
'http://-a.b.co' || false
'http://a.b-.co' || false
'http://1.1.1.1.1' || false
'http://123.123.123' || false
'http://3628126748' || false
'http://.www.foo.bar/' || false
'http://www.foo.bar./' || false
'http://.www.foo.bar./' || false
}
def "should generate a regex for a number [#textToMatch] that is a match [#shouldMatch]"() {

View File

@@ -49,6 +49,27 @@
<artifactId>spock-global-unroll</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-contract-wiremock</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>

View File

@@ -16,13 +16,18 @@
package org.springframework.cloud.contract.verifier.wiremock
import com.github.tomakehurst.wiremock.junit.WireMockRule
import com.github.tomakehurst.wiremock.matching.RegexPattern
import com.github.tomakehurst.wiremock.stubbing.StubMapping
import groovy.json.JsonOutput
import org.junit.Rule
import org.junit.rules.TemporaryFolder
import org.skyscreamer.jsonassert.JSONAssert
import org.springframework.boot.test.web.client.TestRestTemplate
import org.springframework.cloud.contract.verifier.dsl.wiremock.WireMockStubMapping
import org.springframework.cloud.contract.verifier.file.ContractMetadata
import org.springframework.http.RequestEntity
import org.springframework.util.SocketUtils
import spock.lang.Issue
import spock.lang.Specification
@@ -30,8 +35,15 @@ import java.util.regex.Pattern
class DslToWireMockClientConverterSpec extends Specification {
@Rule
public TemporaryFolder tmpFolder = new TemporaryFolder()
static int port = SocketUtils.findAvailableTcpPort()
@Rule public WireMockRule wireMockRule = new WireMockRule(port)
@Rule public TemporaryFolder tmpFolder = new TemporaryFolder()
TestRestTemplate restTemplate = new TestRestTemplate()
String url
def setup() {
url = "http://localhost:${port}"
}
def "should convert DSL file to WireMock JSON"() {
given:
@@ -56,7 +68,11 @@ class DslToWireMockClientConverterSpec extends Specification {
{"request":{"method":"PUT","urlPattern":"/[0-9]{2}"},"response":{"status":200}}
''', json, false)
and:
stubMappingIsValidWireMockStub(json)
StubMapping mapping = stubMappingIsValidWireMockStub(json)
and:
wireMockRule.addStubMapping(mapping)
and:
restTemplate.exchange(RequestEntity.put("${url}/12".toURI()).body(""), String)
}
@Issue("196")
@@ -89,7 +105,11 @@ class DslToWireMockClientConverterSpec extends Specification {
}}
''', json, false)
and:
stubMappingIsValidWireMockStub(json)
StubMapping mapping = stubMappingIsValidWireMockStub(json)
and:
wireMockRule.addStubMapping(mapping)
and:
restTemplate.exchange(RequestEntity.get("${url}/foo".toURI()).build(), String)
}
def "should convert DSL file with a nested list to WireMock JSON"() {
@@ -104,7 +124,6 @@ class DslToWireMockClientConverterSpec extends Specification {
url '/api/12'
headers {
header 'Content-Type': 'application/vnd.org.springframework.cloud.contract.verifier.twitter-places-analyzer.v1+json'
}
body '''
[{
@@ -195,10 +214,43 @@ class DslToWireMockClientConverterSpec extends Specification {
}
''', json, false)
and:
stubMappingIsValidWireMockStub(json)
StubMapping mapping = stubMappingIsValidWireMockStub(json)
and:
wireMockRule.addStubMapping(mapping)
and:
restTemplate.exchange(RequestEntity.put("${url}/api/12".toURI())
.header('Content-Type', 'application/vnd.org.springframework.cloud.contract.verifier.twitter-places-analyzer.v1+json')
.body('''
[{
"created_at": "Sat Jul 26 09:38:57 +0000 2014",
"id": 492967299297845248,
"id_str": "492967299297845248",
"text": "Gonna see you at Warsaw",
"place":
{
"attributes":{},
"bounding_box":
{
"coordinates":
[[
[-77.119759,38.791645],
[-76.909393,38.791645],
[-76.909393,38.995548],
[-77.119759,38.995548]
]],
"type":"Polygon"
},
"country":"United States",
"country_code":"US",
"full_name":"Washington, DC",
"id":"01fbe706f872cb32",
"name":"Washington",
"place_type":"city",
"url": "http://api.twitter.com/1/geo/id/01fbe706f872cb32.json"
}
}]'''), String)
}
@Issue("262")
def "should create stub with map inside list"() {
given:
@@ -233,7 +285,13 @@ class DslToWireMockClientConverterSpec extends Specification {
{"request":{"urlPath":"/foos","method":"GET"},"response":{"body":"[{\\"id\\":\\"123\\"},{\\"id\\":\\"567\\"}]"}}
''', json, false)
and:
stubMappingIsValidWireMockStub(json)
StubMapping mapping = stubMappingIsValidWireMockStub(json)
and:
wireMockRule.addStubMapping(mapping)
and:
def response = restTemplate.exchange(RequestEntity.get("${url}/foos".toURI()).build(), String)
response.headers.get('Content-Type') == ['application/json']
JSONAssert.assertEquals('''[ { "id":"123" }, { "id": "567" } ]''', response.body, false)
}
@@ -265,7 +323,12 @@ class DslToWireMockClientConverterSpec extends Specification {
and:
!json.contains('cursor')
and:
stubMappingIsValidWireMockStub(json)
StubMapping mapping = stubMappingIsValidWireMockStub(json)
and:
wireMockRule.addStubMapping(mapping)
and:
def response = restTemplate.exchange(RequestEntity.get("${url}/foos".toURI()).build(), String)
response.body
}
def 'should convert dsl to wiremock to show it in the docs'() {
@@ -332,7 +395,17 @@ class DslToWireMockClientConverterSpec extends Specification {
// end::wiremock[]
, json, false)
and:
stubMappingIsValidWireMockStub(json)
StubMapping mapping = stubMappingIsValidWireMockStub(json)
and:
wireMockRule.addStubMapping(mapping)
and:
def response = restTemplate.exchange(RequestEntity.post("${url}/users/password".toURI())
.header("Content-Type", "application/json")
.body('''{"email":"abc@abc.com", "callback_url":"http://partners.com"}''')
, String)
response.headers.get('Content-Type') == ['application/json']
response.statusCodeValue == 404
JSONAssert.assertEquals('''{"code":"123123","message":"User not found by email == [not.existing@user.com]"}"''', response.body, false)
}
def 'should convert dsl to wiremock with stub matchers'() {
@@ -343,7 +416,7 @@ class DslToWireMockClientConverterSpec extends Specification {
file.write('''
org.springframework.cloud.contract.spec.Contract.make {
request {
method 'GET'
method 'POST'
urlPath '/get'
body([
duck: 123,
@@ -451,7 +524,7 @@ class DslToWireMockClientConverterSpec extends Specification {
{
"request" : {
"urlPath" : "/get",
"method" : "GET",
"method" : "POST",
"headers" : {
"Content-Type" : {
"matches" : "application/json.*"
@@ -501,7 +574,60 @@ class DslToWireMockClientConverterSpec extends Specification {
//end::matchers[]
, json, false)
and:
stubMappingIsValidWireMockStub(json)
StubMapping mapping = stubMappingIsValidWireMockStub(json)
and:
wireMockRule.addStubMapping(mapping)
and:
def response = restTemplate.exchange(RequestEntity.post("${url}/get".toURI())
.header("Content-Type", "application/json")
.body(JsonOutput.toJson([
duck: 123,
alpha: "abc",
number: 123,
aBoolean: true,
date: "2017-01-01",
dateTime: "2017-01-01T01:23:45",
time: "01:02:34",
valueWithoutAMatcher: "foo",
valueWithTypeMatch: "string",
list: [
some: [
nested: [
json: "with value",
anothervalue: 4
]
],
someother: [
nested: [
json: "with value",
anothervalue: 4
]
]
]
]))
, String)
response.headers.get('Content-Type') == ['application/json']
response.statusCodeValue == 200
JSONAssert.assertEquals(JsonOutput.toJson([
duck: 123,
alpha: "abc",
number: 123,
aBoolean: true,
date: "2017-01-01",
dateTime: "2017-01-01T01:23:45",
time: "01:02:34",
valueWithoutAMatcher: "foo",
valueWithTypeMatch: "string",
valueWithMin: [
1,2,3
],
valueWithMax: [
1,2,3
],
valueWithMinMax: [
1,2,3
],
]), response.body, false)
}
def 'should convert dsl to wiremock with stub matchers with docs example'() {
@@ -514,7 +640,7 @@ class DslToWireMockClientConverterSpec extends Specification {
priority 1
request {
method 'POST'
url '/users/password'
url '/users/password2'
headers {
header 'Content-Type': 'application/json'
}
@@ -550,7 +676,7 @@ class DslToWireMockClientConverterSpec extends Specification {
'''
{
"request" : {
"url" : "/users/password",
"url" : "/users/password2",
"method" : "POST",
"bodyPatterns" : [ {
"matchesJsonPath" : "$[?(@.email =~ /([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\\\.[a-zA-Z]{2,4})/)]"
@@ -575,15 +701,26 @@ class DslToWireMockClientConverterSpec extends Specification {
'''
, json, false)
and:
stubMappingIsValidWireMockStub(json)
StubMapping mapping = stubMappingIsValidWireMockStub(json)
and:
wireMockRule.addStubMapping(mapping)
and:
def response = restTemplate.exchange(RequestEntity.post("${url}/users/password2".toURI())
.header("Content-Type", "application/json")
.body('''{"email":"abc@abc.com", "callback_url":"http://partners.com"}''')
, String)
response.headers.get('Content-Type') == ['application/json']
response.statusCodeValue == 404
JSONAssert.assertEquals('''{"code":"123123","message":"User not found by email == [not.existing@user.com]"}"''', response.body, false)
}
void stubMappingIsValidWireMockStub(String mappingDefinition) {
StubMapping stubMappingIsValidWireMockStub(String mappingDefinition) {
StubMapping stubMapping = WireMockStubMapping.buildFrom(mappingDefinition)
stubMapping.request.bodyPatterns.findAll { it.isPresent() && it instanceof RegexPattern }.every {
Pattern.compile(it.getValue())
}
assert !mappingDefinition.contains('org.springframework.cloud.contract.spec.internal')
return stubMapping
}
}

View File

@@ -16,13 +16,13 @@
package org.springframework.cloud.contract.verifier.util;
import static org.apache.commons.lang3.StringEscapeUtils.escapeJava;
import java.util.LinkedList;
import java.util.regex.Pattern;
import com.toomuchcoding.jsonassert.JsonVerifiable;
import static org.apache.commons.lang3.StringEscapeUtils.escapeJava;
/**
* Implementation of the {@link MethodBufferingJsonVerifiable} that contains a list
* of String method commands that need to be executed to assert JSONs.
@@ -212,7 +212,8 @@ class DelegatingJsonVerifiable implements MethodBufferingJsonVerifiable {
* an double escaped text. Related to https://github.com/spring-cloud/spring-cloud-contract/issues/169
*/
private String escapedHackedJavaText(String value) {
return escapeJava(value).replace("\\\"", "\"");
return escapeJava(value)
.replace("\\\"", "\"");
}
@Override

View File

@@ -133,7 +133,8 @@ class JsonToJsonPathsConverter {
throw new IllegalStateException("Value [${bodyMatcher.path()}] not found in JSON [${JsonOutput.toJson(body)}]", e)
}
} else {
return "=~ /(${value})/"
String convertedValue = value.toString().replace('/', '\\\\/')
return "=~ /(${convertedValue})/"
}
}

View File

@@ -58,7 +58,7 @@ class MapConverter {
}
} catch (Exception ignore) {
}
return extractValue(value, closure);
return extractValue(value, closure)
} else if (value instanceof Map) {
return convert(value as Map, closure)
} else if (value instanceof List) {
@@ -77,7 +77,7 @@ class MapConverter {
if (newValue instanceof Map || newValue instanceof List || newValue instanceof String && value) {
return transformValues(newValue, closure)
}
return newValue;
return newValue
})
}

View File

@@ -1039,8 +1039,8 @@ class JaxRsClientMethodBuilderSpec extends Specification implements WireMockStub
then:
test.contains('assertThatJson(parsedJson).field("aBoolean").matches("(true|false)")')
test.contains('assertThatJson(parsedJson).field("alpha").matches("[\\\\p{L}]*")')
test.contains('assertThatJson(parsedJson).field("hostname").matches("((http[s]?|ftp):\\\\/)\\\\/?([^:\\\\/\\\\s]+)(:[0-9]{1,5})?")')
test.contains('assertThatJson(parsedJson).field("url").matches("((www\\\\.|(http|https|ftp|news|file)+\\\\:\\\\/\\\\/)[_.a-z0-9-]+\\\\.[a-z0-9\\\\/_:@=.+?,##%&~-]*[^.|\\\\\'|\\\\# |!|\\\\(|?|,| |>|<|;|\\\\)])")')
test.contains('assertThatJson(parsedJson).field("hostname").matches("((http[s]?|ftp):/)/?([^:/\\\\s]+)(:[0-9]{1,5})?")')
test.contains('assertThatJson(parsedJson).field("url").matches("^(?:(?:[A-Za-z][+-.\\\\w^_]*:/{2})?(?:\\\\S+(?::\\\\S*)?@)?(?:(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)|(?:(?:[a-z\\\\u00a1-\\\\uffff0-9]-*)*[a-z\\\\u00a1-\\\\uffff0-9]+)(?:\\\\.(?:[a-z\\\\u00a1-\\\\uffff0-9]-*)*[a-z\\\\u00a1-\\\\uffff0-9]+)*(?:\\\\.(?:[a-z\\\\u00a1-\\\\uffff]{2,})))(?::\\\\d{2,5})?(?:/\\\\S*)?)')
test.contains('assertThatJson(parsedJson).field("number").matches("-?\\\\d*(\\\\.\\\\d+)?")')
test.contains('assertThatJson(parsedJson).field("email").matches("[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\\\.[a-zA-Z]{2,4}")')
test.contains('assertThatJson(parsedJson).field("ip").matches("([01]?\\\\d\\\\d?|2[0-4]\\\\d|25[0-5])\\\\.([01]?\\\\d\\\\d?|2[0-4]\\\\d|25[0-5])\\\\.([01]?\\\\d\\\\d?|2[0-4]\\\\d|25[0-5])\\\\.([01]?\\\\d\\\\d?|2[0-4]\\\\d|25[0-5])")')
@@ -1053,8 +1053,19 @@ class JaxRsClientMethodBuilderSpec extends Specification implements WireMockStub
test.contains('assertThatJson(parsedJson).field("nonEmptyString").matches(".+")')
test.contains('assertThatJson(parsedJson).field("anyOf").matches("^foo' + endOfLineRegexSymbol + '|^bar' + endOfLineRegexSymbol + '")')
!test.contains('cursor')
!test.contains('REGEXP>>')
and:
SyntaxChecker.tryToCompile(methodBuilderName, blockBuilder.toString())
and:
String jsonSample = '''\
String json = "{\\"duck\\":\\"8\\",\\"alpha\\":\\"YAJEOWYGMFBEWPMEMAZI\\",\\"number\\":-2095030871,\\"aBoolean\\":true,\\"ip\\":\\"129.168.99.100\\",\\"hostname\\":\\"http://foo389886219.com\\",\\"email\\":\\"foo@bar1367573183.com\\",\\"url\\":\\"http://foo-597104692.com\\",\\"uuid\\":\\"e436b817-b764-49a2-908e-967f2f99eb9f\\",\\"date\\":\\"2014-04-14\\",\\"dateTime\\":\\"2011-01-11T12:23:34\\",\\"time\\":\\"12:20:30\\",\\"iso8601WithOffset\\":\\"2015-05-15T12:23:34.123Z\\",\\"nonBlankString\\":\\"EPZWVIRHSUAPBJMMQSFO\\",\\"nonEmptyString\\":\\"RVMFDSEQFHRQFVUVQPIA\\",\\"anyOf\\":\\"foo\\"}";
DocumentContext parsedJson = JsonPath.parse(json);
'''
and:
LinkedList<String> lines = [] as LinkedList<String>
test.eachLine { if (it.contains("assertThatJson")) lines << it else it }
lines.addFirst(jsonSample)
SyntaxChecker.tryToRun(methodBuilderName, lines.join("\n"))
where:
methodBuilderName | methodBuilder | endOfLineRegexSymbol
"JaxRsClientSpockMethodRequestProcessingBodyBuilder"| { org.springframework.cloud.contract.spec.Contract dsl -> new JaxRsClientSpockMethodRequestProcessingBodyBuilder(dsl, properties) } | '\\$'

View File

@@ -1880,7 +1880,7 @@ World.'''"""
}
@Issue('#149')
def "should allow easier way of providing dynamic values for [#methodBuilderName]"() {
def "should allow easier way of providing dynamic values for [#methodBuilderName]"() {
given:
Contract contractDsl = Contract.make {
request {
@@ -1940,8 +1940,8 @@ World.'''"""
then:
test.contains('assertThatJson(parsedJson).field("aBoolean").matches("(true|false)")')
test.contains('assertThatJson(parsedJson).field("alpha").matches("[\\\\p{L}]*")')
test.contains('assertThatJson(parsedJson).field("hostname").matches("((http[s]?|ftp):\\\\/)\\\\/?([^:\\\\/\\\\s]+)(:[0-9]{1,5})?")')
test.contains('assertThatJson(parsedJson).field("url").matches("((www\\\\.|(http|https|ftp|news|file)+\\\\:\\\\/\\\\/)[_.a-z0-9-]+\\\\.[a-z0-9\\\\/_:@=.+?,##%&~-]*[^.|\\\\\'|\\\\# |!|\\\\(|?|,| |>|<|;|\\\\)])")')
test.contains('assertThatJson(parsedJson).field("hostname").matches("((http[s]?|ftp):/)/?([^:/\\\\s]+)(:[0-9]{1,5})?")')
test.contains('assertThatJson(parsedJson).field("url").matches("^(?:(?:[A-Za-z][+-.\\\\w^_]*:/{2})?(?:\\\\S+(?::\\\\S*)?@)?(?:(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)|(?:(?:[a-z\\\\u00a1-\\\\uffff0-9]-*)*[a-z\\\\u00a1-\\\\uffff0-9]+)(?:\\\\.(?:[a-z\\\\u00a1-\\\\uffff0-9]-*)*[a-z\\\\u00a1-\\\\uffff0-9]+)*(?:\\\\.(?:[a-z\\\\u00a1-\\\\uffff]{2,})))(?::\\\\d{2,5})?(?:/\\\\S*)?)')
test.contains('assertThatJson(parsedJson).field("number").matches("-?\\\\d*(\\\\.\\\\d+)?")')
test.contains('assertThatJson(parsedJson).field("email").matches("[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\\\.[a-zA-Z]{2,4}")')
test.contains('assertThatJson(parsedJson).field("ip").matches("([01]?\\\\d\\\\d?|2[0-4]\\\\d|25[0-5])\\\\.([01]?\\\\d\\\\d?|2[0-4]\\\\d|25[0-5])\\\\.([01]?\\\\d\\\\d?|2[0-4]\\\\d|25[0-5])\\\\.([01]?\\\\d\\\\d?|2[0-4]\\\\d|25[0-5])")')
@@ -1954,8 +1954,19 @@ World.'''"""
test.contains('assertThatJson(parsedJson).field("nonEmptyString").matches(".+")')
test.contains('assertThatJson(parsedJson).field("anyOf").matches("^foo' + endOfLineRegExSymbol + '|^bar' + endOfLineRegExSymbol + '")')
!test.contains('cursor')
!test.contains('REGEXP>>')
and:
SyntaxChecker.tryToCompile(methodBuilderName, blockBuilder.toString())
and:
String jsonSample = '''\
String json = "{\\"duck\\":\\"8\\",\\"alpha\\":\\"YAJEOWYGMFBEWPMEMAZI\\",\\"number\\":-2095030871,\\"aBoolean\\":true,\\"ip\\":\\"129.168.99.100\\",\\"hostname\\":\\"http://foo389886219.com\\",\\"email\\":\\"foo@bar1367573183.com\\",\\"url\\":\\"http://foo-597104692.com\\",\\"uuid\\":\\"e436b817-b764-49a2-908e-967f2f99eb9f\\",\\"date\\":\\"2014-04-14\\",\\"dateTime\\":\\"2011-01-11T12:23:34\\",\\"time\\":\\"12:20:30\\",\\"iso8601WithOffset\\":\\"2015-05-15T12:23:34.123Z\\",\\"nonBlankString\\":\\"EPZWVIRHSUAPBJMMQSFO\\",\\"nonEmptyString\\":\\"RVMFDSEQFHRQFVUVQPIA\\",\\"anyOf\\":\\"foo\\"}";
DocumentContext parsedJson = JsonPath.parse(json);
'''
and:
LinkedList<String> lines = [] as LinkedList<String>
test.eachLine { if (it.contains("assertThatJson")) lines << it else it }
lines.addFirst(jsonSample)
SyntaxChecker.tryToRun(methodBuilderName, lines.join("\n"))
where:
methodBuilderName | methodBuilder | endOfLineRegExSymbol
"MockMvcSpockMethodBuilder" | { Contract dsl -> new MockMvcSpockMethodRequestProcessingBodyBuilder(dsl, properties) } | '\\$'

View File

@@ -763,6 +763,14 @@ class JsonToJsonPathsConverterSpec extends Specification {
'$.a.b.c[?(@.d =~ /(.*)/)]' == JsonToJsonPathsConverter.convertJsonPathAndRegexToAJsonPath(matcher(MatchingType.REGEX, jsonPath, regexPattern))
}
def "should convert a json path with regex to a regex checking json path that has a / in it"() {
given:
String jsonPath = '$.a.b.c.d'
String regexPattern = "/.*/"
expect:
'$.a.b.c[?(@.d =~ /(\\\\/.*\\\\/)/)]' == JsonToJsonPathsConverter.convertJsonPathAndRegexToAJsonPath(matcher(MatchingType.REGEX, jsonPath, regexPattern))
}
def "should convert a json path with value to a equality checking json path without quotes for numbers"() {
given:
String jsonPath = '$.a.b.c.d'

View File

@@ -5,6 +5,9 @@ import org.codehaus.groovy.control.CompilerConfiguration
import org.codehaus.groovy.control.customizers.ASTTransformationCustomizer
import org.codehaus.groovy.control.customizers.ImportCustomizer
import org.mdkt.compiler.InMemoryJavaCompiler
import org.springframework.util.ReflectionUtils
import java.lang.reflect.Method
/**
* checking the syntax of produced scripts
@@ -51,6 +54,17 @@ class SyntaxChecker {
}
}
static void tryToRun(String builderName, String test) {
if (builderName.toLowerCase().contains("spock")) {
Script script = tryToCompileGroovy(test)
script.run()
} else {
Class clazz = tryToCompileJava(test)
Method method = ReflectionUtils.findMethod(clazz, "method")
method.invoke(clazz.newInstance())
}
}
// no static compilation due to bug in Groovy https://issues.apache.org/jira/browse/GROOVY-8055
static void tryToCompileWithoutCompileStatic(String builderName, String test) {
if (builderName.toLowerCase().contains("spock")) {
@@ -60,7 +74,7 @@ class SyntaxChecker {
}
}
static void tryToCompileGroovy(String test, boolean compileStatic = true) {
static Script tryToCompileGroovy(String test, boolean compileStatic = true) {
def imports = new ImportCustomizer()
CompilerConfiguration configuration = new CompilerConfiguration()
if (compileStatic) {
@@ -75,7 +89,7 @@ class SyntaxChecker {
sourceCode.append("WebTarget webTarget")
sourceCode.append("\n")
sourceCode.append(test)
new GroovyShell(SyntaxChecker.classLoader, configuration).parse(sourceCode.toString())
return new GroovyShell(SyntaxChecker.classLoader, configuration).parse(sourceCode.toString())
}
static Class tryToCompileJava(String test) {
@@ -89,11 +103,11 @@ class SyntaxChecker {
sourceCode.append("${DEFAULT_IMPORTS_AS_STRING}\n")
sourceCode.append("${STATIC_IMPORTS}\n")
sourceCode.append("\n")
sourceCode.append("class ${className} {\n")
sourceCode.append("public class ${className} {\n")
sourceCode.append("\n")
sourceCode.append(" WebTarget webTarget;")
sourceCode.append("\n")
sourceCode.append(" void method() {\n")
sourceCode.append(" public void method() {\n")
sourceCode.append(" ${test}\n")
sourceCode.append(" }\n")
sourceCode.append("}")