[#42] Modified the optional functionality

This commit is contained in:
Marcin Grzejszczak
2015-10-05 22:38:12 +02:00
parent bb73cdd4b0
commit b0bb4cb75f
11 changed files with 190 additions and 214 deletions

View File

@@ -16,20 +16,16 @@ class Common {
@Delegate private final RegexPatterns regexPatterns = new RegexPatterns()
Map<String, DslProperty> convertObjectsToDslProperties(Map<String, Object> body) {
return (body.collectEntries {
return body.collectEntries {
Map.Entry<String, Object> entry ->
[(entry.key): toDslProperty(entry.value)]
} as Map<String, DslProperty>).findAll {
!(it.value.clientValue instanceof Optional || it.value.serverValue instanceof Optional)
}
} as Map<String, DslProperty>
}
Collection convertObjectsToDslProperties(List body) {
return (body.collect {
Object element -> toDslProperty(element)
} as List).findAll {
!(it instanceof Optional)
}
} as List)
}
DslProperty toDslProperty(Object property) {
@@ -78,6 +74,10 @@ class Common {
return Pattern.compile(regex)
}
OptionalProperty optional(Object object) {
return new OptionalProperty(object)
}
ExecutionProperty execute(String commandToExecute) {
return new ExecutionProperty(commandToExecute)
}
@@ -98,8 +98,8 @@ class Common {
return new ServerDslProperty(serverValue)
}
Optional optional() {
return new Optional()
void assertThatSidesMatch(OptionalProperty stubSide, Object testSide) {
assert testSide ==~ Pattern.compile(stubSide.optionalPattern())
}
void assertThatSidesMatch(Pattern pattern, String value) {

View File

@@ -1,7 +0,0 @@
package io.codearte.accurest.dsl.internal
/**
* Marker class to show that an element of message is optional
*/
class Optional {
}

View File

@@ -0,0 +1,13 @@
package io.codearte.accurest.dsl.internal
class OptionalProperty {
final Object value
OptionalProperty(Object value) {
this.value = value
}
String optionalPattern() {
return "($value)?"
}
}

View File

@@ -3,7 +3,6 @@ import groovy.transform.CompileStatic
import groovy.transform.EqualsAndHashCode
import groovy.transform.ToString
import groovy.transform.TypeChecked
import groovy.xml.MarkupBuilder
@TypeChecked
@EqualsAndHashCode
@@ -129,6 +128,10 @@ class Request extends Common {
return new MatchingStrategy(true, MatchingStrategy.Type.ABSENT)
}
void assertThatSidesMatch(Object stubSide, OptionalProperty testSide) {
throw new IllegalStateException("Optional can be used only for the stub side of the request!")
}
}
@CompileStatic

View File

@@ -49,6 +49,9 @@ class Response extends Common {
this.body = new Body(bodyAsValue)
}
void assertThatSidesMatch(OptionalProperty stubSide, Object testSide) {
throw new IllegalStateException("Optional can be used only in the test side of the response!")
}
}
@CompileStatic

View File

@@ -8,7 +8,7 @@ import io.codearte.accurest.dsl.internal.DslProperty
import io.codearte.accurest.dsl.internal.ExecutionProperty
import io.codearte.accurest.dsl.internal.Headers
import io.codearte.accurest.dsl.internal.MatchingStrategy
import io.codearte.accurest.dsl.internal.Optional
import io.codearte.accurest.dsl.internal.OptionalProperty
import org.codehaus.groovy.runtime.GStringImpl
import java.util.regex.Matcher
@@ -27,9 +27,9 @@ class ContentUtils {
private static final Pattern TEMPORARY_PATTERN_HOLDER = Pattern.compile('.*REGEXP>>(.*)<<.*')
private static final Pattern TEMPORARY_EXECUTION_PATTERN_HOLDER = Pattern.compile('EXECUTION>>(.*)<<')
private static final Pattern TEMPORARY_OPTIONAL_PATTERN_HOLDER = Pattern.compile('OPTIONAL>>(.*)<<')
private static final String JSON_VALUE_PATTERN_FOR_REGEX = 'REGEXP>>%s<<'
private static final String JSON_VALUE_OPTIONAL = 'OPTIONAL>><<'
private static final Pattern OPTIONAL_PATTERN_HOLDER = Pattern.compile(JSON_VALUE_OPTIONAL)
private static final String JSON_VALUE_PATTERN_FOR_OPTIONAL = 'OPTIONAL>>%s<<'
private static final String JSON_VALUE_PATTERN_FOR_EXECUTION = '"EXECUTION>>%s<<"'
/**
@@ -160,8 +160,8 @@ class ContentUtils {
return String.format(JSON_VALUE_PATTERN_FOR_REGEX, pattern.pattern())
}
private static String transformJSONStringValue(Optional optional, Closure valueProvider) {
return JSON_VALUE_OPTIONAL
private static String transformJSONStringValue(OptionalProperty optional, Closure valueProvider) {
return String.format(JSON_VALUE_PATTERN_FOR_OPTIONAL, optional.value)
}
private static String transformJSONStringValue(ExecutionProperty property, Closure valueProvider) {
@@ -213,23 +213,25 @@ class ContentUtils {
static Object returnParsedObject(String string) {
Matcher matcher = TEMPORARY_PATTERN_HOLDER.matcher(string.trim())
if (matcher.matches()) {
List val = matcher[0] as List
String pattern = val[1]
return Pattern.compile(pattern)
return Pattern.compile(patternFromMatchingGroup(matcher))
}
Matcher executionMatcher = TEMPORARY_EXECUTION_PATTERN_HOLDER.matcher(string.trim())
if (executionMatcher.matches()) {
List val = executionMatcher[0] as List
String pattern = val[1]
return new ExecutionProperty(pattern)
return new ExecutionProperty(patternFromMatchingGroup(executionMatcher))
}
Matcher optionalMatcher = OPTIONAL_PATTERN_HOLDER.matcher(string.trim())
Matcher optionalMatcher = TEMPORARY_OPTIONAL_PATTERN_HOLDER.matcher(string.trim())
if (optionalMatcher.matches()) {
return new Optional()
String patternToMatch = patternFromMatchingGroup(optionalMatcher)
return Pattern.compile(new OptionalProperty(patternToMatch).optionalPattern())
}
return string
}
private static String patternFromMatchingGroup(Matcher matcher) {
List val = matcher[0] as List
return val[1]
}
public static ContentType recognizeContentTypeFromHeader(Headers headers) {
String content = headers?.entries.find { it.name == "Content-Type" } ?.clientValue?.toString()
if (content?.endsWith("json")) {

View File

@@ -1,9 +1,9 @@
package io.codearte.accurest.util
import java.util.regex.Pattern
import groovy.json.JsonSlurper
import io.codearte.accurest.dsl.internal.ExecutionProperty
import io.codearte.accurest.dsl.internal.OptionalProperty
import java.util.regex.Pattern
/**
* @author Marcin Grzejszczak
*/
@@ -30,7 +30,7 @@ class JsonToJsonPathsConverter {
JsonPaths pathsAndValues = [] as Set
Object convertedJson = MapConverter.getClientOrServerSideValues(json, clientSide)
traverseRecursivelyForKey(convertedJson, ROOT_JSON_PATH_ELEMENT) { String key, Object value ->
if (value instanceof ExecutionProperty || value instanceof io.codearte.accurest.dsl.internal.Optional) {
if (value instanceof ExecutionProperty) {
return
}
JsonPathEntry entry = getValueToInsert(key, value)
@@ -128,13 +128,19 @@ class JsonToJsonPathsConverter {
protected static String compareWith(Object value) {
if (value instanceof Pattern) {
return """=~ /${(value as Pattern).pattern()}/"""
return patternComparison((value as Pattern).pattern())
} else if (value instanceof OptionalProperty) {
return patternComparison((value as OptionalProperty).optionalPattern())
} else if (value instanceof GString) {
return """=~ /${RegexpBuilders.buildGStringRegexpForTestSide(value)}/"""
}
return """== ${potentiallyWrappedWithQuotesValue(value)}"""
}
protected static String patternComparison(String pattern){
return """=~ /$pattern/"""
}
protected static String potentiallyWrappedWithQuotesValue(Object value) {
return value instanceof Number ? value : "'$value'"
}

View File

@@ -1,9 +1,6 @@
package io.codearte.accurest.util
import groovy.json.JsonSlurper
import io.codearte.accurest.dsl.internal.DslProperty
import io.codearte.accurest.dsl.internal.Optional
/**
* @author Marcin Grzejszczak
*/
@@ -41,8 +38,6 @@ class MapConverter {
return map.collectEntries {
key, value ->
[key, transformValues(value, closure)]
}.findAll {
!(it.value instanceof Optional)
}
}

View File

@@ -5,6 +5,7 @@ import io.codearte.accurest.dsl.WireMockStubStrategy
import io.codearte.accurest.dsl.WireMockStubVerifier
import spock.lang.Issue
import spock.lang.Specification
import spock.lang.Unroll
/**
* @author Jakub Kubrynski
@@ -553,89 +554,77 @@ class MockMvcSpockMethodBuilderSpec extends Specification implements WireMockStu
}
@Issue('42')
def "should omit an optional field from body resolution"() {
@Unroll
def "should not omit the optional field in the test creation"() {
given:
GroovyDsl contractDsl = GroovyDsl.make {
priority 1
request {
method 'POST'
url '/users/password'
headers {
header 'Content-Type': 'application/json'
}
body(
email: optional(),
callback_url: $(client(regex(hostname())), server('http://partners.com'))
)
}
response {
status 404
headers {
header 'Content-Type': 'application/json'
}
body(
code: optional(),
message: "User not found by email = [${value(server(regex(email())), client('not.existing@user.com'))}]"
)
}
}
MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl)
BlockBuilder blockBuilder = new BlockBuilder(" ")
when:
builder.appendTo(blockBuilder)
def spockTest = blockBuilder.toString()
then:
!spockTest.contains('''body('{"email":''')
!spockTest.contains('''parsedJson.read(\'\'\'$[?(@.email''')
spockTest.contains('''"email":"abc@abc.com"''')
spockTest.contains('''parsedJson.read(\'\'\'$[?(@.code =~ /(123123)?/)]''')
!spockTest.contains('''REGEXP''')
!spockTest.contains('''OPTIONAL''')
!spockTest.contains('''Optional''')
}
@Issue('42')
def "should omit an optional field from body resolution with GString"() {
given:
GroovyDsl contractDsl = GroovyDsl.make {
priority 1
request {
method 'POST'
url '/users/password'
headers {
header 'Content-Type': 'application/json'
!spockTest.contains('''OptionalProperty''')
where:
contractDsl << [
GroovyDsl.make {
priority 1
request {
method 'POST'
url '/users/password'
headers {
header 'Content-Type': 'application/json'
}
body(
email: $(stub(optional(regex(email()))), test('abc@abc.com')),
callback_url: $(stub(regex(hostname())), test('http://partners.com'))
)
}
body(
""" {
"email" : "${value(optional())}",
response {
status 404
headers {
header 'Content-Type': 'application/json'
}
body(
code: value(stub("123123"), test(optional("123123"))),
message: "User not found by email = [${value(test(regex(email())), stub('not.existing@user.com'))}]"
)
}
},
GroovyDsl.make {
priority 1
request {
method 'POST'
url '/users/password'
headers {
header 'Content-Type': 'application/json'
}
body(
""" {
"email" : "${value(stub(optional(regex(email()))), test('abc@abc.com'))}",
"callback_url" : "${value(client(regex(hostname())), server('http://partners.com'))}"
}
"""
)
}
response {
status 404
headers {
header 'Content-Type': 'application/json'
)
}
body(
""" {
"code" : "${value(optional())}",
response {
status 404
headers {
header 'Content-Type': 'application/json'
}
body(
""" {
"code" : "${value(stub(123123), test(optional(123123)))}",
"message" : "User not found by email = [${value(server(regex(email())), client('not.existing@user.com'))}]"
}
"""
)
)
}
}
}
MockMvcSpockMethodBodyBuilder builder = new MockMvcSpockMethodBodyBuilder(contractDsl)
BlockBuilder blockBuilder = new BlockBuilder(" ")
when:
builder.appendTo(blockBuilder)
def spockTest = blockBuilder.toString()
then:
!spockTest.contains('''body('{"email":''')
!spockTest.contains('''parsedJson.read(\'\'\'$[?(@.code''')
!spockTest.contains('''REGEXP''')
!spockTest.contains('''OPTIONAL''')
!spockTest.contains('''Optional''')
]
}
@Issue('72')

View File

@@ -5,6 +5,7 @@ import groovy.json.JsonSlurper
import io.codearte.accurest.util.AssertionUtil
import spock.lang.Issue
import spock.lang.Specification
import spock.lang.Unroll
class WireMockGroovyDslSpec extends Specification implements WireMockStubVerifier {
@@ -1294,125 +1295,96 @@ class WireMockGroovyDslSpec extends Specification implements WireMockStubVerifie
}
@Issue('42')
def 'should generate stub without optional parameters with GString'() {
given:
GroovyDsl groovyDsl = GroovyDsl.make {
priority 1
request {
method 'POST'
url '/users/password'
headers {
header 'Content-Type': 'application/json'
@Unroll
def 'should generate stub without optional parameters'() {
when:
String wireMockStub = new WireMockStubStrategy(contractDsl).toWireMockClientStub()
then:
AssertionUtil.assertThatJsonsAreEqual(('''
{
"request" : {
"url" : "/users/password",
"method" : "POST",
"bodyPatterns" : [ {
"matchesJsonPath" : "$[?(@.callback_url =~ /((http[s]?|ftp):\\\\/)\\\\/?([^:\\\\/\\\\s]+)(:[0-9]{1,5})?/)]"
}, {
"matchesJsonPath" : "$[?(@.email =~ /([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\\\.[a-zA-Z]{2,4})?/)]"
} ],
"headers" : {
"Content-Type" : {
"equalTo" : "application/json"
}
}
},
"response" : {
"status" : 404,
"body" : "{\\"code\\":\\"123123\\",\\"message\\":\\"User not found by email = [not.existing@user.com]\\"}",
"headers" : {
"Content-Type" : "application/json"
}
},
"priority" : 1
}
'''), wireMockStub)
and:
stubMappingIsValidWireMockStub(wireMockStub)
where:
contractDsl << [
GroovyDsl.make {
priority 1
request {
method 'POST'
url '/users/password'
headers {
header 'Content-Type': 'application/json'
}
body(
email: $(stub(optional(regex(email()))), test('abc@abc.com')),
callback_url: $(stub(regex(hostname())), test('http://partners.com'))
)
}
body(
""" {
"email" : "${value(optional())}",
response {
status 404
headers {
header 'Content-Type': 'application/json'
}
body(
code: $(stub("123123"), test(optional("123123"))),
message: "User not found by email = [${value(test(regex(email())), stub('not.existing@user.com'))}]"
)
}
},
GroovyDsl.make {
priority 1
request {
method 'POST'
url '/users/password'
headers {
header 'Content-Type': 'application/json'
}
body(
""" {
"email" : "${value(stub(optional(regex(email()))), test('abc@abc.com'))}",
"callback_url" : "${value(client(regex(hostname())), server('http://partners.com'))}"
}
"""
)
}
response {
status 404
headers {
header 'Content-Type': 'application/json'
)
}
body(
""" {
"code" : "${value(optional())}",
response {
status 404
headers {
header 'Content-Type': 'application/json'
}
body(
""" {
"code" : "${value(stub(123123), test(optional(123123)))}",
"message" : "User not found by email = [${value(server(regex(email())), client('not.existing@user.com'))}]"
}
"""
)
}
}
when:
String wireMockStub = new WireMockStubStrategy(groovyDsl).toWireMockClientStub()
then:
AssertionUtil.assertThatJsonsAreEqual(('''
{
"request" : {
"url" : "/users/password",
"method" : "POST",
"bodyPatterns" : [ {
"matchesJsonPath" : "$[?(@.callback_url =~ /((http[s]?|ftp):\\\\/)\\\\/?([^:\\\\/\\\\s]+)(:[0-9]{1,5})?/)]"
} ],
"headers" : {
"Content-Type" : {
"equalTo" : "application/json"
}
}
},
"response" : {
"status" : 404,
"body" : "{\\"message\\":\\"User not found by email = [not.existing@user.com]\\"}",
"headers" : {
"Content-Type" : "application/json"
}
},
"priority" : 1
}
'''), wireMockStub)
and:
stubMappingIsValidWireMockStub(wireMockStub)
}
@Issue('42')
def 'should generate stub without optional parameters'() {
given:
GroovyDsl groovyDsl = GroovyDsl.make {
priority 1
request {
method 'POST'
url '/users/password'
headers {
header 'Content-Type': 'application/json'
)
}
body(
email: optional(),
callback_url: $(client(regex(hostname())), server('http://partners.com'))
)
}
response {
status 404
headers {
header 'Content-Type': 'application/json'
}
body(
code: optional(),
message: "User not found by email = [${value(server(regex(email())), client('not.existing@user.com'))}]"
)
}
}
when:
String wireMockStub = new WireMockStubStrategy(groovyDsl).toWireMockClientStub()
then:
AssertionUtil.assertThatJsonsAreEqual(('''
{
"request" : {
"url" : "/users/password",
"method" : "POST",
"bodyPatterns" : [ {
"matchesJsonPath" : "$[?(@.callback_url =~ /((http[s]?|ftp):\\\\/)\\\\/?([^:\\\\/\\\\s]+)(:[0-9]{1,5})?/)]"
} ],
"headers" : {
"Content-Type" : {
"equalTo" : "application/json"
}
}
},
"response" : {
"status" : 404,
"body" : "{\\"message\\":\\"User not found by email = [not.existing@user.com]\\"}",
"headers" : {
"Content-Type" : "application/json"
}
},
"priority" : 1
}
'''), wireMockStub)
and:
stubMappingIsValidWireMockStub(wireMockStub)
]
}
String toJsonString(value) {

View File

@@ -10,7 +10,7 @@ trait WireMockStubVerifier {
stubMapping.request.bodyPatterns.findAll { it.matches }.every {
Pattern.compile(it.matches)
}
assert !mappingDefinition.contains('DslProperty')
assert !mappingDefinition.contains('io.codearte.accurest.dsl.internal')
}
}