Merge branch '1.0.x'

This commit is contained in:
Marcin Grzejszczak
2017-04-14 09:59:31 +02:00
11 changed files with 346 additions and 37 deletions

View File

@@ -48,7 +48,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

@@ -57,6 +57,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,14 +16,19 @@
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.spec.Contract
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
@@ -31,8 +36,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:
@@ -57,7 +69,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)
}
def "should convert DSL file with list of contracts to WireMock JSONs"() {
@@ -143,7 +159,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"() {
@@ -158,7 +178,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 '''
[{
@@ -249,10 +268,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:
@@ -287,7 +339,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)
}
@@ -319,7 +377,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'() {
@@ -386,7 +449,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'() {
@@ -397,7 +470,7 @@ class DslToWireMockClientConverterSpec extends Specification {
file.write('''
org.springframework.cloud.contract.spec.Contract.make {
request {
method 'GET'
method 'POST'
urlPath '/get'
body([
duck: 123,
@@ -505,7 +578,7 @@ class DslToWireMockClientConverterSpec extends Specification {
{
"request" : {
"urlPath" : "/get",
"method" : "GET",
"method" : "POST",
"headers" : {
"Content-Type" : {
"matches" : "application/json.*"
@@ -555,7 +628,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'() {
@@ -568,7 +694,7 @@ class DslToWireMockClientConverterSpec extends Specification {
priority 1
request {
method 'POST'
url '/users/password'
url '/users/password2'
headers {
header 'Content-Type': 'application/json'
}
@@ -604,7 +730,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})/)]"
@@ -629,15 +755,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

@@ -136,7 +136,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

@@ -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

@@ -1879,7 +1879,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 {
@@ -1939,8 +1939,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])")')
@@ -1953,8 +1953,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("}")