Fixed the invalid regex escape

fixes #162
This commit is contained in:
Marcin Grzejszczak
2016-12-07 16:29:11 +01:00
parent 545874c951
commit c067774eee
9 changed files with 84 additions and 6 deletions

View File

@@ -1311,7 +1311,7 @@ Example of a `pom.xml` inside the `server` folder.
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>1.8</java.version>
<spring-cloud-contract.version>1.0.3.BUILD-SNAPSHOT</spring-cloud-contract.version>
<spring-cloud-contract.version>1.1.0.BUILD-SNAPSHOT</spring-cloud-contract.version>
<spring-cloud-dependencies.version>Camden.BUILD-SNAPSHOT</spring-cloud-dependencies.version>
</properties>

View File

@@ -20,6 +20,8 @@ import groovy.transform.EqualsAndHashCode
import groovy.transform.ToString
import groovy.transform.TypeChecked
import java.util.regex.Pattern
/**
* Represents a set of headers of a request / response or a message
*
@@ -68,6 +70,10 @@ class Headers {
return new DslProperty(value)
}
protected NotToEscapePattern notEscaped(Pattern pattern) {
return new NotToEscapePattern(pattern)
}
/**
* Converts the headers into their stub side representations and returns as
* a map of String key => Object value.

View File

@@ -0,0 +1,21 @@
package org.springframework.cloud.contract.spec.internal
import java.util.regex.Pattern
/**
* Special case of Patterns that we don't want to escape
*
* @author Marcin Grzejszczak
* @since 1.0.3
*/
class NotToEscapePattern extends DslProperty<Pattern> {
NotToEscapePattern(Pattern clientValue, Pattern serverValue) {
super(clientValue, serverValue)
}
NotToEscapePattern(Pattern singleValue) {
super(singleValue)
}
}

View File

