Merge branch '1.0.x'
This commit is contained in:
@@ -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() {
|
||||
|
||||
@@ -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 + ")\$")
|
||||
}
|
||||
|
||||
@@ -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]"() {
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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})/"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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) } | '\\$'
|
||||
|
||||
@@ -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) } | '\\$'
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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("}")
|
||||
|
||||
Reference in New Issue
Block a user