@@ -144,7 +144,7 @@ class Response extends Common {
@Override
DslProperty matching(String value) {
return $(p(Pattern.compile("${RegexpUtils.escapeSpecialRegexWithSingleEscape(value)}.*")),
return $(p(notEscaped(Pattern.compile("${RegexpUtils.escapeSpecialRegexWithSingleEscape(value)}.*"))),
c(value))
}
}

View File

@@ -169,6 +169,10 @@ abstract class JUnitMethodBodyBuilder extends RequestProcessingMethodBodyBuilder
private String buildEscapedMatchesMethod(Pattern headerValue) {
String escapedHeader = convertUnicodeEscapesIfRequired("$headerValue")
return createMatchesMethod(escapedHeader)
}
protected String createMatchesMethod(String escapedHeader) {
return "matches(\"$escapedHeader\")"
}

View File

@@ -21,6 +21,7 @@ import groovy.transform.TypeChecked
import org.springframework.cloud.contract.spec.Contract
import org.springframework.cloud.contract.spec.internal.Header
import org.springframework.cloud.contract.spec.internal.ExecutionProperty
import org.springframework.cloud.contract.spec.internal.NotToEscapePattern
import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties
import java.util.regex.Pattern
@@ -62,6 +63,13 @@ class MockMvcJUnitMethodBodyBuilder extends JUnitMethodBodyBuilder {
return null
}
@Override
protected void processHeaderElement(BlockBuilder blockBuilder, String property, Object value) {
if (value instanceof NotToEscapePattern) {
blockBuilder.addLine("assertThat(response.header(\"$property\")).${createMatchesMethod(value.serverValue.pattern())};")
}
}
@Override
protected void processHeaderElement(BlockBuilder blockBuilder, String property, String value) {
blockBuilder.addLine("assertThat(response.header(\"$property\")).${createHeaderComparison(value)}")

View File

@@ -21,6 +21,7 @@ import groovy.transform.TypeChecked
import org.springframework.cloud.contract.spec.Contract
import org.springframework.cloud.contract.spec.internal.ExecutionProperty
import org.springframework.cloud.contract.spec.internal.Header
import org.springframework.cloud.contract.spec.internal.NotToEscapePattern
import org.springframework.cloud.contract.verifier.config.ContractVerifierConfigProperties
import java.util.regex.Pattern
@@ -55,6 +56,12 @@ class MockMvcSpockMethodRequestProcessingBodyBuilder extends SpockMethodRequestP
return 'response.body.asString()'
}
@Override
protected void processHeaderElement(BlockBuilder blockBuilder, String property, Object value) {
if (value instanceof NotToEscapePattern) {
blockBuilder.addLine("response.header('$property') ${patternComparison(value.serverValue)}")
}
}
@Override
protected void processHeaderElement(BlockBuilder blockBuilder, String property, ExecutionProperty exec) {
blockBuilder.addLine("${exec.insertValue("response.header(\'$property\')")}")

View File

@@ -147,7 +147,7 @@ abstract class SpockMethodRequestProcessingBodyBuilder extends RequestProcessing
return patternComparison(headerValue)
}
private String patternComparison(Pattern pattern) {
protected String patternComparison(Pattern pattern) {
return "==~ java.util.regex.Pattern.compile('$pattern')"
}

View File

@@ -22,6 +22,7 @@ import org.springframework.cloud.contract.verifier.dsl.WireMockStubVerifier
import spock.lang.Issue
import spock.lang.Shared
import spock.lang.Specification
import spock.lang.Unroll
import spock.util.environment.RestoreSystemProperties
import java.util.regex.Pattern
@@ -1537,7 +1538,7 @@ World.'''"""
responseElement: $(producer(regex('[0-9]{7}')))
])
headers {
contentType("application/vnd.fraud.v1.json")
contentType("application/vnd.fraud.v1+json")
}
}
}
@@ -1557,8 +1558,8 @@ World.'''"""
strippedTest.matches(""".*header\\("header", "application\\/vnd\\.fraud\\.v1\\+json;.*"\\).*""")
strippedTest.matches(""".*body\\('''\\{"requestElement":"[0-9]{5}"\\}'''\\).*""")
strippedTest.matches(""".*put\\("/foo/[0-9]{5}"\\).*""")
strippedTest.contains("""response.header('Content-Type') ==~ java.util.regex.Pattern.compile('application/vnd\\.fraud\\.v1\\.json.*')""")
"application/vnd.fraud.v1+json;charset=UTF-8".matches('application/vnd.fraud.v1.json.*')
strippedTest.contains("""response.header('Content-Type') ==~ java.util.regex.Pattern.compile('application/vnd\\.fraud\\.v1\\+json.*')""")
"application/vnd.fraud.v1+json;charset=UTF-8".matches('application/vnd\\.fraud\\.v1\\+json.*')
strippedTest.contains("""assertThatJson(parsedJson).field("responseElement").matches("[0-9]{7}")""")
}
@@ -1749,4 +1750,35 @@ World.'''"""
test.contains('assertThatJson(parsedJson).field("uuid").matches("[a-z0-9]{8}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{4}-[a-z0-9]{12}")')
!test.contains('cursor')
}
@Issue('#162')
@Unroll
def "should escape regex properly for content type"() {
given:
Contract contractDsl = Contract.make {
request {
method GET()
url 'get'
headers {
contentType("application/vnd.fraud.v1+json")
}
}
response {
status 200
headers {
contentType("application/vnd.fraud.v1+json")
}
}
}
MethodBodyBuilder builder = methodBuilder(contractDsl)
BlockBuilder blockBuilder = new BlockBuilder(" ")
when:
builder.appendTo(blockBuilder)
def test = blockBuilder.toString()
then:
test.contains('application/vnd\\.fraud\\.v1\\+json.*')
where:
methodBuilder << [{ Contract dsl -> new MockMvcSpockMethodRequestProcessingBodyBuilder(dsl, properties)},
{ Contract dsl -> new MockMvcJUnitMethodBodyBuilder(dsl, properties)}]
}
}